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