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