3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-06 17:44:09 +00:00
yosys/misc/py_wrap_generator.py
Benedikt Tutzer 9c59a56aa4 Expose global variables and allow logging to python streams
Global variables are now accessible via the Yosys class.
To capture Yosys output, once can now register an output stream in
Pyosys.
2019-10-09 13:59:35 +02:00

2374 lines
76 KiB
Python

#
# yosys -- Yosys Open SYnthesis Suite
#
# Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
#
# 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<SigSpec, SigSpec>").replace("SigSig", "std::pair<SigSpec, SigSpec>")
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(" "):
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, "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:]
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
#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"
#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,
"dict" : DictTranslator,
"std::pair" : TupleTranslator,
"std::map" : MapTranslator
}
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
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.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()
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()
if self.link_type == link_types.derive:
text = "\n\t\tclass_<" + self.name + ">(\"" + 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 + ">(\"Cpp" + 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", "YOSYS_NAMESPACE::Module::Bu0", "YOSYS_NAMESPACE::CaseRule::optimize"]
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.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"]:
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
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
def gen_alias(self):
self.alias = self.name
for arg in self.args:
self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","")
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 += ");\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:
text += "{}"
else:
text += "\n\t\t{"
for arg in self.args:
text += "\n\t\t\t(void)" + arg.gen_varname() + ";"
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 += ") YS_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 +=")\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 += ")\n\t{"
for arg in self.args:
text += arg.gen_translation_cpp()
text += "\n\t\t"
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 += "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 += ");"
text += ")\n\t\t{"
text += "\n\t\t\tif(boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
text += "\n\t\t\t\t" + call_string
text += "\n\t\t\telse"
text += "\n\t\t\t\t" + 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 += ")\n\t\t{"
text += "\n\t\t\tthis->" + 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] + ")>"
else:
text += "void)>"
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 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 = []
class_ = None
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)
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] + ugly_line.count("{") - ugly_line.count("}"))
if namespaces[-1][1] == 0:
debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
del namespaces[-1]
i += 1
continue
if class_ == None and (str.startswith(ugly_line, "struct ") or str.startswith(ugly_line, "class")) and ugly_line.count(";") == 0:
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)
class_ = (class_by_name(struct_name), ugly_line.count("{"))#calc_ident(line))
if struct_name in classnames:
class_[0].namespace = complete_namespace
i += 1
continue
if class_ != None:
class_ = (class_[0], class_[1] + ugly_line.count("{") - ugly_line.count("}"))
if class_[1] == 0:
if class_[0] == None:
debug("\tExiting unknown class", 3)
else:
debug("\tExiting class " + class_[0].name, 3)
class_ = None
private_segment = False
i += 1
continue
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] + ugly_line.count("{") - ugly_line.count("}"))
if namespaces[-1][1] == 0:
debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
del namespaces[-1]
if class_ != None:
class_ = (class_[0] , class_[1] + ugly_line.count("{") - ugly_line.count("}"))
if class_[1] == 0:
if class_[0] == None:
debug("\tExiting unknown class", 3)
else:
debug("\tExiting class " + class_[0].name, 3)
class_ = None
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 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()]
other.gen_alias()
fun.gen_alias()
if fun.gen_decl_hash_py() == other.gen_decl_hash_py():
fun.duplicate = True
debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3)
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()
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 Clifford Wolf <clifford@clifford.at>
*
* 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 <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/wrapper.hpp>
#include <boost/python/call.hpp>
#include <boost/python.hpp>
#include <iosfwd> // std::streamsize
#include <iostream>
#include <boost/iostreams/concepts.hpp> // boost::iostreams::sink
#include <boost/iostreams/stream.hpp>
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<std::streamsize> 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<PythonOutputDevice>* output = new boost::iostreams::stream<PythonOutputDevice>(object);
Yosys::log_streams.insert(Yosys::log_streams.begin(), output);
};
BOOST_PYTHON_MODULE(libyosys)
{
using namespace boost::python;
class_<Initializer>("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_<YosysStatics>(\"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")