5656 Variable ,
5757 Array ,
5858 FundamentalSpecifier ,
59+ FunctionType ,
5960)
6061
6162__file_dir__ = Path (__file__ ).absolute ().parent
@@ -177,11 +178,11 @@ def __post_init__(self):
177178 denylist = frozenset ({"bits" , "bitvectorize" }),
178179 ),
179180 PyosysClass ("AttrObject" , denylist = frozenset ({"get_blackbox_attribute" })),
180- PyosysClass ("NamedObject" , denylist = frozenset ({ "get_blackbox_attribute" }) ),
181+ PyosysClass ("NamedObject" ),
181182 PyosysClass ("Selection" ),
182183 # PyosysClass("Monitor"), # Virtual methods, manually bridged
183- PyosysClass ("CaseRule" , denylist = frozenset ({ "get_blackbox_attribute" }) ),
184- PyosysClass ("SwitchRule" , denylist = frozenset ({ "get_blackbox_attribute" }) ),
184+ PyosysClass ("CaseRule" ),
185+ PyosysClass ("SwitchRule" ),
185186 PyosysClass ("SyncRule" ),
186187 PyosysClass (
187188 "Process" ,
@@ -219,7 +220,7 @@ def __post_init__(self):
219220 ),
220221 PyosysClass (
221222 "Design" ,
222- string_expr = "s.hashidx_" ,
223+ string_expr = "std::to_string( s.hashidx_) " ,
223224 hash_expr = "s" ,
224225 denylist = frozenset ({"selected_whole_modules" }), # deprecated
225226 ),
@@ -241,13 +242,17 @@ class PyosysType:
241242
242243 @classmethod
243244 def from_type (Self , type_obj , drop_const = False ) -> "PyosysType" :
244- const = type_obj .const and not drop_const
245+ const = hasattr ( type_obj , "const" ) and type_obj .const and not drop_const
245246 if isinstance (type_obj , Pointer ):
246247 ptr_to = Self .from_type (type_obj .ptr_to )
247248 return Self ("ptr" , (ptr_to ,), const )
248249 elif isinstance (type_obj , Reference ):
249250 ref_to = Self .from_type (type_obj .ref_to )
250251 return Self ("ref" , (ref_to ,), const )
252+ elif isinstance (type_obj , FunctionType ):
253+ ret_type = Self .from_type (type_obj .return_type )
254+ param_types = (Self .from_type (p .type ) for p in type_obj .parameters )
255+ return Self ("fn" , (ret_type , * param_types ), False )
251256 assert isinstance (
252257 type_obj , Type
253258 ), f"unexpected c++ type object of type { type (type_obj )} "
@@ -270,6 +275,16 @@ def generate_identifier(self):
270275 if title == "Dict" :
271276 key , value = self .specialization
272277 return f"{ key .generate_identifier ()} To{ value .generate_identifier ()} { title } "
278+ elif title == "Fn" :
279+ identifier = self .specialization [0 ].generate_identifier ()
280+ if identifier == "Void" :
281+ identifier = ""
282+ else :
283+ identifier += "From"
284+ identifier += "And" .join (
285+ p .generate_identifier () for p in self .specialization [1 :]
286+ )
287+ return identifier
273288
274289 return (
275290 "" .join (spec .generate_identifier () for spec in self .specialization ) + title
@@ -283,6 +298,9 @@ def generate_cpp_name(self):
283298 return const_prefix + f"{ self .specialization [0 ].generate_cpp_name ()} *"
284299 elif self .base == "ref" :
285300 return const_prefix + f"{ self .specialization [0 ].generate_cpp_name ()} &"
301+ elif self .base == "fn" :
302+ param_cpp_names = (s .generate_cpp_name () for s in self .specialization [1 :])
303+ return f"{ self .specialization [0 ].generate_cpp_name ()} ({ ',' .join (param_cpp_names )} )"
286304 else :
287305 return (
288306 const_prefix
@@ -301,7 +319,7 @@ def __init__(
301319 self .f = wrapper_stream
302320 self .f_inc = header_stream
303321 self .found_containers : Dict [PyosysType , Any ] = {}
304- self .class_registry : Dict [str , ClassScope ] = {}
322+ self .class_registry : Dict [str , Tuple [ ClassScope , PyosysClass ] ] = {}
305323
306324 # entry point
307325 def generate (self ):
@@ -380,7 +398,7 @@ def find_containers(
380398 if isinstance (type_info , Reference ):
381399 return PyosysWrapperGenerator .find_containers (containers , type_info .ref_to )
382400 if not isinstance (type_info , Type ):
383- return ()
401+ return {}
384402 segments = type_info .typename .segments
385403 containers_found = {}
386404 for segment in segments :
@@ -411,19 +429,23 @@ def find_anonymous_union(cls: ClassScope):
411429 def get_parameter_types (function : Function ) -> str :
412430 return ", " .join (p .type .format () for p in function .parameters )
413431
414- def register_containers (self , target : Union [Function , Field , Variable ]):
432+ def register_containers (self , target : Union [Function , Field , Variable ]) -> bool :
415433 supported = ("dict" , "idict" , "pool" , "set" , "vector" )
434+ found = False
416435 if isinstance (target , Function ):
417- self .found_containers . update (
418- self . find_containers ( supported , target . return_type )
419- )
436+ return_type_containers = self .find_containers ( supported , target . return_type )
437+ found = found or len ( return_type_containers )
438+ self . found_containers . update ( return_type_containers )
420439
421440 for parameter in target .parameters :
422- self .found_containers . update (
423- self . find_containers ( supported , parameter . type )
424- )
441+ parameter_containers = self .find_containers ( supported , parameter . type )
442+ found = found or len ( parameter_containers )
443+ self . found_containers . update ( parameter_containers )
425444 else :
426- self .found_containers .update (self .find_containers (supported , target .type ))
445+ variable_containers = self .find_containers (supported , target .type )
446+ found = found or len (variable_containers )
447+ self .found_containers .update (variable_containers )
448+ return found
427449
428450 # processors
429451 def get_overload_cast (
@@ -470,9 +492,9 @@ def get_definition_args(
470492
471493 def_args = [f'"{ python_function_basename } "' ]
472494 def_args .append (self .get_overload_cast (function , class_basename ))
473- for parameter in function .parameters :
474- # ASSUMPTION: there are no unnamed parameters in the yosys codebase
475- parameter_arg = f'py::arg("{ parameter . name } ")'
495+ for i , parameter in enumerate ( function .parameters ) :
496+ name = parameter . name or f"arg { i } "
497+ parameter_arg = f'py::arg("{ name } ")'
476498 if parameter .default is not None :
477499 parameter_arg += f" = { parameter .default .format ()} "
478500 def_args .append (parameter_arg )
@@ -525,8 +547,12 @@ def process_method(self, metadata: PyosysClass, function: Method):
525547 if function .static :
526548 definition_fn = "def_static"
527549
550+ definition_args = self .get_definition_args (
551+ function , metadata .name , python_name_override
552+ )
553+
528554 print (
529- f"\t \t \t .{ definition_fn } ({ ', ' .join (self . get_definition_args ( function , metadata . name , python_name_override ) )} )" ,
555+ f"\t \t \t .{ definition_fn } ({ ', ' .join (definition_args )} )" ,
530556 file = self .f ,
531557 )
532558
@@ -565,16 +591,28 @@ def process_field(self, metadata: PyosysClass, field: Field):
565591 # care
566592 return
567593
568- self .register_containers (field )
594+ has_containers = self .register_containers (field )
569595
570596 definition_fn = f"def_{ 'readonly' if field .type .const else 'readwrite' } "
571597 if field .static :
572598 definition_fn += "_static"
573599
574600 field_python_basename = keyword_aliases .get (field .name , field .name )
575601
602+ def_args = [
603+ f'"{ field_python_basename } "' ,
604+ f"&{ metadata .name } ::{ field .name } " ,
605+ ]
606+ if has_containers :
607+ # always reference with lifetime managed by c++
608+ # never copy containers.
609+ def_args .append ("py::return_value_policy::reference" )
610+ else :
611+ # automatic reference: managed by c++ lifetime if pointer, copied to
612+ # python if rvalue, moved to python if lvalue
613+ def_args .append ("py::return_value_policy::automatic_reference" )
576614 print (
577- f' \t \t \t .{ definition_fn } (" { field_python_basename } ", & { metadata . name } :: { field . name } )' ,
615+ f" \t \t \t .{ definition_fn } ({ ', ' . join ( def_args ) } )" ,
578616 file = self .f ,
579617 )
580618
@@ -603,16 +641,20 @@ def process_variable(self, variable: Variable):
603641 )
604642
605643 def process_class_members (
606- self , metadata : PyosysClass , cls : ClassScope , basename : str
644+ self ,
645+ metadata : PyosysClass ,
646+ base_metadata : PyosysClass ,
647+ cls : ClassScope ,
648+ basename : str ,
607649 ):
608650 for method in cls .methods :
609- if method .name .segments [- 1 ].name in metadata .denylist :
651+ if method .name .segments [- 1 ].name in base_metadata .denylist :
610652 continue
611653 self .process_method (metadata , method )
612654
613655 visited_anonymous_unions = set ()
614656 for field_ in cls .fields :
615- if field_ .name in metadata .denylist :
657+ if field_ .name in base_metadata .denylist :
616658 continue
617659 self .process_field (metadata , field_ )
618660
@@ -627,6 +669,16 @@ def process_class_members(
627669 for subfield in subclass .fields :
628670 self .process_field (metadata , subfield )
629671
672+ for base in cls .class_decl .bases :
673+ if base .access != "public" :
674+ continue
675+ name = base .typename .segments [- 1 ].format ()
676+ if processed := self .class_registry .get (name ):
677+ base_scope , base_metadata = processed
678+ self .process_class_members (
679+ metadata , base_metadata , base_scope , basename
680+ )
681+
630682 def process_class (
631683 self ,
632684 metadata : PyosysClass ,
@@ -638,7 +690,7 @@ def process_class(
638690 segment .format () for segment in pqname .segments
639691 ]
640692 basename = full_path .pop ()
641- self .class_registry [basename ] = cls
693+ self .class_registry [basename ] = ( cls , metadata )
642694
643695 declaration_namespace = "::" .join (full_path )
644696 tpl_args = [basename ]
@@ -649,19 +701,17 @@ def process_class(
649701 file = self .f ,
650702 )
651703
652- self .process_class_members (metadata , cls , basename )
653- for base in cls .class_decl .bases :
654- if base .access != "public" :
655- continue
656- name = base .typename .segments [- 1 ].format ()
657- if base_scope := self .class_registry .get (name ):
658- self .process_class_members (metadata , base_scope , basename )
704+ self .process_class_members (metadata , metadata , cls , basename )
659705
660706 if expr := metadata .string_expr :
661707 print (
662708 f'\t \t .def("__str__", [](const { basename } &s) {{ return { expr } ; }})' ,
663709 file = self .f ,
664710 )
711+ print (
712+ f'\t \t .def("__repr__", [](const { basename } &s) {{ std::stringstream ss; ss << "<{ basename } " << { expr } << ">"; return ss.str(); }})' ,
713+ file = self .f ,
714+ )
665715
666716 if expr := metadata .hash_expr :
667717 print (
0 commit comments