301 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
		
		
			
		
	
	
			301 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
| 
								 | 
							
								"""Various helpers for instantiation."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import itertools
							 | 
						||
| 
								 | 
							
								from copy import deepcopy
							 | 
						||
| 
								 | 
							
								from typing import List, Sequence, Union
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import gtwrap.interface_parser as parser
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ClassMembers = Union[parser.Constructor, parser.Method, parser.StaticMethod,
							 | 
						||
| 
								 | 
							
								                     parser.GlobalFunction, parser.Operator, parser.Variable,
							 | 
						||
| 
								 | 
							
								                     parser.Enum]
							 | 
						||
| 
								 | 
							
								InstantiatedMembers = Union['InstantiatedConstructor', 'InstantiatedMethod',
							 | 
						||
| 
								 | 
							
								                            'InstantiatedStaticMethod',
							 | 
						||
| 
								 | 
							
								                            'InstantiatedGlobalFunction']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def is_scoped_template(template_typenames: Sequence[str],
							 | 
						||
| 
								 | 
							
								                       str_arg_typename: str):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Check if the template given by `str_arg_typename` is a scoped template e.g. T::Value,
							 | 
						||
| 
								 | 
							
								    and if so, return what template from `template_typenames` and
							 | 
						||
| 
								 | 
							
								    the corresponding index matches the scoped template correctly.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    for idx, template in enumerate(template_typenames):
							 | 
						||
| 
								 | 
							
								        if "::" in str_arg_typename and \
							 | 
						||
| 
								 | 
							
								            template in str_arg_typename.split("::"):
							 | 
						||
| 
								 | 
							
								            return template, idx
							 | 
						||
| 
								 | 
							
								    return False, -1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def instantiate_type(
							 | 
						||
| 
								 | 
							
								        ctype: parser.Type,
							 | 
						||
| 
								 | 
							
								        template_typenames: Sequence[str],
							 | 
						||
| 
								 | 
							
								        instantiations: Sequence[parser.Typename],
							 | 
						||
| 
								 | 
							
								        cpp_typename: parser.Typename,
							 | 
						||
| 
								 | 
							
								        instantiated_class: 'InstantiatedClass' = None) -> parser.Type:
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Instantiate template typename for `ctype`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Args:
							 | 
						||
| 
								 | 
							
								        ctype: The original argument type.
							 | 
						||
| 
								 | 
							
								        template_typenames: List of strings representing the templates.
							 | 
						||
| 
								 | 
							
								        instantiations: List of the instantiations of the templates in `template_typenames`.
							 | 
						||
| 
								 | 
							
								        cpp_typename: Full-namespace cpp class name of this instantiation
							 | 
						||
| 
								 | 
							
								            to replace for arguments of type named `This`.
							 | 
						||
| 
								 | 
							
								        instiated_class: The instantiated class which, if provided,
							 | 
						||
| 
								 | 
							
								            will be used for instantiating `This`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Returns:
							 | 
						||
| 
								 | 
							
								        If `ctype`'s name is in the `template_typenames`, return the
							 | 
						||
| 
								 | 
							
								        corresponding type to replace in `instantiations`.
							 | 
						||
| 
								 | 
							
								        If ctype name is `This`, return the new typename `cpp_typename`.
							 | 
						||
| 
								 | 
							
								        Otherwise, return the original ctype.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    # make a deep copy so that there is no overwriting of original template params
							 | 
						||
| 
								 | 
							
								    ctype = deepcopy(ctype)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Check if the return type has template parameters as the typename's name
							 | 
						||
| 
								 | 
							
								    if ctype.typename.instantiations:
							 | 
						||
| 
								 | 
							
								        for idx, instantiation in enumerate(ctype.typename.instantiations):
							 | 
						||
| 
								 | 
							
								            if instantiation.name in template_typenames:
							 | 
						||
| 
								 | 
							
								                template_idx = template_typenames.index(instantiation.name)
							 | 
						||
| 
								 | 
							
								                ctype.typename.instantiations[idx].name =\
							 | 
						||
| 
								 | 
							
								                    instantiations[template_idx]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    str_arg_typename = str(ctype.typename)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Check if template is a scoped template e.g. T::Value where T is the template
							 | 
						||
| 
								 | 
							
								    scoped_template, scoped_idx = is_scoped_template(template_typenames,
							 | 
						||
| 
								 | 
							
								                                                     str_arg_typename)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Instantiate templates which have enumerated instantiations in the template.
							 | 
						||
| 
								 | 
							
								    # E.g. `template<T={double}>`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Instantiate scoped templates, e.g. T::Value.
							 | 
						||
| 
								 | 
							
								    if scoped_template:
							 | 
						||
| 
								 | 
							
								        # Create a copy of the instantiation so we can modify it.
							 | 
						||
| 
								 | 
							
								        instantiation = deepcopy(instantiations[scoped_idx])
							 | 
						||
| 
								 | 
							
								        # Replace the part of the template with the instantiation
							 | 
						||
| 
								 | 
							
								        instantiation.name = str_arg_typename.replace(scoped_template,
							 | 
						||
| 
								 | 
							
								                                                      instantiation.name)
							 | 
						||
| 
								 | 
							
								        return parser.Type(
							 | 
						||
| 
								 | 
							
								            typename=instantiation,
							 | 
						||
| 
								 | 
							
								            is_const=ctype.is_const,
							 | 
						||
| 
								 | 
							
								            is_shared_ptr=ctype.is_shared_ptr,
							 | 
						||
| 
								 | 
							
								            is_ptr=ctype.is_ptr,
							 | 
						||
| 
								 | 
							
								            is_ref=ctype.is_ref,
							 | 
						||
| 
								 | 
							
								            is_basic=ctype.is_basic,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    # Check for exact template match.
							 | 
						||
| 
								 | 
							
								    elif str_arg_typename in template_typenames:
							 | 
						||
| 
								 | 
							
								        idx = template_typenames.index(str_arg_typename)
							 | 
						||
| 
								 | 
							
								        return parser.Type(
							 | 
						||
| 
								 | 
							
								            typename=instantiations[idx],
							 | 
						||
| 
								 | 
							
								            is_const=ctype.is_const,
							 | 
						||
| 
								 | 
							
								            is_shared_ptr=ctype.is_shared_ptr,
							 | 
						||
| 
								 | 
							
								            is_ptr=ctype.is_ptr,
							 | 
						||
| 
								 | 
							
								            is_ref=ctype.is_ref,
							 | 
						||
| 
								 | 
							
								            is_basic=ctype.is_basic,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # If a method has the keyword `This`, we replace it with the (instantiated) class.
							 | 
						||
| 
								 | 
							
								    elif str_arg_typename == 'This':
							 | 
						||
| 
								 | 
							
								        # Check if the class is template instantiated
							 | 
						||
| 
								 | 
							
								        # so we can replace it with the instantiated version.
							 | 
						||
| 
								 | 
							
								        if instantiated_class:
							 | 
						||
| 
								 | 
							
								            name = instantiated_class.original.name
							 | 
						||
| 
								 | 
							
								            namespaces_name = instantiated_class.namespaces()
							 | 
						||
| 
								 | 
							
								            namespaces_name.append(name)
							 | 
						||
| 
								 | 
							
								            cpp_typename = parser.Typename(
							 | 
						||
| 
								 | 
							
								                namespaces_name,
							 | 
						||
| 
								 | 
							
								                instantiations=instantiated_class.instantiations)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return parser.Type(
							 | 
						||
| 
								 | 
							
								            typename=cpp_typename,
							 | 
						||
| 
								 | 
							
								            is_const=ctype.is_const,
							 | 
						||
| 
								 | 
							
								            is_shared_ptr=ctype.is_shared_ptr,
							 | 
						||
| 
								 | 
							
								            is_ptr=ctype.is_ptr,
							 | 
						||
| 
								 | 
							
								            is_ref=ctype.is_ref,
							 | 
						||
| 
								 | 
							
								            is_basic=ctype.is_basic,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Case when 'This' is present in the type namespace, e.g `This::Subclass`.
							 | 
						||
| 
								 | 
							
								    elif 'This' in str_arg_typename:
							 | 
						||
| 
								 | 
							
								        # Check if `This` is in the namespaces
							 | 
						||
| 
								 | 
							
								        if 'This' in ctype.typename.namespaces:
							 | 
						||
| 
								 | 
							
								            # Simply get the index of `This` in the namespace and
							 | 
						||
| 
								 | 
							
								            # replace it with the instantiated name.
							 | 
						||
| 
								 | 
							
								            namespace_idx = ctype.typename.namespaces.index('This')
							 | 
						||
| 
								 | 
							
								            ctype.typename.namespaces[namespace_idx] = cpp_typename.name
							 | 
						||
| 
								 | 
							
								        # Else check if it is in the template namespace, e.g vector<This::Value>
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            for idx, instantiation in enumerate(ctype.typename.instantiations):
							 | 
						||
| 
								 | 
							
								                if 'This' in instantiation.namespaces:
							 | 
						||
| 
								 | 
							
								                    ctype.typename.instantiations[idx].namespaces = \
							 | 
						||
| 
								 | 
							
								                        cpp_typename.namespaces + [cpp_typename.name]
							 | 
						||
| 
								 | 
							
								        return ctype
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        return ctype
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def instantiate_args_list(
							 | 
						||
| 
								 | 
							
								        args_list: Sequence[parser.Argument],
							 | 
						||
| 
								 | 
							
								        template_typenames: Sequence[parser.template.Typename],
							 | 
						||
| 
								 | 
							
								        instantiations: Sequence, cpp_typename: parser.Typename):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Instantiate template typenames in an argument list.
							 | 
						||
| 
								 | 
							
								    Type with name `This` will be replaced by @p `cpp_typename`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @param[in] args_list A list of `parser.Argument` to instantiate.
							 | 
						||
| 
								 | 
							
								    @param[in] template_typenames List of template typenames to instantiate,
							 | 
						||
| 
								 | 
							
								        e.g. ['T', 'U', 'V'].
							 | 
						||
| 
								 | 
							
								    @param[in] instantiations List of specific types to instantiate, each
							 | 
						||
| 
								 | 
							
								        associated with each template typename. Each type is a parser.Typename,
							 | 
						||
| 
								 | 
							
								        including its name and full namespaces.
							 | 
						||
| 
								 | 
							
								    @param[in] cpp_typename Full-namespace cpp class name of this instantiation
							 | 
						||
| 
								 | 
							
								        to replace for arguments of type named `This`.
							 | 
						||
| 
								 | 
							
								    @return A new list of parser.Argument which types are replaced with their
							 | 
						||
| 
								 | 
							
								        instantiations.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    instantiated_args = []
							 | 
						||
| 
								 | 
							
								    for arg in args_list:
							 | 
						||
| 
								 | 
							
								        new_type = instantiate_type(arg.ctype, template_typenames,
							 | 
						||
| 
								 | 
							
								                                    instantiations, cpp_typename)
							 | 
						||
| 
								 | 
							
								        instantiated_args.append(
							 | 
						||
| 
								 | 
							
								            parser.Argument(name=arg.name, ctype=new_type,
							 | 
						||
| 
								 | 
							
								                            default=arg.default))
							 | 
						||
| 
								 | 
							
								    return instantiated_args
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def instantiate_return_type(
							 | 
						||
| 
								 | 
							
								        return_type: parser.ReturnType,
							 | 
						||
| 
								 | 
							
								        template_typenames: Sequence[parser.template.Typename],
							 | 
						||
| 
								 | 
							
								        instantiations: Sequence[parser.Typename],
							 | 
						||
| 
								 | 
							
								        cpp_typename: parser.Typename,
							 | 
						||
| 
								 | 
							
								        instantiated_class: 'InstantiatedClass' = None):
							 | 
						||
| 
								 | 
							
								    """Instantiate the return type."""
							 | 
						||
| 
								 | 
							
								    new_type1 = instantiate_type(return_type.type1,
							 | 
						||
| 
								 | 
							
								                                 template_typenames,
							 | 
						||
| 
								 | 
							
								                                 instantiations,
							 | 
						||
| 
								 | 
							
								                                 cpp_typename,
							 | 
						||
| 
								 | 
							
								                                 instantiated_class=instantiated_class)
							 | 
						||
| 
								 | 
							
								    if return_type.type2:
							 | 
						||
| 
								 | 
							
								        new_type2 = instantiate_type(return_type.type2,
							 | 
						||
| 
								 | 
							
								                                     template_typenames,
							 | 
						||
| 
								 | 
							
								                                     instantiations,
							 | 
						||
| 
								 | 
							
								                                     cpp_typename,
							 | 
						||
| 
								 | 
							
								                                     instantiated_class=instantiated_class)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        new_type2 = ''
							 | 
						||
| 
								 | 
							
								    return parser.ReturnType(new_type1, new_type2)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def instantiate_name(original_name: str,
							 | 
						||
| 
								 | 
							
								                     instantiations: Sequence[parser.Typename]):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Concatenate instantiated types with `original_name` to form a new
							 | 
						||
| 
								 | 
							
								    instantiated name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    NOTE: To avoid conflicts, we should include the instantiation's
							 | 
						||
| 
								 | 
							
								    namespaces, but that is too verbose.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    instantiated_names = []
							 | 
						||
| 
								 | 
							
								    for inst in instantiations:
							 | 
						||
| 
								 | 
							
								        # Ensure the first character of the type is capitalized
							 | 
						||
| 
								 | 
							
								        name = inst.instantiated_name()
							 | 
						||
| 
								 | 
							
								        # Using `capitalize` on the complete name causes other caps to be lower case
							 | 
						||
| 
								 | 
							
								        instantiated_names.append(name.replace(name[0], name[0].capitalize()))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return "{}{}".format(original_name, "".join(instantiated_names))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class InstantiationHelper:
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Helper class for instantiation templates.
							 | 
						||
| 
								 | 
							
								    Requires that `instantiation_type` defines a class method called
							 | 
						||
| 
								 | 
							
								    `construct` to generate the appropriate object type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Signature for `construct` should be
							 | 
						||
| 
								 | 
							
								    ```
							 | 
						||
| 
								 | 
							
								        construct(method,
							 | 
						||
| 
								 | 
							
								                  typenames,
							 | 
						||
| 
								 | 
							
								                  class_instantiations,
							 | 
						||
| 
								 | 
							
								                  method_instantiations,
							 | 
						||
| 
								 | 
							
								                  instantiated_args,
							 | 
						||
| 
								 | 
							
								                  parent=parent)
							 | 
						||
| 
								 | 
							
								    ```
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    def __init__(self, instantiation_type: InstantiatedMembers):
							 | 
						||
| 
								 | 
							
								        self.instantiation_type = instantiation_type
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def instantiate(self, instantiated_methods: List[InstantiatedMembers],
							 | 
						||
| 
								 | 
							
								                    method: ClassMembers, typenames: Sequence[str],
							 | 
						||
| 
								 | 
							
								                    class_instantiations: Sequence[parser.Typename],
							 | 
						||
| 
								 | 
							
								                    method_instantiations: Sequence[parser.Typename],
							 | 
						||
| 
								 | 
							
								                    parent: 'InstantiatedClass'):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Instantiate both the class and method level templates.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        instantiations = class_instantiations + method_instantiations
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        instantiated_args = instantiate_args_list(method.args.list(),
							 | 
						||
| 
								 | 
							
								                                                  typenames, instantiations,
							 | 
						||
| 
								 | 
							
								                                                  parent.cpp_typename())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        instantiated_methods.append(
							 | 
						||
| 
								 | 
							
								            self.instantiation_type.construct(method,
							 | 
						||
| 
								 | 
							
								                                              typenames,
							 | 
						||
| 
								 | 
							
								                                              class_instantiations,
							 | 
						||
| 
								 | 
							
								                                              method_instantiations,
							 | 
						||
| 
								 | 
							
								                                              instantiated_args,
							 | 
						||
| 
								 | 
							
								                                              parent=parent))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return instantiated_methods
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def multilevel_instantiation(self, methods_list: Sequence[ClassMembers],
							 | 
						||
| 
								 | 
							
								                                 typenames: Sequence[str],
							 | 
						||
| 
								 | 
							
								                                 parent: 'InstantiatedClass'):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Helper to instantiate methods at both the class and method level.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Args:
							 | 
						||
| 
								 | 
							
								            methods_list: The list of methods in the class to instantiated.
							 | 
						||
| 
								 | 
							
								            typenames: List of class level template parameters, e.g. ['T'].
							 | 
						||
| 
								 | 
							
								            parent: The instantiated class to which `methods_list` belongs.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        instantiated_methods = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for method in methods_list:
							 | 
						||
| 
								 | 
							
								            # We creare a copy since we will modify the typenames list.
							 | 
						||
| 
								 | 
							
								            method_typenames = deepcopy(typenames)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if isinstance(method.template, parser.template.Template):
							 | 
						||
| 
								 | 
							
								                method_typenames.extend(method.template.typenames)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # Get all combinations of template args
							 | 
						||
| 
								 | 
							
								                for instantiations in itertools.product(
							 | 
						||
| 
								 | 
							
								                        *method.template.instantiations):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    instantiated_methods = self.instantiate(
							 | 
						||
| 
								 | 
							
								                        instantiated_methods,
							 | 
						||
| 
								 | 
							
								                        method,
							 | 
						||
| 
								 | 
							
								                        typenames=method_typenames,
							 | 
						||
| 
								 | 
							
								                        class_instantiations=parent.instantiations,
							 | 
						||
| 
								 | 
							
								                        method_instantiations=list(instantiations),
							 | 
						||
| 
								 | 
							
								                        parent=parent)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                # If no constructor level templates, just use the class templates
							 | 
						||
| 
								 | 
							
								                instantiated_methods = self.instantiate(
							 | 
						||
| 
								 | 
							
								                    instantiated_methods,
							 | 
						||
| 
								 | 
							
								                    method,
							 | 
						||
| 
								 | 
							
								                    typenames=method_typenames,
							 | 
						||
| 
								 | 
							
								                    class_instantiations=parent.instantiations,
							 | 
						||
| 
								 | 
							
								                    method_instantiations=[],
							 | 
						||
| 
								 | 
							
								                    parent=parent)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return instantiated_methods
							 |