Merge pull request #3310 from robinsonb5-PRs/master
[yosys.git] / misc / py_wrap_generator.py
1 #
2 # yosys -- Yosys Open SYnthesis Suite
3 #
4 # Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
5 #
6 # Permission to use, copy, modify, and/or distribute this software for any
7 # purpose with or without fee is hereby granted, provided that the above
8 # copyright notice and this permission notice appear in all copies.
9 #
10 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #
18 # Author Benedikt Tutzer
19 #
20
21 import copy
22
23 #Map c++ operator Syntax to Python functions
24 wrappable_operators = {
25 "<" : "__lt__",
26 "==": "__eq__",
27 "!=": "__ne__",
28 "+" : "__add__",
29 "-" : "__sub__",
30 "*" : "__mul__",
31 "/" : "__div__",
32 "()": "__call__"
33 }
34
35 #Restrict certain strings from being function names in Python
36 keyword_aliases = {
37 "in" : "in_",
38 "False" : "False_",
39 "None" : "None_",
40 "True" : "True_",
41 "and" : "and_",
42 "as" : "as_",
43 "assert" : "assert_",
44 "break" : "break_",
45 "class" : "class_",
46 "continue" : "continue_",
47 "def" : "def_",
48 "del" : "del_",
49 "elif" : "elif_",
50 "else" : "else_",
51 "except" : "except_",
52 "for" : "for_",
53 "from" : "from_",
54 "global" : "global_",
55 "if" : "if_",
56 "import" : "import_",
57 "in" : "in_",
58 "is" : "is_",
59 "lambda" : "lambda_",
60 "nonlocal" : "nonlocal_",
61 "not" : "not_",
62 "or" : "or_",
63 "pass" : "pass_",
64 "raise" : "raise_",
65 "return" : "return_",
66 "try" : "try_",
67 "while" : "while_",
68 "with" : "with_",
69 "yield" : "yield_"
70 }
71
72 #These can be used without any explicit conversion
73 primitive_types = ["void", "bool", "int", "double", "size_t", "std::string",
74 "string", "State", "char_p"]
75
76 from enum import Enum
77
78 #Ways to link between Python- and C++ Objects
79 class link_types(Enum):
80 global_list = 1 #Manage a global list of objects in C++, the Python
81 #object contains a key to find the corresponding C++
82 #object and a Pointer to the object to verify it is
83 #still the same, making collisions unlikely to happen
84 ref_copy = 2 #The Python object contains a copy of the C++ object.
85 #The C++ object is deleted when the Python object gets
86 #deleted
87 pointer = 3 #The Python Object contains a pointer to it's C++
88 #counterpart
89 derive = 4 #The Python-Wrapper is derived from the C++ object.
90
91 class attr_types(Enum):
92 star = "*"
93 amp = "&"
94 ampamp = "&&"
95 default = ""
96
97 #For source-files
98 class Source:
99 name = ""
100 classes = []
101
102 def __init__(self, name, classes):
103 self.name = name
104 self.classes = classes
105
106 #Splits a list by the given delimiter, without splitting strings inside
107 #pointy-brackets (< and >)
108 def split_list(str_def, delim):
109 str_def = str_def.strip()
110 if len(str_def) == 0:
111 return []
112 if str_def.count(delim) == 0:
113 return [str_def]
114 if str_def.count("<") == 0:
115 return str_def.split(delim)
116 if str_def.find("<") < str_def.find(" "):
117 closing = find_closing(str_def[str_def.find("<")+1:], "<", ">") + str_def.find("<")
118 comma = str_def[closing:].find(delim)
119 if comma == -1:
120 return [str_def]
121 comma = closing + comma
122 else:
123 comma = str_def.find(delim)
124 rest = split_list(str_def[comma+1:], delim)
125 ret = [str_def[:comma]]
126 if rest != None and len(rest) != 0:
127 ret.extend(rest)
128 return ret
129
130 #Represents a Type
131 class WType:
132 name = ""
133 cont = None
134 attr_type = attr_types.default
135
136 def __init__(self, name = "", cont = None, attr_type = attr_types.default):
137 self.name = name
138 self.cont = cont
139 self.attr_type = attr_type
140
141 #Python type-string
142 def gen_text(self):
143 text = self.name
144 if self.name in enum_names:
145 text = enum_by_name(self.name).namespace + "::" + self.name
146 if self.cont != None:
147 return known_containers[self.name].typename
148 return text
149
150 #C++ type-string
151 def gen_text_cpp(self):
152 postfix = ""
153 if self.attr_type == attr_types.star:
154 postfix = "*"
155 if self.name in primitive_types:
156 return self.name + postfix
157 if self.name in enum_names:
158 return enum_by_name(self.name).namespace + "::" + self.name + postfix
159 if self.name in classnames:
160 return class_by_name(self.name).namespace + "::" + self.name + postfix
161 text = self.name
162 if self.cont != None:
163 text += "<"
164 for a in self.cont.args:
165 text += a.gen_text_cpp() + ", "
166 text = text[:-2]
167 text += ">"
168 return text
169
170 @staticmethod
171 def from_string(str_def, containing_file, line_number):
172 str_def = str_def.strip()
173 if len(str_def) == 0:
174 return None
175 str_def = str_def.replace("RTLIL::SigSig", "std::pair<SigSpec, SigSpec>").replace("SigSig", "std::pair<SigSpec, SigSpec>")
176 t = WType()
177 t.name = ""
178 t.cont = None
179 t.attr_type = attr_types.default
180 if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "):
181 candidate = WContainer.from_string(str_def, containing_file, line_number)
182 if candidate == None:
183 return None
184 t.name = str_def[:str_def.find("<")]
185
186 if t.name.count("*") + t.name.count("&") > 1:
187 return None
188
189 if t.name.count("*") == 1 or str_def[0] == '*' or str_def[-1] == '*':
190 t.attr_type = attr_types.star
191 t.name = t.name.replace("*","")
192 elif t.name.count("&&") == 1:
193 t.attr_type = attr_types.ampamp
194 t.name = t.name.replace("&&","")
195 elif t.name.count("&") == 1 or str_def[0] == '&' or str_def[-1] == '&':
196 t.attr_type = attr_types.amp
197 t.name = t.name.replace("&","")
198
199 t.cont = candidate
200 if(t.name not in known_containers):
201 return None
202 return t
203
204 prefix = ""
205
206 if str.startswith(str_def, "unsigned "):
207 prefix = "unsigned "
208 str_def = str_def[9:]
209 while str.startswith(str_def, "long "):
210 prefix= "long " + prefix
211 str_def = str_def[5:]
212 while str.startswith(str_def, "short "):
213 prefix = "short " + prefix
214 str_def = str_def[6:]
215
216 str_def = str_def.split("::")[-1]
217
218 if str_def.count("*") + str_def.count("&") >= 2:
219 return None
220
221 if str_def.count("*") == 1:
222 t.attr_type = attr_types.star
223 str_def = str_def.replace("*","")
224 elif str_def.count("&&") == 1:
225 t.attr_type = attr_types.ampamp
226 str_def = str_def.replace("&&","")
227 elif str_def.count("&") == 1:
228 t.attr_type = attr_types.amp
229 str_def = str_def.replace("&","")
230
231 if len(str_def) > 0 and str_def.split("::")[-1] not in primitive_types and str_def.split("::")[-1] not in classnames and str_def.split("::")[-1] not in enum_names:
232 return None
233
234 if str_def.count(" ") == 0:
235 t.name = (prefix + str_def).replace("char_p", "char *")
236 t.cont = None
237 return t
238 return None
239
240 #Represents a container-type
241 class WContainer:
242 name = ""
243 args = []
244
245 def from_string(str_def, containing_file, line_number):
246 if str_def == None or len(str_def) < 4:
247 return None
248 cont = WContainer()
249 cont.name = str_def[:str_def.find("<")]
250 str_def = str_def[str_def.find("<")+1:find_closing(str_def, "<", ">")]
251 cont.args = []
252 for arg in split_list(str_def, ","):
253 candidate = WType.from_string(arg.strip(), containing_file, line_number)
254 if candidate == None:
255 return None
256 if candidate.name == "void":
257 return None
258 cont.args.append(candidate)
259 return cont
260
261 #Translators between Python and C++ containers
262 #Base Type
263 class Translator:
264 tmp_cntr = 0
265 typename = "DefaultType"
266 orig_name = "DefaultCpp"
267
268 @classmethod
269 def gen_type(c, types):
270 return "\nImplement a function that outputs the c++ type of this container here\n"
271
272 @classmethod
273 def translate(c, varname, types, prefix):
274 return "\nImplement a function translating a python container to a c++ container here\n"
275
276 @classmethod
277 def translate_cpp(c, varname, types, prefix, ref):
278 return "\nImplement a function translating a c++ container to a python container here\n"
279
280 #Translates list-types (vector, pool, set), that only differ in their name and
281 #the name of the insertion function
282 class PythonListTranslator(Translator):
283 typename = "boost::python::list"
284 insert_name = "Default"
285
286 #generate the c++ type string
287 @classmethod
288 def gen_type(c, types):
289 text = c.orig_name + "<"
290 if types[0].name in primitive_types:
291 text += types[0].name
292 elif types[0].name in known_containers:
293 text += known_containers[types[0].name].gen_type(types[0].cont.args)
294 else:
295 text += class_by_name(types[0].name).namespace + "::" + types[0].name
296 if types[0].attr_type == attr_types.star:
297 text += "*"
298 text += ">"
299 return text
300
301 #Generate C++ code to translate from a boost::python::list
302 @classmethod
303 def translate(c, varname, types, prefix):
304 text = prefix + c.gen_type(types) + " " + varname + "___tmp;"
305 cntr_name = "cntr_" + str(Translator.tmp_cntr)
306 Translator.tmp_cntr = Translator.tmp_cntr + 1
307 text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "); " + cntr_name + "++)"
308 text += prefix + "{"
309 tmp_name = "tmp_" + str(Translator.tmp_cntr)
310 Translator.tmp_cntr = Translator.tmp_cntr + 1
311 if types[0].name in known_containers:
312 text += prefix + "\t" + known_containers[types[0].name].typename + " " + tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "[" + cntr_name + "]);"
313 text += known_containers[types[0].name].translate(tmp_name, types[0].cont.args, prefix+"\t")
314 tmp_name = tmp_name + "___tmp"
315 text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");"
316 elif types[0].name in classnames:
317 text += prefix + "\t" + types[0].name + "* " + tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "[" + cntr_name + "]);"
318 if types[0].attr_type == attr_types.star:
319 text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + "->get_cpp_obj());"
320 else:
321 text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());"
322 else:
323 text += prefix + "\t" + types[0].name + " " + tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "[" + cntr_name + "]);"
324 text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");"
325 text += prefix + "}"
326 return text
327
328 #Generate C++ code to translate to a boost::python::list
329 @classmethod
330 def translate_cpp(c, varname, types, prefix, ref):
331 text = prefix + c.typename + " " + varname + "___tmp;"
332 tmp_name = "tmp_" + str(Translator.tmp_cntr)
333 Translator.tmp_cntr = Translator.tmp_cntr + 1
334 if ref:
335 text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
336 else:
337 text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
338 text += prefix + "{"
339 if types[0].name in classnames:
340 if types[0].attr_type == attr_types.star:
341 text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));"
342 else:
343 text += prefix + "\t" + varname + "___tmp.append(*" + types[0].name + "::get_py_obj(&" + tmp_name + "));"
344 elif types[0].name in known_containers:
345 text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[0].attr_type == attr_types.star)
346 text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + "___tmp);"
347 else:
348 text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");"
349 text += prefix + "}"
350 return text
351
352 class IDictTranslator(PythonListTranslator):
353 typename = "boost::python::list"
354 orig_name = "idict"
355 insert_name = ""
356
357 #Sub-type for std::set
358 class SetTranslator(PythonListTranslator):
359 insert_name = ".insert"
360 orig_name = "std::set"
361
362 #Sub-type for std::vector
363 class VectorTranslator(PythonListTranslator):
364 insert_name = ".push_back"
365 orig_name = "std::vector"
366
367 #Sub-type for pool
368 class PoolTranslator(PythonListTranslator):
369 insert_name = ".insert"
370 orig_name = "pool"
371
372 #Translates dict-types (dict, std::map), that only differ in their name and
373 #the name of the insertion function
374 class PythonDictTranslator(Translator):
375 typename = "boost::python::dict"
376 insert_name = "Default"
377
378 @classmethod
379 def gen_type(c, types):
380 text = c.orig_name + "<"
381 if types[0].name in primitive_types:
382 text += types[0].name
383 elif types[0].name in known_containers:
384 text += known_containers[types[0].name].gen_type(types[0].cont.args)
385 else:
386 text += class_by_name(types[0].name).namespace + "::" + types[0].name
387 if types[0].attr_type == attr_types.star:
388 text += "*"
389 text += ", "
390 if types[1].name in primitive_types:
391 text += types[1].name
392 elif types[1].name in known_containers:
393 text += known_containers[types[1].name].gen_type(types[1].cont.args)
394 else:
395 text += class_by_name(types[1].name).namespace + "::" + types[1].name
396 if types[1].attr_type == attr_types.star:
397 text += "*"
398 text += ">"
399 return text
400
401 #Generate c++ code to translate from a boost::python::dict
402 @classmethod
403 def translate(c, varname, types, prefix):
404 text = prefix + c.gen_type(types) + " " + varname + "___tmp;"
405 text += prefix + "boost::python::list " + varname + "_keylist = " + varname + ".keys();"
406 cntr_name = "cntr_" + str(Translator.tmp_cntr)
407 Translator.tmp_cntr = Translator.tmp_cntr + 1
408 text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "_keylist); " + cntr_name + "++)"
409 text += prefix + "{"
410 key_tmp_name = "key_tmp_" + str(Translator.tmp_cntr)
411 val_tmp_name = "val_tmp_" + str(Translator.tmp_cntr)
412 Translator.tmp_cntr = Translator.tmp_cntr + 1
413
414 if types[0].name in known_containers:
415 text += prefix + "\t" + known_containers[types[0].name].typename + " " + key_tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "_keylist[ " + cntr_name + " ]);"
416 text += known_containers[types[0].name].translate(key_tmp_name, types[0].cont.args, prefix+"\t")
417 key_tmp_name = key_tmp_name + "___tmp"
418 elif types[0].name in classnames:
419 text += prefix + "\t" + types[0].name + "* " + key_tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "_keylist[ " + cntr_name + " ]);"
420 else:
421 text += prefix + "\t" + types[0].name + " " + key_tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "_keylist[ " + cntr_name + " ]);"
422
423 if types[1].name in known_containers:
424 text += prefix + "\t" + known_containers[types[1].name].typename + " " + val_tmp_name + " = boost::python::extract<" + known_containers[types[1].name].typename + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
425 text += known_containers[types[1].name].translate(val_tmp_name, types[1].cont.args, prefix+"\t")
426 val_tmp_name = val_tmp_name + "___tmp"
427 elif types[1].name in classnames:
428 text += prefix + "\t" + types[1].name + "* " + val_tmp_name + " = boost::python::extract<" + types[1].name + "*>(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
429 else:
430 text += prefix + "\t" + types[1].name + " " + val_tmp_name + " = boost::python::extract<" + types[1].name + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
431
432 text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(std::pair<" + types[0].gen_text_cpp() + ", " + types[1].gen_text_cpp() + ">("
433
434 if types[0].name not in classnames:
435 text += key_tmp_name
436 else:
437 if types[0].attr_type != attr_types.star:
438 text += "*"
439 text += key_tmp_name + "->get_cpp_obj()"
440
441 text += ", "
442 if types[1].name not in classnames:
443 text += val_tmp_name
444 else:
445 if types[1].attr_type != attr_types.star:
446 text += "*"
447 text += val_tmp_name + "->get_cpp_obj()"
448 text += "));\n" + prefix + "}"
449 return text
450
451 #Generate c++ code to translate to a boost::python::dict
452 @classmethod
453 def translate_cpp(c, varname, types, prefix, ref):
454 text = prefix + c.typename + " " + varname + "___tmp;"
455 tmp_name = "tmp_" + str(Translator.tmp_cntr)
456 Translator.tmp_cntr = Translator.tmp_cntr + 1
457 if ref:
458 text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
459 else:
460 text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
461 text += prefix + "{"
462 if types[1].name in known_containers:
463 text += prefix + "\tauto " + tmp_name + "_second = " + tmp_name + ".second;"
464 text += known_containers[types[1].name].translate_cpp(tmp_name + "_second", types[1].cont.args, prefix + "\t", types[1].attr_type == attr_types.star)
465
466 if types[0].name in classnames:
467 text += prefix + "\t" + varname + "___tmp[" + types[0].name + "::get_py_obj(" + tmp_name + ".first)] = "
468 elif types[0].name not in known_containers:
469 text += prefix + "\t" + varname + "___tmp[" + tmp_name + ".first] = "
470
471 if types[1].name in classnames:
472 if types[1].attr_type == attr_types.star:
473 text += types[1].name + "::get_py_obj(" + tmp_name + ".second);"
474 else:
475 text += "*" + types[1].name + "::get_py_obj(&" + tmp_name + ".second);"
476 elif types[1].name in known_containers:
477 text += tmp_name + "_second___tmp;"
478 else:
479 text += tmp_name + ".second;"
480 text += prefix + "}"
481 return text
482
483 #Sub-type for dict
484 class DictTranslator(PythonDictTranslator):
485 insert_name = "insert"
486 orig_name = "dict"
487
488 #Sub_type for std::map
489 class MapTranslator(PythonDictTranslator):
490 insert_name = "insert"
491 orig_name = "std::map"
492
493 #Translator for std::pair. Derived from PythonDictTranslator because the
494 #gen_type function is the same (because both have two template parameters)
495 class TupleTranslator(PythonDictTranslator):
496 typename = "boost::python::tuple"
497 orig_name = "std::pair"
498
499 #Generate c++ code to translate from a boost::python::tuple
500 @classmethod
501 def translate(c, varname, types, prefix):
502 text = prefix + types[0].name + " " + varname + "___tmp_0 = boost::python::extract<" + types[0].name + ">(" + varname + "[0]);"
503 text += prefix + types[1].name + " " + varname + "___tmp_1 = boost::python::extract<" + types[1].name + ">(" + varname + "[1]);"
504 text += prefix + TupleTranslator.gen_type(types) + " " + varname + "___tmp("
505 if types[0].name.split(" ")[-1] in primitive_types:
506 text += varname + "___tmp_0, "
507 else:
508 text += varname + "___tmp_0.get_cpp_obj(), "
509 if types[1].name.split(" ")[-1] in primitive_types:
510 text += varname + "___tmp_1);"
511 else:
512 text += varname + "___tmp_1.get_cpp_obj());"
513 return text
514
515 #Generate c++ code to translate to a boost::python::tuple
516 @classmethod
517 def translate_cpp(c, varname, types, prefix, ref):
518 # if the tuple is a pair of SigSpecs (aka SigSig), then we need
519 # to call get_py_obj() on each item in the tuple
520 if types[0].name in classnames:
521 first_var = types[0].name + "::get_py_obj(" + varname + ".first)"
522 else:
523 first_var = varname + ".first"
524 if types[1].name in classnames:
525 second_var = types[1].name + "::get_py_obj(" + varname + ".second)"
526 else:
527 second_var = varname + ".second"
528 text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + first_var + ", " + second_var + ");"
529 return text
530
531 #Associate the Translators with their c++ type
532 known_containers = {
533 "std::set" : SetTranslator,
534 "std::vector" : VectorTranslator,
535 "pool" : PoolTranslator,
536 "idict" : IDictTranslator,
537 "dict" : DictTranslator,
538 "std::pair" : TupleTranslator,
539 "std::map" : MapTranslator
540 }
541
542 class Attribute:
543 wtype = None
544 varname = None
545 is_const = False
546 default_value = None
547 pos = None
548 pos_counter = 0
549
550 def __init__(self, wtype, varname, is_const = False, default_value = None):
551 self.wtype = wtype
552 self.varname = varname
553 self.is_const = is_const
554 self.default_value = None
555 self.container = None
556
557 @staticmethod
558 def from_string(str_def, containing_file, line_number):
559 if len(str_def) < 3:
560 return None
561 orig = str_def
562 arg = Attribute(None, None)
563 prefix = ""
564 arg.wtype = None
565 arg.varname = None
566 arg.is_const = False
567 arg.default_value = None
568 arg.container = None
569 if str.startswith(str_def, "const "):
570 arg.is_const = True
571 str_def = str_def[6:]
572 if str.startswith(str_def, "unsigned "):
573 prefix = "unsigned "
574 str_def = str_def[9:]
575 while str.startswith(str_def, "long "):
576 prefix= "long " + prefix
577 str_def = str_def[5:]
578 while str.startswith(str_def, "short "):
579 prefix = "short " + prefix
580 str_def = str_def[6:]
581
582 if str_def.find("<") != -1 and str_def.find("<") < str_def.find(" "):
583 closing = find_closing(str_def[str_def.find("<"):], "<", ">") + str_def.find("<") + 1
584 arg.wtype = WType.from_string(str_def[:closing].strip(), containing_file, line_number)
585 str_def = str_def[closing+1:]
586 else:
587 if str_def.count(" ") > 0:
588 arg.wtype = WType.from_string(prefix + str_def[:str_def.find(" ")].strip(), containing_file, line_number)
589 str_def = str_def[str_def.find(" ")+1:]
590 else:
591 arg.wtype = WType.from_string(prefix + str_def.strip(), containing_file, line_number)
592 str_def = ""
593 arg.varname = ""
594
595 if arg.wtype == None:
596 return None
597 if str_def.count("=") == 0:
598 arg.varname = str_def.strip()
599 if arg.varname.find(" ") > 0:
600 return None
601 else:
602 arg.varname = str_def[:str_def.find("=")].strip()
603 if arg.varname.find(" ") > 0:
604 return None
605 str_def = str_def[str_def.find("=")+1:].strip()
606 arg.default_value = str_def[arg.varname.find("=")+1:].strip()
607 if len(arg.varname) == 0:
608 arg.varname = None
609 return arg
610 if arg.varname[0] == '*':
611 arg.wtype.attr_type = attr_types.star
612 arg.varname = arg.varname[1:]
613 elif arg.varname[0] == '&':
614 if arg.wtype.attr_type != attr_types.default:
615 return None
616 if arg.varname[1] == '&':
617 arg.wtype.attr_type = attr_types.ampamp
618 arg.varname = arg.varname[2:]
619 else:
620 arg.wtype.attr_type = attr_types.amp
621 arg.varname = arg.varname[1:]
622 return arg
623
624 #Generates the varname. If the attribute has no name in the header file,
625 #a name is generated
626 def gen_varname(self):
627 if self.varname != None:
628 return self.varname
629 if self.wtype.name == "void":
630 return ""
631 if self.pos == None:
632 self.pos = Attribute.pos_counter
633 Attribute.pos_counter = Attribute.pos_counter + 1
634 return "gen_varname_" + str(self.pos)
635
636 #Generates the text for the function headers with wrapper types
637 def gen_listitem(self):
638 prefix = ""
639 if self.is_const:
640 prefix = "const "
641 if self.wtype.name in classnames:
642 return prefix + self.wtype.name + "* " + self.gen_varname()
643 if self.wtype.name in known_containers:
644 return prefix + known_containers[self.wtype.name].typename + " " + self.gen_varname()
645 return prefix + self.wtype.name + " " + self.gen_varname()
646
647 #Generates the test for the function headers with c++ types
648 def gen_listitem_cpp(self):
649 prefix = ""
650 if self.is_const:
651 prefix = "const "
652 infix = ""
653 if self.wtype.attr_type == attr_types.star:
654 infix = "*"
655 elif self.wtype.attr_type == attr_types.amp:
656 infix = "&"
657 elif self.wtype.attr_type == attr_types.ampamp:
658 infix = "&&"
659 if self.wtype.name in known_containers:
660 return prefix + known_containers[self.wtype.name].gen_type(self.wtype.cont.args) + " " + infix + self.gen_varname()
661 if self.wtype.name in classnames:
662 return prefix + class_by_name(self.wtype.name).namespace + "::" + self.wtype.name + " " + infix + self.gen_varname()
663 return prefix + self.wtype.name + " " + infix + self.gen_varname()
664
665 #Generates the listitem withtout the varname, so the signature can be
666 #compared
667 def gen_listitem_hash(self):
668 prefix = ""
669 if self.is_const:
670 prefix = "const "
671 if self.wtype.name in classnames:
672 return prefix + self.wtype.name + "* "
673 if self.wtype.name in known_containers:
674 return known_containers[self.wtype.name].typename
675 return prefix + self.wtype.name
676
677 #Generate Translation code for the attribute
678 def gen_translation(self):
679 if self.wtype.name in known_containers:
680 return known_containers[self.wtype.name].translate(self.gen_varname(), self.wtype.cont.args, "\n\t\t")
681 return ""
682
683 #Generate Translation code from c++ for the attribute
684 def gen_translation_cpp(self):
685 if self.wtype.name in known_containers:
686 return known_containers[self.wtype.name].translate_cpp(self.gen_varname(), self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
687 return ""
688
689 #Generate Text for the call
690 def gen_call(self):
691 ret = self.gen_varname()
692 if self.wtype.name in known_containers:
693 if self.wtype.attr_type == attr_types.star:
694 return "&" + ret + "___tmp"
695 return ret + "___tmp"
696 if self.wtype.name in classnames:
697 if self.wtype.attr_type != attr_types.star:
698 ret = "*" + ret
699 return ret + "->get_cpp_obj()"
700 if self.wtype.name == "char *" and self.gen_varname() in ["format", "fmt"]:
701 return "\"%s\", " + self.gen_varname()
702 if self.wtype.attr_type == attr_types.star:
703 return "&" + ret
704 return ret
705
706 def gen_call_cpp(self):
707 ret = self.gen_varname()
708 if self.wtype.name.split(" ")[-1] in primitive_types or self.wtype.name in enum_names:
709 if self.wtype.attr_type == attr_types.star:
710 return "&" + ret
711 return ret
712 if self.wtype.name not in classnames:
713 if self.wtype.attr_type == attr_types.star:
714 return "&" + ret + "___tmp"
715 return ret + "___tmp"
716 if self.wtype.attr_type != attr_types.star:
717 ret = "*" + ret
718 return self.wtype.name + "::get_py_obj(" + self.gen_varname() + ")"
719
720 #Generate cleanup code
721 def gen_cleanup(self):
722 if self.wtype.name in primitive_types or self.wtype.name in classnames or self.wtype.name in enum_names or not self.wtype.attr_type == attr_types.star or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
723 return ""
724 return "\n\t\tdelete " + self.gen_varname() + "___tmp;"
725
726 class WClass:
727 name = None
728 namespace = None
729 link_type = None
730 base_class = None
731 id_ = None
732 string_id = None
733 hash_id = None
734 needs_clone = False
735 found_funs = []
736 found_vars = []
737 found_constrs = []
738
739 def __init__(self, name, link_type, id_, string_id = None, hash_id = None, needs_clone = False):
740 self.name = name
741 self.namespace = None
742 self.base_class = None
743 self.link_type = link_type
744 self.id_ = id_
745 self.string_id = string_id
746 self.hash_id = hash_id
747 self.needs_clone = needs_clone
748 self.found_funs = []
749 self.found_vars = []
750 self.found_constrs = []
751
752 def printable_constrs(self):
753 ret = 0
754 for con in self.found_constrs:
755 if not con.protected:
756 ret += 1
757 return ret
758
759 def gen_decl(self, filename):
760 long_name = self.namespace + "::" + self.name
761
762 text = "\n\t// WRAPPED from " + filename
763 text += "\n\tstruct " + self.name
764 if self.link_type == link_types.derive:
765 text += " : public " + self.namespace + "::" + self.name
766 text += "\n\t{\n"
767
768 if self.link_type != link_types.derive:
769
770 text += "\t\t" + long_name + "* ref_obj;\n"
771
772 if self.link_type == link_types.ref_copy or self.link_type == link_types.pointer:
773 text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn ref_obj;\n\t\t}\n"
774 elif self.link_type == link_types.global_list:
775 text += "\t\t" + self.id_.wtype.name + " " + self.id_.varname + ";\n"
776 text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{"
777 text += "\n\t\t\t" + long_name + "* ret = " + long_name + "::get_all_" + self.name.lower() + "s()->at(this->" + self.id_.varname + ");"
778 text += "\n\t\t\tif(ret != NULL && ret == this->ref_obj)"
779 text += "\n\t\t\t\treturn ret;"
780 text += "\n\t\t\tthrow std::runtime_error(\"" + self.name + "'s c++ object does not exist anymore.\");"
781 text += "\n\t\t\treturn NULL;"
782 text += "\n\t\t}\n"
783
784 #if self.link_type != link_types.pointer:
785 text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{"
786 text += "\n\t\t\tif(ref == nullptr){"
787 text += "\n\t\t\t\tthrow std::runtime_error(\"" + self.name + " does not exist.\");"
788 text += "\n\t\t\t}"
789 text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
790 if self.link_type == link_types.pointer:
791 text += "\n\t\t\tret->ref_obj = ref;"
792 if self.link_type == link_types.ref_copy:
793 if self.needs_clone:
794 text += "\n\t\t\tret->ref_obj = ref->clone();"
795 else:
796 text += "\n\t\t\tret->ref_obj = new "+long_name+"(*ref);"
797 if self.link_type == link_types.global_list:
798 text += "\n\t\t\tret->ref_obj = ref;"
799 text += "\n\t\t\tret->" + self.id_.varname + " = ret->ref_obj->" + self.id_.varname + ";"
800 text += "\n\t\t\treturn ret;"
801 text += "\n\t\t}\n"
802
803 if self.link_type == link_types.ref_copy:
804 text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + " ref)\n\t\t{"
805 text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
806 if self.needs_clone:
807 text += "\n\t\t\tret->ref_obj = ref.clone();"
808 else:
809 text += "\n\t\t\tret->ref_obj = new "+long_name+"(ref);"
810 text += "\n\t\t\treturn ret;"
811 text += "\n\t\t}\n"
812
813 for con in self.found_constrs:
814 text += con.gen_decl()
815 if self.base_class is not None:
816 text += "\n\t\tvirtual ~" + self.name + "() { };"
817 for var in self.found_vars:
818 text += var.gen_decl()
819 for fun in self.found_funs:
820 text += fun.gen_decl()
821
822
823 if self.link_type == link_types.derive:
824 duplicates = {}
825 for fun in self.found_funs:
826 if fun.name in duplicates:
827 fun.gen_alias()
828 duplicates[fun.name].gen_alias()
829 else:
830 duplicates[fun.name] = fun
831
832 text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn (" + self.namespace + "::" + self.name +"*)this;\n\t\t}\n"
833 text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{"
834 text += "\n\t\t\treturn (" + self.name + "*)ref;"
835 text += "\n\t\t}\n"
836
837 for con in self.found_constrs:
838 text += con.gen_decl_derive()
839 for var in self.found_vars:
840 text += var.gen_decl()
841 for fun in self.found_funs:
842 text += fun.gen_decl_virtual()
843
844 if self.hash_id != None:
845 text += "\n\t\tunsigned int get_hash_py()"
846 text += "\n\t\t{"
847 text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";"
848 text += "\n\t\t}"
849
850 text += "\n\t};\n"
851
852 if self.link_type == link_types.derive:
853 text += "\n\tstruct " + self.name + "Wrap : " + self.name + ", boost::python::wrapper<" + self.name + ">"
854 text += "\n\t{"
855
856 for con in self.found_constrs:
857 text += con.gen_decl_wrapperclass()
858 for fun in self.found_funs:
859 text += fun.gen_default_impl()
860
861 text += "\n\t};"
862
863 text += "\n\tstd::ostream &operator<<(std::ostream &ostr, const " + self.name + " &ref)"
864 text += "\n\t{"
865 text += "\n\t\tostr << \"" + self.name
866 if self.string_id != None:
867 text +=" \\\"\""
868 text += " << ref.get_cpp_obj()->" + self.string_id
869 text += " << \"\\\"\""
870 else:
871 text += " at \" << ref.get_cpp_obj()"
872 text += ";"
873 text += "\n\t\treturn ostr;"
874 text += "\n\t}"
875 text += "\n"
876
877 return text
878
879 def gen_funs(self, filename):
880 text = ""
881 if self.link_type != link_types.derive:
882 for con in self.found_constrs:
883 text += con.gen_def()
884 for var in self.found_vars:
885 text += var.gen_def()
886 for fun in self.found_funs:
887 text += fun.gen_def()
888 else:
889 for var in self.found_vars:
890 text += var.gen_def()
891 for fun in self.found_funs:
892 text += fun.gen_def_virtual()
893 return text
894
895 def gen_boost_py_body(self):
896 text = ""
897 if self.printable_constrs() == 0 or not self.contains_default_constr():
898 text += ", no_init"
899 text += ")"
900 text += "\n\t\t\t.def(boost::python::self_ns::str(boost::python::self_ns::self))"
901 text += "\n\t\t\t.def(boost::python::self_ns::repr(boost::python::self_ns::self))"
902 for con in self.found_constrs:
903 text += con.gen_boost_py()
904 for var in self.found_vars:
905 text += var.gen_boost_py()
906 static_funs = []
907 for fun in self.found_funs:
908 text += fun.gen_boost_py()
909 if fun.is_static and fun.alias not in static_funs:
910 static_funs.append(fun.alias)
911 for fun in static_funs:
912 text += "\n\t\t\t.staticmethod(\"" + fun + "\")"
913
914 if self.hash_id != None:
915 text += "\n\t\t\t.def(\"__hash__\", &" + self.name + "::get_hash_py)"
916 text += "\n\t\t\t;\n"
917 return text
918
919 def gen_boost_py(self):
920 body = self.gen_boost_py_body()
921 base_info = ""
922 if self.base_class is not None:
923 base_info = ", bases<" + (self.base_class.name) + ">"
924
925 if self.link_type == link_types.derive:
926 text = "\n\t\tclass_<" + self.name + base_info + ">(\"Cpp" + self.name + "\""
927 text += body
928 text += "\n\t\tclass_<" + self.name
929 text += "Wrap, boost::noncopyable"
930 text += ">(\"" + self.name + "\""
931 text += body
932 else:
933 text = "\n\t\tclass_<" + self.name + base_info + ">(\"" + self.name + "\""
934 text += body
935 return text
936
937
938 def contains_default_constr(self):
939 for c in self.found_constrs:
940 if len(c.args) == 0:
941 return True
942 return False
943
944 #CONFIGURE HEADER-FILES TO BE PARSED AND CLASSES EXPECTED IN THEM HERE
945
946 sources = [
947 Source("kernel/celltypes",[
948 WClass("CellType", link_types.pointer, None, None, "type.hash()", True),
949 WClass("CellTypes", link_types.pointer, None, None, None, True)
950 ]
951 ),
952 Source("kernel/consteval",[
953 WClass("ConstEval", link_types.pointer, None, None, None, True)
954 ]
955 ),
956 Source("kernel/log",[]),
957 Source("kernel/register",[
958 WClass("Pass", link_types.derive, None, None, None, True),
959 ]
960 ),
961 Source("kernel/rtlil",[
962 WClass("IdString", link_types.ref_copy, None, "str()", "hash()"),
963 WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"),
964 WClass("AttrObject", link_types.ref_copy, None, None, None),
965 WClass("Selection", link_types.ref_copy, None, None, None),
966 WClass("Monitor", link_types.derive, None, None, None),
967 WClass("CaseRule",link_types.ref_copy, None, None, None, True),
968 WClass("SwitchRule",link_types.ref_copy, None, None, None, True),
969 WClass("SyncRule", link_types.ref_copy, None, None, None, True),
970 WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"),
971 WClass("SigChunk", link_types.ref_copy, None, None, None),
972 WClass("SigBit", link_types.ref_copy, None, None, "hash()"),
973 WClass("SigSpec", link_types.ref_copy, None, None, "hash()"),
974 WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
975 WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
976 WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
977 WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
978 WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()")
979 ]
980 ),
981 #Source("kernel/satgen",[
982 # ]
983 # ),
984 #Source("libs/ezsat/ezsat",[
985 # ]
986 # ),
987 #Source("libs/ezsat/ezminisat",[
988 # ]
989 # ),
990 Source("kernel/sigtools",[
991 WClass("SigMap", link_types.pointer, None, None, None, True)
992 ]
993 ),
994 Source("kernel/yosys",[
995 ]
996 ),
997 Source("kernel/cost",[])
998 ]
999
1000 blacklist_methods = ["YOSYS_NAMESPACE::Pass::run_register", "YOSYS_NAMESPACE::Module::Pow"]
1001
1002 enum_names = ["State","SyncType","ConstFlags"]
1003
1004 enums = [] #Do not edit
1005 glbls = []
1006
1007 unowned_functions = []
1008
1009 classnames = []
1010 for source in sources:
1011 for wclass in source.classes:
1012 classnames.append(wclass.name)
1013
1014 def class_by_name(name):
1015 for source in sources:
1016 for wclass in source.classes:
1017 if wclass.name == name:
1018 return wclass
1019 return None
1020
1021 def enum_by_name(name):
1022 for e in enums:
1023 if e.name == name:
1024 return e
1025 return None
1026
1027 def find_closing(text, open_tok, close_tok):
1028 if text.find(open_tok) == -1 or text.find(close_tok) <= text.find(open_tok):
1029 return text.find(close_tok)
1030 return text.find(close_tok) + find_closing(text[text.find(close_tok)+1:], open_tok, close_tok) + 1
1031
1032 def unpretty_string(s):
1033 s = s.strip()
1034 while s.find(" ") != -1:
1035 s = s.replace(" "," ")
1036 while s.find("\t") != -1:
1037 s = s.replace("\t"," ")
1038 s = s.replace(" (","(")
1039 return s
1040
1041 class WEnum:
1042 name = None
1043 namespace = None
1044 values = []
1045
1046 def from_string(str_def, namespace, line_number):
1047 str_def = str_def.strip()
1048 if not str.startswith(str_def, "enum "):
1049 return None
1050 if str_def.count(";") != 1:
1051 return None
1052 str_def = str_def[5:]
1053 enum = WEnum()
1054 split = str_def.split(":")
1055 if(len(split) != 2):
1056 return None
1057 enum.name = split[0].strip()
1058 if enum.name not in enum_names:
1059 return None
1060 str_def = split[1]
1061 if str_def.count("{") != str_def.count("}") != 1:
1062 return None
1063 if len(str_def) < str_def.find("}")+2 or str_def[str_def.find("}")+1] != ';':
1064 return None
1065 str_def = str_def.split("{")[-1].split("}")[0]
1066 enum.values = []
1067 for val in str_def.split(','):
1068 enum.values.append(val.strip().split('=')[0].strip())
1069 enum.namespace = namespace
1070 return enum
1071
1072 def gen_boost_py(self):
1073 text = "\n\t\tenum_<" + self.namespace + "::" + self.name + ">(\"" + self.name + "\")\n"
1074 for value in self.values:
1075 text += "\t\t\t.value(\"" + value + "\"," + self.namespace + "::" + value + ")\n"
1076 text += "\t\t\t;\n"
1077 return text
1078
1079 def __str__(self):
1080 ret = "Enum " + self.namespace + "::" + self.name + "(\n"
1081 for val in self.values:
1082 ret = ret + "\t" + val + "\n"
1083 return ret + ")"
1084
1085 def __repr__(self):
1086 return __str__(self)
1087
1088 class WConstructor:
1089 orig_text = None
1090 args = []
1091 containing_file = None
1092 member_of = None
1093 duplicate = False
1094 protected = False
1095
1096 def __init__(self, containing_file, class_):
1097 self.orig_text = "Auto generated default constructor"
1098 self.args = []
1099 self.containing_file = containing_file
1100 self.member_of = class_
1101 self.protected = False
1102
1103 def from_string(str_def, containing_file, class_, line_number, protected = False):
1104 if class_ == None:
1105 return None
1106 if str_def.count("delete;") > 0:
1107 return None
1108 con = WConstructor(containing_file, class_)
1109 con.orig_text = str_def
1110 con.args = []
1111 con.duplicate = False
1112 con.protected = protected
1113 if str.startswith(str_def, "inline "):
1114 str_def = str_def[7:]
1115 if not str.startswith(str_def, class_.name + "("):
1116 return None
1117 str_def = str_def[len(class_.name)+1:]
1118 found = find_closing(str_def, "(", ")")
1119 if found == -1:
1120 return None
1121 str_def = str_def[0:found].strip()
1122 if len(str_def) == 0:
1123 return con
1124 for arg in split_list(str_def, ","):
1125 parsed = Attribute.from_string(arg.strip(), containing_file, line_number)
1126 if parsed == None:
1127 return None
1128 con.args.append(parsed)
1129 return con
1130
1131 def gen_decl(self):
1132 if self.duplicate or self.protected:
1133 return ""
1134 text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1135 text += "\n\t\t" + self.member_of.name + "("
1136 for arg in self.args:
1137 text += arg.gen_listitem() + ", "
1138 if len(self.args) > 0:
1139 text = text[:-2]
1140 text += ");\n"
1141 return text
1142
1143 def gen_decl_derive(self):
1144 if self.duplicate or self.protected:
1145 return ""
1146 text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1147 text += "\n\t\t" + self.member_of.name + "("
1148 for arg in self.args:
1149 text += arg.gen_listitem() + ", "
1150 if len(self.args) > 0:
1151 text = text[:-2]
1152 text += ")"
1153 if len(self.args) == 0:
1154 return text + "{}"
1155 text += " : "
1156 text += self.member_of.namespace + "::" + self.member_of.name + "("
1157 for arg in self.args:
1158 text += arg.gen_call() + ", "
1159 if len(self.args) > 0:
1160 text = text[:-2]
1161 text += "){}\n"
1162 return text
1163
1164 def gen_decl_wrapperclass(self):
1165 if self.duplicate or self.protected:
1166 return ""
1167 text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1168 text += "\n\t\t" + self.member_of.name + "Wrap("
1169 for arg in self.args:
1170 text += arg.gen_listitem() + ", "
1171 if len(self.args) > 0:
1172 text = text[:-2]
1173 text += ")"
1174 if len(self.args) == 0:
1175 return text + "{}"
1176 text += " : "
1177 text += self.member_of.name + "("
1178 for arg in self.args:
1179 text += arg.gen_call() + ", "
1180 if len(self.args) > 0:
1181 text = text[:-2]
1182 text += "){}\n"
1183 return text
1184
1185 def gen_decl_hash_py(self):
1186 text = self.member_of.name + "("
1187 for arg in self.args:
1188 text += arg.gen_listitem_hash() + ", "
1189 if len(self.args) > 0:
1190 text = text[:-2]
1191 text += ");"
1192 return text
1193
1194 def gen_def(self):
1195 if self.duplicate or self.protected:
1196 return ""
1197 text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1198 text += "\n\t" + self.member_of.name + "::" + self.member_of.name + "("
1199 for arg in self.args:
1200 text += arg.gen_listitem() + ", "
1201 if len(self.args) > 0:
1202 text = text[:-2]
1203 text +=")\n\t{"
1204 for arg in self.args:
1205 text += arg.gen_translation()
1206 if self.member_of.link_type != link_types.derive:
1207 text += "\n\t\tthis->ref_obj = new " + self.member_of.namespace + "::" + self.member_of.name + "("
1208 for arg in self.args:
1209 text += arg.gen_call() + ", "
1210 if len(self.args) > 0:
1211 text = text[:-2]
1212 if self.member_of.link_type != link_types.derive:
1213 text += ");"
1214 if self.member_of.link_type == link_types.global_list:
1215 text += "\n\t\tthis->" + self.member_of.id_.varname + " = this->ref_obj->" + self.member_of.id_.varname + ";"
1216 for arg in self.args:
1217 text += arg.gen_cleanup()
1218 text += "\n\t}\n"
1219 return text
1220
1221 def gen_boost_py(self):
1222 if self.duplicate or self.protected or len(self.args) == 0:
1223 return ""
1224 text = "\n\t\t\t.def(init"
1225 text += "<"
1226 for a in self.args:
1227 text += a.gen_listitem_hash() + ", "
1228 text = text[0:-2] + ">())"
1229 return text
1230
1231 class WFunction:
1232 orig_text = None
1233 is_static = False
1234 is_inline = False
1235 is_virtual = False
1236 ret_attr_type = attr_types.default
1237 is_operator = False
1238 ret_type = None
1239 name = None
1240 alias = None
1241 args = []
1242 containing_file = None
1243 member_of = None
1244 duplicate = False
1245 namespace = ""
1246
1247 def from_string(str_def, containing_file, class_, line_number, namespace):
1248 if str_def.count("delete;") > 0:
1249 return None
1250 func = WFunction()
1251 func.is_static = False
1252 func.is_inline = False
1253 func.is_virtual = False
1254 func.ret_attr_type = attr_types.default
1255 func.is_operator = False
1256 func.member_of = None
1257 func.orig_text = str_def
1258 func.args = []
1259 func.containing_file = containing_file
1260 func.member_of = class_
1261 func.duplicate = False
1262 func.namespace = namespace
1263 str_def = str_def.replace("operator ","operator")
1264 if str.startswith(str_def, "static "):
1265 func.is_static = True
1266 str_def = str_def[7:]
1267 else:
1268 func.is_static = False
1269 if str.startswith(str_def, "inline "):
1270 func.is_inline = True
1271 str_def = str_def[7:]
1272 else:
1273 func.is_inline = False
1274 if str.startswith(str_def, "virtual "):
1275 func.is_virtual = True
1276 str_def = str_def[8:]
1277 else:
1278 func.is_virtual = False
1279
1280 if str_def.count(" ") == 0:
1281 return None
1282
1283 parts = split_list(str_def.strip(), " ")
1284
1285 prefix = ""
1286 i = 0
1287 for part in parts:
1288 if part in ["unsigned", "long", "short"]:
1289 prefix += part + " "
1290 i += 1
1291 else:
1292 break
1293 parts = parts[i:]
1294
1295 if len(parts) <= 1:
1296 return None
1297
1298 func.ret_type = WType.from_string(prefix + parts[0], containing_file, line_number)
1299
1300 if func.ret_type == None:
1301 return None
1302
1303 str_def = parts[1]
1304 for part in parts[2:]:
1305 str_def = str_def + " " + part
1306
1307 found = str_def.find("(")
1308 if found == -1 or (str_def.find(" ") != -1 and found > str_def.find(" ")):
1309 return None
1310 func.name = str_def[:found]
1311 str_def = str_def[found:]
1312 if func.name.find("operator") != -1 and str.startswith(str_def, "()("):
1313 func.name += "()"
1314 str_def = str_def[2:]
1315 str_def = str_def[1:]
1316 if func.name.find("operator") != -1:
1317 func.is_operator = True
1318 if func.name.find("*") == 0:
1319 func.name = func.name.replace("*", "")
1320 func.ret_type.attr_type = attr_types.star
1321 if func.name.find("&&") == 0:
1322 func.name = func.name.replace("&&", "")
1323 func.ret_type.attr_type = attr_types.ampamp
1324 if func.name.find("&") == 0:
1325 func.name = func.name.replace("&", "")
1326 func.ret_type.attr_type = attr_types.amp
1327
1328 found = find_closing(str_def, "(", ")")
1329 if found == -1:
1330 return None
1331 str_def = str_def[0:found]
1332 if func.name in blacklist_methods:
1333 return None
1334 if func.namespace != None and func.namespace != "":
1335 if (func.namespace + "::" + func.name) in blacklist_methods:
1336 return None
1337 if func.member_of != None:
1338 if (func.namespace + "::" + func.member_of.name + "::" + func.name) in blacklist_methods:
1339 return None
1340 if func.is_operator and func.name.replace(" ","").replace("operator","").split("::")[-1] not in wrappable_operators:
1341 return None
1342
1343 testname = func.name
1344 if func.is_operator:
1345 testname = testname[:testname.find("operator")]
1346 if testname.count(")") != 0 or testname.count("(") != 0 or testname.count("~") != 0 or testname.count(";") != 0 or testname.count(">") != 0 or testname.count("<") != 0 or testname.count("throw") != 0:
1347 return None
1348
1349 func.alias = func.name
1350 if func.name in keyword_aliases:
1351 func.alias = keyword_aliases[func.name]
1352 str_def = str_def[:found].strip()
1353 if(len(str_def) == 0):
1354 return func
1355 for arg in split_list(str_def, ","):
1356 if arg.strip() == "...":
1357 continue
1358 parsed = Attribute.from_string(arg.strip(), containing_file, line_number)
1359 if parsed == None:
1360 return None
1361 func.args.append(parsed)
1362 return func
1363
1364 def gen_alias(self):
1365 self.alias = self.name
1366 for arg in self.args:
1367 self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","")
1368
1369 def gen_decl(self):
1370 if self.duplicate:
1371 return ""
1372 text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1373 text += "\n\t\t"
1374 if self.is_static:
1375 text += "static "
1376 text += self.ret_type.gen_text() + " " + self.alias + "("
1377 for arg in self.args:
1378 text += arg.gen_listitem()
1379 text += ", "
1380 if len(self.args) > 0:
1381 text = text[:-2]
1382 text += ");\n"
1383 return text
1384
1385 def gen_decl_virtual(self):
1386 if self.duplicate:
1387 return ""
1388 if not self.is_virtual:
1389 return self.gen_decl()
1390 text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1391 text += "\n\t\tvirtual "
1392 if self.is_static:
1393 text += "static "
1394 text += self.ret_type.gen_text() + " py_" + self.alias + "("
1395 for arg in self.args:
1396 text += arg.gen_listitem()
1397 text += ", "
1398 if len(self.args) > 0:
1399 text = text[:-2]
1400 text += ")"
1401 if len(self.args) == 0:
1402 text += "{}"
1403 else:
1404 text += "\n\t\t{"
1405 for arg in self.args:
1406 text += "\n\t\t\t(void)" + arg.gen_varname() + ";"
1407 text += "\n\t\t}\n"
1408 text += "\n\t\tvirtual "
1409 if self.is_static:
1410 text += "static "
1411 text += self.ret_type.gen_text() + " " + self.name + "("
1412 for arg in self.args:
1413 text += arg.gen_listitem_cpp()
1414 text += ", "
1415 if len(self.args) > 0:
1416 text = text[:-2]
1417 text += ") override;\n"
1418 return text
1419
1420 def gen_decl_hash_py(self):
1421 text = self.ret_type.gen_text() + " " + self.alias + "("
1422 for arg in self.args:
1423 text += arg.gen_listitem_hash() + ", "
1424 if len(self.args) > 0:
1425 text = text[:-2]
1426 text += ");"
1427 return text
1428
1429 def gen_def(self):
1430 if self.duplicate:
1431 return ""
1432 text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1433 text += "\n\t" + self.ret_type.gen_text() + " "
1434 if self.member_of != None:
1435 text += self.member_of.name + "::"
1436 text += self.alias + "("
1437 for arg in self.args:
1438 text += arg.gen_listitem()
1439 text += ", "
1440 if len(self.args) > 0:
1441 text = text[:-2]
1442 text +=")\n\t{"
1443 for arg in self.args:
1444 text += arg.gen_translation()
1445 text += "\n\t\t"
1446 if self.ret_type.name != "void":
1447 if self.ret_type.name in known_containers:
1448 text += self.ret_type.gen_text_cpp()
1449 else:
1450 text += self.ret_type.gen_text()
1451 if self.ret_type.name in classnames or (self.ret_type.name in known_containers and self.ret_type.attr_type == attr_types.star):
1452 text += "*"
1453 text += " ret_ = "
1454 if self.ret_type.name in classnames:
1455 text += self.ret_type.name + "::get_py_obj("
1456 if self.member_of == None:
1457 text += "::" + self.namespace + "::" + self.alias + "("
1458 elif self.is_static:
1459 text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
1460 else:
1461 text += "this->get_cpp_obj()->" + self.name + "("
1462 for arg in self.args:
1463 text += arg.gen_call() + ", "
1464 if len(self.args) > 0:
1465 text = text[:-2]
1466 if self.ret_type.name in classnames:
1467 text += ")"
1468 text += ");"
1469 for arg in self.args:
1470 text += arg.gen_cleanup()
1471 if self.ret_type.name != "void":
1472 if self.ret_type.name in classnames:
1473 text += "\n\t\treturn *ret_;"
1474 elif self.ret_type.name in known_containers:
1475 text += known_containers[self.ret_type.name].translate_cpp("ret_", self.ret_type.cont.args, "\n\t\t", self.ret_type.attr_type == attr_types.star)
1476 text += "\n\t\treturn ret____tmp;"
1477 else:
1478 text += "\n\t\treturn ret_;"
1479 text += "\n\t}\n"
1480 return text
1481
1482 def gen_def_virtual(self):
1483 if self.duplicate:
1484 return ""
1485 if not self.is_virtual:
1486 return self.gen_def()
1487 text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
1488 text += "\n\t"
1489 if self.is_static:
1490 text += "static "
1491 text += self.ret_type.gen_text() + " " + self.member_of.name + "::" + self.name + "("
1492 for arg in self.args:
1493 text += arg.gen_listitem_cpp()
1494 text += ", "
1495 if len(self.args) > 0:
1496 text = text[:-2]
1497 text += ")\n\t{"
1498 for arg in self.args:
1499 text += arg.gen_translation_cpp()
1500 text += "\n\t\t"
1501 if self.member_of == None:
1502 text += "::" + self.namespace + "::" + self.alias + "("
1503 elif self.is_static:
1504 text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
1505 else:
1506 text += "py_" + self.alias + "("
1507 for arg in self.args:
1508 text += arg.gen_call_cpp() + ", "
1509 if len(self.args) > 0:
1510 text = text[:-2]
1511 if self.ret_type.name in classnames:
1512 text += ")"
1513 text += ");"
1514 for arg in self.args:
1515 text += arg.gen_cleanup()
1516 text += "\n\t}\n"
1517 return text
1518
1519 def gen_default_impl(self):
1520 if self.duplicate:
1521 return ""
1522 if not self.is_virtual:
1523 return ""
1524 text = "\n\n\t\t" + self.ret_type.gen_text() + " py_" + self.alias + "("
1525 for arg in self.args:
1526 text += arg.gen_listitem() + ", "
1527 if len(self.args) > 0:
1528 text = text[:-2]
1529
1530 call_string = "py_" + self.alias + "("
1531 for arg in self.args:
1532 call_string += arg.gen_varname() + ", "
1533 if len(self.args) > 0:
1534 call_string = call_string[0:-2]
1535 call_string += ");"
1536
1537 text += ")\n\t\t{"
1538 text += "\n\t\t\tif(boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
1539 text += "\n\t\t\t\t" + call_string
1540 text += "\n\t\t\telse"
1541 text += "\n\t\t\t\t" + self.member_of.name + "::" + call_string
1542 text += "\n\t\t}"
1543
1544 text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
1545 for arg in self.args:
1546 text += arg.gen_listitem() + ", "
1547 if len(self.args) > 0:
1548 text = text[:-2]
1549 text += ")\n\t\t{"
1550 text += "\n\t\t\tthis->" + self.member_of.name + "::" + call_string
1551 text += "\n\t\t}"
1552 return text
1553
1554
1555 def gen_boost_py(self):
1556 if self.duplicate:
1557 return ""
1558 if self.member_of == None:
1559 text = "\n\t\tdef"
1560 else:
1561 text = "\n\t\t\t.def"
1562 if len(self.args) > -1:
1563 if self.ret_type.name in known_containers:
1564 text += "<" + known_containers[self.ret_type.name].typename + " "
1565 else:
1566 text += "<" + self.ret_type.name + " "
1567 if self.member_of == None or self.is_static:
1568 text += "(*)("
1569 else:
1570 text += "(" + self.member_of.name + "::*)("
1571 for a in self.args:
1572 text += a.gen_listitem_hash() + ", "
1573 if len(self.args) > 0:
1574 text = text[0:-2] + ")>"
1575 else:
1576 text += "void)>"
1577
1578 if self.is_operator:
1579 text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\""
1580 else:
1581 if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual:
1582 text += "(\"py_" + self.alias + "\""
1583 else:
1584 text += "(\"" + self.alias + "\""
1585 if self.member_of != None:
1586 text += ", &" + self.member_of.name + "::"
1587 if self.member_of.link_type == link_types.derive and self.is_virtual:
1588 text += "py_" + self.alias
1589 text += ", &" + self.member_of.name + "Wrap::default_py_" + self.alias
1590 else:
1591 text += self.alias
1592
1593 text += ")"
1594 else:
1595 text += ", " + "YOSYS_PYTHON::" + self.alias + ");"
1596 return text
1597
1598 class WMember:
1599 orig_text = None
1600 wtype = attr_types.default
1601 name = None
1602 containing_file = None
1603 member_of = None
1604 namespace = ""
1605 is_const = False
1606
1607 def from_string(str_def, containing_file, class_, line_number, namespace):
1608 member = WMember()
1609 member.orig_text = str_def
1610 member.wtype = None
1611 member.name = ""
1612 member.containing_file = containing_file
1613 member.member_of = class_
1614 member.namespace = namespace
1615 member.is_const = False
1616
1617 if str.startswith(str_def, "const "):
1618 member.is_const = True
1619 str_def = str_def[6:]
1620
1621 if str_def.count(" ") == 0:
1622 return None
1623
1624 parts = split_list(str_def.strip(), " ")
1625
1626 prefix = ""
1627 i = 0
1628 for part in parts:
1629 if part in ["unsigned", "long", "short"]:
1630 prefix += part + " "
1631 i += 1
1632 else:
1633 break
1634 parts = parts[i:]
1635
1636 if len(parts) <= 1:
1637 return None
1638
1639 member.wtype = WType.from_string(prefix + parts[0], containing_file, line_number)
1640
1641 if member.wtype == None:
1642 return None
1643
1644 str_def = parts[1]
1645 for part in parts[2:]:
1646 str_def = str_def + " " + part
1647
1648 if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1:
1649 return None
1650
1651 found = str_def.find(";")
1652 if found == -1:
1653 return None
1654
1655 found_eq = str_def.find("=")
1656 if found_eq != -1:
1657 found = found_eq
1658
1659 member.name = str_def[:found]
1660 str_def = str_def[found+1:]
1661 if member.name.find("*") == 0:
1662 member.name = member.name.replace("*", "")
1663 member.wtype.attr_type = attr_types.star
1664 if member.name.find("&&") == 0:
1665 member.name = member.name.replace("&&", "")
1666 member.wtype.attr_type = attr_types.ampamp
1667 if member.name.find("&") == 0:
1668 member.name = member.name.replace("&", "")
1669 member.wtype.attr_type = attr_types.amp
1670
1671 if(len(str_def.strip()) != 0):
1672 return None
1673
1674 if len(member.name.split(",")) > 1:
1675 member_list = []
1676 for name in member.name.split(","):
1677 name = name.strip();
1678 member_list.append(WMember())
1679 member_list[-1].orig_text = member.orig_text
1680 member_list[-1].wtype = member.wtype
1681 member_list[-1].name = name
1682 member_list[-1].containing_file = member.containing_file
1683 member_list[-1].member_of = member.member_of
1684 member_list[-1].namespace = member.namespace
1685 member_list[-1].is_const = member.is_const
1686 return member_list
1687
1688 return member
1689
1690 def gen_decl(self):
1691 text = "\n\t\t" + self.wtype.gen_text() + " get_var_py_" + self.name + "();\n"
1692 if self.is_const:
1693 return text
1694 if self.wtype.name in classnames:
1695 text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs);\n"
1696 else:
1697 text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs);\n"
1698 return text
1699
1700 def gen_def(self):
1701 text = "\n\t" + self.wtype.gen_text() + " " + self.member_of.name +"::get_var_py_" + self.name + "()"
1702 text += "\n\t{\n\t\t"
1703 if self.wtype.attr_type == attr_types.star:
1704 text += "if(this->get_cpp_obj()->" + self.name + " == NULL)\n\t\t\t"
1705 text += "throw std::runtime_error(\"Member \\\"" + self.name + "\\\" is NULL\");\n\t\t"
1706 if self.wtype.name in known_containers:
1707 text += self.wtype.gen_text_cpp()
1708 else:
1709 text += self.wtype.gen_text()
1710
1711 if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
1712 text += "*"
1713 text += " ret_ = "
1714 if self.wtype.name in classnames:
1715 text += self.wtype.name + "::get_py_obj("
1716 if self.wtype.attr_type != attr_types.star:
1717 text += "&"
1718 text += "this->get_cpp_obj()->" + self.name
1719 if self.wtype.name in classnames:
1720 text += ")"
1721 text += ";"
1722
1723 if self.wtype.name in classnames:
1724 text += "\n\t\treturn *ret_;"
1725 elif self.wtype.name in known_containers:
1726 text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
1727 text += "\n\t\treturn ret____tmp;"
1728 else:
1729 text += "\n\t\treturn ret_;"
1730 text += "\n\t}\n"
1731
1732 if self.is_const:
1733 return text
1734
1735 ret = Attribute(self.wtype, "rhs");
1736
1737 if self.wtype.name in classnames:
1738 text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)"
1739 else:
1740 text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)"
1741 text += "\n\t{"
1742 text += ret.gen_translation()
1743 text += "\n\t\tthis->get_cpp_obj()->" + self.name + " = " + ret.gen_call() + ";"
1744 text += "\n\t}\n"
1745
1746 return text;
1747
1748 def gen_boost_py(self):
1749 text = "\n\t\t\t.add_property(\"" + self.name + "\", &" + self.member_of.name + "::get_var_py_" + self.name
1750 if not self.is_const:
1751 text += ", &" + self.member_of.name + "::set_var_py_" + self.name
1752 text += ")"
1753 return text
1754
1755 class WGlobal:
1756 orig_text = None
1757 wtype = attr_types.default
1758 name = None
1759 containing_file = None
1760 namespace = ""
1761 is_const = False
1762
1763 def from_string(str_def, containing_file, line_number, namespace):
1764 glbl = WGlobal()
1765 glbl.orig_text = str_def
1766 glbl.wtype = None
1767 glbl.name = ""
1768 glbl.containing_file = containing_file
1769 glbl.namespace = namespace
1770 glbl.is_const = False
1771
1772 if not str.startswith(str_def, "extern"):
1773 return None
1774 str_def = str_def[7:]
1775
1776 if str.startswith(str_def, "const "):
1777 glbl.is_const = True
1778 str_def = str_def[6:]
1779
1780 if str_def.count(" ") == 0:
1781 return None
1782
1783 parts = split_list(str_def.strip(), " ")
1784
1785 prefix = ""
1786 i = 0
1787 for part in parts:
1788 if part in ["unsigned", "long", "short"]:
1789 prefix += part + " "
1790 i += 1
1791 else:
1792 break
1793 parts = parts[i:]
1794
1795 if len(parts) <= 1:
1796 return None
1797
1798 glbl.wtype = WType.from_string(prefix + parts[0], containing_file, line_number)
1799
1800 if glbl.wtype == None:
1801 return None
1802
1803 str_def = parts[1]
1804 for part in parts[2:]:
1805 str_def = str_def + " " + part
1806
1807 if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1:
1808 return None
1809
1810 found = str_def.find(";")
1811 if found == -1:
1812 return None
1813
1814 found_eq = str_def.find("=")
1815 if found_eq != -1:
1816 found = found_eq
1817
1818 glbl.name = str_def[:found]
1819 str_def = str_def[found+1:]
1820 if glbl.name.find("*") == 0:
1821 glbl.name = glbl.name.replace("*", "")
1822 glbl.wtype.attr_type = attr_types.star
1823 if glbl.name.find("&&") == 0:
1824 glbl.name = glbl.name.replace("&&", "")
1825 glbl.wtype.attr_type = attr_types.ampamp
1826 if glbl.name.find("&") == 0:
1827 glbl.name = glbl.name.replace("&", "")
1828 glbl.wtype.attr_type = attr_types.amp
1829
1830 if(len(str_def.strip()) != 0):
1831 return None
1832
1833 if len(glbl.name.split(",")) > 1:
1834 glbl_list = []
1835 for name in glbl.name.split(","):
1836 name = name.strip();
1837 glbl_list.append(WGlobal())
1838 glbl_list[-1].orig_text = glbl.orig_text
1839 glbl_list[-1].wtype = glbl.wtype
1840 glbl_list[-1].name = name
1841 glbl_list[-1].containing_file = glbl.containing_file
1842 glbl_list[-1].namespace = glbl.namespace
1843 glbl_list[-1].is_const = glbl.is_const
1844 return glbl_list
1845
1846 return glbl
1847
1848 def gen_def(self):
1849 text = "\n\t"
1850 if self.is_const:
1851 text += "const "
1852 text += self.wtype.gen_text() + " get_var_py_" + self.name + "()"
1853 text += "\n\t{\n\t\t"
1854 if self.wtype.attr_type == attr_types.star:
1855 text += "if(" + self.namespace + "::" + self.name + " == NULL)\n\t\t\t"
1856 text += "throw std::runtime_error(\"" + self.namespace + "::" + self.name + " is NULL\");\n\t\t"
1857 if self.wtype.name in known_containers:
1858 text += self.wtype.gen_text_cpp()
1859 else:
1860 if self.is_const:
1861 text += "const "
1862 text += self.wtype.gen_text()
1863
1864 if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
1865 text += "*"
1866 text += " ret_ = "
1867 if self.wtype.name in classnames:
1868 text += self.wtype.name + "::get_py_obj("
1869 if self.wtype.attr_type != attr_types.star:
1870 text += "&"
1871 text += self.namespace + "::" + self.name
1872 if self.wtype.name in classnames:
1873 text += ")"
1874 text += ";"
1875
1876 if self.wtype.name in classnames:
1877 text += "\n\t\treturn *ret_;"
1878 elif self.wtype.name in known_containers:
1879 text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
1880 text += "\n\t\treturn ret____tmp;"
1881 else:
1882 text += "\n\t\treturn ret_;"
1883 text += "\n\t}\n"
1884
1885 if self.is_const:
1886 return text
1887
1888 ret = Attribute(self.wtype, "rhs");
1889
1890 if self.wtype.name in classnames:
1891 text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)"
1892 else:
1893 text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)"
1894 text += "\n\t{"
1895 text += ret.gen_translation()
1896 text += "\n\t\t" + self.namespace + "::" + self.name + " = " + ret.gen_call() + ";"
1897 text += "\n\t}\n"
1898
1899 return text;
1900
1901 def gen_boost_py(self):
1902 text = "\n\t\t\t.add_static_property(\"" + self.name + "\", &" + "YOSYS_PYTHON::get_var_py_" + self.name
1903 if not self.is_const:
1904 text += ", &YOSYS_PYTHON::set_var_py_" + self.name
1905 text += ")"
1906 return text
1907
1908 def concat_namespace(tuple_list):
1909 if len(tuple_list) == 0:
1910 return ""
1911 ret = ""
1912 for namespace in tuple_list:
1913 ret += "::" + namespace[0]
1914 return ret[2:]
1915
1916 def calc_ident(text):
1917 if len(text) == 0 or text[0] != ' ':
1918 return 0
1919 return calc_ident(text[1:]) + 1
1920
1921 def assure_length(text, length, left = False):
1922 if len(text) > length:
1923 return text[:length]
1924 if left:
1925 return text + " "*(length - len(text))
1926 return " "*(length - len(text)) + text
1927
1928 def parse_header(source):
1929 debug("Parsing " + source.name + ".pyh",1)
1930 source_file = open(source.name + ".pyh", "r")
1931
1932 source_text = []
1933 in_line = source_file.readline()
1934
1935 namespaces = []
1936
1937 while(in_line):
1938 if(len(in_line)>1):
1939 source_text.append(in_line.replace("char *", "char_p ").replace("char* ", "char_p "))
1940 in_line = source_file.readline()
1941
1942 i = 0
1943
1944 namespaces = []
1945 class_ = None
1946 private_segment = False
1947
1948 while i < len(source_text):
1949 line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
1950 ugly_line = unpretty_string(line)
1951
1952 # for anonymous unions, ignore union enclosure by skipping start line and replacing end line with new line
1953 if 'union {' in line:
1954 j = i+1
1955 while j < len(source_text):
1956 union_line = source_text[j]
1957 if '};' in union_line:
1958 source_text[j] = '\n'
1959 break
1960 j += 1
1961 if j != len(source_text):
1962 i += 1
1963 continue
1964
1965 if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1:
1966 namespace_name = ugly_line[10:].replace("{","").strip()
1967 namespaces.append((namespace_name, ugly_line.count("{")))
1968 debug("-----NAMESPACE " + concat_namespace(namespaces) + "-----",3)
1969 i += 1
1970 continue
1971
1972 if len(namespaces) != 0:
1973 namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}"))
1974 if namespaces[-1][1] == 0:
1975 debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
1976 del namespaces[-1]
1977 i += 1
1978 continue
1979
1980 if class_ == None and (str.startswith(ugly_line, "struct ") or str.startswith(ugly_line, "class")) and ugly_line.count(";") == 0:
1981
1982 struct_name = ugly_line.split(" ")[1].split("::")[-1]
1983 impl_namespaces = ugly_line.split(" ")[1].split("::")[:-1]
1984 complete_namespace = concat_namespace(namespaces)
1985 for namespace in impl_namespaces:
1986 complete_namespace += "::" + namespace
1987 debug("\tFound " + struct_name + " in " + complete_namespace,2)
1988
1989 base_class_name = None
1990 if len(ugly_line.split(" : ")) > 1: # class is derived
1991 deriv_str = ugly_line.split(" : ")[1]
1992 if len(deriv_str.split("::")) > 1: # namespace of base class is given
1993 base_class_name = deriv_str.split("::", 1)[1]
1994 else:
1995 base_class_name = deriv_str.split(" ")[0]
1996 debug("\t " + struct_name + " is derived from " + base_class_name,2)
1997 base_class = class_by_name(base_class_name)
1998
1999 class_ = (class_by_name(struct_name), ugly_line.count("{"))#calc_ident(line))
2000 if struct_name in classnames:
2001 class_[0].namespace = complete_namespace
2002 class_[0].base_class = base_class
2003 i += 1
2004 continue
2005
2006 if class_ != None:
2007 class_ = (class_[0], class_[1] + ugly_line.count("{") - ugly_line.count("}"))
2008 if class_[1] == 0:
2009 if class_[0] == None:
2010 debug("\tExiting unknown class", 3)
2011 else:
2012 debug("\tExiting class " + class_[0].name, 3)
2013 class_ = None
2014 private_segment = False
2015 i += 1
2016 continue
2017
2018 if class_ != None and (line.find("private:") != -1 or line.find("protected:") != -1):
2019 private_segment = True
2020 i += 1
2021 continue
2022 if class_ != None and line.find("public:") != -1:
2023 private_segment = False
2024 i += 1
2025 continue
2026
2027 candidate = None
2028
2029 if private_segment and class_ != None and class_[0] != None:
2030 candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i, True)
2031 if candidate != None:
2032 debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
2033 class_[0].found_constrs.append(candidate)
2034 i += 1
2035 continue
2036
2037 if not private_segment and (class_ == None or class_[0] != None):
2038 if class_ != None:
2039 candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
2040 else:
2041 candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces))
2042 if candidate != None and candidate.name.find("::") == -1:
2043 if class_ == None:
2044 debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
2045 unowned_functions.append(candidate)
2046 else:
2047 debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
2048 class_[0].found_funs.append(candidate)
2049 else:
2050 candidate = WEnum.from_string(ugly_line, concat_namespace(namespaces), i)
2051 if candidate != None:
2052 enums.append(candidate)
2053 debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
2054 elif class_ != None and class_[1] == 1:
2055 candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i)
2056 if candidate != None:
2057 debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
2058 class_[0].found_constrs.append(candidate)
2059 else:
2060 candidate = WMember.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
2061 if candidate != None:
2062 if type(candidate) == list:
2063 for c in candidate:
2064 debug("\t\tFound member \"" + c.name + "\" of class \"" + class_[0].name + "\" of type \"" + c.wtype.name + "\"", 2)
2065 class_[0].found_vars.extend(candidate)
2066 else:
2067 debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2)
2068 class_[0].found_vars.append(candidate)
2069 if candidate == None and class_ == None:
2070 candidate = WGlobal.from_string(ugly_line, source.name, i, concat_namespace(namespaces))
2071 if candidate != None:
2072 if type(candidate) == list:
2073 for c in candidate:
2074 glbls.append(c)
2075 debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2)
2076 else:
2077 glbls.append(candidate)
2078 debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2)
2079
2080 j = i
2081 line = unpretty_string(line)
2082 while candidate == None and j+1 < len(source_text) and line.count(';') <= 1 and line.count("(") >= line.count(")"):
2083 j += 1
2084 line = line + "\n" + unpretty_string(source_text[j])
2085 if class_ != None:
2086 candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
2087 else:
2088 candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces))
2089 if candidate != None and candidate.name.find("::") == -1:
2090 if class_ == None:
2091 debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
2092 unowned_functions.append(candidate)
2093 else:
2094 debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
2095 class_[0].found_funs.append(candidate)
2096 continue
2097 candidate = WEnum.from_string(line, concat_namespace(namespaces), i)
2098 if candidate != None:
2099 enums.append(candidate)
2100 debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
2101 continue
2102 if class_ != None:
2103 candidate = WConstructor.from_string(line, source.name, class_[0], i)
2104 if candidate != None:
2105 debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
2106 class_[0].found_constrs.append(candidate)
2107 continue
2108 if class_ == None:
2109 candidate = WGlobal.from_string(line, source.name, i, concat_namespace(namespaces))
2110 if candidate != None:
2111 if type(candidate) == list:
2112 for c in candidate:
2113 glbls.append(c)
2114 debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2)
2115 else:
2116 glbls.append(candidate)
2117 debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2)
2118 continue
2119 if candidate != None:
2120 while i < j:
2121 i += 1
2122 line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
2123 ugly_line = unpretty_string(line)
2124 if len(namespaces) != 0:
2125 namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}"))
2126 if namespaces[-1][1] == 0:
2127 debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
2128 del namespaces[-1]
2129 if class_ != None:
2130 class_ = (class_[0] , class_[1] + ugly_line.count("{") - ugly_line.count("}"))
2131 if class_[1] == 0:
2132 if class_[0] == None:
2133 debug("\tExiting unknown class", 3)
2134 else:
2135 debug("\tExiting class " + class_[0].name, 3)
2136 class_ = None
2137 private_segment = False
2138 i += 1
2139 else:
2140 i += 1
2141
2142 def debug(message, level):
2143 if level <= debug.debug_level:
2144 print(message)
2145
2146 def expand_function(f):
2147 fun_list = []
2148 arg_list = []
2149 for arg in f.args:
2150 if arg.default_value != None and (arg.wtype.name.split(" ")[-1] in primitive_types or arg.wtype.name in enum_names or (arg.wtype.name in classnames and arg.default_value == "nullptr")):
2151 fi = copy.deepcopy(f)
2152 fi.args = copy.deepcopy(arg_list)
2153 fun_list.append(fi)
2154 arg_list.append(arg)
2155 fun_list.append(f)
2156 return fun_list
2157
2158 def expand_functions():
2159 global unowned_functions
2160 new_funs = []
2161 for fun in unowned_functions:
2162 new_funs.extend(expand_function(fun))
2163 unowned_functions = new_funs
2164 for source in sources:
2165 for class_ in source.classes:
2166 new_funs = []
2167 for fun in class_.found_funs:
2168 new_funs.extend(expand_function(fun))
2169 class_.found_funs = new_funs
2170
2171 def inherit_members():
2172 for source in sources:
2173 for class_ in source.classes:
2174 if class_.base_class:
2175 base_funs = copy.deepcopy(class_.base_class.found_funs)
2176 for fun in base_funs:
2177 fun.member_of = class_
2178 fun.namespace = class_.namespace
2179 base_vars = copy.deepcopy(class_.base_class.found_vars)
2180 for var in base_vars:
2181 var.member_of = class_
2182 var.namespace = class_.namespace
2183 class_.found_funs.extend(base_funs)
2184 class_.found_vars.extend(base_vars)
2185
2186 def clean_duplicates():
2187 for source in sources:
2188 for class_ in source.classes:
2189 known_decls = {}
2190 for fun in class_.found_funs:
2191 if fun.gen_decl_hash_py() in known_decls:
2192 debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
2193 other = known_decls[fun.gen_decl_hash_py()]
2194 other.gen_alias()
2195 fun.gen_alias()
2196 if fun.gen_decl_hash_py() == other.gen_decl_hash_py():
2197 fun.duplicate = True
2198 debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3)
2199 else:
2200 known_decls[fun.gen_decl_hash_py()] = fun
2201 known_decls = []
2202 for con in class_.found_constrs:
2203 if con.gen_decl_hash_py() in known_decls:
2204 debug("Multiple declarations of " + con.gen_decl_hash_py(),3)
2205 con.duplicate = True
2206 else:
2207 known_decls.append(con.gen_decl_hash_py())
2208 known_decls = []
2209 for fun in unowned_functions:
2210 if fun.gen_decl_hash_py() in known_decls:
2211 debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
2212 fun.duplicate = True
2213 else:
2214 known_decls.append(fun.gen_decl_hash_py())
2215
2216 def gen_wrappers(filename, debug_level_ = 0):
2217 debug.debug_level = debug_level_
2218 for source in sources:
2219 parse_header(source)
2220
2221 expand_functions()
2222 inherit_members()
2223 clean_duplicates()
2224
2225 import shutil
2226 import math
2227 col = shutil.get_terminal_size((80,20)).columns
2228 debug("-"*col, 1)
2229 debug("-"*math.floor((col-7)/2)+"SUMMARY"+"-"*math.ceil((col-7)/2), 1)
2230 debug("-"*col, 1)
2231 for source in sources:
2232 for class_ in source.classes:
2233 debug("Class " + assure_length(class_.name, len(max(classnames, key=len)), True) + " contains " + assure_length(str(len(class_.found_vars)), 3, False) + " member variables, "+ assure_length(str(len(class_.found_funs)), 3, False) + " methods and " + assure_length(str(len(class_.found_constrs)), 2, False) + " constructors", 1)
2234 if len(class_.found_constrs) == 0:
2235 class_.found_constrs.append(WConstructor(source.name, class_))
2236 debug(str(len(unowned_functions)) + " functions are unowned", 1)
2237 debug(str(len(unowned_functions)) + " global variables", 1)
2238 for enum in enums:
2239 debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1)
2240 debug("-"*col, 1)
2241 wrapper_file = open(filename, "w+")
2242 wrapper_file.write(
2243 """/*
2244 * yosys -- Yosys Open SYnthesis Suite
2245 *
2246 * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
2247 *
2248 * Permission to use, copy, modify, and/or distribute this software for any
2249 * purpose with or without fee is hereby granted, provided that the above
2250 * copyright notice and this permission notice appear in all copies.
2251 *
2252 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2253 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2254 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2255 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2256 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2257 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2258 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2259 *
2260 * This is a generated file and can be overwritten by make
2261 */
2262
2263 #ifdef WITH_PYTHON
2264 """)
2265 for source in sources:
2266 wrapper_file.write("#include \""+source.name+".h\"\n")
2267 wrapper_file.write("""
2268 #include <boost/python/module.hpp>
2269 #include <boost/python/class.hpp>
2270 #include <boost/python/wrapper.hpp>
2271 #include <boost/python/call.hpp>
2272 #include <boost/python.hpp>
2273 #include <iosfwd> // std::streamsize
2274 #include <iostream>
2275 #include <boost/iostreams/concepts.hpp> // boost::iostreams::sink
2276 #include <boost/iostreams/stream.hpp>
2277 USING_YOSYS_NAMESPACE
2278
2279 namespace YOSYS_PYTHON {
2280
2281 struct YosysStatics{};
2282 """)
2283
2284 for source in sources:
2285 for wclass in source.classes:
2286 wrapper_file.write("\n\tstruct " + wclass.name + ";")
2287
2288 wrapper_file.write("\n")
2289
2290 for source in sources:
2291 for wclass in source.classes:
2292 wrapper_file.write(wclass.gen_decl(source.name))
2293
2294 wrapper_file.write("\n")
2295
2296 for source in sources:
2297 for wclass in source.classes:
2298 wrapper_file.write(wclass.gen_funs(source.name))
2299
2300 for fun in unowned_functions:
2301 wrapper_file.write(fun.gen_def())
2302
2303 for glbl in glbls:
2304 wrapper_file.write(glbl.gen_def())
2305
2306 wrapper_file.write(""" struct Initializer
2307 {
2308 Initializer() {
2309 if(!Yosys::yosys_already_setup())
2310 {
2311 Yosys::log_streams.push_back(&std::cout);
2312 Yosys::log_error_stderr = true;
2313 Yosys::yosys_setup();
2314 }
2315 }
2316
2317 Initializer(Initializer const &) {}
2318
2319 ~Initializer() {
2320 Yosys::yosys_shutdown();
2321 }
2322 };
2323
2324
2325 /// source: https://stackoverflow.com/questions/26033781/converting-python-io-object-to-stdostream-when-using-boostpython?noredirect=1&lq=1
2326 /// @brief Type that implements the Boost.IOStream's Sink and Flushable
2327 /// concept for writing data to Python object that support:
2328 /// n = object.write(str) # n = None or bytes written
2329 /// object.flush() # if flush exists, then it is callable
2330 class PythonOutputDevice
2331 {
2332 public:
2333
2334 // This class models both the Sink and Flushable concepts.
2335 struct category
2336 : boost::iostreams::sink_tag,
2337 boost::iostreams::flushable_tag
2338 {};
2339
2340 explicit
2341 PythonOutputDevice(boost::python::object object)
2342 : object_(object)
2343 {}
2344
2345 // Sink concept.
2346 public:
2347
2348 typedef char char_type;
2349
2350 std::streamsize write(const char* buffer, std::streamsize buffer_size)
2351 {
2352 namespace python = boost::python;
2353 // Copy the buffer to a python string.
2354 python::str data(buffer, buffer_size);
2355
2356 // Invoke write on the python object, passing in the data. The following
2357 // is equivalent to:
2358 // n = object_.write(data)
2359 python::extract<std::streamsize> bytes_written(
2360 object_.attr("write")(data));
2361
2362 // Per the Sink concept, return the number of bytes written. If the
2363 // Python return value provides a numeric result, then use it. Otherwise,
2364 // such as the case of a File object, use the buffer_size.
2365 return bytes_written.check()
2366 ? bytes_written
2367 : buffer_size;
2368 }
2369
2370 // Flushable concept.
2371 public:
2372
2373 bool flush()
2374 {
2375 // If flush exists, then call it.
2376 boost::python::object flush = object_.attr("flush");
2377 if (!flush.is_none())
2378 {
2379 flush();
2380 }
2381
2382 // Always return true. If an error occurs, an exception should be thrown.
2383 return true;
2384 }
2385
2386 private:
2387 boost::python::object object_;
2388 };
2389
2390 /// @brief Use an auxiliary function to adapt the legacy function.
2391 void log_to_stream(boost::python::object object)
2392 {
2393 // Create an ostream that delegates to the python object.
2394 boost::iostreams::stream<PythonOutputDevice>* output = new boost::iostreams::stream<PythonOutputDevice>(object);
2395 Yosys::log_streams.insert(Yosys::log_streams.begin(), output);
2396 };
2397
2398
2399 BOOST_PYTHON_MODULE(libyosys)
2400 {
2401 using namespace boost::python;
2402
2403 class_<Initializer>("Initializer");
2404 scope().attr("_hidden") = new Initializer();
2405
2406 def("log_to_stream", &log_to_stream);
2407 """)
2408
2409 for enum in enums:
2410 wrapper_file.write(enum.gen_boost_py())
2411
2412 for source in sources:
2413 for wclass in source.classes:
2414 wrapper_file.write(wclass.gen_boost_py())
2415
2416 for fun in unowned_functions:
2417 wrapper_file.write(fun.gen_boost_py())
2418
2419 wrapper_file.write("\n\n\t\tclass_<YosysStatics>(\"Yosys\")\n")
2420 for glbl in glbls:
2421 wrapper_file.write(glbl.gen_boost_py())
2422 wrapper_file.write("\t\t;\n")
2423
2424 wrapper_file.write("\n\t}\n}\n#endif")
2425
2426 def print_includes():
2427 for source in sources:
2428 print(source.name + ".pyh")