示例#1
0
 def visit_FunctionDef(self, node):
     name = node.name
     returns = type_from_annotation(node.returns, f"{name} return", True)
     args = []
     for arg in node.args.args:
         argname = arg.arg
         typed = type_from_annotation(arg.annotation, f"{name}: {argname}", False)
         args.append((argname, typed))
     
     self.headers[name] = FunctionHeader(returns, args)
示例#2
0
 def visit_AnnAssign(self, node):
     if (isinstance(node.target, ast.Attribute) and
         isinstance(node.target.value, ast.Name) and
         node.target.value.id == "self"):
         # got a member variable definition
         name = node.target.attr
         typed = type_from_annotation(node.annotation, node.target, True)
         self.attributes[name] = typed
示例#3
0
    def visit_FunctionDef(self, node):
        name = node.name
        returns = type_from_annotation(node.returns, f"{name} return", True)
        args = []
        for arg in node.args.args:
            argname = arg.arg
            typed = type_from_annotation(arg.annotation, f"{name}: {argname}", False)
            args.append((argname, typed))
        
        self.methods[name] = FunctionHeader(returns, args)

        # If this is the __init__ method, any assignments to self.<something>
        # are effectively variable declarations.
        if name == "__init__":
            finder = InstanceAttributeFinder(dict(args))
            for line in node.body:
                finder.visit(line)
            
            self.instance_attributes = finder.attributes
示例#4
0
 def visit_arg(self, node):
     if node.arg == "self":
         if not self.is_init:
             ref_type = "&mut " if node.arg in self.mutable_ref_vars else "&"
             print(f"{ref_type}self", end='')
             self.next_separator = ", "
     else:
         typed = type_from_annotation(node.annotation, node.arg, False)
         ref_type = "&mut " if node.arg in self.mutable_ref_vars else ""
         if ref_type:
             typed = dereference(typed)
         mutable = "mut " if node.arg in self.mutable_vars else ""
         print(
             f"{self.next_separator}{mutable}{node.arg}: {ref_type}{typed}",
             end='')
         self.variables.add(node.arg)
         self.next_separator = ", "
示例#5
0
    def visit_AnnAssign(self, node):
        """
        Hinted variable assignment statement, such as x: int = 42

        We do not yet handle non-simple assignments such as
        (x): int = 42
        """
        # treatment depends on whether it is the first time we
        # have seen this variable. (Do not use shadowing.)
        mutable, declared, _, _ = self.sex_variable(node.target)
        if declared:
            print(f"{self.pretty()}", end='')
        else:
            mut = "mut " if mutable else ""
            print(f"{self.pretty()}let {mut}", end='')

        self.visit(node.target)
        typed = type_from_annotation(node.annotation, node.target, True)
        print(f": {typed} = ", end='')
        self.precedence = 0  # don't need params around value
        self.visit_and_optionally_convert(node.value)
        print(";")
示例#6
0
 def visit_AnnAssign(self, node):
     self.visit(node.value)
     typed = type_from_annotation(node.annotation, node.target, True)
     self.handle_assignment(node.target, typed)
示例#7
0
    def visit_Call(self, node):
        """
        Try to find the return type of the function we are calling.
        Also assign the right types to the args.
        """
        # recurse through the arguments
        prev = self.current_type
        arg_types = []
        for a in node.args:
            self.visit(a)
            arg_types.append(self.current_type)
        self.current_type = prev

        # a few functions are well-known (and in any case, they
        # do not behave properly with the below code)
        func_path = get_node_path(node.func)
        if func_path and len(func_path) == 1 and func_path[0] in STANDARD_FUNCTION_RETURNS:
            self.set_type(STANDARD_FUNCTION_RETURNS[func_path[0]](arg_types), node)
            return

        # Assume function names with no module are defined locally
        if len(func_path) == 1:
            if func_path[0] not in self.headers:
                print(f"Warning: cannot find function return for: {func_path[0]}",
                    file = sys.stderr)
            else:
                self.set_type(self.headers[func_path[0]].returns, node)

        # If the first part of the path is a known variable, then this is
        # a method call on that variable. Ignore for now, apart from setting
        # the type of the variable
        if func_path[0] in self.vars:
            self.in_call = True
            self.visit(node.func)
            self.in_call = False
            typed = method_return_type(self.current_type, func_path[1])
            self.set_type(typed, node)

            # for now, we assume that any method invoked on an object can
            # mutate that object
            self.vars[func_path[0]].mutable_ref = True
            return

        # We currently only handle module.func_name
        if len(func_path) != 2:
            return

        # Locate the function. In order to do this, we actually load
        # the module of interest into our own process space. Maybe
        # consider making this optional, as it is a bit of a
        # sledgehammer.
        module_name = func_path[0]
        func_name = func_path[1]
        namespace = load_and_import_module(module_name)
        func = getattr(namespace, func_name)

        # try to find the type of the function
        types = get_type_hints(func)
        if "return" in types:
            typed = type_from_annotation(types["return"],func_name, True)
            self.set_type(typed, node)
示例#8
0
 def visit_arg(self, node):
     typed = type_from_annotation(node.annotation, node.arg, False)
     if node.arg in self.vars:
         raise Exception(f"Repeated argument: {node.arg}")
     self.vars[node.arg] = VariableInfo(True, typed)
     self.type_by_node[node] = typed
示例#9
0
    def visit_FunctionDef(self, node):
        # Analyse the variables in this function to see which need
        # to be predeclared or marked as mutable
        analyser = VariableAnalyser(self.headers, self.class_headers,
                                    self.current_self)
        analyser.visit(node)
        self.type_by_node = analyser.get_type_by_node()

        # function name. Always public, as Python has no
        # private functions. Special handling for __init__,
        # which we always call "new".
        self.is_init = node.name == "__init__"
        name = "new" if self.is_init else node.name
        pub = "" if self.in_trait else "pub "
        print(f"{self.pretty()}{pub}fn {name}(", end='')

        # start with a clean set of variables
        # (do we need to worry about nested functions?)
        self.variables.clear()
        self.mutable_vars = analyser.get_mutable_vars()
        self.mutable_ref_vars = analyser.get_mutable_ref_vars()

        # function arg list
        self.next_separator = ""
        self.generic_visit(node.args)

        # return value
        if self.is_init:
            print(f") -> {self.current_self}", end='')
        elif node.returns is not None:
            typed = type_from_annotation(node.returns, "return", True)
            print(f") -> {typed}", end='')
        else:
            print(")", end='')

        # if all the function does is to pass, and we are in a trait,
        # just leave as a declaration.
        if (self.in_trait_definition and len(node.body) == 1
                and isinstance(node.body[0], ast.Pass)):
            print(";")
            return

        print(" {")
        self.add_pretty(1)

        # start with any variable declarations
        for (var, typed, default) in analyser.get_predeclared_vars():
            self.variables.add(var)
            print(f"{self.pretty()}let mut {var}: {typed} = {default};")

        # body of the function, but special handling for __init__
        for expr in node.body:
            self.visit(expr)

        # in the case of __init__, we now want to return the object
        if self.is_init:
            print(f"{self.pretty()}{self.current_self} {OPEN_BRACE}")
            self.add_pretty(1)
            classdef = self.class_headers[self.current_self]
            for member, _ in classdef.instance_attributes.items():
                print(f"{self.pretty()}{member}: tmp_{member},")
            self.add_pretty(-1)
            print(f"{self.pretty()}{CLOSE_BRACE}")

        self.add_pretty(-1)
        print(f"{self.pretty()}{CLOSE_BRACE}")
        print()

        # clean the set of variables. The names do not leak past here
        self.variables.clear()