#
# 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 + "]);"