1 | /* |
2 | * Copyright 2011,2015 Sven Verdoolaege. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * |
11 | * 2. Redistributions in binary form must reproduce the above |
12 | * copyright notice, this list of conditions and the following |
13 | * disclaimer in the documentation and/or other materials provided |
14 | * with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY SVEN VERDOOLAEGE ''AS IS'' AND ANY |
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR |
20 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
23 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | * |
28 | * The views and conclusions contained in the software and documentation |
29 | * are those of the authors and should not be interpreted as |
30 | * representing official policies, either expressed or implied, of |
31 | * Sven Verdoolaege. |
32 | */ |
33 | |
34 | #include "isl_config.h" |
35 | |
36 | #include <stdarg.h> |
37 | #include <stdio.h> |
38 | |
39 | #include <algorithm> |
40 | #include <iostream> |
41 | #include <map> |
42 | #include <vector> |
43 | |
44 | #include "python.h" |
45 | #include "generator.h" |
46 | |
47 | /* Argument format for Python methods with a fixed number of arguments. |
48 | */ |
49 | static const char *fixed_arg_fmt = "arg%d" ; |
50 | /* Argument format for Python methods with a variable number of arguments. |
51 | */ |
52 | static const char *var_arg_fmt = "args[%d]" ; |
53 | |
54 | /* Drop the "isl_" initial part of the type name "name". |
55 | */ |
56 | static string type2python(string name) |
57 | { |
58 | return name.substr(pos: 4); |
59 | } |
60 | |
61 | /* Print the arguments of a method with "n_arg" arguments, starting at "first". |
62 | */ |
63 | void python_generator::print_method_arguments(int first, int n_arg) |
64 | { |
65 | for (int i = first; i < n_arg; ++i) { |
66 | if (i > first) |
67 | printf(format: ", " ); |
68 | printf(format: "arg%d" , i); |
69 | } |
70 | } |
71 | |
72 | /* Print the start of a definition for method "name" |
73 | * (without specifying the arguments). |
74 | * If "is_static" is set, then mark the python method as static. |
75 | * |
76 | * If the method is called "from", then rename it to "convert_from" |
77 | * because "from" is a python keyword. |
78 | */ |
79 | static void print_method_def(bool is_static, const string &name) |
80 | { |
81 | const char *s; |
82 | |
83 | if (is_static) |
84 | printf(format: " @staticmethod\n" ); |
85 | |
86 | s = name.c_str(); |
87 | if (name == "from" ) |
88 | s = "convert_from" ; |
89 | |
90 | printf(format: " def %s" , s); |
91 | } |
92 | |
93 | /* Print the header of the method "name" with "n_arg" arguments. |
94 | * If "is_static" is set, then mark the python method as static. |
95 | */ |
96 | void python_generator::(bool is_static, const string &name, |
97 | int n_arg) |
98 | { |
99 | print_method_def(is_static, name); |
100 | printf(format: "(" ); |
101 | print_method_arguments(first: 0, n_arg); |
102 | printf(format: "):\n" ); |
103 | } |
104 | |
105 | /* Print formatted output with the given indentation. |
106 | */ |
107 | static void print_indent(int indent, const char *format, ...) |
108 | { |
109 | va_list args; |
110 | |
111 | printf(format: "%*s" , indent, " " ); |
112 | va_start(args, format); |
113 | vprintf(format: format, arg: args); |
114 | va_end(args); |
115 | } |
116 | |
117 | /* Print a check that the argument in position "pos" is of type "type" |
118 | * with the given indentation. |
119 | * If this fails and if "upcast" is set, then convert the first |
120 | * argument to "super" and call the method "name" on it, passing |
121 | * the remaining of the "n" arguments. |
122 | * If the check fails and "upcast" is not set, then simply raise |
123 | * an exception. |
124 | * If "upcast" is not set, then the "super", "name" and "n" arguments |
125 | * to this function are ignored. |
126 | * "fmt" is the format for printing Python method arguments. |
127 | */ |
128 | void python_generator::print_type_check(int indent, const string &type, |
129 | const char *fmt, int pos, bool upcast, const string &super, |
130 | const string &name, int n) |
131 | { |
132 | print_indent(indent, format: "try:\n" ); |
133 | print_indent(indent, format: " if not " ); |
134 | printf(format: fmt, pos); |
135 | printf(format: ".__class__ is %s:\n" , type.c_str()); |
136 | print_indent(indent, format: " " ); |
137 | printf(format: fmt, pos); |
138 | printf(format: " = %s(" , type.c_str()); |
139 | printf(format: fmt, pos); |
140 | printf(format: ")\n" ); |
141 | print_indent(indent, format: "except:\n" ); |
142 | if (upcast) { |
143 | print_indent(indent, format: " return %s(" , |
144 | type2python(name: super).c_str()); |
145 | printf(format: fmt, 0); |
146 | printf(format: ").%s(" , name.c_str()); |
147 | for (int i = 1; i < n; ++i) { |
148 | if (i != 1) |
149 | printf(format: ", " ); |
150 | printf(format: fmt, i); |
151 | } |
152 | printf(format: ")\n" ); |
153 | } else |
154 | print_indent(indent, format: " raise\n" ); |
155 | } |
156 | |
157 | /* For each of the "n" initial arguments of the function "method" |
158 | * that refer to an isl structure, |
159 | * including the object on which the method is called, |
160 | * check if the corresponding actual argument is of the right type. |
161 | * If not, try and convert it to the right type. |
162 | * If that doesn't work and if "super" contains at least one element, |
163 | * try and convert self to the type of the first superclass in "super" and |
164 | * call the corresponding method. |
165 | * If "first_is_ctx" is set, then the first argument is skipped. |
166 | */ |
167 | void python_generator::print_type_checks(const string &cname, |
168 | FunctionDecl *method, bool first_is_ctx, int n, |
169 | const vector<string> &super) |
170 | { |
171 | for (int i = first_is_ctx; i < n; ++i) { |
172 | ParmVarDecl *param = method->getParamDecl(i); |
173 | string type; |
174 | |
175 | if (!is_isl_type(param->getOriginalType())) |
176 | continue; |
177 | type = type2python(extract_type(param->getOriginalType())); |
178 | if (!first_is_ctx && i > 0 && super.size() > 0) |
179 | print_type_check(indent: 8, type, fmt: fixed_arg_fmt, |
180 | pos: i - first_is_ctx, upcast: true, |
181 | super: super[0], name: cname, n); |
182 | else |
183 | print_type_check(indent: 8, type, fmt: fixed_arg_fmt, |
184 | pos: i - first_is_ctx, upcast: false, super: "" , name: cname, n: -1); |
185 | } |
186 | } |
187 | |
188 | /* Print a call to the *_copy function corresponding to "type". |
189 | */ |
190 | void python_generator::print_copy(QualType type) |
191 | { |
192 | string type_s = extract_type(type); |
193 | |
194 | printf(format: "isl.%s_copy" , type_s.c_str()); |
195 | } |
196 | |
197 | /* Construct a wrapper for callback argument "param" (at position "arg"). |
198 | * Assign the wrapper to "cb{arg}". |
199 | * |
200 | * The wrapper converts the arguments of the callback to python types, |
201 | * taking a copy if the C callback does not take its arguments. |
202 | * If any exception is thrown, the wrapper keeps track of it in exc_info[0] |
203 | * and returns a value indicating an error. Otherwise the wrapper |
204 | * returns a value indicating success. |
205 | * In case the C callback is expected to return an isl_stat, |
206 | * the error value is -1 and the success value is 0. |
207 | * In case the C callback is expected to return an isl_bool, |
208 | * the error value is -1 and the success value is 1 or 0 depending |
209 | * on the result of the Python callback. |
210 | * Otherwise, None is returned to indicate an error and |
211 | * a copy of the object in case of success. |
212 | */ |
213 | void python_generator::print_callback(ParmVarDecl *param, int arg) |
214 | { |
215 | QualType type = param->getOriginalType(); |
216 | const FunctionProtoType *fn = extract_prototype(type); |
217 | QualType return_type = fn->getReturnType(); |
218 | unsigned n_arg = fn->getNumArgs(); |
219 | |
220 | printf(format: " exc_info = [None]\n" ); |
221 | printf(format: " fn = CFUNCTYPE(" ); |
222 | if (is_isl_stat(return_type) || is_isl_bool(return_type)) |
223 | printf(format: "c_int" ); |
224 | else |
225 | printf(format: "c_void_p" ); |
226 | for (unsigned i = 0; i < n_arg - 1; ++i) { |
227 | if (!is_isl_type(fn->getArgType(i))) |
228 | die(msg: "Argument has non-isl type" ); |
229 | printf(format: ", c_void_p" ); |
230 | } |
231 | printf(format: ", c_void_p)\n" ); |
232 | printf(format: " def cb_func(" ); |
233 | for (unsigned i = 0; i < n_arg; ++i) { |
234 | if (i) |
235 | printf(format: ", " ); |
236 | printf(format: "cb_arg%d" , i); |
237 | } |
238 | printf(format: "):\n" ); |
239 | for (unsigned i = 0; i < n_arg - 1; ++i) { |
240 | string arg_type; |
241 | arg_type = type2python(extract_type(fn->getArgType(i))); |
242 | printf(format: " cb_arg%d = %s(ctx=arg0.ctx, ptr=" , |
243 | i, arg_type.c_str()); |
244 | if (!callback_takes_argument(param, i)) |
245 | print_copy(fn->getArgType(i)); |
246 | printf(format: "(cb_arg%d))\n" , i); |
247 | } |
248 | printf(format: " try:\n" ); |
249 | if (is_isl_stat(return_type)) |
250 | printf(format: " arg%d(" , arg); |
251 | else |
252 | printf(format: " res = arg%d(" , arg); |
253 | for (unsigned i = 0; i < n_arg - 1; ++i) { |
254 | if (i) |
255 | printf(format: ", " ); |
256 | printf(format: "cb_arg%d" , i); |
257 | } |
258 | printf(format: ")\n" ); |
259 | printf(format: " except BaseException as e:\n" ); |
260 | printf(format: " exc_info[0] = e\n" ); |
261 | if (is_isl_stat(return_type) || is_isl_bool(return_type)) |
262 | printf(format: " return -1\n" ); |
263 | else |
264 | printf(format: " return None\n" ); |
265 | if (is_isl_stat(return_type)) { |
266 | printf(format: " return 0\n" ); |
267 | } else if (is_isl_bool(return_type)) { |
268 | printf(format: " return 1 if res else 0\n" ); |
269 | } else { |
270 | printf(format: " return " ); |
271 | print_copy(return_type); |
272 | printf(format: "(res.ptr)\n" ); |
273 | } |
274 | printf(format: " cb%d = fn(cb_func)\n" , arg); |
275 | } |
276 | |
277 | /* Print the argument at position "arg" in call to "fd". |
278 | * "fmt" is the format for printing Python method arguments. |
279 | * "skip" is the number of initial arguments of "fd" that are |
280 | * skipped in the Python method. |
281 | * |
282 | * If the (first) argument is an isl_ctx, then print "ctx", |
283 | * assuming that the caller has made the context available |
284 | * in a "ctx" variable. |
285 | * Otherwise, if the argument is a callback, then print a reference to |
286 | * the corresponding callback wrapper. |
287 | * Otherwise, if the argument is marked as consuming a reference, |
288 | * then pass a copy of the pointer stored in the corresponding |
289 | * argument passed to the Python method. |
290 | * Otherwise, if the argument is a string, then the python string is first |
291 | * encoded as a byte sequence, using 'ascii' as encoding. This assumes |
292 | * that all strings passed to isl can be converted to 'ascii'. |
293 | * Otherwise, if the argument is a pointer, then pass this pointer itself. |
294 | * Otherwise, pass the argument directly. |
295 | */ |
296 | void python_generator::print_arg_in_call(FunctionDecl *fd, const char *fmt, |
297 | int arg, int skip) |
298 | { |
299 | ParmVarDecl *param = fd->getParamDecl(arg); |
300 | QualType type = param->getOriginalType(); |
301 | if (is_isl_ctx(type)) { |
302 | printf(format: "ctx" ); |
303 | } else if (is_callback(type)) { |
304 | printf(format: "cb%d" , arg - skip); |
305 | } else if (takes(param)) { |
306 | print_copy(type); |
307 | printf(format: "(" ); |
308 | printf(format: fmt, arg - skip); |
309 | printf(format: ".ptr)" ); |
310 | } else if (is_string(type)) { |
311 | printf(format: fmt, arg - skip); |
312 | printf(format: ".encode('ascii')" ); |
313 | } else if (type->isPointerType()) { |
314 | printf(format: fmt, arg - skip); |
315 | printf(format: ".ptr" ); |
316 | } else { |
317 | printf(format: fmt, arg - skip); |
318 | } |
319 | } |
320 | |
321 | /* Generate code that raises the exception captured in "exc_info", if any, |
322 | * with the given indentation. |
323 | */ |
324 | static void print_rethrow(int indent, const char *exc_info) |
325 | { |
326 | print_indent(indent, format: "if %s is not None:\n" , exc_info); |
327 | print_indent(indent, format: " raise %s\n" , exc_info); |
328 | } |
329 | |
330 | /* Print code with the given indentation that checks |
331 | * whether any of the persistent callbacks of "clazz" |
332 | * is set and if it failed with an exception. If so, the 'exc_info' |
333 | * field contains the exception and is raised again. |
334 | * The field is cleared because the callback and its data may get reused. |
335 | * "fmt" is the format for printing Python method arguments. |
336 | */ |
337 | static void print_persistent_callback_failure_check(int indent, |
338 | const isl_class &clazz, const char *fmt) |
339 | { |
340 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
341 | set<FunctionDecl *>::const_iterator in; |
342 | |
343 | for (in = callbacks.begin(); in != callbacks.end(); ++in) { |
344 | string callback_name = clazz.persistent_callback_name(*in); |
345 | |
346 | print_indent(indent, format: "if hasattr(" ); |
347 | printf(format: fmt, 0); |
348 | printf(format: ", '%s') and " , callback_name.c_str()); |
349 | printf(format: fmt, 0); |
350 | printf(format: ".%s['exc_info'] != None:\n" , callback_name.c_str()); |
351 | print_indent(indent, format: " exc_info = " ); |
352 | printf(format: fmt, 0); |
353 | printf(format: ".%s['exc_info'][0]\n" , callback_name.c_str()); |
354 | print_indent(indent, format: " " ); |
355 | printf(format: fmt, 0); |
356 | printf(format: ".%s['exc_info'][0] = None\n" , callback_name.c_str()); |
357 | print_rethrow(indent: indent + 4, exc_info: "exc_info" ); |
358 | } |
359 | } |
360 | |
361 | /* Print the return statement of the python method corresponding |
362 | * to the C function "method" with the given indentation. |
363 | * If the object on which the method was called |
364 | * may have a persistent callback, then first check if any of those failed. |
365 | * "fmt" is the format for printing Python method arguments. |
366 | * |
367 | * If the method returns a new instance of the same object type and |
368 | * if the class has any persistent callbacks, then the data |
369 | * for these callbacks are copied from the original to the new object. |
370 | * If the method it itself setting a persistent callback, |
371 | * then keep track of the constructed C callback (such that it doesn't |
372 | * get destroyed) and the data structure that holds the captured exception |
373 | * (such that it can be raised again). |
374 | * The callback appears in position 1 and the C callback is therefore |
375 | * called "cb1". |
376 | * |
377 | * If the return type is a (const) char *, then convert the result |
378 | * to a Python string, raising an error on NULL and freeing |
379 | * the C string if needed. For python 3 compatibility, the string returned |
380 | * by isl is explicitly decoded as an 'ascii' string. This is correct |
381 | * as all strings returned by isl are expected to be 'ascii'. |
382 | * |
383 | * If the return type is isl_stat, isl_bool or isl_size, then |
384 | * raise an error on isl_stat_error, isl_bool_error or isl_size_error. |
385 | * In case of isl_bool, the result is converted to |
386 | * a Python boolean. |
387 | * In case of isl_size, the result is converted to a Python int. |
388 | */ |
389 | void python_generator::print_method_return(int indent, const isl_class &clazz, |
390 | FunctionDecl *method, const char *fmt) |
391 | { |
392 | QualType return_type = method->getReturnType(); |
393 | |
394 | if (!is_static(clazz, method)) |
395 | print_persistent_callback_failure_check(indent, clazz, fmt); |
396 | |
397 | if (is_isl_type(return_type)) { |
398 | string type; |
399 | |
400 | type = type2python(extract_type(return_type)); |
401 | print_indent(indent, |
402 | format: "obj = %s(ctx=ctx, ptr=res)\n" , type.c_str()); |
403 | if (is_mutator(clazz, method) && |
404 | clazz.has_persistent_callbacks()) |
405 | print_indent(indent, format: "obj.copy_callbacks(arg0)\n" ); |
406 | if (clazz.persistent_callbacks.count(method)) { |
407 | string callback_name; |
408 | |
409 | callback_name = clazz.persistent_callback_name(method); |
410 | print_indent(indent, format: "obj.%s = { 'func': cb1, " |
411 | "'exc_info': exc_info }\n" , |
412 | callback_name.c_str()); |
413 | } |
414 | print_indent(indent, format: "return obj\n" ); |
415 | } else if (is_string(return_type)) { |
416 | print_indent(indent, format: "if res == 0:\n" ); |
417 | print_indent(indent, format: " raise Error\n" ); |
418 | print_indent(indent, format: "string = " |
419 | "cast(res, c_char_p).value.decode('ascii')\n" ); |
420 | |
421 | if (gives(method)) |
422 | print_indent(indent, format: "libc.free(res)\n" ); |
423 | |
424 | print_indent(indent, format: "return string\n" ); |
425 | } else if (is_isl_neg_error(return_type)) { |
426 | print_indent(indent, format: "if res < 0:\n" ); |
427 | print_indent(indent, format: " raise Error\n" ); |
428 | if (is_isl_bool(return_type)) |
429 | print_indent(indent, format: "return bool(res)\n" ); |
430 | else if (is_isl_size(return_type)) |
431 | print_indent(indent, format: "return int(res)\n" ); |
432 | } else { |
433 | print_indent(indent, format: "return res\n" ); |
434 | } |
435 | } |
436 | |
437 | /* Print a python "get" method corresponding to the C function "fd" |
438 | * in class "clazz" using a name that includes the "get_" prefix. |
439 | * |
440 | * This method simply calls the variant without the "get_" prefix and |
441 | * returns its result. |
442 | * Note that static methods are not considered to be "get" methods. |
443 | */ |
444 | void python_generator::print_get_method(const isl_class &clazz, |
445 | FunctionDecl *fd) |
446 | { |
447 | string get_name = clazz.base_method_name(fd); |
448 | string name = clazz.method_name(fd); |
449 | int num_params = fd->getNumParams(); |
450 | |
451 | print_method_header(is_static: false, name: get_name, n_arg: num_params); |
452 | printf(format: " return arg0.%s(" , name.c_str()); |
453 | print_method_arguments(first: 1, n_arg: num_params); |
454 | printf(format: ")\n" ); |
455 | } |
456 | |
457 | /* Print a call to "method", along with the corresponding |
458 | * return statement, with the given indentation. |
459 | * "drop_ctx" is set if the first argument is an isl_ctx. |
460 | * |
461 | * A "ctx" variable is first initialized as it may be needed |
462 | * in the first call to print_arg_in_call and in print_method_return. |
463 | * |
464 | * If the method has any callback function, then any exception |
465 | * thrown in any callback also need to be rethrown. |
466 | */ |
467 | void python_generator::print_method_call(int indent, const isl_class &clazz, |
468 | FunctionDecl *method, const char *fmt, int drop_ctx) |
469 | { |
470 | string fullname = method->getName().str(); |
471 | int num_params = method->getNumParams(); |
472 | int drop_user = 0; |
473 | |
474 | if (drop_ctx) { |
475 | print_indent(indent, format: "ctx = Context.getDefaultInstance()\n" ); |
476 | } else { |
477 | print_indent(indent, format: "ctx = " ); |
478 | printf(format: fmt, 0); |
479 | printf(format: ".ctx\n" ); |
480 | } |
481 | print_indent(indent, format: "res = isl.%s(" , fullname.c_str()); |
482 | for (int i = 0; i < num_params; ++i) { |
483 | if (i > 0) |
484 | printf(format: ", " ); |
485 | print_arg_in_call(method, fmt, i, drop_ctx + drop_user); |
486 | if (!is_callback_arg(method, i)) |
487 | continue; |
488 | ++drop_user; |
489 | ++i; |
490 | printf(format: ", None" ); |
491 | } |
492 | printf(format: ")\n" ); |
493 | |
494 | if (drop_user > 0) |
495 | print_rethrow(indent, exc_info: "exc_info[0]" ); |
496 | |
497 | print_method_return(indent, clazz, method, fmt); |
498 | } |
499 | |
500 | /* Print a python method corresponding to the C function "method". |
501 | * "super" contains the superclasses of the class to which the method belongs, |
502 | * with the first element corresponding to the annotation that appears |
503 | * closest to the annotated type. This superclass is the least |
504 | * general extension of the annotated type in the linearization |
505 | * of the class hierarchy. |
506 | * |
507 | * If the first argument of "method" is something other than an instance |
508 | * of the class, then mark the python method as static. |
509 | * If, moreover, this first argument is an isl_ctx, then remove |
510 | * it from the arguments of the Python method. |
511 | * |
512 | * If the function has any callback arguments, then it also has corresponding |
513 | * "user" arguments. Since Python has closures, there is no need for such |
514 | * user arguments in the Python interface, so we simply drop them. |
515 | * We also create a wrapper ("cb{arg}") for each callback. |
516 | * |
517 | * If the function consumes a reference, then we pass it a copy of |
518 | * the actual argument. |
519 | * |
520 | * For methods that are identified as "get" methods, also |
521 | * print a variant of the method using a name that includes |
522 | * the "get_" prefix. |
523 | */ |
524 | void python_generator::print_method(const isl_class &clazz, |
525 | FunctionDecl *method, vector<string> super) |
526 | { |
527 | string cname = clazz.method_name(method); |
528 | int num_params = method->getNumParams(); |
529 | int drop_user = 0; |
530 | int drop_ctx = first_arg_is_isl_ctx(method); |
531 | |
532 | for (int i = 1; i < num_params; ++i) { |
533 | if (is_callback_arg(method, i)) |
534 | drop_user += 1; |
535 | } |
536 | |
537 | print_method_header(is_static: is_static(clazz, method), name: cname, |
538 | n_arg: num_params - drop_ctx - drop_user); |
539 | |
540 | print_type_checks(cname, method, drop_ctx, |
541 | num_params, super); |
542 | drop_user = 0; |
543 | for (int i = 1; i < num_params; ++i) { |
544 | ParmVarDecl *param = method->getParamDecl(i); |
545 | QualType type = param->getOriginalType(); |
546 | if (!is_callback(type)) |
547 | continue; |
548 | print_callback(param, i - drop_ctx - drop_user); |
549 | drop_user += 1; |
550 | } |
551 | print_method_call(8, clazz, method, fixed_arg_fmt, drop_ctx); |
552 | |
553 | if (clazz.is_get_method(method)) |
554 | print_get_method(clazz, method); |
555 | } |
556 | |
557 | /* Print a condition that checks whether Python method argument "i" |
558 | * corresponds to the C function argument type "type". |
559 | */ |
560 | static void print_argument_check(QualType type, int i) |
561 | { |
562 | if (generator::is_isl_type(type)) { |
563 | string type_str; |
564 | type_str = generator::extract_type(type); |
565 | type_str = type2python(name: type_str); |
566 | printf(format: "args[%d].__class__ is %s" , i, type_str.c_str()); |
567 | } else if (type->isPointerType()) { |
568 | printf(format: "type(args[%d]) == str" , i); |
569 | } else { |
570 | printf(format: "type(args[%d]) == int" , i); |
571 | } |
572 | } |
573 | |
574 | /* Is any element of "vector" set? |
575 | */ |
576 | static bool any(const std::vector<bool> &vector) |
577 | { |
578 | return std::find(first: vector.begin(), last: vector.end(), val: true) != vector.end(); |
579 | } |
580 | |
581 | /* Print a test that checks whether the arguments passed |
582 | * to the Python method correspond to the arguments |
583 | * expected by "fd" and |
584 | * check if the object on which the method is called, if any, |
585 | * is of the right type. |
586 | * "drop_ctx" is set if the first argument of "fd" is an isl_ctx, |
587 | * which does not appear as an argument to the Python method. |
588 | * |
589 | * If an automatic conversion function is available for any |
590 | * of the argument types, then also allow the argument |
591 | * to be of the type as prescribed by the second input argument |
592 | * of the conversion function. |
593 | * The corresponding arguments are then converted to the expected types |
594 | * if needed. |
595 | * The object on which the method is called is also converted if needed. |
596 | * The argument tuple first needs to be converted to a list |
597 | * in order to be able to modify the entries. |
598 | */ |
599 | void python_generator::print_argument_checks(const isl_class &clazz, |
600 | FunctionDecl *fd, int drop_ctx) |
601 | { |
602 | int num_params = fd->getNumParams(); |
603 | bool is_static = generator::is_static(clazz, fd); |
604 | int first = is_static ? drop_ctx : 1; |
605 | std::vector<bool> convert(num_params); |
606 | |
607 | printf(format: " if len(args) == %d" , num_params - drop_ctx); |
608 | for (int i = first; i < num_params; ++i) { |
609 | ParmVarDecl *param = fd->getParamDecl(i); |
610 | QualType type = param->getOriginalType(); |
611 | const Type *ptr = type.getTypePtr(); |
612 | |
613 | printf(format: " and " ); |
614 | if (conversions.count(ptr) == 0) { |
615 | print_argument_check(type, i - drop_ctx); |
616 | } else { |
617 | QualType type2 = conversions.at(ptr)->getOriginalType(); |
618 | convert[i] = true; |
619 | printf(format: "(" ); |
620 | print_argument_check(type, i - drop_ctx); |
621 | printf(format: " or " ); |
622 | print_argument_check(type2, i - drop_ctx); |
623 | printf(format: ")" ); |
624 | } |
625 | } |
626 | printf(format: ":\n" ); |
627 | |
628 | if (is_static && !any(vector: convert)) |
629 | return; |
630 | print_indent(indent: 12, format: "args = list(args)\n" ); |
631 | first = is_static ? drop_ctx : 0; |
632 | for (int i = first; i < num_params; ++i) { |
633 | bool is_self = !is_static && i == 0; |
634 | ParmVarDecl *param = fd->getParamDecl(i); |
635 | string type; |
636 | |
637 | if (!is_self && !convert[i]) |
638 | continue; |
639 | type = type2python(extract_type(param->getOriginalType())); |
640 | print_type_check(indent: 12, type, fmt: var_arg_fmt, |
641 | pos: i - drop_ctx, upcast: false, super: "" , name: "" , n: -1); |
642 | } |
643 | } |
644 | |
645 | /* Print part of an overloaded python method corresponding to the C function |
646 | * "method". |
647 | * "drop_ctx" is set if the first argument of "method" is an isl_ctx. |
648 | * |
649 | * In particular, print code to test whether the arguments passed to |
650 | * the python method correspond to the arguments expected by "method" |
651 | * and to call "method" if they do. |
652 | */ |
653 | void python_generator::print_method_overload(const isl_class &clazz, |
654 | FunctionDecl *method) |
655 | { |
656 | int drop_ctx = first_arg_is_isl_ctx(method); |
657 | |
658 | print_argument_checks(clazz, method, drop_ctx); |
659 | print_method_call(12, clazz, method, var_arg_fmt, drop_ctx); |
660 | } |
661 | |
662 | /* Print a python method with a name derived from "fullname" |
663 | * corresponding to the C functions "methods". |
664 | * "super" contains the superclasses of the class to which the method belongs. |
665 | * |
666 | * If "methods" consists of a single element that is not marked overloaded, |
667 | * the use print_method to print the method. |
668 | * Otherwise, print an overloaded method with pieces corresponding |
669 | * to each function in "methods". |
670 | */ |
671 | void python_generator::print_method(const isl_class &clazz, |
672 | const string &fullname, const function_set &methods, |
673 | vector<string> super) |
674 | { |
675 | string cname; |
676 | function_set::const_iterator it; |
677 | FunctionDecl *any_method; |
678 | |
679 | any_method = *methods.begin(); |
680 | if (methods.size() == 1 && !is_overload(any_method)) { |
681 | print_method(clazz, fullname: any_method, methods: super); |
682 | return; |
683 | } |
684 | |
685 | cname = clazz.method_name(any_method); |
686 | |
687 | print_method_def(is_static(clazz, any_method), cname); |
688 | printf(format: "(*args):\n" ); |
689 | |
690 | for (it = methods.begin(); it != methods.end(); ++it) |
691 | print_method_overload(clazz, *it); |
692 | printf(format: " raise Error\n" ); |
693 | } |
694 | |
695 | /* Print a python method "name" corresponding to "fd" setting |
696 | * the enum value "value". |
697 | * "super" contains the superclasses of the class to which the method belongs, |
698 | * with the first element corresponding to the annotation that appears |
699 | * closest to the annotated type. |
700 | * |
701 | * The last argument of the C function does not appear in the method call, |
702 | * but is fixed to "value" instead. |
703 | * Other than that, the method printed here is similar to one |
704 | * printed by python_generator::print_method, except that |
705 | * some of the special cases do not occur. |
706 | */ |
707 | void python_generator::print_set_enum(const isl_class &clazz, |
708 | FunctionDecl *fd, int value, const string &name, |
709 | const vector<string> &super) |
710 | { |
711 | string fullname = fd->getName().str(); |
712 | int num_params = fd->getNumParams(); |
713 | |
714 | print_method_header(is_static: is_static(clazz, fd), name, n_arg: num_params - 1); |
715 | |
716 | print_type_checks(name, fd, false, num_params - 1, super); |
717 | printf(format: " ctx = arg0.ctx\n" ); |
718 | printf(format: " res = isl.%s(" , fullname.c_str()); |
719 | for (int i = 0; i < num_params - 1; ++i) { |
720 | if (i) |
721 | printf(format: ", " ); |
722 | print_arg_in_call(fd, fixed_arg_fmt, i, 0); |
723 | } |
724 | printf(format: ", %d" , value); |
725 | printf(format: ")\n" ); |
726 | print_method_return(8, clazz, fd, fixed_arg_fmt); |
727 | } |
728 | |
729 | /* Print python methods corresponding to "fd", which sets an enum. |
730 | * "super" contains the superclasses of the class to which the method belongs, |
731 | * with the first element corresponding to the annotation that appears |
732 | * closest to the annotated type. |
733 | * |
734 | * A method is generated for each value in the enum, setting |
735 | * the enum to that value. |
736 | */ |
737 | void python_generator::print_set_enum(const isl_class &clazz, |
738 | FunctionDecl *fd, const vector<string> &super) |
739 | { |
740 | vector<set_enum>::const_iterator it; |
741 | const vector<set_enum> &set_enums = clazz.set_enums.at(fd); |
742 | |
743 | for (it = set_enums.begin(); it != set_enums.end(); ++it) |
744 | print_set_enum(clazz, fd, it->value, it->method_name, super); |
745 | } |
746 | |
747 | /* Print part of the constructor for this isl_class. |
748 | * |
749 | * In particular, check if the actual arguments correspond to the |
750 | * formal arguments of "cons" and if so call "cons" and put the |
751 | * result in self.ptr and a reference to the default context in self.ctx. |
752 | */ |
753 | void python_generator::print_constructor(const isl_class &clazz, |
754 | FunctionDecl *cons) |
755 | { |
756 | string fullname = cons->getName().str(); |
757 | string cname = clazz.method_name(cons); |
758 | int num_params = cons->getNumParams(); |
759 | int drop_ctx = first_arg_is_isl_ctx(cons); |
760 | |
761 | print_argument_checks(clazz, cons, drop_ctx); |
762 | printf(format: " self.ctx = Context.getDefaultInstance()\n" ); |
763 | printf(format: " self.ptr = isl.%s(" , fullname.c_str()); |
764 | if (drop_ctx) |
765 | printf(format: "self.ctx" ); |
766 | for (int i = drop_ctx; i < num_params; ++i) { |
767 | if (i) |
768 | printf(format: ", " ); |
769 | print_arg_in_call(cons, var_arg_fmt, i, drop_ctx); |
770 | } |
771 | printf(format: ")\n" ); |
772 | printf(format: " return\n" ); |
773 | } |
774 | |
775 | /* The definition of the part of constructor for the "id" class |
776 | * that construct an object from a name and a user object, |
777 | * without the initial newline. |
778 | * |
779 | * Just like the parts generated by python_generator::print_constructor, |
780 | * the result of the isl_id_alloc call is stored in self.ptr and |
781 | * a reference to the default context is stored in self.ctx. |
782 | * Also, just like any other constructor or method with a string argument, |
783 | * the python string is first encoded as a byte sequence, |
784 | * using 'ascii' as encoding. |
785 | * |
786 | * Since the isl_id keeps a reference to the Python user object, |
787 | * the reference count of the Python object needs to be incremented, |
788 | * but only if the construction of the isl_id is successful. |
789 | * The reference count of the Python object is decremented again |
790 | * by Context.free_user when the reference count of the isl_id |
791 | * drops to zero. |
792 | */ |
793 | static const char *const id_constructor_user = &R"( |
794 | if len(args) == 2 and type(args[0]) == str: |
795 | self.ctx = Context.getDefaultInstance() |
796 | name = args[0].encode('ascii') |
797 | self.ptr = isl.isl_id_alloc(self.ctx, name, args[1]) |
798 | self.ptr = isl.isl_id_set_free_user(self.ptr, Context.free_user) |
799 | if self.ptr is not None: |
800 | pythonapi.Py_IncRef(py_object(args[1])) |
801 | return |
802 | )" [1]; |
803 | |
804 | /* Print any special constructor parts of this class that are not |
805 | * automatically derived from the C interface. |
806 | * |
807 | * In particular, print a special constructor part for the "id" class. |
808 | */ |
809 | void python_generator::print_special_constructors(const isl_class &clazz) |
810 | { |
811 | if (clazz.name != "isl_id" ) |
812 | return; |
813 | |
814 | printf(format: "%s" , id_constructor_user); |
815 | } |
816 | |
817 | /* The definition of an "id" method |
818 | * for retrieving the user object associated to the identifier, |
819 | * without the initial newline. |
820 | * |
821 | * The isl_id needs to have been created by the constructor |
822 | * in id_constructor_user. That is, it needs to have a user pointer and |
823 | * it needs to have its free_user callback set to Context.free_user. |
824 | * The functions need to be cast to c_void_p to be able to compare |
825 | * the addresses. |
826 | * |
827 | * Return None if any of the checks fail. |
828 | * Note that isl_id_get_user returning NULL automatically results in None. |
829 | */ |
830 | static const char *const id_user = &R"( |
831 | def user(self): |
832 | free_user = cast(Context.free_user, c_void_p) |
833 | id_free_user = cast(isl.isl_id_get_free_user(self.ptr), c_void_p) |
834 | if id_free_user.value != free_user.value: |
835 | return None |
836 | return isl.isl_id_get_user(self.ptr) |
837 | )" [1]; |
838 | |
839 | /* Print any special methods of this class that are not |
840 | * automatically derived from the C interface. |
841 | * |
842 | * In particular, print a special method for the "id" class. |
843 | */ |
844 | void python_generator::print_special_methods(const isl_class &clazz) |
845 | { |
846 | if (clazz.name != "isl_id" ) |
847 | return; |
848 | |
849 | printf(format: "%s" , id_user); |
850 | } |
851 | |
852 | /* If "clazz" has a type function describing subclasses, |
853 | * then add constructors that allow each of these subclasses |
854 | * to be treated as an object to the superclass. |
855 | */ |
856 | void python_generator::print_upcast_constructors(const isl_class &clazz) |
857 | { |
858 | map<int, string>::const_iterator i; |
859 | |
860 | if (!clazz.fn_type) |
861 | return; |
862 | |
863 | for (i = clazz.type_subclasses.begin(); |
864 | i != clazz.type_subclasses.end(); ++i) { |
865 | printf(format: " if len(args) == 1 and " |
866 | "isinstance(args[0], %s):\n" , |
867 | type2python(name: i->second).c_str()); |
868 | printf(format: " self.ctx = args[0].ctx\n" ); |
869 | printf(format: " self.ptr = isl.%s_copy(args[0].ptr)\n" , |
870 | clazz.name.c_str()); |
871 | printf(format: " return\n" ); |
872 | } |
873 | } |
874 | |
875 | /* Print the header of the class "name" with superclasses "super". |
876 | * The order of the superclasses is the opposite of the order |
877 | * in which the corresponding annotations appear in the source code. |
878 | * If "clazz" is a subclass derived from a type function, |
879 | * then the immediate superclass is recorded in "clazz" itself. |
880 | */ |
881 | void python_generator::(const isl_class &clazz, |
882 | const string &name, const vector<string> &super) |
883 | { |
884 | printf(format: "class %s" , name.c_str()); |
885 | if (super.size() > 0) { |
886 | printf(format: "(" ); |
887 | for (unsigned i = 0; i < super.size(); ++i) { |
888 | if (i > 0) |
889 | printf(format: ", " ); |
890 | printf(format: "%s" , type2python(name: super[i]).c_str()); |
891 | } |
892 | printf(format: ")" ); |
893 | } else if (clazz.is_type_subclass()) { |
894 | printf(format: "(%s)" , type2python(name: clazz.superclass_name).c_str()); |
895 | } else { |
896 | printf(format: "(object)" ); |
897 | } |
898 | printf(format: ":\n" ); |
899 | } |
900 | |
901 | /* Tell ctypes about the return type of "fd". |
902 | * In particular, if "fd" returns a pointer to an isl object, |
903 | * then tell ctypes it returns a "c_void_p". |
904 | * If "fd" returns a char *, then simply tell ctypes. |
905 | * |
906 | * Nothing needs to be done for functions returning |
907 | * isl_bool, isl_stat or isl_size since they are represented by an int and |
908 | * ctypes assumes that a function returns int by default. |
909 | */ |
910 | void python_generator::print_restype(FunctionDecl *fd) |
911 | { |
912 | string fullname = fd->getName().str(); |
913 | QualType type = fd->getReturnType(); |
914 | if (is_isl_type(type)) |
915 | printf(format: "isl.%s.restype = c_void_p\n" , fullname.c_str()); |
916 | else if (is_string(type)) |
917 | printf(format: "isl.%s.restype = POINTER(c_char)\n" , fullname.c_str()); |
918 | } |
919 | |
920 | /* Tell ctypes about the types of the arguments of the function "fd". |
921 | * |
922 | * Any callback argument is followed by a user pointer argument. |
923 | * Each such pair or arguments is handled together. |
924 | */ |
925 | void python_generator::print_argtypes(FunctionDecl *fd) |
926 | { |
927 | string fullname = fd->getName().str(); |
928 | int n = fd->getNumParams(); |
929 | |
930 | printf(format: "isl.%s.argtypes = [" , fullname.c_str()); |
931 | for (int i = 0; i < n; ++i) { |
932 | ParmVarDecl *param = fd->getParamDecl(i); |
933 | QualType type = param->getOriginalType(); |
934 | if (i) |
935 | printf(format: ", " ); |
936 | if (is_isl_ctx(type)) |
937 | printf(format: "Context" ); |
938 | else if (is_isl_type(type)) |
939 | printf(format: "c_void_p" ); |
940 | else if (is_callback(type)) |
941 | printf(format: "c_void_p, c_void_p" ); |
942 | else if (is_string(type)) |
943 | printf(format: "c_char_p" ); |
944 | else if (is_long(type)) |
945 | printf(format: "c_long" ); |
946 | else |
947 | printf(format: "c_int" ); |
948 | |
949 | if (is_callback(type)) |
950 | ++i; |
951 | } |
952 | printf(format: "]\n" ); |
953 | } |
954 | |
955 | /* Print type definitions for the method 'fd'. |
956 | */ |
957 | void python_generator::print_method_type(FunctionDecl *fd) |
958 | { |
959 | print_restype(fd); |
960 | print_argtypes(fd); |
961 | } |
962 | |
963 | /* If "clazz" has a type function describing subclasses or |
964 | * if it is one of those type subclasses, then print a __new__ method. |
965 | * |
966 | * In the superclass, the __new__ method constructs an object |
967 | * of the subclass type specified by the type function, |
968 | * raising an error on an error type. |
969 | * In the subclass, the __new__ method reverts to the original behavior. |
970 | */ |
971 | void python_generator::print_new(const isl_class &clazz, |
972 | const string &python_name) |
973 | { |
974 | if (!clazz.fn_type && !clazz.is_type_subclass()) |
975 | return; |
976 | |
977 | printf(format: " def __new__(cls, *args, **keywords):\n" ); |
978 | |
979 | if (clazz.fn_type) { |
980 | map<int, string>::const_iterator i; |
981 | |
982 | printf(format: " if \"ptr\" in keywords:\n" ); |
983 | printf(" type = isl.%s(keywords[\"ptr\"])\n" , |
984 | clazz.fn_type->getNameAsString().c_str()); |
985 | |
986 | for (i = clazz.type_subclasses.begin(); |
987 | i != clazz.type_subclasses.end(); ++i) { |
988 | printf(format: " if type == %d:\n" , i->first); |
989 | printf(format: " return %s(**keywords)\n" , |
990 | type2python(name: i->second).c_str()); |
991 | } |
992 | printf(format: " raise Error\n" ); |
993 | } |
994 | |
995 | printf(format: " return super(%s, cls).__new__(cls)\n" , |
996 | python_name.c_str()); |
997 | } |
998 | |
999 | /* Print declarations for methods printing the class representation, |
1000 | * provided there is a corresponding *_to_str function. |
1001 | * |
1002 | * In particular, provide an implementation of __str__ and __repr__ methods to |
1003 | * override the default representation used by python. Python uses __str__ to |
1004 | * pretty print the class (e.g., when calling print(obj)) and uses __repr__ |
1005 | * when printing a precise representation of an object (e.g., when dumping it |
1006 | * in the REPL console). |
1007 | * |
1008 | * Check the type of the argument before calling the *_to_str function |
1009 | * on it in case the method was called on an object from a subclass. |
1010 | * |
1011 | * The return value of the *_to_str function is decoded to a python string |
1012 | * assuming an 'ascii' encoding. This is necessary for python 3 compatibility. |
1013 | */ |
1014 | void python_generator::print_representation(const isl_class &clazz, |
1015 | const string &python_name) |
1016 | { |
1017 | if (!clazz.fn_to_str) |
1018 | return; |
1019 | |
1020 | printf(format: " def __str__(arg0):\n" ); |
1021 | print_type_check(indent: 8, type: python_name, fmt: fixed_arg_fmt, pos: 0, upcast: false, super: "" , name: "" , n: -1); |
1022 | printf(format: " ptr = isl.%s(arg0.ptr)\n" , |
1023 | string(clazz.fn_to_str->getName()).c_str()); |
1024 | printf(format: " res = cast(ptr, c_char_p).value.decode('ascii')\n" ); |
1025 | printf(format: " libc.free(ptr)\n" ); |
1026 | printf(format: " return res\n" ); |
1027 | printf(format: " def __repr__(self):\n" ); |
1028 | printf(format: " s = str(self)\n" ); |
1029 | printf(format: " if '\"' in s:\n" ); |
1030 | printf(format: " return 'isl.%s(\"\"\"%%s\"\"\")' %% s\n" , |
1031 | python_name.c_str()); |
1032 | printf(format: " else:\n" ); |
1033 | printf(format: " return 'isl.%s(\"%%s\")' %% s\n" , |
1034 | python_name.c_str()); |
1035 | } |
1036 | |
1037 | /* If "clazz" has any persistent callbacks, then print the definition |
1038 | * of a "copy_callbacks" function that copies the persistent callbacks |
1039 | * from one object to another. |
1040 | */ |
1041 | void python_generator::print_copy_callbacks(const isl_class &clazz) |
1042 | { |
1043 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
1044 | set<FunctionDecl *>::const_iterator in; |
1045 | |
1046 | if (!clazz.has_persistent_callbacks()) |
1047 | return; |
1048 | |
1049 | printf(format: " def copy_callbacks(self, obj):\n" ); |
1050 | for (in = callbacks.begin(); in != callbacks.end(); ++in) { |
1051 | string callback_name = clazz.persistent_callback_name(*in); |
1052 | |
1053 | printf(format: " if hasattr(obj, '%s'):\n" , |
1054 | callback_name.c_str()); |
1055 | printf(format: " self.%s = obj.%s\n" , |
1056 | callback_name.c_str(), callback_name.c_str()); |
1057 | } |
1058 | } |
1059 | |
1060 | /* Print code to set method type signatures. |
1061 | * |
1062 | * To be able to call C functions it is necessary to explicitly set their |
1063 | * argument and result types. Do this for all exported constructors and |
1064 | * methods (including those that set a persistent callback and |
1065 | * those that set an enum value), |
1066 | * as well as for the *_to_str and the type function, if they exist. |
1067 | * Assuming each exported class has a *_copy and a *_free method, |
1068 | * also unconditionally set the type of such methods. |
1069 | */ |
1070 | void python_generator::print_method_types(const isl_class &clazz) |
1071 | { |
1072 | function_set::const_iterator in; |
1073 | map<string, function_set>::const_iterator it; |
1074 | map<FunctionDecl *, vector<set_enum> >::const_iterator ie; |
1075 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
1076 | |
1077 | for (in = clazz.constructors.begin(); in != clazz.constructors.end(); |
1078 | ++in) |
1079 | print_method_type(*in); |
1080 | |
1081 | for (in = callbacks.begin(); in != callbacks.end(); ++in) |
1082 | print_method_type(*in); |
1083 | for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it) |
1084 | for (in = it->second.begin(); in != it->second.end(); ++in) |
1085 | print_method_type(*in); |
1086 | for (ie = clazz.set_enums.begin(); ie != clazz.set_enums.end(); ++ie) |
1087 | print_method_type(ie->first); |
1088 | |
1089 | print_method_type(clazz.fn_copy); |
1090 | print_method_type(clazz.fn_free); |
1091 | if (clazz.fn_to_str) |
1092 | print_method_type(clazz.fn_to_str); |
1093 | if (clazz.fn_type) |
1094 | print_method_type(clazz.fn_type); |
1095 | } |
1096 | |
1097 | /* Print out the definition of this isl_class. |
1098 | * |
1099 | * We first check if this isl_class is a subclass of one or more other classes. |
1100 | * If it is, we make sure those superclasses are printed out first. |
1101 | * |
1102 | * Then we print a constructor with several cases, one for constructing |
1103 | * a Python object from a return value, one for each function that |
1104 | * was marked as a constructor, a class specific constructor, if any, and |
1105 | * one for each type based subclass. |
1106 | * |
1107 | * Next, we print out some common methods, class specific methods and |
1108 | * the methods corresponding |
1109 | * to functions that are not marked as constructors, including those |
1110 | * that set a persistent callback and those that set an enum value. |
1111 | * |
1112 | * Finally, we tell ctypes about the types of the arguments of the |
1113 | * constructor functions and the return types of those function returning |
1114 | * an isl object. |
1115 | */ |
1116 | void python_generator::print(const isl_class &clazz) |
1117 | { |
1118 | string p_name = type2python(name: clazz.subclass_name); |
1119 | vector<string> super = find_superclasses(clazz.type); |
1120 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
1121 | |
1122 | for (unsigned i = 0; i < super.size(); ++i) |
1123 | if (done.find(x: super[i]) == done.end()) |
1124 | print(clazz: classes[super[i]]); |
1125 | if (clazz.is_type_subclass() && done.find(x: clazz.name) == done.end()) |
1126 | print(clazz: classes[clazz.name]); |
1127 | done.insert(x: clazz.subclass_name); |
1128 | |
1129 | printf(format: "\n" ); |
1130 | print_class_header(clazz, name: p_name, super); |
1131 | printf(format: " def __init__(self, *args, **keywords):\n" ); |
1132 | |
1133 | printf(format: " if \"ptr\" in keywords:\n" ); |
1134 | printf(format: " self.ctx = keywords[\"ctx\"]\n" ); |
1135 | printf(format: " self.ptr = keywords[\"ptr\"]\n" ); |
1136 | printf(format: " return\n" ); |
1137 | |
1138 | for (const auto &cons : clazz.constructors) |
1139 | print_constructor(clazz, cons); |
1140 | print_special_constructors(clazz); |
1141 | print_upcast_constructors(clazz); |
1142 | printf(format: " raise Error\n" ); |
1143 | printf(format: " def __del__(self):\n" ); |
1144 | printf(format: " if hasattr(self, 'ptr'):\n" ); |
1145 | printf(format: " isl.%s_free(self.ptr)\n" , clazz.name.c_str()); |
1146 | |
1147 | print_new(clazz, python_name: p_name); |
1148 | print_representation(clazz, python_name: p_name); |
1149 | print_copy_callbacks(clazz); |
1150 | |
1151 | print_special_methods(clazz); |
1152 | for (const auto &callback : callbacks) |
1153 | print_method(clazz, callback, super); |
1154 | for (const auto &kvp : clazz.methods) |
1155 | print_method(clazz, fullname: kvp.first, methods: kvp.second, super); |
1156 | for (const auto &kvp : clazz.set_enums) |
1157 | print_set_enum(clazz, kvp.first, super); |
1158 | |
1159 | printf(format: "\n" ); |
1160 | |
1161 | print_method_types(clazz); |
1162 | } |
1163 | |
1164 | /* Generate a python interface based on the extracted types and |
1165 | * functions. |
1166 | * |
1167 | * Print out each class in turn. If one of these is a subclass of some |
1168 | * other class, make sure the superclass is printed out first. |
1169 | * functions. |
1170 | */ |
1171 | void python_generator::generate() |
1172 | { |
1173 | map<string, isl_class>::iterator ci; |
1174 | |
1175 | for (ci = classes.begin(); ci != classes.end(); ++ci) { |
1176 | if (done.find(x: ci->first) == done.end()) |
1177 | print(ci->second); |
1178 | } |
1179 | } |
1180 | |