def visit_unsupported_body(self, node, name, body): if self._throw_on_unimplemented: raise AstNotImplementedError(f"{name} not implemented", node) else: return self.comment( f"{name} unimplemented on line {node.lineno}:{node.col_offset}" )
def _slice_value(self, node: ast.Subscript): # 3.9 compatibility shim if sys.version_info < (3, 9, 0): if isinstance(node.slice, ast.Index): slice_value = node.slice.value else: slice_value = node.slice else: if isinstance(node.slice, ast.Slice): raise AstNotImplementedError("Advanced Slicing not supported", node) slice_value = node.slice return slice_value
def visit(self, node): if node is None: raise AstEmptyNodeFound if type(node) in symbols: return c_symbol(node) else: try: return super().visit(node) except AstNotImplementedError: raise except Exception as e: raise AstNotImplementedError(e, node) from e
def visit_Subscript(self, node): value = self.visit(node.value) if isinstance(node.slice, ast.Ellipsis): raise AstNotImplementedError("Ellipsis not supported", node) slice_value = self._slice_value(node) index = self.visit(slice_value) if hasattr(node, "is_annotation"): if value in self.CONTAINER_TYPES: value = self.CONTAINER_TYPES[value] return "{0}<{1}>".format(value, index) return f"{value}[{index}]"
def visit_FunctionDef(self, node) -> str: signature = ["fn"] if node.scopes[-1] is ast.ClassDef: raise AstNotImplementedError( "Class methods are not supported yet.", node) signature.append(node.name) generics: Set[str] = set() args: List[Tuple[str, str]] = [] for arg in node.args.args: typename, id = self.visit(arg) if typename is None: # receiver typename = "<struct name>" # TODO: fetch struct name from node.scopes elif len(typename) == 1 and typename.isupper(): generics.add(typename) args.append((typename, id)) str_args: List[str] = [] for typename, id in args: if typename == "": for c in string.ascii_uppercase: if c not in generics: generics.add(c) typename = c if typename == "": raise AstNotImplementedError( "Cannot use more than 26 generics in a function.", node) str_args.append(f"{id} {typename}") signature.append(f"({', '.join(str_args)})") if not is_void_function(node): signature.append( self._typename_from_annotation(node, attr="returns")) body = "\n".join([self.indent(self.visit(n)) for n in node.body]) return f"{' '.join(signature)} {{\n{body}\n}}"
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_Call(self, node) -> str: fname = self.visit(node.func) vargs = [] if node.args: vargs += [self.visit(a) for a in node.args] if node.keywords: vargs += [self.visit(kw.value) for kw in node.keywords] ret = self._dispatch(node, fname, vargs) if ret is not None: return ret if any(i is None for i in vargs): raise AstNotImplementedError(f"Call {fname} ({vargs}) not supported", node) args = ", ".join(vargs) return f"{fname}({args})"
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> ast.Call: new_node = None for comp in node.generators: if isinstance(comp.target, ast.Name): self.redirects[comp.target.id] = "it" elif isinstance(comp.target, ast.Tuple): for idx, elem in enumerate(comp.target.elts): assert isinstance(elem, ast.Name) self.redirects[elem.id] = f"it[{idx}]" else: raise AstNotImplementedError( f"Unknown target type {type(node.target).__qualname__}", node) subnode = comp.iter for cmp in comp.ifs: chain = create_ast_node("placeholder.filter(placeholder)", at_node=node) chain.func.value = subnode chain.args[0] = cmp subnode = chain chain = create_ast_node("placeholder.map(placeholder)", at_node=node) chain.func.value = subnode chain.args[0] = node.elt subnode = chain if new_node is None: new_node = subnode else: new_node.args[0] = subnode self.visit(new_node) self.redirects.clear() return new_node
def visit_AnnAssign(self, node): target, type_str, val = super().visit_AnnAssign(node) if val == None: return f"(declare-const {target} {type_str})" else: raise AstNotImplementedError(f"{val} can't be assigned", node)
def visit_Starred(self, node: ast.Starred) -> str: raise AstNotImplementedError( "Starred expressions are not supported yet.", node)
def visit_Global(self, node: ast.Global) -> str: raise AstNotImplementedError("Globals are not supported yet.", node)
def visit_Try(self, node: ast.Try, finallybody: bool = None) -> str: raise AstNotImplementedError("Exceptions are not supported yet.", node)
def visit_ClassDef(self, node: ast.ClassDef) -> str: raise AstNotImplementedError("Classes are not supported yet.", node)
def visit_StrEnum(self, node: ast.ClassDef) -> str: raise AstNotImplementedError("String enums are not supported in V.", node)
def visit_Lambda(self, node: ast.Lambda) -> str: raise AstNotImplementedError("Lambdas are not supported yet.", node)
def visit_Await(self, node: ast.Await) -> str: raise AstNotImplementedError("asyncio is not supported.", node)
def visit_With(self, node: ast.With) -> str: raise AstNotImplementedError( "`with` statements are not supported yet.", node)
def visit_Raise(self, node: ast.Raise) -> str: raise AstNotImplementedError("Exceptions are not supported yet.", node)
def visit_Delete(self, node: ast.Delete) -> str: raise AstNotImplementedError( "`delete` statements are not supported yet.", node)
def visit_ExceptHandler(self, node) -> str: raise AstNotImplementedError("Exceptions are not supported yet.", node)
def visit_Yield(self, node: ast.Yield) -> str: raise AstNotImplementedError("Generators are not supported yet.", node)
def visit_Call(self, node): params = [self.visit(arg) for arg in node.args] if any(t is None for t in params): raise AstNotImplementedError(f"Call({params}) not implemented", node) params = ",".join(params) return "{0}({1})".format(self.visit(node.func), params)
def visit_DictComp(self, node: ast.DictComp) -> str: raise AstNotImplementedError( "Dict comprehensions are not supported yet.", node)
def visit_IntFlag(self, node: ast.ClassDef) -> str: raise AstNotImplementedError("Enums are not supported yet.", node)
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> str: raise AstNotImplementedError( "Comprehensions should have been handled in the rewriter.", node)
def visit_AsyncFunctionDef(self, node: ast.FunctionDef) -> str: raise AstNotImplementedError("asyncio is not supported.", node)
def visit_Set(self, node: ast.Set) -> str: raise AstNotImplementedError("Sets are not implemented in V yet.", node)