# # yosys -- Yosys Open SYnthesis Suite # # Copyright (C) 2012 Claire Xenia Wolf # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Author Benedikt Tutzer # import copy #Map c++ operator Syntax to Python functions wrappable_operators = { "<" : "__lt__", "==": "__eq__", "!=": "__ne__", "+" : "__add__", "-" : "__sub__", "*" : "__mul__", "/" : "__div__", "()": "__call__" } #Restrict certain strings from being function names in Python keyword_aliases = { "in" : "in_", "False" : "False_", "None" : "None_", "True" : "True_", "and" : "and_", "as" : "as_", "assert" : "assert_", "break" : "break_", "class" : "class_", "continue" : "continue_", "def" : "def_", "del" : "del_", "elif" : "elif_", "else" : "else_", "except" : "except_", "for" : "for_", "from" : "from_", "global" : "global_", "if" : "if_", "import" : "import_", "in" : "in_", "is" : "is_", "lambda" : "lambda_", "nonlocal" : "nonlocal_", "not" : "not_", "or" : "or_", "pass" : "pass_", "raise" : "raise_", "return" : "return_", "try" : "try_", "while" : "while_", "with" : "with_", "yield" : "yield_" } #These can be used without any explicit conversion primitive_types = ["void", "bool", "int", "double", "size_t", "std::string", "string", "State", "char_p"] from enum import Enum #Ways to link between Python- and C++ Objects class link_types(Enum): global_list = 1 #Manage a global list of objects in C++, the Python #object contains a key to find the corresponding C++ #object and a Pointer to the object to verify it is #still the same, making collisions unlikely to happen ref_copy = 2 #The Python object contains a copy of the C++ object. #The C++ object is deleted when the Python object gets #deleted pointer = 3 #The Python Object contains a pointer to it's C++ #counterpart derive = 4 #The Python-Wrapper is derived from the C++ object. class attr_types(Enum): star = "*" amp = "&" ampamp = "&&" default = "" #For source-files class Source: name = "" classes = [] def __init__(self, name, classes): self.name = name self.classes = classes #Splits a list by the given delimiter, without splitting strings inside #pointy-brackets (< and >) def split_list(str_def, delim): str_def = str_def.strip() if len(str_def) == 0: return [] if str_def.count(delim) == 0: return [str_def] if str_def.count("<") == 0: return str_def.split(delim) if str_def.find("<") < str_def.find(" "): closing = find_closing(str_def[str_def.find("<")+1:], "<", ">") + str_def.find("<") comma = str_def[closing:].find(delim) if comma == -1: return [str_def] comma = closing + comma else: comma = str_def.find(delim) rest = split_list(str_def[comma+1:], delim) ret = [str_def[:comma]] if rest != None and len(rest) != 0: ret.extend(rest) return ret #Represents a Type class WType: name = "" cont = None attr_type = attr_types.default def __init__(self, name = "", cont = None, attr_type = attr_types.default): self.name = name self.cont = cont self.attr_type = attr_type #Python type-string def gen_text(self): text = self.name if self.name in enum_names: text = enum_by_name(self.name).namespace + "::" + self.name if self.cont != None: return known_containers[self.name].typename return text #C++ type-string def gen_text_cpp(self): postfix = "" if self.attr_type == attr_types.star: postfix = "*" if self.name in primitive_types: return self.name + postfix if self.name in enum_names: return enum_by_name(self.name).namespace + "::" + self.name + postfix if self.name in classnames: return class_by_name(self.name).namespace + "::" + self.name + postfix text = self.name if self.cont != None: text += "<" for a in self.cont.args: text += a.gen_text_cpp() + ", " text = text[:-2] text += ">" return text @staticmethod def from_string(str_def, containing_file, line_number): str_def = str_def.strip() if len(str_def) == 0: return None str_def = str_def.replace("RTLIL::SigSig", "std::pair").replace("SigSig", "std::pair") t = WType() t.name = "" t.cont = None t.attr_type = attr_types.default if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "): str_def = str_def.replace("const ", "") candidate = WContainer.from_string(str_def, containing_file, line_number) if candidate == None: return None t.name = str_def[:str_def.find("<")] if t.name.count("*") + t.name.count("&") > 1: return None if t.name.count("*") == 1 or str_def[0] == '*' or str_def[-1] == '*': t.attr_type = attr_types.star t.name = t.name.replace("*","") elif t.name.count("&&") == 1: t.attr_type = attr_types.ampamp t.name = t.name.replace("&&","") elif t.name.count("&") == 1 or str_def[0] == '&' or str_def[-1] == '&': t.attr_type = attr_types.amp t.name = t.name.replace("&","") t.cont = candidate if(t.name not in known_containers): return None return t prefix = "" if str.startswith(str_def, "const "): if "char_p" in str_def: prefix = "const " str_def = str_def[6:] if str.startswith(str_def, "unsigned "): prefix = "unsigned " + prefix str_def = str_def[9:] while str.startswith(str_def, "long "): prefix= "long " + prefix str_def = str_def[5:] while str.startswith(str_def, "short "): prefix = "short " + prefix str_def = str_def[6:] str_def = str_def.split("::")[-1] if str_def.count("*") + str_def.count("&") >= 2: return None if str_def.count("*") == 1: t.attr_type = attr_types.star str_def = str_def.replace("*","") elif str_def.count("&&") == 1: t.attr_type = attr_types.ampamp str_def = str_def.replace("&&","") elif str_def.count("&") == 1: t.attr_type = attr_types.amp str_def = str_def.replace("&","") 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: return None if str_def.count(" ") == 0: t.name = (prefix + str_def).replace("char_p", "char *") t.cont = None return t return None #Represents a container-type class WContainer: name = "" args = [] def from_string(str_def, containing_file, line_number): if str_def == None or len(str_def) < 4: return None cont = WContainer() cont.name = str_def[:str_def.find("<")] str_def = str_def[str_def.find("<")+1:find_closing(str_def, "<", ">")] cont.args = [] for arg in split_list(str_def, ","): candidate = WType.from_string(arg.strip(), containing_file, line_number) if candidate == None: return None if candidate.name == "void": return None cont.args.append(candidate) return cont #Translators between Python and C++ containers #Base Type class Translator: tmp_cntr = 0 typename = "DefaultType" orig_name = "DefaultCpp" @classmethod def gen_type(c, types): return "\nImplement a function that outputs the c++ type of this container here\n" @classmethod def translate(c, varname, types, prefix): return "\nImplement a function translating a python container to a c++ container here\n" @classmethod def translate_cpp(c, varname, types, prefix, ref): return "\nImplement a function translating a c++ container to a python container here\n" #Translates list-types (vector, pool, set), that only differ in their name and #the name of the insertion function class PythonListTranslator(Translator): typename = "boost::python::list" insert_name = "Default" #generate the c++ type string @classmethod def gen_type(c, types): text = c.orig_name + "<" if types[0].name in primitive_types: text += types[0].name elif types[0].name in known_containers: text += known_containers[types[0].name].gen_type(types[0].cont.args) else: text += class_by_name(types[0].name).namespace + "::" + types[0].name if types[0].attr_type == attr_types.star: text += "*" text += ">" return text #Generate C++ code to translate from a boost::python::list @classmethod def translate(c, varname, types, prefix): text = prefix + c.gen_type(types) + " " + varname + "___tmp;" cntr_name = "cntr_" + str(Translator.tmp_cntr) Translator.tmp_cntr = Translator.tmp_cntr + 1 text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "); " + cntr_name + "++)" text += prefix + "{" tmp_name = "tmp_" + str(Translator.tmp_cntr) Translator.tmp_cntr = Translator.tmp_cntr + 1 if types[0].name in known_containers: text += prefix + "\t" + known_containers[types[0].name].typename + " " + tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "[" + cntr_name + "]);" text += known_containers[types[0].name].translate(tmp_name, types[0].cont.args, prefix+"\t") tmp_name = tmp_name + "___tmp" text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");" elif types[0].name in classnames: text += prefix + "\t" + types[0].name + "* " + tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "[" + cntr_name + "]);" if types[0].attr_type == attr_types.star: text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + "->get_cpp_obj());" else: text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());" else: text += prefix + "\t" + types[0].name + " " + tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "[" + cntr_name + "]);" text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");" text += prefix + "}" return text #Generate C++ code to translate to a boost::python::list @classmethod def translate_cpp(c, varname, types, prefix, ref): text = prefix + c.typename + " " + varname + "___tmp;" tmp_name = "tmp_" + str(Translator.tmp_cntr) Translator.tmp_cntr = Translator.tmp_cntr + 1 if ref: text += prefix + "for(auto " + tmp_name + " : *" + varname + ")" else: text += prefix + "for(auto " + tmp_name + " : " + varname + ")" text += prefix + "{" if types[0].name in classnames: if types[0].attr_type == attr_types.star: text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));" else: text += prefix + "\t" + varname + "___tmp.append(*" + types[0].name + "::get_py_obj(&" + tmp_name + "));" elif types[0].name in known_containers: text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[0].attr_type == attr_types.star) text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + "___tmp);" else: text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");" text += prefix + "}" return text class IDictTranslator(PythonListTranslator): typename = "boost::python::list" orig_name = "idict" insert_name = "" #Sub-type for std::set class SetTranslator(PythonListTranslator): insert_name = ".insert" orig_name = "std::set" #Sub-type for std::vector class VectorTranslator(PythonListTranslator): insert_name = ".push_back" orig_name = "std::vector" #Sub-type for pool class PoolTranslator(PythonListTranslator): insert_name = ".insert" orig_name = "pool" #Sub-type for ObjRange class ObjRangeTranslator(PythonListTranslator): orig_name = "RTLIL::ObjRange" #Translates dict-types (dict, std::map), that only differ in their name and #the name of the insertion function class PythonDictTranslator(Translator): typename = "boost::python::dict" insert_name = "Default" @classmethod def gen_type(c, types): text = c.orig_name + "<" if types[0].name in primitive_types: text += types[0].name elif types[0].name in known_containers: text += known_containers[types[0].name].gen_type(types[0].cont.args) else: text += class_by_name(types[0].name).namespace + "::" + types[0].name if types[0].attr_type == attr_types.star: text += "*" text += ", " if types[1].name in primitive_types: text += types[1].name elif types[1].name in known_containers: text += known_containers[types[1].name].gen_type(types[1].cont.args) else: text += class_by_name(types[1].name).namespace + "::" + types[1].name if types[1].attr_type == attr_types.star: text += "*" text += ">" return text #Generate c++ code to translate from a boost::python::dict @classmethod def translate(c, varname, types, prefix): text = prefix + c.gen_type(types) + " " + varname + "___tmp;" text += prefix + "boost::python::list " + varname + "_keylist = " + varname + ".keys();" cntr_name = "cntr_" + str(Translator.tmp_cntr) Translator.tmp_cntr = Translator.tmp_cntr + 1 text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "_keylist); " + cntr_name + "++)" text += prefix + "{" key_tmp_name = "key_tmp_" + str(Translator.tmp_cntr) val_tmp_name = "val_tmp_" + str(Translator.tmp_cntr) Translator.tmp_cntr = Translator.tmp_cntr + 1 if types[0].name in known_containers: 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 + " ]);" text += known_containers[types[0].name].translate(key_tmp_name, types[0].cont.args, prefix+"\t") key_tmp_name = key_tmp_name + "___tmp" elif types[0].name in classnames: text += prefix + "\t" + types[0].name + "* " + key_tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "_keylist[ " + cntr_name + " ]);" else: text += prefix + "\t" + types[0].name + " " + key_tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "_keylist[ " + cntr_name + " ]);" if types[1].name in known_containers: 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 + " ]]);" text += known_containers[types[1].name].translate(val_tmp_name, types[1].cont.args, prefix+"\t") val_tmp_name = val_tmp_name + "___tmp" elif types[1].name in classnames: text += prefix + "\t" + types[1].name + "* " + val_tmp_name + " = boost::python::extract<" + types[1].name + "*>(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);" else: text += prefix + "\t" + types[1].name + " " + val_tmp_name + " = boost::python::extract<" + types[1].name + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);" text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(std::pair<" + types[0].gen_text_cpp() + ", " + types[1].gen_text_cpp() + ">(" if types[0].name not in classnames: text += key_tmp_name else: if types[0].attr_type != attr_types.star: text += "*" text += key_tmp_name + "->get_cpp_obj()" text += ", " if types[1].name not in classnames: text += val_tmp_name else: if types[1].attr_type != attr_types.star: text += "*" text += val_tmp_name + "->get_cpp_obj()" text += "));\n" + prefix + "}" return text #Generate c++ code to translate to a boost::python::dict @classmethod def translate_cpp(c, varname, types, prefix, ref): text = prefix + c.typename + " " + varname + "___tmp;" tmp_name = "tmp_" + str(Translator.tmp_cntr) Translator.tmp_cntr = Translator.tmp_cntr + 1 if ref: text += prefix + "for(auto " + tmp_name + " : *" + varname + ")" else: text += prefix + "for(auto " + tmp_name + " : " + varname + ")" text += prefix + "{" if types[1].name in known_containers: text += prefix + "\tauto " + tmp_name + "_second = " + tmp_name + ".second;" text += known_containers[types[1].name].translate_cpp(tmp_name + "_second", types[1].cont.args, prefix + "\t", types[1].attr_type == attr_types.star) if types[0].name in classnames: text += prefix + "\t" + varname + "___tmp[" + types[0].name + "::get_py_obj(" + tmp_name + ".first)] = " elif types[0].name not in known_containers: text += prefix + "\t" + varname + "___tmp[" + tmp_name + ".first] = " if types[1].name in classnames: if types[1].attr_type == attr_types.star: text += types[1].name + "::get_py_obj(" + tmp_name + ".second);" else: text += "*" + types[1].name + "::get_py_obj(&" + tmp_name + ".second);" elif types[1].name in known_containers: text += tmp_name + "_second___tmp;" else: text += tmp_name + ".second;" text += prefix + "}" return text #Sub-type for dict class DictTranslator(PythonDictTranslator): insert_name = "insert" orig_name = "dict" #Sub_type for std::map class MapTranslator(PythonDictTranslator): insert_name = "insert" orig_name = "std::map" #Translator for std::pair. Derived from PythonDictTranslator because the #gen_type function is the same (because both have two template parameters) class TupleTranslator(PythonDictTranslator): typename = "boost::python::tuple" orig_name = "std::pair" #Generate c++ code to translate from a boost::python::tuple @classmethod def translate(c, varname, types, prefix): text = prefix + types[0].name + " " + varname + "___tmp_0 = boost::python::extract<" + types[0].name + ">(" + varname + "[0]);" text += prefix + types[1].name + " " + varname + "___tmp_1 = boost::python::extract<" + types[1].name + ">(" + varname + "[1]);" text += prefix + TupleTranslator.gen_type(types) + " " + varname + "___tmp(" if types[0].name.split(" ")[-1] in primitive_types: text += varname + "___tmp_0, " else: text += varname + "___tmp_0.get_cpp_obj(), " if types[1].name.split(" ")[-1] in primitive_types: text += varname + "___tmp_1);" else: text += varname + "___tmp_1.get_cpp_obj());" return text #Generate c++ code to translate to a boost::python::tuple @classmethod def translate_cpp(c, varname, types, prefix, ref): # if the tuple is a pair of SigSpecs (aka SigSig), then we need # to call get_py_obj() on each item in the tuple if types[0].name in classnames: first_var = types[0].name + "::get_py_obj(" + varname + ".first)" else: first_var = varname + ".first" if types[1].name in classnames: second_var = types[1].name + "::get_py_obj(" + varname + ".second)" else: second_var = varname + ".second" text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + first_var + ", " + second_var + ");" return text #Associate the Translators with their c++ type known_containers = { "std::set" : SetTranslator, "std::vector" : VectorTranslator, "pool" : PoolTranslator, "idict" : IDictTranslator, "dict" : DictTranslator, "std::pair" : TupleTranslator, "std::map" : MapTranslator, "RTLIL::ObjRange" : ObjRangeTranslator } class Attribute: wtype = None varname = None is_const = False default_value = None pos = None pos_counter = 0 def __init__(self, wtype, varname, is_const = False, default_value = None): self.wtype = wtype self.varname = varname self.is_const = is_const self.default_value = None self.container = None @staticmethod def from_string(str_def, containing_file, line_number): if len(str_def) < 3: return None orig = str_def arg = Attribute(None, None) prefix = "" arg.wtype = None arg.varname = None arg.is_const = False arg.default_value = None arg.container = None if str.startswith(str_def, "const "): arg.is_const = True str_def = str_def[6:] if str.startswith(str_def, "unsigned "): prefix = "unsigned " str_def = str_def[9:] while str.startswith(str_def, "long "): prefix= "long " + prefix str_def = str_def[5:] while str.startswith(str_def, "short "): prefix = "short " + prefix str_def = str_def[6:] if str_def.find("<") != -1 and str_def.find("<") < str_def.find(" "): closing = find_closing(str_def[str_def.find("<"):], "<", ">") + str_def.find("<") + 1 arg.wtype = WType.from_string(str_def[:closing].strip(), containing_file, line_number) str_def = str_def[closing+1:] else: if str_def.count(" ") > 0: arg.wtype = WType.from_string(prefix + str_def[:str_def.find(" ")].strip(), containing_file, line_number) str_def = str_def[str_def.find(" ")+1:] else: arg.wtype = WType.from_string(prefix + str_def.strip(), containing_file, line_number) str_def = "" arg.varname = "" if arg.wtype == None: return None if str_def.count("=") == 0: arg.varname = str_def.strip() if arg.varname.find(" ") > 0: return None else: arg.varname = str_def[:str_def.find("=")].strip() if arg.varname.find(" ") > 0: return None str_def = str_def[str_def.find("=")+1:].strip() arg.default_value = str_def[arg.varname.find("=")+1:].strip() if len(arg.varname) == 0: arg.varname = None return arg if arg.varname[0] == '*': arg.wtype.attr_type = attr_types.star arg.varname = arg.varname[1:] elif arg.varname[0] == '&': if arg.wtype.attr_type != attr_types.default: return None if arg.varname[1] == '&': arg.wtype.attr_type = attr_types.ampamp arg.varname = arg.varname[2:] else: arg.wtype.attr_type = attr_types.amp arg.varname = arg.varname[1:] return arg #Generates the varname. If the attribute has no name in the header file, #a name is generated def gen_varname(self): if self.varname != None: return self.varname if self.wtype.name == "void": return "" if self.pos == None: self.pos = Attribute.pos_counter Attribute.pos_counter = Attribute.pos_counter + 1 return "gen_varname_" + str(self.pos) #Generates the text for the function headers with wrapper types def gen_listitem(self): prefix = "" if self.is_const: prefix = "const " if self.wtype.name in classnames: return prefix + self.wtype.name + "* " + self.gen_varname() if self.wtype.name in known_containers: return prefix + known_containers[self.wtype.name].typename + " " + self.gen_varname() return prefix + self.wtype.name + " " + self.gen_varname() #Generates the test for the function headers with c++ types def gen_listitem_cpp(self): prefix = "" if self.is_const: prefix = "const " infix = "" if self.wtype.attr_type == attr_types.star: infix = "*" elif self.wtype.attr_type == attr_types.amp: infix = "&" elif self.wtype.attr_type == attr_types.ampamp: infix = "&&" if self.wtype.name in known_containers: return prefix + known_containers[self.wtype.name].gen_type(self.wtype.cont.args) + " " + infix + self.gen_varname() if self.wtype.name in classnames: return prefix + class_by_name(self.wtype.name).namespace + "::" + self.wtype.name + " " + infix + self.gen_varname() return prefix + self.wtype.name + " " + infix + self.gen_varname() #Generates the listitem withtout the varname, so the signature can be #compared def gen_listitem_hash(self): prefix = "" if self.is_const: prefix = "const " if self.wtype.name in classnames: return prefix + self.wtype.name + "* " if self.wtype.name in known_containers: return known_containers[self.wtype.name].typename return prefix + self.wtype.name #Generate Translation code for the attribute def gen_translation(self): if self.wtype.name in known_containers: return known_containers[self.wtype.name].translate(self.gen_varname(), self.wtype.cont.args, "\n\t\t") return "" #Generate Translation code from c++ for the attribute def gen_translation_cpp(self): if self.wtype.name in known_containers: 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) return "" #Generate Text for the call def gen_call(self): ret = self.gen_varname() if self.wtype.name in known_containers: if self.wtype.attr_type == attr_types.star: return "&" + ret + "___tmp" return ret + "___tmp" if self.wtype.name in classnames: if self.wtype.attr_type != attr_types.star: ret = "*" + ret return ret + "->get_cpp_obj()" if self.wtype.name == "char *" and self.gen_varname() in ["format", "fmt"]: return "\"%s\", " + self.gen_varname() if self.wtype.attr_type == attr_types.star: return "&" + ret return ret def gen_call_cpp(self): ret = self.gen_varname() if self.wtype.name.split(" ")[-1] in primitive_types or self.wtype.name in enum_names: if self.wtype.attr_type == attr_types.star: return "&" + ret return ret if self.wtype.name not in classnames: if self.wtype.attr_type == attr_types.star: return "&" + ret + "___tmp" return ret + "___tmp" if self.wtype.attr_type != attr_types.star: ret = "*" + ret return self.wtype.name + "::get_py_obj(" + self.gen_varname() + ")" #Generate cleanup code def gen_cleanup(self): 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): return "" return "\n\t\tdelete " + self.gen_varname() + "___tmp;" class WClass: name = None namespace = None link_type = None base_class = None id_ = None string_id = None hash_id = None needs_clone = False found_funs = [] found_vars = [] found_constrs = [] def __init__(self, name, link_type, id_, string_id = None, hash_id = None, needs_clone = False): self.name = name self.namespace = None self.base_class = None self.link_type = link_type self.id_ = id_ self.string_id = string_id self.hash_id = hash_id self.needs_clone = needs_clone self.found_funs = [] self.found_vars = [] self.found_constrs = [] def printable_constrs(self): ret = 0 for con in self.found_constrs: if not con.protected: ret += 1 return ret def gen_decl(self, filename): long_name = self.namespace + "::" + self.name text = "\n\t// WRAPPED from " + filename text += "\n\tstruct " + self.name if self.link_type == link_types.derive: text += " : public " + self.namespace + "::" + self.name text += "\n\t{\n" if self.link_type != link_types.derive: text += "\t\t" + long_name + "* ref_obj;\n" if self.link_type == link_types.ref_copy or self.link_type == link_types.pointer: text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn ref_obj;\n\t\t}\n" elif self.link_type == link_types.global_list: text += "\t\t" + self.id_.wtype.name + " " + self.id_.varname + ";\n" text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{" text += "\n\t\t\t" + long_name + "* ret = " + long_name + "::get_all_" + self.name.lower() + "s()->at(this->" + self.id_.varname + ");" text += "\n\t\t\tif(ret != NULL && ret == this->ref_obj)" text += "\n\t\t\t\treturn ret;" text += "\n\t\t\tthrow std::runtime_error(\"" + self.name + "'s c++ object does not exist anymore.\");" text += "\n\t\t\treturn NULL;" text += "\n\t\t}\n" #if self.link_type != link_types.pointer: text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{" text += "\n\t\t\tif(ref == nullptr){" text += "\n\t\t\t\tthrow std::runtime_error(\"" + self.name + " does not exist.\");" text += "\n\t\t\t}" text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));" if self.link_type == link_types.pointer: text += "\n\t\t\tret->ref_obj = ref;" if self.link_type == link_types.ref_copy: if self.needs_clone: text += "\n\t\t\tret->ref_obj = ref->clone();" else: text += "\n\t\t\tret->ref_obj = new "+long_name+"(*ref);" if self.link_type == link_types.global_list: text += "\n\t\t\tret->ref_obj = ref;" text += "\n\t\t\tret->" + self.id_.varname + " = ret->ref_obj->" + self.id_.varname + ";" text += "\n\t\t\treturn ret;" text += "\n\t\t}\n" if self.link_type == link_types.ref_copy: text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + " ref)\n\t\t{" text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));" if self.needs_clone: text += "\n\t\t\tret->ref_obj = ref.clone();" else: text += "\n\t\t\tret->ref_obj = new "+long_name+"(ref);" text += "\n\t\t\treturn ret;" text += "\n\t\t}\n" for con in self.found_constrs: text += con.gen_decl() if self.base_class is not None: text += "\n\t\tvirtual ~" + self.name + "() { };" for var in self.found_vars: text += var.gen_decl() for fun in self.found_funs: text += fun.gen_decl() if self.link_type == link_types.derive: duplicates = {} for fun in self.found_funs: if fun.name in duplicates: fun.gen_alias() duplicates[fun.name].gen_alias() else: duplicates[fun.name] = fun 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" text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{" text += "\n\t\t\treturn (" + self.name + "*)ref;" text += "\n\t\t}\n" for con in self.found_constrs: text += con.gen_decl_derive() for var in self.found_vars: text += var.gen_decl() for fun in self.found_funs: text += fun.gen_decl_virtual() if self.hash_id != None: text += "\n\t\tunsigned int get_hash_py()" text += "\n\t\t{" text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";" text += "\n\t\t}" text += "\n\t};\n" if self.link_type == link_types.derive: text += "\n\tstruct " + self.name + "Wrap : " + self.name + ", boost::python::wrapper<" + self.name + ">" text += "\n\t{" for con in self.found_constrs: text += con.gen_decl_wrapperclass() for fun in self.found_funs: text += fun.gen_default_impl() text += "\n\t};" text += "\n\tstd::ostream &operator<<(std::ostream &ostr, const " + self.name + " &ref)" text += "\n\t{" text += "\n\t\tostr << \"" + self.name if self.string_id != None: text +=" \\\"\"" text += " << ref.get_cpp_obj()->" + self.string_id text += " << \"\\\"\"" else: text += " at \" << ref.get_cpp_obj()" text += ";" text += "\n\t\treturn ostr;" text += "\n\t}" text += "\n" return text def gen_funs(self, filename): text = "" if self.link_type != link_types.derive: for con in self.found_constrs: text += con.gen_def() for var in self.found_vars: text += var.gen_def() for fun in self.found_funs: text += fun.gen_def() else: for var in self.found_vars: text += var.gen_def() for fun in self.found_funs: text += fun.gen_def_virtual() return text def gen_boost_py_body(self): text = "" if self.printable_constrs() == 0 or not self.contains_default_constr(): text += ", no_init" text += ")" text += "\n\t\t\t.def(boost::python::self_ns::str(boost::python::self_ns::self))" text += "\n\t\t\t.def(boost::python::self_ns::repr(boost::python::self_ns::self))" for con in self.found_constrs: text += con.gen_boost_py() for var in self.found_vars: text += var.gen_boost_py() static_funs = [] for fun in self.found_funs: text += fun.gen_boost_py() if fun.is_static and fun.alias not in static_funs: static_funs.append(fun.alias) for fun in static_funs: text += "\n\t\t\t.staticmethod(\"" + fun + "\")" if self.hash_id != None: text += "\n\t\t\t.def(\"__hash__\", &" + self.name + "::get_hash_py)" text += "\n\t\t\t;\n" return text def gen_boost_py(self): body = self.gen_boost_py_body() base_info = "" if self.base_class is not None: base_info = ", bases<" + (self.base_class.name) + ">" if self.link_type == link_types.derive: text = "\n\t\tclass_<" + self.name + base_info + ">(\"Cpp" + self.name + "\"" text += body text += "\n\t\tclass_<" + self.name text += "Wrap, boost::noncopyable" text += ">(\"" + self.name + "\"" text += body else: text = "\n\t\tclass_<" + self.name + base_info + ">(\"" + self.name + "\"" text += body return text def contains_default_constr(self): for c in self.found_constrs: if len(c.args) == 0: return True return False #CONFIGURE HEADER-FILES TO BE PARSED AND CLASSES EXPECTED IN THEM HERE sources = [ Source("kernel/celltypes",[ WClass("CellType", link_types.pointer, None, None, "type.hash()", True), WClass("CellTypes", link_types.pointer, None, None, None, True) ] ), Source("kernel/consteval",[ WClass("ConstEval", link_types.pointer, None, None, None, True) ] ), Source("kernel/log",[]), Source("kernel/register",[ WClass("Pass", link_types.derive, None, None, None, True), ] ), Source("kernel/rtlil",[ WClass("IdString", link_types.ref_copy, None, "str()", "hash()"), WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"), WClass("AttrObject", link_types.ref_copy, None, None, None), WClass("Selection", link_types.ref_copy, None, None, None), WClass("Monitor", link_types.derive, None, None, None), WClass("CaseRule",link_types.ref_copy, None, None, None, True), WClass("SwitchRule",link_types.ref_copy, None, None, None, True), WClass("SyncRule", link_types.ref_copy, None, None, None, True), WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"), WClass("SigChunk", link_types.ref_copy, None, None, None), WClass("SigBit", link_types.ref_copy, None, None, "hash()"), WClass("SigSpec", link_types.ref_copy, None, None, "hash()"), WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()") ] ), #Source("kernel/satgen",[ # ] # ), #Source("libs/ezsat/ezsat",[ # ] # ), #Source("libs/ezsat/ezminisat",[ # ] # ), Source("kernel/sigtools",[ WClass("SigMap", link_types.pointer, None, None, None, True) ] ), Source("kernel/yosys",[ ] ), Source("kernel/cost",[]) ] blacklist_methods = ["YOSYS_NAMESPACE::Pass::run_register", "YOSYS_NAMESPACE::Module::Pow"] enum_names = ["State","SyncType","ConstFlags"] enums = [] #Do not edit glbls = [] unowned_functions = [] classnames = [] for source in sources: for wclass in source.classes: classnames.append(wclass.name) def class_by_name(name): for source in sources: for wclass in source.classes: if wclass.name == name: return wclass return None def enum_by_name(name): for e in enums: if e.name == name: return e return None def find_closing(text, open_tok, close_tok): if text.find(open_tok) == -1 or text.find(close_tok) <= text.find(open_tok): return text.find(close_tok) return text.find(close_tok) + find_closing(text[text.find(close_tok)+1:], open_tok, close_tok) + 1 def unpretty_string(s): s = s.strip() while s.find(" ") != -1: s = s.replace(" "," ") while s.find("\t") != -1: s = s.replace("\t"," ") s = s.replace(" (","(") return s class WEnum: name = None namespace = None values = [] def from_string(str_def, namespace, line_number): str_def = str_def.strip() if not str.startswith(str_def, "enum "): return None if str_def.count(";") != 1: return None str_def = str_def[5:] enum = WEnum() split = str_def.split(":") if(len(split) != 2): return None enum.name = split[0].strip() if enum.name not in enum_names: return None str_def = split[1] if str_def.count("{") != str_def.count("}") != 1: return None if len(str_def) < str_def.find("}")+2 or str_def[str_def.find("}")+1] != ';': return None str_def = str_def.split("{")[-1].split("}")[0] enum.values = [] for val in str_def.split(','): enum.values.append(val.strip().split('=')[0].strip()) enum.namespace = namespace return enum def gen_boost_py(self): text = "\n\t\tenum_<" + self.namespace + "::" + self.name + ">(\"" + self.name + "\")\n" for value in self.values: text += "\t\t\t.value(\"" + value + "\"," + self.namespace + "::" + value + ")\n" text += "\t\t\t;\n" return text def __str__(self): ret = "Enum " + self.namespace + "::" + self.name + "(\n" for val in self.values: ret = ret + "\t" + val + "\n" return ret + ")" def __repr__(self): return __str__(self) class WConstructor: orig_text = None args = [] containing_file = None member_of = None duplicate = False protected = False def __init__(self, containing_file, class_): self.orig_text = "Auto generated default constructor" self.args = [] self.containing_file = containing_file self.member_of = class_ self.protected = False def from_string(str_def, containing_file, class_, line_number, protected = False): if class_ == None: return None if str_def.count("delete;") > 0: return None con = WConstructor(containing_file, class_) con.orig_text = str_def con.args = [] con.duplicate = False con.protected = protected if str.startswith(str_def, "inline "): str_def = str_def[7:] if not str.startswith(str_def, class_.name + "("): return None str_def = str_def[len(class_.name)+1:] found = find_closing(str_def, "(", ")") if found == -1: return None str_def = str_def[0:found].strip() if len(str_def) == 0: return con for arg in split_list(str_def, ","): parsed = Attribute.from_string(arg.strip(), containing_file, line_number) if parsed == None: return None con.args.append(parsed) return con def gen_decl(self): if self.duplicate or self.protected: return "" text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t\t" + self.member_of.name + "(" for arg in self.args: text += arg.gen_listitem() + ", " if len(self.args) > 0: text = text[:-2] text += ");\n" return text def gen_decl_derive(self): if self.duplicate or self.protected: return "" text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t\t" + self.member_of.name + "(" for arg in self.args: text += arg.gen_listitem() + ", " if len(self.args) > 0: text = text[:-2] text += ")" if len(self.args) == 0: return text + "{}" text += " : " text += self.member_of.namespace + "::" + self.member_of.name + "(" for arg in self.args: text += arg.gen_call() + ", " if len(self.args) > 0: text = text[:-2] text += "){}\n" return text def gen_decl_wrapperclass(self): if self.duplicate or self.protected: return "" text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t\t" + self.member_of.name + "Wrap(" for arg in self.args: text += arg.gen_listitem() + ", " if len(self.args) > 0: text = text[:-2] text += ")" if len(self.args) == 0: return text + "{}" text += " : " text += self.member_of.name + "(" for arg in self.args: text += arg.gen_call() + ", " if len(self.args) > 0: text = text[:-2] text += "){}\n" return text def gen_decl_hash_py(self): text = self.member_of.name + "(" for arg in self.args: text += arg.gen_listitem_hash() + ", " if len(self.args) > 0: text = text[:-2] text += ");" return text def gen_def(self): if self.duplicate or self.protected: return "" text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t" + self.member_of.name + "::" + self.member_of.name + "(" for arg in self.args: text += arg.gen_listitem() + ", " if len(self.args) > 0: text = text[:-2] text +=")\n\t{" for arg in self.args: text += arg.gen_translation() if self.member_of.link_type != link_types.derive: text += "\n\t\tthis->ref_obj = new " + self.member_of.namespace + "::" + self.member_of.name + "(" for arg in self.args: text += arg.gen_call() + ", " if len(self.args) > 0: text = text[:-2] if self.member_of.link_type != link_types.derive: text += ");" if self.member_of.link_type == link_types.global_list: text += "\n\t\tthis->" + self.member_of.id_.varname + " = this->ref_obj->" + self.member_of.id_.varname + ";" for arg in self.args: text += arg.gen_cleanup() text += "\n\t}\n" return text def gen_boost_py(self): if self.duplicate or self.protected or len(self.args) == 0: return "" text = "\n\t\t\t.def(init" text += "<" for a in self.args: text += a.gen_listitem_hash() + ", " text = text[0:-2] + ">())" return text class WFunction: orig_text = None is_static = False is_inline = False is_virtual = False ret_attr_type = attr_types.default is_operator = False ret_type = None name = None alias = None args = [] containing_file = None member_of = None duplicate = False namespace = "" def from_string(str_def, containing_file, class_, line_number, namespace): if str_def.count("delete;") > 0: return None func = WFunction() func.is_static = False func.is_inline = False func.is_virtual = False func.is_const = False func.ret_attr_type = attr_types.default func.is_operator = False func.member_of = None func.orig_text = str_def func.args = [] func.containing_file = containing_file func.member_of = class_ func.duplicate = False func.namespace = namespace str_def = str_def.replace("operator ","operator") if str.startswith(str_def, "static "): func.is_static = True str_def = str_def[7:] else: func.is_static = False if str.startswith(str_def, "inline "): func.is_inline = True str_def = str_def[7:] else: func.is_inline = False if str.startswith(str_def, "virtual "): func.is_virtual = True str_def = str_def[8:] else: func.is_virtual = False if str_def.count(" ") == 0: return None parts = split_list(str_def.strip(), " ") prefix = "" i = 0 for part in parts: if part in ["unsigned", "long", "short", "const"]: prefix += part + " " i += 1 else: break parts = parts[i:] if len(parts) <= 1: return None func.ret_type = WType.from_string(prefix + parts[0], containing_file, line_number) if func.ret_type == None: return None str_def = parts[1] for part in parts[2:]: str_def = str_def + " " + part found = str_def.find("(") if found == -1 or (str_def.find(" ") != -1 and found > str_def.find(" ")): return None func.name = str_def[:found] str_def = str_def[found:] if func.name.find("operator") != -1 and str.startswith(str_def, "()("): func.name += "()" str_def = str_def[2:] str_def = str_def[1:] if func.name.find("operator") != -1: func.is_operator = True if func.name.find("*") == 0: func.name = func.name.replace("*", "") func.ret_type.attr_type = attr_types.star if func.name.find("&&") == 0: func.name = func.name.replace("&&", "") func.ret_type.attr_type = attr_types.ampamp if func.name.find("&") == 0: func.name = func.name.replace("&", "") func.ret_type.attr_type = attr_types.amp found = find_closing(str_def, "(", ")") if found == -1: return None post_qualifiers = str_def[found + 1:].lstrip().replace("{", " {") + " " if post_qualifiers.startswith("const "): func.is_const = True str_def = str_def[0:found] if func.name in blacklist_methods: return None if func.namespace != None and func.namespace != "": if (func.namespace + "::" + func.name) in blacklist_methods: return None if func.member_of != None: if (func.namespace + "::" + func.member_of.name + "::" + func.name) in blacklist_methods: return None if func.is_operator and func.name.replace(" ","").replace("operator","").split("::")[-1] not in wrappable_operators: return None testname = func.name if func.is_operator: testname = testname[:testname.find("operator")] 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: return None func.alias = func.name if func.name in keyword_aliases: func.alias = keyword_aliases[func.name] str_def = str_def[:found].strip() if(len(str_def) == 0): return func for arg in split_list(str_def, ","): if arg.strip() == "...": continue parsed = Attribute.from_string(arg.strip(), containing_file, line_number) if parsed == None: return None func.args.append(parsed) return func @property def mangled_name(self): mangled_typename = lambda code: code.replace("::", "_").replace("<","_").replace(">","_") \ .replace(" ","").replace("*","").replace(",","") return self.name + "".join( f"__{mangled_typename(arg.wtype.gen_text_cpp())}" for arg in self.args ) def gen_alias(self): self.alias = self.mangled_name def gen_post_qualifiers(self, derived=False): if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual and derived: # we drop the qualifiers when deriving callbacks to be implemented in Python return '' return ' const' if self.is_const else '' def gen_decl(self): if self.duplicate: return "" text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t\t" if self.is_static: text += "static " text += self.ret_type.gen_text() + " " + self.alias + "(" for arg in self.args: text += arg.gen_listitem() text += ", " if len(self.args) > 0: text = text[:-2] text += f"){self.gen_post_qualifiers()};\n" return text def gen_decl_virtual(self): if self.duplicate: return "" if not self.is_virtual: return self.gen_decl() text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t\tvirtual " if self.is_static: text += "static " text += self.ret_type.gen_text() + " py_" + self.alias + "(" for arg in self.args: text += arg.gen_listitem() text += ", " if len(self.args) > 0: text = text[:-2] text += ")" if len(self.args) == 0 and self.ret_type.name == "void": text += "{}" else: text += "\n\t\t{" for arg in self.args: text += "\n\t\t\t(void)" + arg.gen_varname() + ";" if self.ret_type.name == "void": pass elif self.ret_type.name == "bool": text += "\n\t\t\treturn false;" else: raise NotImplementedError(self.ret_type.name) text += "\n\t\t}\n" text += "\n\t\tvirtual " if self.is_static: text += "static " text += self.ret_type.gen_text() + " " + self.name + "(" for arg in self.args: text += arg.gen_listitem_cpp() text += ", " if len(self.args) > 0: text = text[:-2] text += f"){self.gen_post_qualifiers()} override;\n" return text def gen_decl_hash_py(self): text = self.ret_type.gen_text() + " " + self.alias + "(" for arg in self.args: text += arg.gen_listitem_hash() + ", " if len(self.args) > 0: text = text[:-2] text += ");" return text def gen_def(self): if self.duplicate: return "" text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t" + self.ret_type.gen_text() + " " if self.member_of != None: text += self.member_of.name + "::" text += self.alias + "(" for arg in self.args: text += arg.gen_listitem() text += ", " if len(self.args) > 0: text = text[:-2] text += f"){self.gen_post_qualifiers()}\n\t{{" for arg in self.args: text += arg.gen_translation() text += "\n\t\t" if self.ret_type.name != "void": if self.ret_type.name in known_containers: text += self.ret_type.gen_text_cpp() else: text += self.ret_type.gen_text() if self.ret_type.name in classnames or (self.ret_type.name in known_containers and self.ret_type.attr_type == attr_types.star): text += "*" text += " ret_ = " if self.ret_type.name in classnames: text += self.ret_type.name + "::get_py_obj(" if self.member_of == None: text += "::" + self.namespace + "::" + self.alias + "(" elif self.is_static: text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "(" else: text += "this->get_cpp_obj()->" + self.name + "(" for arg in self.args: text += arg.gen_call() + ", " if len(self.args) > 0: text = text[:-2] if self.ret_type.name in classnames: text += ")" text += ");" for arg in self.args: text += arg.gen_cleanup() if self.ret_type.name != "void": if self.ret_type.name in classnames: text += "\n\t\treturn *ret_;" elif self.ret_type.name in known_containers: 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) text += "\n\t\treturn ret____tmp;" else: text += "\n\t\treturn ret_;" text += "\n\t}\n" return text def gen_def_virtual(self): if self.duplicate: return "" if not self.is_virtual: return self.gen_def() text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file text += "\n\t" if self.is_static: text += "static " text += self.ret_type.gen_text() + " " + self.member_of.name + "::" + self.name + "(" for arg in self.args: text += arg.gen_listitem_cpp() text += ", " if len(self.args) > 0: text = text[:-2] text += f"){self.gen_post_qualifiers()}\n\t{{" for arg in self.args: text += arg.gen_translation_cpp() return_stmt = "return " if self.ret_type.name != "void" else "" text += f"\n\t\t{return_stmt}" if self.member_of == None: text += "::" + self.namespace + "::" + self.alias + "(" elif self.is_static: text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "(" else: text += f"const_cast<{self.member_of.name}*>(this)->py_" + self.alias + "(" for arg in self.args: text += arg.gen_call_cpp() + ", " if len(self.args) > 0: text = text[:-2] if self.ret_type.name in classnames: text += ")" text += ");" for arg in self.args: text += arg.gen_cleanup() text += "\n\t}\n" return text def gen_default_impl(self): if self.duplicate: return "" if not self.is_virtual: return "" text = "\n\n\t\t" + self.ret_type.gen_text() + " py_" + self.alias + "(" for arg in self.args: text += arg.gen_listitem() + ", " if len(self.args) > 0: text = text[:-2] call_string = "py_" + self.alias + "(" for arg in self.args: call_string += arg.gen_varname() + ", " if len(self.args) > 0: call_string = call_string[0:-2] call_string += ");" return_stmt = "return " if self.ret_type.name != "void" else "" text += ")\n\t\t{" text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))" text += f"\n\t\t\t\t{return_stmt}" + call_string text += "\n\t\t\telse" text += f"\n\t\t\t\t{return_stmt}" + self.member_of.name + "::" + call_string text += "\n\t\t}" text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "(" for arg in self.args: text += arg.gen_listitem() + ", " if len(self.args) > 0: text = text[:-2] text += f")\n\t\t{{" text += f"\n\t\t\t{return_stmt}this->" + self.member_of.name + "::" + call_string text += "\n\t\t}" return text def gen_boost_py(self): if self.duplicate: return "" if self.member_of == None: text = "\n\t\tdef" else: text = "\n\t\t\t.def" if len(self.args) > -1: if self.ret_type.name in known_containers: text += "<" + known_containers[self.ret_type.name].typename + " " else: text += "<" + self.ret_type.name + " " if self.member_of == None or self.is_static: text += "(*)(" else: text += "(" + self.member_of.name + "::*)(" for a in self.args: text += a.gen_listitem_hash() + ", " if len(self.args) > 0: text = text[0:-2] + f"){self.gen_post_qualifiers(True)}>" else: text += f"void){self.gen_post_qualifiers(True)}>" if self.is_operator: text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\"" else: if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual: text += "(\"py_" + self.alias + "\"" else: text += "(\"" + self.alias + "\"" if self.member_of != None: text += ", &" + self.member_of.name + "::" if self.member_of.link_type == link_types.derive and self.is_virtual: text += "py_" + self.alias text += ", &" + self.member_of.name + "Wrap::default_py_" + self.alias else: text += self.alias text += ")" else: text += ", " + "YOSYS_PYTHON::" + self.alias + ");" return text class WMember: orig_text = None wtype = attr_types.default name = None containing_file = None member_of = None namespace = "" is_const = False def from_string(str_def, containing_file, class_, line_number, namespace): member = WMember() member.orig_text = str_def member.wtype = None member.name = "" member.containing_file = containing_file member.member_of = class_ member.namespace = namespace member.is_const = False if str.startswith(str_def, "const "): member.is_const = True str_def = str_def[6:] if str_def.count(" ") == 0: return None parts = split_list(str_def.strip(), " ") prefix = "" i = 0 for part in parts: if part in ["unsigned", "long", "short"]: prefix += part + " " i += 1 else: break parts = parts[i:] if len(parts) <= 1: return None member.wtype = WType.from_string(prefix + parts[0], containing_file, line_number) if member.wtype == None: return None str_def = parts[1] for part in parts[2:]: str_def = str_def + " " + part if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1: return None found = str_def.find(";") if found == -1: return None found_eq = str_def.find("=") if found_eq != -1: found = found_eq member.name = str_def[:found] str_def = str_def[found+1:] if member.name.find("*") == 0: member.name = member.name.replace("*", "") member.wtype.attr_type = attr_types.star if member.name.find("&&") == 0: member.name = member.name.replace("&&", "") member.wtype.attr_type = attr_types.ampamp if member.name.find("&") == 0: member.name = member.name.replace("&", "") member.wtype.attr_type = attr_types.amp if(len(str_def.strip()) != 0): return None if len(member.name.split(",")) > 1: member_list = [] for name in member.name.split(","): name = name.strip(); member_list.append(WMember()) member_list[-1].orig_text = member.orig_text member_list[-1].wtype = member.wtype member_list[-1].name = name member_list[-1].containing_file = member.containing_file member_list[-1].member_of = member.member_of member_list[-1].namespace = member.namespace member_list[-1].is_const = member.is_const return member_list return member def gen_decl(self): text = "\n\t\t" + self.wtype.gen_text() + " get_var_py_" + self.name + "();\n" if self.is_const: return text if self.wtype.name in classnames: text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs);\n" else: text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs);\n" return text def gen_def(self): text = "\n\t" + self.wtype.gen_text() + " " + self.member_of.name +"::get_var_py_" + self.name + "()" text += "\n\t{\n\t\t" if self.wtype.attr_type == attr_types.star: text += "if(this->get_cpp_obj()->" + self.name + " == NULL)\n\t\t\t" text += "throw std::runtime_error(\"Member \\\"" + self.name + "\\\" is NULL\");\n\t\t" if self.wtype.name in known_containers: text += self.wtype.gen_text_cpp() else: text += self.wtype.gen_text() if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star): text += "*" text += " ret_ = " if self.wtype.name in classnames: text += self.wtype.name + "::get_py_obj(" if self.wtype.attr_type != attr_types.star: text += "&" text += "this->get_cpp_obj()->" + self.name if self.wtype.name in classnames: text += ")" text += ";" if self.wtype.name in classnames: text += "\n\t\treturn *ret_;" elif self.wtype.name in known_containers: text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star) text += "\n\t\treturn ret____tmp;" else: text += "\n\t\treturn ret_;" text += "\n\t}\n" if self.is_const: return text ret = Attribute(self.wtype, "rhs"); if self.wtype.name in classnames: text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)" else: text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)" text += "\n\t{" text += ret.gen_translation() text += "\n\t\tthis->get_cpp_obj()->" + self.name + " = " + ret.gen_call() + ";" text += "\n\t}\n" return text; def gen_boost_py(self): text = "\n\t\t\t.add_property(\"" + self.name + "\", &" + self.member_of.name + "::get_var_py_" + self.name if not self.is_const: text += ", &" + self.member_of.name + "::set_var_py_" + self.name text += ")" return text class WGlobal: orig_text = None wtype = attr_types.default name = None containing_file = None namespace = "" is_const = False def from_string(str_def, containing_file, line_number, namespace): glbl = WGlobal() glbl.orig_text = str_def glbl.wtype = None glbl.name = "" glbl.containing_file = containing_file glbl.namespace = namespace glbl.is_const = False if not str.startswith(str_def, "extern"): return None str_def = str_def[7:] if str.startswith(str_def, "const "): glbl.is_const = True str_def = str_def[6:] if str_def.count(" ") == 0: return None parts = split_list(str_def.strip(), " ") prefix = "" i = 0 for part in parts: if part in ["unsigned", "long", "short"]: prefix += part + " " i += 1 else: break parts = parts[i:] if len(parts) <= 1: return None glbl.wtype = WType.from_string(prefix + parts[0], containing_file, line_number) if glbl.wtype == None: return None str_def = parts[1] for part in parts[2:]: str_def = str_def + " " + part if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1: return None found = str_def.find(";") if found == -1: return None found_eq = str_def.find("=") if found_eq != -1: found = found_eq glbl.name = str_def[:found] str_def = str_def[found+1:] if glbl.name.find("*") == 0: glbl.name = glbl.name.replace("*", "") glbl.wtype.attr_type = attr_types.star if glbl.name.find("&&") == 0: glbl.name = glbl.name.replace("&&", "") glbl.wtype.attr_type = attr_types.ampamp if glbl.name.find("&") == 0: glbl.name = glbl.name.replace("&", "") glbl.wtype.attr_type = attr_types.amp if(len(str_def.strip()) != 0): return None if len(glbl.name.split(",")) > 1: glbl_list = [] for name in glbl.name.split(","): name = name.strip(); glbl_list.append(WGlobal()) glbl_list[-1].orig_text = glbl.orig_text glbl_list[-1].wtype = glbl.wtype glbl_list[-1].name = name glbl_list[-1].containing_file = glbl.containing_file glbl_list[-1].namespace = glbl.namespace glbl_list[-1].is_const = glbl.is_const return glbl_list return glbl def gen_def(self): text = "\n\t" if self.is_const: text += "const " text += self.wtype.gen_text() + " get_var_py_" + self.name + "()" text += "\n\t{\n\t\t" if self.wtype.attr_type == attr_types.star: text += "if(" + self.namespace + "::" + self.name + " == NULL)\n\t\t\t" text += "throw std::runtime_error(\"" + self.namespace + "::" + self.name + " is NULL\");\n\t\t" if self.wtype.name in known_containers: text += self.wtype.gen_text_cpp() else: if self.is_const: text += "const " text += self.wtype.gen_text() if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star): text += "*" text += " ret_ = " if self.wtype.name in classnames: text += self.wtype.name + "::get_py_obj(" if self.wtype.attr_type != attr_types.star: text += "&" text += self.namespace + "::" + self.name if self.wtype.name in classnames: text += ")" text += ";" if self.wtype.name in classnames: text += "\n\t\treturn *ret_;" elif self.wtype.name in known_containers: text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star) text += "\n\t\treturn ret____tmp;" else: text += "\n\t\treturn ret_;" text += "\n\t}\n" if self.is_const: return text ret = Attribute(self.wtype, "rhs"); if self.wtype.name in classnames: text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)" else: text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)" text += "\n\t{" text += ret.gen_translation() text += "\n\t\t" + self.namespace + "::" + self.name + " = " + ret.gen_call() + ";" text += "\n\t}\n" return text; def gen_boost_py(self): text = "\n\t\t\t.add_static_property(\"" + self.name + "\", &" + "YOSYS_PYTHON::get_var_py_" + self.name if not self.is_const: text += ", &YOSYS_PYTHON::set_var_py_" + self.name text += ")" return text def concat_namespace(tuple_list): if len(tuple_list) == 0: return "" ret = "" for namespace in tuple_list: ret += "::" + namespace[0] return ret[2:] def calc_ident(text): if len(text) == 0 or text[0] != ' ': return 0 return calc_ident(text[1:]) + 1 def assure_length(text, length, left = False): if len(text) > length: return text[:length] if left: return text + " "*(length - len(text)) return " "*(length - len(text)) + text def nesting_delta(s): return s.count("{") - s.count("}") def parse_header(source): debug("Parsing " + source.name + ".pyh",1) source_file = open(source.name + ".pyh", "r") source_text = [] in_line = source_file.readline() namespaces = [] while(in_line): if(len(in_line)>1): source_text.append(in_line.replace("char *", "char_p ").replace("char* ", "char_p ")) in_line = source_file.readline() i = 0 namespaces = [] classes = [] private_segment = False while i < len(source_text): line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }") ugly_line = unpretty_string(line) debug(f"READ:>> {line}", 2) # for anonymous unions, ignore union enclosure by skipping start line and replacing end line with new line if 'union {' in line: j = i+1 while j < len(source_text): union_line = source_text[j] if '};' in union_line: source_text[j] = '\n' break j += 1 if j != len(source_text): i += 1 continue if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1: namespace_name = ugly_line[10:].replace("{","").strip() namespaces.append((namespace_name, ugly_line.count("{"))) debug("-----NAMESPACE " + concat_namespace(namespaces) + "-----",3) i += 1 continue if len(namespaces) != 0: namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + nesting_delta(ugly_line)) if namespaces[-1][1] == 0: debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3) namespaces.pop() i += 1 continue if (str.startswith(ugly_line, "struct ") or str.startswith(ugly_line, "class")) and ugly_line.count(";") == 0: # Opening a record declaration which isn't a forward declaration struct_name = ugly_line.split(" ")[1].split("::")[-1] impl_namespaces = ugly_line.split(" ")[1].split("::")[:-1] complete_namespace = concat_namespace(namespaces) for namespace in impl_namespaces: complete_namespace += "::" + namespace debug("\tFound " + struct_name + " in " + complete_namespace,2) base_class_name = None if len(ugly_line.split(" : ")) > 1: # class is derived deriv_str = ugly_line.split(" : ")[1] if len(deriv_str.split("::")) > 1: # namespace of base class is given base_class_name = deriv_str.split("::", 1)[1] else: base_class_name = deriv_str.split(" ")[0] debug("\t " + struct_name + " is derived from " + base_class_name,2) base_class = class_by_name(base_class_name) c = (class_by_name(struct_name), ugly_line.count("{"))#calc_ident(line)) debug(f"switch to {struct_name} in namespace {namespaces}", 2) if struct_name in classnames: c[0].namespace = complete_namespace c[0].base_class = base_class classes.append(c) i += 1 continue if len(classes): c = (classes[-1][0], classes[-1][1] + nesting_delta(ugly_line)) classes[-1] = c if c[1] == 0: if c[0] == None: debug("\tExiting unknown class", 3) else: debug("\tExiting class " + c[0].name, 3) classes.pop() private_segment = False i += 1 continue class_ = classes[-1] if classes else None if class_ != None and (line.find("private:") != -1 or line.find("protected:") != -1): private_segment = True i += 1 continue if class_ != None and line.find("public:") != -1: private_segment = False i += 1 continue candidate = None if private_segment and class_ != None and class_[0] != None: candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i, True) if candidate != None: debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) class_[0].found_constrs.append(candidate) i += 1 continue if not private_segment and (class_ == None or class_[0] != None): if class_ != None: candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces)) else: candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces)) if candidate != None and candidate.name.find("::") == -1: if class_ == None: debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) unowned_functions.append(candidate) else: debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) class_[0].found_funs.append(candidate) else: candidate = WEnum.from_string(ugly_line, concat_namespace(namespaces), i) if candidate != None: enums.append(candidate) debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) elif class_ != None and class_[1] == 1: candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i) if candidate != None: debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) class_[0].found_constrs.append(candidate) else: candidate = WMember.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces)) if candidate != None: if type(candidate) == list: for c in candidate: debug("\t\tFound member \"" + c.name + "\" of class \"" + class_[0].name + "\" of type \"" + c.wtype.name + "\"", 2) class_[0].found_vars.extend(candidate) else: debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2) class_[0].found_vars.append(candidate) if candidate == None and class_ == None: candidate = WGlobal.from_string(ugly_line, source.name, i, concat_namespace(namespaces)) if candidate != None: if type(candidate) == list: for c in candidate: glbls.append(c) debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2) else: glbls.append(candidate) debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2) j = i line = unpretty_string(line) while candidate == None and j+1 < len(source_text) and line.count(';') <= 1 and line.count("(") >= line.count(")"): j += 1 line = line + "\n" + unpretty_string(source_text[j]) if class_ != None: candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces)) else: candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces)) if candidate != None and candidate.name.find("::") == -1: if class_ == None: debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) unowned_functions.append(candidate) else: debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) class_[0].found_funs.append(candidate) continue candidate = WEnum.from_string(line, concat_namespace(namespaces), i) if candidate != None: enums.append(candidate) debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2) continue if class_ != None: candidate = WConstructor.from_string(line, source.name, class_[0], i) if candidate != None: debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2) class_[0].found_constrs.append(candidate) continue if class_ == None: candidate = WGlobal.from_string(line, source.name, i, concat_namespace(namespaces)) if candidate != None: if type(candidate) == list: for c in candidate: glbls.append(c) debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2) else: glbls.append(candidate) debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2) continue if candidate != None: while i < j: i += 1 line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }") ugly_line = unpretty_string(line) if len(namespaces) != 0: namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + nesting_delta(ugly_line)) if namespaces[-1][1] == 0: debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3) namespaces.pop() if len(classes): c = (classes[-1][0] , classes[-1][1] + nesting_delta(ugly_line)) classes[-1] = c if c[1] == 0: if c[0] == None: debug("\tExiting unknown class", 3) else: debug("\tExiting class " + c[0].name, 3) classes.pop() private_segment = False i += 1 else: i += 1 def debug(message, level): if level <= debug.debug_level: print(message) def expand_function(f): fun_list = [] arg_list = [] for arg in f.args: 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")): fi = copy.deepcopy(f) fi.args = copy.deepcopy(arg_list) fun_list.append(fi) arg_list.append(arg) fun_list.append(f) return fun_list def expand_functions(): global unowned_functions new_funs = [] for fun in unowned_functions: new_funs.extend(expand_function(fun)) unowned_functions = new_funs for source in sources: for class_ in source.classes: new_funs = [] for fun in class_.found_funs: new_funs.extend(expand_function(fun)) class_.found_funs = new_funs def inherit_members(): for source in sources: for class_ in source.classes: if class_.base_class: base_funs = copy.deepcopy(class_.base_class.found_funs) for fun in base_funs: fun.member_of = class_ fun.namespace = class_.namespace base_vars = copy.deepcopy(class_.base_class.found_vars) for var in base_vars: var.member_of = class_ var.namespace = class_.namespace class_.found_funs.extend(base_funs) class_.found_vars.extend(base_vars) def clean_duplicates(): for source in sources: for class_ in source.classes: known_decls = {} for fun in class_.found_funs: if fun.gen_decl_hash_py() in known_decls: debug("Multiple declarations of " + fun.gen_decl_hash_py(),3) other = known_decls[fun.gen_decl_hash_py()] if fun.mangled_name == other.mangled_name: fun.duplicate = True debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3) continue other.gen_alias() fun.gen_alias() else: known_decls[fun.gen_decl_hash_py()] = fun known_decls = [] for con in class_.found_constrs: if con.gen_decl_hash_py() in known_decls: debug("Multiple declarations of " + con.gen_decl_hash_py(),3) con.duplicate = True else: known_decls.append(con.gen_decl_hash_py()) known_decls = [] for fun in unowned_functions: if fun.gen_decl_hash_py() in known_decls: debug("Multiple declarations of " + fun.gen_decl_hash_py(),3) fun.duplicate = True else: known_decls.append(fun.gen_decl_hash_py()) def gen_wrappers(filename, debug_level_ = 0): debug.debug_level = debug_level_ for source in sources: parse_header(source) expand_functions() inherit_members() clean_duplicates() import shutil import math col = shutil.get_terminal_size((80,20)).columns debug("-"*col, 1) debug("-"*math.floor((col-7)/2)+"SUMMARY"+"-"*math.ceil((col-7)/2), 1) debug("-"*col, 1) for source in sources: for class_ in source.classes: 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) if len(class_.found_constrs) == 0: class_.found_constrs.append(WConstructor(source.name, class_)) debug(str(len(unowned_functions)) + " functions are unowned", 1) debug(str(len(unowned_functions)) + " global variables", 1) for enum in enums: debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1) debug("-"*col, 1) wrapper_file = open(filename, "w+") wrapper_file.write( """/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * This is a generated file and can be overwritten by make */ #ifdef WITH_PYTHON """) for source in sources: wrapper_file.write("#include \""+source.name+".h\"\n") wrapper_file.write(""" #include #include #include #include #include #include // std::streamsize #include #include // boost::iostreams::sink #include USING_YOSYS_NAMESPACE namespace YOSYS_PYTHON { struct YosysStatics{}; """) for source in sources: for wclass in source.classes: wrapper_file.write("\n\tstruct " + wclass.name + ";") wrapper_file.write("\n") for source in sources: for wclass in source.classes: wrapper_file.write(wclass.gen_decl(source.name)) wrapper_file.write("\n") for source in sources: for wclass in source.classes: wrapper_file.write(wclass.gen_funs(source.name)) for fun in unowned_functions: wrapper_file.write(fun.gen_def()) for glbl in glbls: wrapper_file.write(glbl.gen_def()) wrapper_file.write(""" struct Initializer { Initializer() { if(!Yosys::yosys_already_setup()) { Yosys::log_streams.push_back(&std::cout); Yosys::log_error_stderr = true; Yosys::yosys_setup(); } } Initializer(Initializer const &) {} ~Initializer() { Yosys::yosys_shutdown(); } }; /// source: https://stackoverflow.com/questions/26033781/converting-python-io-object-to-stdostream-when-using-boostpython?noredirect=1&lq=1 /// @brief Type that implements the Boost.IOStream's Sink and Flushable /// concept for writing data to Python object that support: /// n = object.write(str) # n = None or bytes written /// object.flush() # if flush exists, then it is callable class PythonOutputDevice { public: // This class models both the Sink and Flushable concepts. struct category : boost::iostreams::sink_tag, boost::iostreams::flushable_tag {}; explicit PythonOutputDevice(boost::python::object object) : object_(object) {} // Sink concept. public: typedef char char_type; std::streamsize write(const char* buffer, std::streamsize buffer_size) { namespace python = boost::python; // Copy the buffer to a python string. python::str data(buffer, buffer_size); // Invoke write on the python object, passing in the data. The following // is equivalent to: // n = object_.write(data) python::extract bytes_written( object_.attr("write")(data)); // Per the Sink concept, return the number of bytes written. If the // Python return value provides a numeric result, then use it. Otherwise, // such as the case of a File object, use the buffer_size. return bytes_written.check() ? bytes_written : buffer_size; } // Flushable concept. public: bool flush() { // If flush exists, then call it. boost::python::object flush = object_.attr("flush"); if (!flush.is_none()) { flush(); } // Always return true. If an error occurs, an exception should be thrown. return true; } private: boost::python::object object_; }; /// @brief Use an auxiliary function to adapt the legacy function. void log_to_stream(boost::python::object object) { // Create an ostream that delegates to the python object. boost::iostreams::stream* output = new boost::iostreams::stream(object); Yosys::log_streams.insert(Yosys::log_streams.begin(), output); }; BOOST_PYTHON_MODULE(libyosys) { using namespace boost::python; class_("Initializer"); scope().attr("_hidden") = new Initializer(); def("log_to_stream", &log_to_stream); """) for enum in enums: wrapper_file.write(enum.gen_boost_py()) for source in sources: for wclass in source.classes: wrapper_file.write(wclass.gen_boost_py()) for fun in unowned_functions: wrapper_file.write(fun.gen_boost_py()) wrapper_file.write("\n\n\t\tclass_(\"Yosys\")\n") for glbl in glbls: wrapper_file.write(glbl.gen_boost_py()) wrapper_file.write("\t\t;\n") wrapper_file.write("\n\t}\n}\n#endif") def print_includes(): for source in sources: print(source.name + ".pyh")