def visit_Assign(self, node: ast.Assign) -> str: assign: List[str] = [] use_temp: bool = len(node.targets) > 1 and isinstance( node.value, ast.Call) if use_temp: assign.append(f"mut tmp := {self.visit(node.value)}") for target in node.targets: kw: str = "mut " if is_mutable(node.scopes, get_id(target)) else "" if use_temp: value: str = "tmp" else: value: str = self.visit(node.value) if isinstance(target, (ast.Tuple, ast.List)): value = value[1:-1] subtargets: List[str] = [] op: str = ":=" for subtarget in target.elts: subkw: str = ("mut " if is_mutable( node.scopes, get_id(subtarget)) else "") subtargets.append(f"{subkw}{self.visit(subtarget)}") definition: Optional[ ast.AST] = node.scopes.parent_scopes.find( get_id(subtarget)) or node.scopes.find( get_id(subtarget)) if definition is not None and defined_before( definition, subtarget): op = "=" elif op == "=": raise AstNotImplementedError( "Mixing declarations and assignment in the same statement is unsupported.", node, ) assign.append(f"{', '.join(subtargets)} {op} {value}") elif isinstance(target, (ast.Subscript, ast.Attribute)): target: str = self.visit(target) assign.append(f"{target} = {value}") elif isinstance(target, ast.Name) and defined_before( node.scopes.parent_scopes.find(target.id) or node.scopes.find(target.id), node, ): target: str = self.visit(target) assign.append(f"{target} = {value}") else: target: str = self.visit(target) assign.append(f"{kw}{target} := {value}") return "\n".join(assign)
def visit_ClassDef(self, node): extractor = DeclarationExtractor(KotlinTranspiler()) extractor.visit(node) declarations = node.declarations = extractor.get_declarations() node.class_assignments = extractor.class_assignments ret = super().visit_ClassDef(node) if ret is not None: return ret fields = [] index = 0 for declaration, typename in declarations.items(): if typename == None: typename = "ST{0}".format(index) index += 1 mut = is_mutable(node.scopes, get_id(declaration)) mut = "var" if mut else "val" fields.append(f"{mut} {declaration}: {typename}") for b in node.body: if isinstance(b, ast.FunctionDef): b.self_type = node.name if node.is_dataclass: fields = ", ".join(fields) body = [self.visit(b) for b in node.body] body = "\n".join(body) return f"data class {node.name}({fields}) {{\n{body}\n}}\n" else: fields = "\n".join(fields) body = [self.visit(b) for b in node.body] body = "\n".join(body) return f"class {node.name} {{\n{fields}\n\n {body}\n}}\n"
def _visit_AssignOne(self, node, target) -> str: kw = "var" if is_mutable(node.scopes, get_id(target)) else "let" if isinstance(target, ast.Tuple): elts = [self.visit(e) for e in target.elts] elts_str = ", ".join(elts) value = self.visit(node.value) return f"{kw} ({elts_str}) = {value}" if isinstance(node.scopes[-1], ast.If): outer_if = node.scopes[-1] target_id = self.visit(target) if target_id in outer_if.common_vars: value = self.visit(node.value) return f"{kw} {target_id} = {value}" if isinstance(target, ast.Subscript) or isinstance( target, ast.Attribute): target = self.visit(target) value = self.visit(node.value) return f"{target} = {value}" definition = node.scopes.parent_scopes.find(get_id(target)) if definition is None: definition = node.scopes.find(get_id(target)) if isinstance(target, ast.Name) and defined_before(definition, node): target = self.visit(target) value = self.visit(node.value) return f"{target} = {value}" else: target = self.visit(target) value = self.visit(node.value) return f"{kw} {target} = {value}"
def visit_Call(self, node: ast.Call) -> str: fname: str = self.visit(node.func) fndef: ast.AST = node.scopes.find(fname) if isinstance(fndef, ast.ClassDef): return self._visit_object_literal(node, fname, fndef) vargs: List[str] = [] for idx, arg in enumerate(node.args): if hasattr(fndef, "args") and is_mutable(fndef.scopes, fndef.args.args[idx].arg): vargs.append(f"mut {self.visit(arg)}") else: vargs.append(self.visit(arg)) if node.keywords: vargs += [self.visit(kw.value) for kw in node.keywords] ret: Optional[str] = self._dispatch(node, fname, vargs) if ret is not None: return ret if vargs: args = ", ".join(vargs) else: args = "" return f"{fname}({args})"
def _compute_kw(self, node, target) -> str: kw = "let" mut = is_mutable(node.scopes, get_id(target)) if is_global(node) or getattr(node, "class_assignment", False): # Note that static are not really supported, as modifying them requires adding # "unsafe" blocks, which pyrs does not do. kw = "pub static" if mut else "pub const" elif mut: kw = "let mut" return kw
def visit_arg(self, node) -> Tuple[Optional[str], str]: id = get_id(node) if id == "self": return (None, "self") typename = "" if node.annotation: typename = self._typename_from_annotation(node) if is_mutable(node.scopes, id): id = f"mut {id}" return (typename, id)
def _visit_AssignOne(self, node, target): kw = "var" if is_mutable(node.scopes, get_id(target)) else "final" if isinstance(target, ast.Tuple): self._usings.add("package:tuple/tuple.dart") elts = [self.visit(e) for e in target.elts] value = self.visit(node.value) value_types = "int, int" count = len(elts) tmp_var = self._get_temp() buf = [f"{kw} {tmp_var} = Tuple{count}<{value_types}>{value};"] for i, elt in enumerate(elts): buf.extend([f"{elt} = {tmp_var}.item{i+1};"]) return "\n".join(buf) if isinstance(node.scopes[-1], ast.If): outer_if = node.scopes[-1] target_id = self.visit(target) if target_id in outer_if.common_vars: value = self.visit(node.value) return f"{kw} {target_id} = {value};" if isinstance(target, ast.Subscript) or isinstance( target, ast.Attribute): target = self.visit(target) value = self.visit(node.value) return f"{target} = {value};" definition = node.scopes.find(target.id) if isinstance(target, ast.Name) and defined_before(definition, node): target = self.visit(target) value = self.visit(node.value) return f"{target} = {value};" elif isinstance(node.value, ast.List): elements = [self.visit(e) for e in node.value.elts] elements = ", ".join(elements) target = self.visit(target) return f"{kw} {target} = [{elements}];" else: typename = self._typename_from_annotation(target) target = self.visit(target) value = self.visit(node.value) if typename != self._default_type: if kw == self._default_type: return f"{typename} {target} = {value};" else: return f"{kw} {target} = {value};" return f"{kw} {typename} {target} = {value};"
def visit_arg(self, node): id = get_id(node) if id == "self": return (None, "self") typename = "T" if node.annotation: typename = self._typename_from_annotation(node) mut = "mut " if is_mutable(node.scopes, id) else "" # TODO: Should we make this if not primitive instead of checking # for container types? That way we cover user defined structs too. if hasattr(node, "container_type"): # Python passes by reference by default. Rust needs explicit borrowing typename = f"&{mut}{typename}" return (typename, id)
def visit_Return(self, node): self.generic_visit(node) if node.value: fndef = None for scope in node.scopes: if isinstance(scope, ast.FunctionDef): fndef = scope break if fndef: if is_reference(node.value): mut = is_mutable(node.scopes, get_id(node.value)) fndef.returns.rust_needs_reference = not mut fndef.rust_return_needs_reference = ( fndef.returns.rust_needs_reference) return node
def visit_arg(self, node): id = get_id(node) if id == "self": return (None, "self") typename = "T" if node.annotation: # This works only for arguments, for all other cases, use container_types mutable = is_mutable(node.scopes, id) use_open_array = isinstance(node.annotation, ast.Subscript) and not mutable typename = self._typename_from_annotation(node) if use_open_array: typename = typename.replace("seq", "openArray") if mutable: typename = f"var {typename}" return (typename, id)
def visit_AnnAssign(self, node: ast.AnnAssign) -> str: target, type_str, val = super().visit_AnnAssign(node) kw: str = "mut " if is_mutable(node.scopes, target) else "" if isinstance(node.value, ast.List): if node.value.elts: elts: List[str] = [] if type_str[2:] in V_WIDTH_RANK: elts.append( f"{type_str[2:]}({self.visit(node.value.elts[0])})") else: elts.append(self.visit(node.value.elts[0])) elts.extend(map(self.visit, node.value.elts[1:])) return f"{kw}{target} := [{', '.join(elts)}]" return f"{kw}{target} := {type_str}{{}}" else: return f"{kw}{target} := {val}"
def _visit_AssignOne(self, node, target): kw = "var" if is_mutable(node.scopes, get_id(target)) else "val" if isinstance(target, ast.Tuple): elts = [self.visit(e) for e in target.elts] elts_str = ", ".join(elts) value = self.visit(node.value) if isinstance(node.value, ast.Tuple): value = f"Pair{value}" return f"{kw} ({elts_str}) = {value}" if isinstance(node.scopes[-1], ast.If): outer_if = node.scopes[-1] target_id = self.visit(target) if target_id in outer_if.common_vars: value = self.visit(node.value) return f"{kw} {target_id} = {value}" if isinstance(target, ast.Subscript) or isinstance( target, ast.Attribute): target = self.visit(target) value = self.visit(node.value) return f"{target} = {value}" definition = node.scopes.find(target.id) if isinstance(target, ast.Name) and defined_before(definition, node): target = self.visit(target) value = self.visit(node.value) return f"{target} = {value}" elif isinstance(node.value, ast.List): elements = [self.visit(e) for e in node.value.elts] elements = ", ".join(elements) target = self.visit(target) return f"{kw} {target} = arrayOf({elements})" else: target = self.visit(target) value = self.visit(node.value) return f"{kw} {target} = {value}"
def _visit_AssignOne(self, node, target): kw = "let" mut = is_mutable(node.scopes, get_id(target)) if is_global(node): # Note that static are not really supported, as modifying them requires adding # "unsafe" blocks, which pyrs does not do. kw = "pub static" if mut else "pub const" elif mut: kw = "let mut" if isinstance(target, ast.Tuple): elts = ", ".join([self.visit(e) for e in target.elts]) value = self.visit(node.value) return f"{kw} ({elts}) = {value};" if isinstance(node.scopes[-1], ast.If): outer_if = node.scopes[-1] target_id = self.visit(target) if target_id in outer_if.common_vars: value = self.visit(node.value) return "{0} = {1};".format(target_id, value) if isinstance(target, ast.Subscript) or isinstance( target, ast.Attribute): target = self.visit(target) value = self.visit(node.value) if value == None: value = "None" return "{0} = {1};".format(target, value) definition = node.scopes.find(target.id) if isinstance(target, ast.Name) and defined_before(definition, node): needs_cast = self._needs_cast(target, node.value) target_str = self.visit(target) value = self.visit(node.value) if needs_cast: target_type = self._typename_from_annotation(target) value = self._assign_cast(value, target_type, target.annotation, node.value.rust_annotation) return f"{target_str} = {value};" elif isinstance(node.value, ast.List): count = len(node.value.elts) target = self.visit(target) value = self.visit(node.value) typename = self._typename_from_annotation(node.value) if kw.startswith("pub "): # Use arrays instead of Vec as globals must have fixed size if value.startswith("vec!"): value = value.replace("vec!", "&") element_type = self._default_type if hasattr(node.value, "container_type"): container_type, element_type = node.value.container_type return f"{kw} {target}: &[{element_type}; {count}] = {value};" mut = "mut " if is_mutable(node.scopes, target) else "" if hasattr(node.value, "container_type"): return f"{kw} {target}: &{mut}{typename} = &{mut}{value};" return f"{kw} {target}: {typename} = {value};" elif isinstance(node.value, ast.Set): target = self.visit(target) value = self.visit(node.value) typename = self._typename_from_annotation(node.value) if kw.startswith("pub "): self._usings.add("lazy_static::lazy_static") if "str" in typename: typename = typename.replace("str", "'static str") return ( f"lazy_static! {{ pub static ref {target}: {typename} = {value}; }}" ) mut = "mut " if is_mutable(node.scopes, target) else "" if hasattr(node.value, "container_type"): return f"{kw} {target}: &{mut}{typename} = &{mut}{value};" return f"{kw} {target}: {typename} = {value};" elif isinstance(node.value, ast.Dict): target = self.visit(target) value = self.visit(node.value) typename = self._typename_from_annotation(node.value) if kw.startswith("pub "): if hasattr(node.value, "container_type"): container_type, element_type = node.value.container_type key_typename, value_typename = element_type if key_typename == "&str": key_typename = "&'static str" if value_typename == "&str": value_typename = "&'static str" typename = f"{key_typename}, {value_typename}" return f"lazy_static! {{ pub static ref {target}: HashMap<{typename}> = {value}; }}" mut = "mut " if is_mutable(node.scopes, target) else "" if hasattr(node.value, "container_type"): return f"{kw} {target}: &{mut}{typename} = &{mut}{value};" return f"{kw} {target}: {typename} = {value};" else: typename = self._typename_from_annotation(target) needs_cast = self._needs_cast(target, node.value) target_str = self.visit(target) value = self.visit(node.value) if needs_cast: value = self._assign_cast(value, typename, target.annotation, node.value.annotation) return f"{kw} {target_str}: {typename} = {value};"
def visit_AnnAssign(self, node): target, type_str, val = super().visit_AnnAssign(node) mut = "mut " if is_mutable(node.scopes, get_id(node.target)) else "" return f"let {mut}{target}: {type_str} = {val};"
def _visit_AssignOne(self, node, target): kw = self._compute_kw(node, target) if isinstance(node.scopes[-1], ast.If): outer_if = node.scopes[-1] target_id = self.visit(target) if target_id in outer_if.common_vars: value = self.visit(node.value) return "{0} = {1};".format(target_id, value) if isinstance(target, ast.Subscript) or isinstance( target, ast.Attribute): target = self.visit(target) value = self.visit(node.value) if value == None: value = "None" return "{0} = {1};".format(target, value) definition = node.scopes.parent_scopes.find(get_id(target)) if definition is None: definition = node.scopes.find(get_id(target)) if isinstance(target, ast.Name) and defined_before(definition, node): needs_cast = self._needs_cast(target, node.value) target_str = self.visit(target) value = self.visit(node.value) if needs_cast: target_type = self._typename_from_annotation(target) value = self._assign_cast(value, target_type, target.annotation, node.value.rust_annotation) return f"{target_str} = {value};" elif isinstance(node.value, ast.List) and kw.startswith("pub "): count = len(node.value.elts) target = self.visit(target) value = self.visit(node.value) # populate node.value.container_type self._typename_from_annotation(node.value) # Use arrays instead of Vec as globals must have fixed size if value.startswith("vec!"): value = value.replace("vec!", "&") element_type = self._default_type if hasattr(node.value, "container_type"): container_type, element_type = node.value.container_type return f"{kw} {target}: &[{element_type}; {count}] = {value};" elif isinstance(node.value, ast.Set) and kw.startswith("pub "): target = self.visit(target) value = self.visit(node.value) typename = self._typename_from_annotation(node.value) self._usings.add("lazy_static::lazy_static") if "str" in typename: typename = typename.replace("str", "'static str") return f"lazy_static! {{ pub static ref {target}: {typename} = {value}; }}" elif isinstance(node.value, ast.Dict) and kw.startswith("pub "): target = self.visit(target) value = self.visit(node.value) typename = self._typename_from_annotation(node.value) self._usings.add("lazy_static::lazy_static") if hasattr(node.value, "container_type"): container_type, element_type = node.value.container_type key_typename, value_typename = element_type if key_typename == "&str": key_typename = "&'static str" if value_typename == "&str": value_typename = "&'static str" typename = f"{key_typename}, {value_typename}" return f"lazy_static! {{ pub static ref {target}: HashMap<{typename}> = {value}; }}" else: typename = self._typename_from_annotation(target) needs_cast = self._needs_cast(target, node.value) target_str = self.visit(target) value = self.visit(node.value) if needs_cast: value = self._assign_cast(value, typename, target.annotation, node.value.annotation) if hasattr(node.value, "container_type"): mut = "mut " if is_mutable(node.scopes, target_str) else "" typename = f"&{mut}{typename}" value = f"&{mut}{value}" optional_typename = (f": {typename}" if typename != self._default_type else "") return f"{kw} {target_str}{optional_typename} = {value};"
def visit_AnnAssign(self, node) -> str: target, type_str, val = super().visit_AnnAssign(node) kw = "var" if is_mutable(node.scopes, target) else "let" if type_str == self._default_type: return f"{kw} {target} = {val}" return f"{kw} {target}: {type_str} = {val}"