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
21import copy
22
23#Map c++ operator Syntax to Python functions
24wrappable_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
36keyword_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
73primitive_types = ["void", "bool", "int", "double", "size_t", "std::string",
74		"string", "State", "char_p"]
75
76from enum import Enum
77
78#Ways to link between Python- and C++ Objects
79class 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
91class attr_types(Enum):
92	star = "*"
93	amp = "&"
94	ampamp = "&&"
95	default = ""
96
97#For source-files
98class 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 >)
108def 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
131class 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
241class 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
263class 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
282class 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
352class IDictTranslator(PythonListTranslator):
353	typename = "boost::python::list"
354	orig_name = "idict"
355	insert_name = ""
356
357#Sub-type for std::set
358class SetTranslator(PythonListTranslator):
359	insert_name = ".insert"
360	orig_name = "std::set"
361
362#Sub-type for std::vector
363class VectorTranslator(PythonListTranslator):
364	insert_name = ".push_back"
365	orig_name = "std::vector"
366
367#Sub-type for pool
368class 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
374class 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
484class DictTranslator(PythonDictTranslator):
485	insert_name = "insert"
486	orig_name = "dict"
487
488#Sub_type for std::map
489class 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)
495class 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
532known_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
542class 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
726class 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
946sources = [
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
1000blacklist_methods = ["YOSYS_NAMESPACE::Pass::run_register", "YOSYS_NAMESPACE::Module::Pow"]
1001
1002enum_names = ["State","SyncType","ConstFlags"]
1003
1004enums = [] #Do not edit
1005glbls = []
1006
1007unowned_functions = []
1008
1009classnames = []
1010for source in sources:
1011	for wclass in source.classes:
1012		classnames.append(wclass.name)
1013
1014def 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
1021def enum_by_name(name):
1022	for e in enums:
1023		if e.name == name:
1024			return e
1025	return None
1026
1027def 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
1032def 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
1041class 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
1088class 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
1231class 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
1598class 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
1755class 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
1908def 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
1916def calc_ident(text):
1917	if len(text) == 0 or text[0] != ' ':
1918		return 0
1919	return calc_ident(text[1:]) + 1
1920
1921def 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
1928def 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
2142def debug(message, level):
2143	if level <= debug.debug_level:
2144		print(message)
2145
2146def 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
2158def 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
2171def 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
2186def 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
2216def 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>
2277USING_YOSYS_NAMESPACE
2278
2279namespace 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
2426def print_includes():
2427	for source in sources:
2428		print(source.name + ".pyh")
2429