Commit f8de89ac authored by Tom Niget's avatar Tom Niget

Add new generics work

parent fd777ef8
.idea
cmake-build-*
typon.egg-info
\ No newline at end of file
......@@ -36,7 +36,7 @@ class socket:
pass
def getaddrinfo(host: str, port: int, family: int = 0, type: int = 0, proto: int = 0, flags: int = 0) -> \
Task[list[tuple[int, int, int, str, tuple[str, int] | str]]]:
Task[list[tuple[int, int, int, str, str]]]: # todo: incomplete return type
pass
AF_UNIX: int
\ No newline at end of file
# coding: utf-8
from typing import TypeVar, Generic
from dataclasses import dataclass
T = TypeVar("T")
@dataclass
class Thing(Generic[T]):
class Thing[T]:
x: T
def f(x: T):
def f[T](x: T):
pass
......
......@@ -196,7 +196,8 @@ def transpile(source, name: str, path=None):
for var in scope.vars.items():
debug(" " * (indent + 1), var)
# disp_scope(res.scope)
disp_scope(res.scope)
exit()
assert isinstance(res, ast.Module)
res.name = "__main__"
......
......@@ -3,48 +3,41 @@ from pathlib import Path
from logging import debug
from transpiler.phases.typing.scope import VarKind, VarDecl, ScopeKind, Scope
from transpiler.phases.typing.stdlib import PRELUDE, StdlibVisitor
from transpiler.phases.typing.types import TY_TYPE, TY_INT, TY_STR, TY_BOOL, TY_COMPLEX, TY_NONE, FunctionType, \
TypeVariable, CppType, PyList, TypeType, Forked, Task, Future, PyIterator, TupleType, TypeOperator, BaseType, \
ModuleType, TY_BYTES, TY_FLOAT, PyDict, TY_SLICE, TY_OBJECT, BuiltinFeature, UnionType, MemberDef
from transpiler.phases.typing.types import TY_TYPE, TY_INT, TY_STR, TY_BOOL, TY_COMPLEX, TY_NONE, ResolvedConcreteType, \
MemberDef
PRELUDE.vars.update({
# "int": VarDecl(VarKind.LOCAL, TY_TYPE, TY_INT),
# "str": VarDecl(VarKind.LOCAL, TY_TYPE, TY_STR),
# "bool": VarDecl(VarKind.LOCAL, TY_TYPE, TY_BOOL),
# "complex": VarDecl(VarKind.LOCAL, TY_TYPE, TY_COMPLEX),
# "None": VarDecl(VarKind.LOCAL, TY_NONE, None),
# "Callable": VarDecl(VarKind.LOCAL, TY_TYPE, FunctionType),
# "TypeVar": VarDecl(VarKind.LOCAL, TY_TYPE, TypeVariable),
# "CppType": VarDecl(VarKind.LOCAL, TY_TYPE, CppType),
# "list": VarDecl(VarKind.LOCAL, TY_TYPE, PyList),
"int": VarDecl(VarKind.LOCAL, TypeType(TY_INT)),
"float": VarDecl(VarKind.LOCAL, TypeType(TY_FLOAT)),
"str": VarDecl(VarKind.LOCAL, TypeType(TY_STR)),
"bytes": VarDecl(VarKind.LOCAL, TypeType(TY_BYTES)),
"bool": VarDecl(VarKind.LOCAL, TypeType(TY_BOOL)),
"complex": VarDecl(VarKind.LOCAL, TypeType(TY_COMPLEX)),
"None": VarDecl(VarKind.LOCAL, TypeType(TY_NONE)),
"Callable": VarDecl(VarKind.LOCAL, TypeType(FunctionType)),
#"TypeVar": VarDecl(VarKind.LOCAL, TypeType(TypeVariable)),
"CppType": VarDecl(VarKind.LOCAL, TypeType(CppType)),
"list": VarDecl(VarKind.LOCAL, TypeType(PyList)),
"dict": VarDecl(VarKind.LOCAL, TypeType(PyDict)),
"Forked": VarDecl(VarKind.LOCAL, TypeType(Forked)),
"Task": VarDecl(VarKind.LOCAL, TypeType(Task)),
"Future": VarDecl(VarKind.LOCAL, TypeType(Future)),
"Iterator": VarDecl(VarKind.LOCAL, TypeType(PyIterator)),
"tuple": VarDecl(VarKind.LOCAL, TypeType(TupleType)),
"slice": VarDecl(VarKind.LOCAL, TypeType(TY_SLICE)),
"object": VarDecl(VarKind.LOCAL, TypeType(TY_OBJECT)),
"BuiltinFeature": VarDecl(VarKind.LOCAL, TypeType(BuiltinFeature)),
"Any": VarDecl(VarKind.LOCAL, TypeType(TY_OBJECT)),
"Optional": VarDecl(VarKind.LOCAL, TypeType(lambda x: UnionType(x, TY_NONE))),
})
# PRELUDE.vars.update({
# "int": VarDecl(VarKind.LOCAL, TypeType(TY_INT)),
# "float": VarDecl(VarKind.LOCAL, TypeType(TY_FLOAT)),
# "str": VarDecl(VarKind.LOCAL, TypeType(TY_STR)),
# "bytes": VarDecl(VarKind.LOCAL, TypeType(TY_BYTES)),
# "bool": VarDecl(VarKind.LOCAL, TypeType(TY_BOOL)),
# "complex": VarDecl(VarKind.LOCAL, TypeType(TY_COMPLEX)),
# "None": VarDecl(VarKind.LOCAL, TypeType(TY_NONE)),
# "Callable": VarDecl(VarKind.LOCAL, TypeType(FunctionType)),
# #"TypeVar": VarDecl(VarKind.LOCAL, TypeType(TypeVariable)),
# "CppType": VarDecl(VarKind.LOCAL, TypeType(CppType)),
# "list": VarDecl(VarKind.LOCAL, TypeType(PyList)),
# "dict": VarDecl(VarKind.LOCAL, TypeType(PyDict)),
# "Forked": VarDecl(VarKind.LOCAL, TypeType(Forked)),
# "Task": VarDecl(VarKind.LOCAL, TypeType(Task)),
# "Future": VarDecl(VarKind.LOCAL, TypeType(Future)),
# "Iterator": VarDecl(VarKind.LOCAL, TypeType(PyIterator)),
# "tuple": VarDecl(VarKind.LOCAL, TypeType(TupleType)),
# "slice": VarDecl(VarKind.LOCAL, TypeType(TY_SLICE)),
# "object": VarDecl(VarKind.LOCAL, TypeType(TY_OBJECT)),
# "BuiltinFeature": VarDecl(VarKind.LOCAL, TypeType(BuiltinFeature)),
# "Any": VarDecl(VarKind.LOCAL, TypeType(TY_OBJECT)),
# "Optional": VarDecl(VarKind.LOCAL, TypeType(lambda x: UnionType(x, TY_NONE))),
# })
typon_std = Path(__file__).parent.parent.parent.parent / "stdlib"
def make_module(name: str, scope: Scope) -> BaseType:
ty = ModuleType([], f"{name}")
def make_module(name: str, scope: Scope) -> ResolvedConcreteType:
class CreatedType(ResolvedConcreteType):
def __str__(self):
return name
ty = CreatedType()
for n, v in scope.vars.items():
ty.fields[n] = MemberDef(v.type, v.val, False)
return ty
......
......@@ -4,33 +4,20 @@ from dataclasses import dataclass, field
from typing import Optional, List
from transpiler.phases.typing.scope import Scope
from transpiler.phases.typing.types import BaseType, TY_NONE, TypeType, TY_SELF, TypeVariable, UnionType
from transpiler.phases.typing.types import BaseType, TY_NONE, TypeVariable, TY_TYPE, ResolvedConcreteType, TypeListType
from transpiler.phases.utils import NodeVisitorSeq
@dataclass
class TypeAnnotationVisitor(NodeVisitorSeq):
scope: Scope
cur_class: Optional[TypeType] = None
typevars: List[TypeVariable] = field(default_factory=list)
def visit_str(self, node: str) -> BaseType:
if node in ("Self", "self") and self.cur_class:
if isinstance(self.cur_class.type_object, abc.ABCMeta) or self.cur_class.type_object.is_protocol_gen or self.cur_class.type_object.is_protocol:
return TY_SELF
else:
return self.cur_class.type_object
if existing := self.scope.get(node):
ty = existing.type
if isinstance(ty, TypeType) and isinstance(ty.type_object, TypeVariable):
if existing is not self.scope.vars.get(node, None):
# Type variable from outer scope, so we copy it
ty = TypeVariable(ty.type_object.name)
self.scope.declare_local(node, TypeType(ty)) # todo: unneeded?
self.typevars.append(ty)
if isinstance(ty, TypeType):
return ty.type_object
return ty
assert isinstance(ty, ResolvedConcreteType)
assert ty.inherits(TY_TYPE)
return ty.inner_type
from transpiler.phases.typing.exceptions import UnknownNameError
raise UnknownNameError(node)
......@@ -46,22 +33,24 @@ class TypeAnnotationVisitor(NodeVisitorSeq):
raise NotImplementedError
def visit_Subscript(self, node: ast.Subscript) -> BaseType:
ty_op = self.visit(node.value)
args = list(node.slice.elts) if type(node.slice) == ast.Tuple else [node.slice]
args = [self.visit(arg) for arg in args]
return ty_op(*args)
# ty_op = self.visit(node.value)
# args = list(node.slice.elts) if type(node.slice) == ast.Tuple else [node.slice]
# args = [self.visit(arg) for arg in args]
# return ty_op(*args)
raise NotImplementedError()
# return TypeOperator([self.visit(node.value)], self.visit(node.slice.value))
def visit_List(self, node: ast.List) -> List[BaseType]:
return [self.visit(elt) for elt in node.elts]
def visit_List(self, node: ast.List) -> BaseType:
return TypeListType([self.visit(elt) for elt in node.elts])
def visit_Attribute(self, node: ast.Attribute) -> BaseType:
left = self.visit(node.value)
res = left.fields[node.attr].type
assert isinstance(res, TypeType)
return res.type_object
raise NotImplementedError()
# left = self.visit(node.value)
# res = left.fields[node.attr].type
# assert isinstance(res, TypeType)
# return res.type_object
def visit_BinOp(self, node: ast.BinOp) -> BaseType:
if isinstance(node.op, ast.BitOr):
return UnionType(self.visit(node.left), self.visit(node.right))
# if isinstance(node.op, ast.BitOr):
# return UnionType(self.visit(node.left), self.visit(node.right))
raise NotImplementedError(node.op)
......@@ -5,8 +5,7 @@ from typing import Dict, Optional
from transpiler.utils import highlight
from transpiler.phases.typing.annotations import TypeAnnotationVisitor
from transpiler.phases.typing.scope import Scope, ScopeKind, VarDecl, VarKind
from transpiler.phases.typing.types import BaseType, TypeVariable, TY_NONE, TypeType, BuiltinFeature, FunctionType, \
Promise, PromiseKind
from transpiler.phases.typing.types import BaseType, TypeVariable, TY_NONE
from transpiler.phases.utils import NodeVisitorSeq, AnnotationName
PRELUDE = Scope.make_global()
......@@ -15,14 +14,13 @@ PRELUDE = Scope.make_global()
class ScoperVisitor(NodeVisitorSeq):
scope: Scope = field(default_factory=lambda: PRELUDE.child(ScopeKind.GLOBAL))
root_decls: Dict[str, VarDecl] = field(default_factory=dict)
cur_class: Optional[TypeType] = None
def expr(self) -> "ScoperExprVisitor":
from transpiler.phases.typing.expr import ScoperExprVisitor
return ScoperExprVisitor(self.scope, self.root_decls)
def anno(self) -> "TypeAnnotationVisitor":
return TypeAnnotationVisitor(self.scope, self.cur_class)
return TypeAnnotationVisitor(self.scope)
def visit_annotation(self, expr: Optional[ast.expr]) -> BaseType:
res = self.anno().visit(expr) if expr else TypeVariable()
......
......@@ -303,3 +303,20 @@ class MissingReturnError(CompileError):
{highlight(' return 1')}
{highlight(' # if x <= 0, the function returns nothing')}
"""
@dataclass
class InconsistentMroError(CompileError):
bases: list[BaseType]
def __str__(self) -> str:
return f"Cannot create a cnossitent method resolution order (MRO) for bases {'\n'.join(map(highlight, self.bases))}"
def detail(self, last_node: ast.AST = None) -> str:
return f"""
This indicates that a class has an inconsistent method resolution order (MRO).
For example:
{highlight('class A: pass')}
{highlight('class B(A): pass')}
{highlight('class C(B, A): pass')}
"""
\ No newline at end of file
......@@ -5,9 +5,7 @@ from typing import List
from transpiler.phases.typing import ScopeKind, VarDecl, VarKind
from transpiler.phases.typing.common import ScoperVisitor, get_iter, get_next, is_builtin
from transpiler.phases.typing.types import BaseType, TupleType, TY_STR, TY_BOOL, TY_INT, \
TY_COMPLEX, TY_NONE, FunctionType, PyList, TypeVariable, PySet, TypeType, PyDict, Promise, PromiseKind, UserType, \
TY_SLICE, TY_FLOAT, RuntimeValue, BuiltinFeature
from transpiler.phases.typing.types import BaseType
from transpiler.utils import linenodata
DUNDER = {
......
......@@ -2,16 +2,67 @@ import ast
import dataclasses
from abc import ABCMeta
from dataclasses import dataclass, field
from typing import Optional, List, Dict
from typing import Optional, List, Dict, Callable
from logging import debug
from transpiler.phases.typing.annotations import TypeAnnotationVisitor
from transpiler.phases.typing.common import PRELUDE, is_builtin
from transpiler.phases.typing.expr import ScoperExprVisitor
from transpiler.phases.typing.scope import Scope, VarDecl, VarKind, ScopeKind
from transpiler.phases.typing.types import BaseType, TypeOperator, FunctionType, TY_VARARG, TypeType, TypeVariable, \
MemberDef, BuiltinFeature
from transpiler.phases.typing.types import BaseType, BuiltinGenericType, BuiltinType, create_builtin_generic_type, \
create_builtin_type, ConcreteType, GenericInstanceType, TypeListType, TypeTupleType, GenericParameter, \
GenericParameterKind, TypeVariable
from transpiler.phases.utils import NodeVisitorSeq
def visit_generic_item(
visit_nongeneric: Callable[[Scope, ConcreteType], None],
node,
output_type: BuiltinGenericType,
scope: Scope,
instance_type = None):
if node.type_params:
output_type.parameters = []
for param in node.type_params:
match param:
case ast.TypeVar(_, _):
kind = GenericParameterKind.NORMAL
case ast.ParamSpec(_):
kind = GenericParameterKind.PARAMETERS
case ast.TypeVarTuple(_):
kind = GenericParameterKind.TUPLE
output_type.parameters.append(GenericParameter(param.name, kind))
if instance_type is None:
class instance_type(GenericInstanceType):
pass
instance_type.__name__ = f"GenericInstance${node.name}"
def instantiate(args: list[ConcreteType]) -> GenericInstanceType:
new_scope = scope.child(ScopeKind.GLOBAL)
args_iter = iter(args)
constraints = []
anno = TypeAnnotationVisitor(new_scope)
for param in node.type_params:
op_val = next(args_iter, None)
if op_val is None:
op_val = TypeVariable()
match param:
case ast.TypeVar(name, bound):
new_scope.declare_local(name, op_val)
if bound is not None:
constraints.append((op_val, anno.visit(bound)))
case ast.ParamSpec(name):
assert isinstance(op_val, TypeListType)
new_scope.declare_local(name, op_val)
case ast.TypeVarTuple(name):
new_scope.declare_local(name, TypeTupleType(list(args_iter)))
for a, b in constraints:
raise NotImplementedError()
new_output_type = instance_type()
visit_nongeneric(new_scope, new_output_type)
return new_output_type
output_type.instantiate_ = instantiate
else:
visit_nongeneric(scope, output_type)
@dataclass
......@@ -34,9 +85,6 @@ class StdlibVisitor(NodeVisitorSeq):
ty = self.anno().visit(node.annotation)
if self.cur_class:
assert isinstance(self.cur_class, TypeType)
if isinstance(self.cur_class.type_object, ABCMeta):
raise NotImplementedError
else:
self.cur_class.type_object.fields[node.target.id] = MemberDef(ty.gen_sub(self.cur_class.type_object, self.typevars))
self.scope.vars[node.target.id] = VarDecl(VarKind.LOCAL, ty)
......@@ -48,71 +96,54 @@ class StdlibVisitor(NodeVisitorSeq):
def visit_ClassDef(self, node: ast.ClassDef):
if existing := self.scope.get(node.name):
ty = existing.type
else:
class BuiltinClassType(TypeOperator):
def __init__(self, *args):
super().__init__(args, node.name, is_reference=True)
ty = TypeType(BuiltinClassType)
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ty)
typevars = []
for b in node.bases:
if isinstance(b, ast.Subscript):
if isinstance(b.slice, ast.Name):
sliceval = [b.slice.id]
elif isinstance(b.slice, ast.Tuple):
sliceval = [n.id for n in b.slice.elts]
if isinstance(b.value, ast.Name) and b.value.id == "Generic":
typevars = sliceval
elif isinstance(b.value, ast.Name) and b.value.id == "Protocol":
typevars = sliceval
ty.type_object.is_protocol_gen = True
NewType = existing.type
else:
idxs = [typevars.index(v) for v in sliceval]
parent = self.visit(b.value)
assert isinstance(parent, TypeType)
assert isinstance(ty.type_object, ABCMeta)
ty.type_object.gen_parents.append(lambda selfvars: parent.type_object(*[selfvars[i] for i in idxs]))
else:
if isinstance(b, ast.Name) and b.id == "Protocol":
ty.type_object.is_protocol_gen = True
else:
parent = self.visit(b)
assert isinstance(parent, TypeType)
if isinstance(ty.type_object, ABCMeta):
ty.type_object.gen_parents.append(parent.type_object)
else:
ty.type_object.parents.append(parent.type_object)
if not typevars and not existing:
ty.type_object = ty.type_object()
cl_scope = self.scope.child(ScopeKind.CLASS)
visitor = StdlibVisitor(cl_scope, ty)
for var in typevars:
visitor.typevars[var] = TypeType(TypeVariable(var))
base_class = create_builtin_generic_type if node.type_params else create_builtin_type
NewType = base_class(node.name)
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, NewType.type_type())
def visit_nongeneric(scope, output: ConcreteType):
cl_scope = scope.child(ScopeKind.CLASS)
visitor = StdlibVisitor(cl_scope, output)
for stmt in node.body:
visitor.visit(stmt)
visit_generic_item(visit_nongeneric, node, NewType, self.scope)
def visit_Pass(self, node: ast.Pass):
pass
def visit_FunctionDef(self, node: ast.FunctionDef):
tc = node.type_comment # todo : lire les commetnaries de type pour les fonctions génériques sinon trouver autre chose
arg_visitor = TypeAnnotationVisitor(self.scope.child(ScopeKind.FUNCTION), self.cur_class)
arg_types = [arg_visitor.visit(arg.annotation or arg.arg) for arg in node.args.args]
ret_type = arg_visitor.visit(node.returns)
ty = FunctionType(arg_types, ret_type)
ty.typevars = arg_visitor.typevars
if node.args.vararg:
ty.variadic = True
ty.optional_at = 1 + len(node.args.args) - len(node.args.defaults)
if self.cur_class:
ty.is_method = True
assert isinstance(self.cur_class, TypeType)
if isinstance(self.cur_class.type_object, ABCMeta):
self.cur_class.type_object.gen_methods[node.name] = lambda t: ty.gen_sub(t, self.typevars)
else:
self.cur_class.type_object.fields[node.name] = MemberDef(ty.gen_sub(self.cur_class.type_object, self.typevars), ())
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ty)
def visit_nongeneric(scope, output: ConcreteType):
cl_scope = scope.child(ScopeKind.CLASS)
visitor = StdlibVisitor(cl_scope, output)
for stmt in node.body:
visitor.visit(stmt)
'''
class arguments(__ast.AST):
""" arguments(arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, arg? kwarg, expr* defaults) """'''
visit_generic_item(visit_nongeneric, node, NewType, self.scope)
# tc = node.type_comment # todo : lire les commetnaries de type pour les fonctions génériques sinon trouver autre chose
# arg_visitor = TypeAnnotationVisitor(self.scope.child(ScopeKind.FUNCTION), self.cur_class)
# arg_types = [arg_visitor.visit(arg.annotation or arg.arg) for arg in node.args.args]
# ret_type = arg_visitor.visit(node.returns)
# ty = FunctionType(arg_types, ret_type)
# ty.typevars = arg_visitor.typevars
# if node.args.vararg:
# ty.variadic = True
# ty.optional_at = 1 + len(node.args.args) - len(node.args.defaults)
# if self.cur_class:
# ty.is_method = True
# assert isinstance(self.cur_class, TypeType)
# if isinstance(self.cur_class.type_object, ABCMeta):
# self.cur_class.type_object.gen_methods[node.name] = lambda t: ty.gen_sub(t, self.typevars)
# else:
# self.cur_class.type_object.fields[node.name] = MemberDef(ty.gen_sub(self.cur_class.type_object, self.typevars), ())
# self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ty)
def visit_Assert(self, node: ast.Assert):
if isinstance(node.test, ast.UnaryOp) and isinstance(node.test.op, ast.Not):
......
import ast
from pathlib import Path
from logging import debug
from transpiler.phases.typing.scope import VarKind, VarDecl, ScopeKind, Scope
from transpiler.phases.typing.stdlib import PRELUDE, StdlibVisitor
from transpiler.phases.typing.types import TY_TYPE, TY_INT, TY_STR, TY_BOOL, TY_COMPLEX, TY_NONE, FunctionType, \
TypeVariable, CppType, PyList, TypeType, Forked, Task, Future, PyIterator, TupleType, TypeOperator, BaseType, \
ModuleType, TY_BYTES, TY_FLOAT, PyDict, TY_SLICE, TY_OBJECT, BuiltinFeature, UnionType, MemberDef
PRELUDE.vars.update({
# "int": VarDecl(VarKind.LOCAL, TY_TYPE, TY_INT),
# "str": VarDecl(VarKind.LOCAL, TY_TYPE, TY_STR),
# "bool": VarDecl(VarKind.LOCAL, TY_TYPE, TY_BOOL),
# "complex": VarDecl(VarKind.LOCAL, TY_TYPE, TY_COMPLEX),
# "None": VarDecl(VarKind.LOCAL, TY_NONE, None),
# "Callable": VarDecl(VarKind.LOCAL, TY_TYPE, FunctionType),
# "TypeVar": VarDecl(VarKind.LOCAL, TY_TYPE, TypeVariable),
# "CppType": VarDecl(VarKind.LOCAL, TY_TYPE, CppType),
# "list": VarDecl(VarKind.LOCAL, TY_TYPE, PyList),
"int": VarDecl(VarKind.LOCAL, TypeType(TY_INT)),
"float": VarDecl(VarKind.LOCAL, TypeType(TY_FLOAT)),
"str": VarDecl(VarKind.LOCAL, TypeType(TY_STR)),
"bytes": VarDecl(VarKind.LOCAL, TypeType(TY_BYTES)),
"bool": VarDecl(VarKind.LOCAL, TypeType(TY_BOOL)),
"complex": VarDecl(VarKind.LOCAL, TypeType(TY_COMPLEX)),
"None": VarDecl(VarKind.LOCAL, TypeType(TY_NONE)),
"Callable": VarDecl(VarKind.LOCAL, TypeType(FunctionType)),
#"TypeVar": VarDecl(VarKind.LOCAL, TypeType(TypeVariable)),
"CppType": VarDecl(VarKind.LOCAL, TypeType(CppType)),
"list": VarDecl(VarKind.LOCAL, TypeType(PyList)),
"dict": VarDecl(VarKind.LOCAL, TypeType(PyDict)),
"Forked": VarDecl(VarKind.LOCAL, TypeType(Forked)),
"Task": VarDecl(VarKind.LOCAL, TypeType(Task)),
"Future": VarDecl(VarKind.LOCAL, TypeType(Future)),
"Iterator": VarDecl(VarKind.LOCAL, TypeType(PyIterator)),
"tuple": VarDecl(VarKind.LOCAL, TypeType(TupleType)),
"slice": VarDecl(VarKind.LOCAL, TypeType(TY_SLICE)),
"object": VarDecl(VarKind.LOCAL, TypeType(TY_OBJECT)),
"BuiltinFeature": VarDecl(VarKind.LOCAL, TypeType(BuiltinFeature)),
"Any": VarDecl(VarKind.LOCAL, TypeType(TY_OBJECT)),
"Optional": VarDecl(VarKind.LOCAL, TypeType(lambda x: UnionType(x, TY_NONE))),
})
typon_std = Path(__file__).parent.parent.parent.parent / "stdlib"
def make_module(name: str, scope: Scope) -> BaseType:
ty = ModuleType([], f"{name}")
for n, v in scope.vars.items():
ty.fields[n] = MemberDef(v.type, v.val, False)
return ty
def discover_module(path: Path, scope):
for child in sorted(path.iterdir()):
if child.is_dir():
mod_scope = PRELUDE.child(ScopeKind.GLOBAL)
discover_module(child, mod_scope)
scope.vars[child.name] = make_mod_decl(child.name, mod_scope)
elif child.name == "__init__.py":
StdlibVisitor(scope).visit(ast.parse(child.read_text()))
debug(f"Visited {child}")
elif child.suffix == ".py":
mod_scope = PRELUDE.child(ScopeKind.GLOBAL)
StdlibVisitor(mod_scope).visit(ast.parse(child.read_text()))
if child.stem[-1] == "_":
child = child.with_name(child.stem[:-1])
scope.vars[child.stem] = make_mod_decl(child.name, mod_scope)
debug(f"Visited {child}")
def make_mod_decl(child, mod_scope):
return VarDecl(VarKind.MODULE, make_module(child, mod_scope), {k: v.type for k, v in mod_scope.vars.items()})
discover_module(typon_std, PRELUDE)
debug("Stdlib visited!")
#exit()
\ No newline at end of file
import abc
import ast
from dataclasses import dataclass, field
from typing import Optional, List
from transpiler.phases.typing.scope import Scope
from transpiler.phases.typing.types import BaseType, TY_NONE, TypeVariable, TY_TYPE, ResolvedConcreteType, TypeListType
from transpiler.phases.utils import NodeVisitorSeq
@dataclass
class TypeAnnotationVisitor(NodeVisitorSeq):
scope: Scope
def visit_str(self, node: str) -> BaseType:
if existing := self.scope.get(node):
ty = existing.type
assert isinstance(ty, ResolvedConcreteType)
assert ty.inherits(TY_TYPE)
return ty.inner_type
from transpiler.phases.typing.exceptions import UnknownNameError
raise UnknownNameError(node)
def visit_Name(self, node: ast.Name) -> BaseType:
return self.visit_str(node.id)
def visit_Constant(self, node: ast.Constant) -> BaseType:
if node.value is None:
return TY_NONE
if type(node.value) == str:
return node.value
raise NotImplementedError
def visit_Subscript(self, node: ast.Subscript) -> BaseType:
# ty_op = self.visit(node.value)
# args = list(node.slice.elts) if type(node.slice) == ast.Tuple else [node.slice]
# args = [self.visit(arg) for arg in args]
# return ty_op(*args)
raise NotImplementedError()
# return TypeOperator([self.visit(node.value)], self.visit(node.slice.value))
def visit_List(self, node: ast.List) -> BaseType:
return TypeListType([self.visit(elt) for elt in node.elts])
def visit_Attribute(self, node: ast.Attribute) -> BaseType:
raise NotImplementedError()
# left = self.visit(node.value)
# res = left.fields[node.attr].type
# assert isinstance(res, TypeType)
# return res.type_object
def visit_BinOp(self, node: ast.BinOp) -> BaseType:
# if isinstance(node.op, ast.BitOr):
# return UnionType(self.visit(node.left), self.visit(node.right))
raise NotImplementedError(node.op)
This diff is collapsed.
# coding: utf-8
import ast
from dataclasses import dataclass, field
from transpiler.phases.typing import FunctionType, ScopeKind, VarDecl, VarKind, TY_NONE
from transpiler.phases.typing.common import ScoperVisitor
from transpiler.phases.typing.types import PromiseKind, Promise, BaseType, MemberDef
@dataclass
class ScoperClassVisitor(ScoperVisitor):
fdecls: list[(ast.FunctionDef, BaseType)] = field(default_factory=list)
def visit_AnnAssign(self, node: ast.AnnAssign):
assert node.value is None, "Class field should not have a value"
assert node.simple == 1, "Class field should be simple (identifier, not parenthesized)"
assert isinstance(node.target, ast.Name)
self.scope.obj_type.fields[node.target.id] = MemberDef(self.visit_annotation(node.annotation))
def visit_Assign(self, node: ast.Assign):
assert len(node.targets) == 1, "Can't use destructuring in class static member"
assert isinstance(node.targets[0], ast.Name)
node.is_declare = True
valtype = self.expr().visit(node.value)
node.targets[0].type = valtype
self.scope.obj_type.fields[node.targets[0].id] = MemberDef(valtype, node.value)
def visit_FunctionDef(self, node: ast.FunctionDef):
ftype = self.parse_function(node)
ftype.parameters[0].unify(self.scope.obj_type)
inner = ftype.return_type
if node.name != "__init__":
ftype.return_type = Promise(ftype.return_type, PromiseKind.TASK)
ftype.is_method = True
self.scope.obj_type.fields[node.name] = MemberDef(ftype, node)
return (node, inner)
import ast
from dataclasses import dataclass, field
from typing import Dict, Optional
from transpiler.utils import highlight
from transpiler.phases.typing.annotations import TypeAnnotationVisitor
from transpiler.phases.typing.scope import Scope, ScopeKind, VarDecl, VarKind
from transpiler.phases.typing.types import BaseType, TypeVariable, TY_NONE, TypeType, BuiltinFeature, FunctionType, \
Promise, PromiseKind
from transpiler.phases.utils import NodeVisitorSeq, AnnotationName
PRELUDE = Scope.make_global()
@dataclass
class ScoperVisitor(NodeVisitorSeq):
scope: Scope = field(default_factory=lambda: PRELUDE.child(ScopeKind.GLOBAL))
root_decls: Dict[str, VarDecl] = field(default_factory=dict)
cur_class: Optional[TypeType] = None
def expr(self) -> "ScoperExprVisitor":
from transpiler.phases.typing.expr import ScoperExprVisitor
return ScoperExprVisitor(self.scope, self.root_decls)
def anno(self) -> "TypeAnnotationVisitor":
return TypeAnnotationVisitor(self.scope, self.cur_class)
def visit_annotation(self, expr: Optional[ast.expr]) -> BaseType:
res = self.anno().visit(expr) if expr else TypeVariable()
assert not isinstance(res, TypeType)
return res
def annotate_arg(self, arg: ast.arg) -> BaseType:
if arg.annotation is None or isinstance(arg.annotation, AnnotationName):
res = TypeVariable()
arg.annotation = AnnotationName(res)
return res
else:
return self.visit_annotation(arg.annotation)
def parse_function(self, node: ast.FunctionDef):
argtypes = [self.annotate_arg(arg) for arg in node.args.args]
rtype = self.visit_annotation(node.returns)
ftype = FunctionType(argtypes, rtype)
scope = self.scope.child(ScopeKind.FUNCTION)
scope.obj_type = ftype
scope.function = scope
node.inner_scope = scope
node.type = ftype
ftype.optional_at = len(node.args.args) - len(node.args.defaults)
for ty, default in zip(argtypes[ftype.optional_at:], node.args.defaults):
self.expr().visit(default).unify(ty)
for arg, ty in zip(node.args.args, argtypes):
scope.vars[arg.arg] = VarDecl(VarKind.LOCAL, ty)
self.fdecls.append((node, rtype))
return ftype
def visit_block(self, block: list[ast.AST]):
if not block:
return
__TB__ = f"running type analysis on block starting with {highlight(block[0])}"
self.fdecls = []
for b in block:
self.visit(b)
if self.fdecls:
old_list = self.fdecls
exc = None
while True:
new_list = []
for node, rtype in old_list:
from transpiler.exceptions import CompileError
try:
self.visit_function_definition(node, rtype)
except CompileError as e:
new_list.append((node, rtype))
if not exc or getattr(node, "is_main", False):
exc = e
if len(new_list) == len(old_list):
raise exc
if not new_list:
break
old_list = new_list
exc = None
def visit_function_definition(self, node, rtype):
__TB__ = f"running type analysis on the body of {highlight(node)}"
__TB_NODE__ = node
from transpiler.phases.typing.block import ScoperBlockVisitor
for b in node.body:
decls = {}
visitor = ScoperBlockVisitor(node.inner_scope, decls)
visitor.fdecls = []
visitor.visit(b)
if len(visitor.fdecls) > 1:
raise NotImplementedError("?")
elif len(visitor.fdecls) == 1:
fnode, frtype = visitor.fdecls[0]
self.visit_function_definition(fnode, frtype)
#del node.inner_scope.vars[fnode.name]
visitor.visit_assign_target(ast.Name(fnode.name), fnode.type)
b.decls = decls
if not node.inner_scope.diverges and not (isinstance(node.type.return_type, Promise) and node.type.return_type.kind == PromiseKind.GENERATOR):
from transpiler.phases.typing.exceptions import TypeMismatchError
try:
rtype.unify(TY_NONE)
except TypeMismatchError as e:
from transpiler.phases.typing.exceptions import MissingReturnError
raise MissingReturnError(node) from e
def get_iter(seq_type):
try:
iter_type = seq_type.fields["__iter__"].type.return_type
except:
from transpiler.phases.typing.exceptions import NotIterableError
raise NotIterableError(seq_type)
return iter_type
def get_next(iter_type):
try:
next_type = iter_type.fields["__next__"].type.return_type
except:
from transpiler.phases.typing.exceptions import NotIteratorError
raise NotIteratorError(iter_type)
return next_type
def is_builtin(x, feature):
return isinstance(x, BuiltinFeature) and x.val == feature
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
import ast
from dataclasses import field, dataclass
from enum import Enum
from typing import Optional, Dict, List, Any
from transpiler.phases.typing.types import BaseType, RuntimeValue
class VarKind(Enum):
"""Kind of variable."""
LOCAL = 1
"""`xxx = ...`"""
GLOBAL = 2
"""`global xxx"""
NONLOCAL = 3
"""`nonlocal xxx`"""
SELF = 4
OUTER_DECL = 5
MODULE = 6
class VarType:
pass
@dataclass
class VarDecl:
kind: VarKind
type: BaseType
val: Any = RuntimeValue()
class ScopeKind(Enum):
GLOBAL = 1
"""Global (module) scope"""
FUNCTION = 2
"""Function scope"""
FUNCTION_INNER = 3
"""Block (if, for, ...) scope inside a function"""
CLASS = 4
"""Class scope"""
@dataclass
class Scope:
parent: Optional["Scope"] = None
kind: ScopeKind = ScopeKind.GLOBAL
function: Optional["Scope"] = None
global_scope: Optional["Scope"] = None
vars: Dict[str, VarDecl] = field(default_factory=dict)
children: List["Scope"] = field(default_factory=list)
obj_type: Optional[BaseType] = None
diverges: bool = False
class_: Optional["Scope"] = None
is_loop: Optional[ast.For | ast.While] = None
@staticmethod
def make_global():
res = Scope()
res.global_scope = res
return res
def is_in_loop(self) -> Optional[ast.For | ast.While]:
if self.is_loop:
return self.is_loop
if self.parent is not None and self.kind != ScopeKind.FUNCTION:
return self.parent.is_in_loop()
return None
def child(self, kind: ScopeKind):
res = Scope(self, kind, self.function, self.global_scope)
if kind == ScopeKind.GLOBAL:
res.global_scope = res
self.children.append(res)
return res
def declare_local(self, name: str, type: BaseType):
"""Declares a local variable"""
self.vars[name] = VarDecl(VarKind.LOCAL, type)
def get(self, name: str, kind: VarKind | set[VarKind] = VarKind.LOCAL, restrict_function: bool = False) -> Optional[VarDecl]:
"""
Gets the variable declaration of a variable in the current scope or any parent scope.
"""
if type(kind) is VarKind:
kind = {kind}
if (res := self.vars.get(name)) and res.kind in kind:
if res.kind == VarKind.GLOBAL:
return self.global_scope.get(name, kind)
elif res.kind == VarKind.NONLOCAL:
return self.function.parent.get(name, VarKind.LOCAL, True)
return res
if self.parent is not None and not (self.kind == ScopeKind.FUNCTION and restrict_function):
return self.parent.get(name, kind, restrict_function)
return None
import ast
import dataclasses
from abc import ABCMeta
from dataclasses import dataclass, field
from typing import Optional, List, Dict
from logging import debug
from transpiler.phases.typing.annotations import TypeAnnotationVisitor
from transpiler.phases.typing.common import PRELUDE, is_builtin
from transpiler.phases.typing.expr import ScoperExprVisitor
from transpiler.phases.typing.scope import Scope, VarDecl, VarKind, ScopeKind
from transpiler.phases.typing.types import BaseType, TypeOperator, FunctionType, TY_VARARG, TypeType, TypeVariable, \
MemberDef, BuiltinFeature
from transpiler.phases.utils import NodeVisitorSeq
@dataclass
class StdlibVisitor(NodeVisitorSeq):
scope: Scope = field(default_factory=lambda: PRELUDE)
cur_class: Optional[BaseType] = None
typevars: Dict[str, BaseType] = field(default_factory=dict)
def expr(self) -> ScoperExprVisitor:
return ScoperExprVisitor(self.scope)
def visit_Module(self, node: ast.Module):
for stmt in node.body:
self.visit(stmt)
def visit_Assign(self, node: ast.Assign):
self.scope.vars[node.targets[0].id] = VarDecl(VarKind.LOCAL, self.visit(node.value))
def visit_AnnAssign(self, node: ast.AnnAssign):
ty = self.anno().visit(node.annotation)
if self.cur_class:
assert isinstance(self.cur_class, TypeType)
if isinstance(self.cur_class.type_object, ABCMeta):
raise NotImplementedError
else:
self.cur_class.type_object.fields[node.target.id] = MemberDef(ty.gen_sub(self.cur_class.type_object, self.typevars))
self.scope.vars[node.target.id] = VarDecl(VarKind.LOCAL, ty)
def visit_ImportFrom(self, node: ast.ImportFrom):
pass
def visit_Import(self, node: ast.Import):
pass
def visit_ClassDef(self, node: ast.ClassDef):
if existing := self.scope.get(node.name):
ty = existing.type
else:
class BuiltinClassType(TypeOperator):
def __init__(self, *args):
super().__init__(args, node.name, is_reference=True)
ty = TypeType(BuiltinClassType)
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ty)
typevars = []
for b in node.bases:
if isinstance(b, ast.Subscript):
if isinstance(b.slice, ast.Name):
sliceval = [b.slice.id]
elif isinstance(b.slice, ast.Tuple):
sliceval = [n.id for n in b.slice.elts]
if isinstance(b.value, ast.Name) and b.value.id == "Generic":
typevars = sliceval
elif isinstance(b.value, ast.Name) and b.value.id == "Protocol":
typevars = sliceval
ty.type_object.is_protocol_gen = True
else:
idxs = [typevars.index(v) for v in sliceval]
parent = self.visit(b.value)
assert isinstance(parent, TypeType)
assert isinstance(ty.type_object, ABCMeta)
ty.type_object.gen_parents.append(lambda selfvars: parent.type_object(*[selfvars[i] for i in idxs]))
else:
if isinstance(b, ast.Name) and b.id == "Protocol":
ty.type_object.is_protocol_gen = True
else:
parent = self.visit(b)
assert isinstance(parent, TypeType)
if isinstance(ty.type_object, ABCMeta):
ty.type_object.gen_parents.append(parent.type_object)
else:
ty.type_object.parents.append(parent.type_object)
if not typevars and not existing:
ty.type_object = ty.type_object()
cl_scope = self.scope.child(ScopeKind.CLASS)
visitor = StdlibVisitor(cl_scope, ty)
for var in typevars:
visitor.typevars[var] = TypeType(TypeVariable(var))
for stmt in node.body:
visitor.visit(stmt)
def visit_Pass(self, node: ast.Pass):
pass
def visit_FunctionDef(self, node: ast.FunctionDef):
tc = node.type_comment # todo : lire les commetnaries de type pour les fonctions génériques sinon trouver autre chose
arg_visitor = TypeAnnotationVisitor(self.scope.child(ScopeKind.FUNCTION), self.cur_class)
arg_types = [arg_visitor.visit(arg.annotation or arg.arg) for arg in node.args.args]
ret_type = arg_visitor.visit(node.returns)
ty = FunctionType(arg_types, ret_type)
ty.typevars = arg_visitor.typevars
if node.args.vararg:
ty.variadic = True
ty.optional_at = 1 + len(node.args.args) - len(node.args.defaults)
if self.cur_class:
ty.is_method = True
assert isinstance(self.cur_class, TypeType)
if isinstance(self.cur_class.type_object, ABCMeta):
self.cur_class.type_object.gen_methods[node.name] = lambda t: ty.gen_sub(t, self.typevars)
else:
self.cur_class.type_object.fields[node.name] = MemberDef(ty.gen_sub(self.cur_class.type_object, self.typevars), ())
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ty)
def visit_Assert(self, node: ast.Assert):
if isinstance(node.test, ast.UnaryOp) and isinstance(node.test.op, ast.Not):
oper = node.test.operand
try:
res = self.expr().visit(oper)
except:
debug(f"Type of {ast.unparse(oper)} := INVALID")
else:
raise AssertionError(f"Assertion should fail, got {res} for {ast.unparse(oper)}")
else:
debug(f"Type of {ast.unparse(node.test)} := {self.expr().visit(node.test)}")
def visit_Call(self, node: ast.Call) -> BaseType:
ty_op = self.visit(node.func)
if is_builtin(ty_op, "TypeVar"):
return TypeType(TypeVariable(*[ast.literal_eval(arg) for arg in node.args]))
if isinstance(ty_op, TypeType):
return TypeType(ty_op.type_object(*[ast.literal_eval(arg) for arg in node.args]))
raise NotImplementedError(ast.unparse(node))
def anno(self) -> "TypeAnnotationVisitor":
return TypeAnnotationVisitor(self.scope, self.cur_class)
def visit_str(self, node: str) -> BaseType:
if existing := self.scope.get(node):
return existing.type
from transpiler.phases.typing.exceptions import UnknownNameError
raise UnknownNameError(node)
def visit_Name(self, node: ast.Name) -> BaseType:
if node.id == "TypeVar":
return BuiltinFeature("TypeVar")
return self.visit_str(node.id)
\ No newline at end of file
import typing
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Dict, Optional, Callable
def get_default_parents():
if obj := globals().get("TY_OBJECT"):
return [obj]
return []
class RuntimeValue:
pass
@dataclass
class MemberDef:
type: "BaseType"
val: typing.Any = RuntimeValue()
in_class_def: bool = True
@dataclass(eq=False)
class BaseType(ABC):
def resolve(self) -> "BaseType":
return self
cur_var = 0
def next_var_id():
global cur_var
cur_var += 1
return cur_var
@dataclass(eq=False)
class ConcreteType(BaseType):
"""
A concrete type is the type of a concrete value.
It has fields and a list of parent concrete types.
Examples: int, str, list[int]
"""
@dataclass(eq=False)
class TypeVariable(ConcreteType):
name: str = field(default_factory=lambda: next_var_id())
resolved: Optional[ConcreteType] = None
def resolve(self) -> ConcreteType:
if self.resolved is None:
return self
return self.resolved.resolve()
def __str__(self):
if self.resolved is None:
# return f"TypeVar[\"{self.name}\"]"
return f"_{self.name}"
return str(self.resolved)
def __eq__(self, other):
if not isinstance(other, BaseType):
return False
if self.resolved is None:
return self == other
return self.resolved == other.resolve()
@dataclass(eq=False)
class ResolvedConcreteType(ConcreteType):
"""
A concrete type is the type of a concrete value.
It has fields and a list of parent concrete types.
Examples: int, str, list[int]
"""
fields: Dict[str, "MemberDef"] = field(default_factory=dict, init=False)
parents: list["ResolvedConcreteType"] = field(default_factory=lambda: [TY_OBJECT], init=False)
def get_mro(self):
"""
Performs linearization according to the MRO spec.
https://www.python.org/download/releases/2.3/mro/
"""
def merge(*lists):
lists = [l for l in lists if len(l) > 0]
for i, l in enumerate(lists):
first = l[0]
for j, l2 in enumerate(lists):
if j == i:
continue
if first in l2:
break
else:
return [first] + merge(*[x[1:] for x in lists if x[0] != first])
# unable to find a next element
from transpiler.phases.typing.exceptions import InconsistentMroError
raise InconsistentMroError(self.parents)
return [self] + merge(*[p.get_mro() for p in self.parents], self.parents)
def inherits(self, parent: BaseType):
return self == parent or any(p.inherits(parent) for p in self.parents)
@dataclass(eq=False, init=False)
class GenericInstanceType(ResolvedConcreteType):
"""
An instance of a generic type.
Examples: list[int], dict[str, object], Callable[[int, int], int]
"""
generic_parent: "GenericType"
generic_args: list[ConcreteType]
def __init__(self):
super().__init__()
def inherits(self, parent: BaseType):
return self.generic_parent == parent or super().inherits(parent)
def __eq__(self, other):
if isinstance(other, GenericInstanceType):
return self.generic_parent == other.generic_parent and self.generic_args == other.generic_args
return False
def __str__(self):
return f"{self.generic_parent}[{', '.join(map(str, self.generic_args))}]"
@dataclass
class GenericConstraint:
left: ResolvedConcreteType
right: ResolvedConcreteType
@dataclass(eq=False, init=False)
class GenericType(BaseType):
parameters: list[str]
def constraints(self, args: list[ConcreteType]) -> list[GenericConstraint]:
return []
@abstractmethod
def _instantiate(self, args: list[ConcreteType]) -> GenericInstanceType:
raise NotImplementedError()
def instantiate(self, args: list[ConcreteType]) -> GenericInstanceType:
res = self._instantiate(args)
res.generic_args = args
res.generic_parent = self
return res
@dataclass(eq=False, init=False)
class BuiltinGenericType(GenericType):
constraints_: Callable[[list[ConcreteType]], list[GenericConstraint]]
instantiate_: Callable[[list[ConcreteType]], GenericInstanceType]
def constraints(self, args: list[ConcreteType]) -> list[GenericConstraint]:
return self.constraints_(args)
def _instantiate(self, args: list[ConcreteType]) -> GenericInstanceType:
return self.instantiate_(args)
def create_builtin_type(name: str):
class CreatedType(BuiltinGenericType):
def __str__(self):
return name
res = CreatedType()
return res
TY_OBJECT = None
TY_OBJECT = create_builtin_type("object")
TY_OBJECT.parents = []
TY_BOOL = create_builtin_type("bool")
TY_INT = create_builtin_type("int")
TY_FLOAT = create_builtin_type("float")
TY_STR = create_builtin_type("str")
TY_BYTES = create_builtin_type("bytes")
TY_COMPLEX = create_builtin_type("complex")
TY_NONE = create_builtin_type("NoneType")
def unimpl(*args, **kwargs):
raise NotImplementedError()
def create_builtin_generic_type(name: str):
class CreatedType(BuiltinGenericType):
def __str__(self):
return name
res = CreatedType()
return res
TY_LIST = create_builtin_generic_type("list")
TY_SET = create_builtin_generic_type("set")
TY_DICT = create_builtin_generic_type("dict")
TY_TUPLE = create_builtin_generic_type("tuple")
@dataclass(unsafe_hash=False)
class TypeListType(ConcreteType):
"""
Special type used to represent a list of types.
Used in function types: Callable[[int, int], int]
"""
contents: list[ConcreteType]
def __str__(self):
return f"[{', '.join(map(str, self.contents))}]"
@dataclass(eq=False)
class CallableInstanceType(GenericInstanceType):
parameters: list[ConcreteType]
return_type: ConcreteType
class CallableType(GenericType):
def __str__(self):
return "Callable"
def _instantiate(self, args: list[ConcreteType]) -> GenericInstanceType:
match args:
case [TypeListType([*args]), ret]:
return CallableInstanceType(args, ret)
case _:
raise ValueError
TY_CALLABLE = CallableType()
@dataclass(eq=False)
class ClassTypeType(GenericInstanceType):
inner_type: BaseType
class ClassType(GenericType):
def __str__(self):
return "Type"
def _instantiate(self, args: list[ConcreteType]) -> GenericInstanceType:
return ClassTypeType(*args)
TY_TYPE = ClassType()
......@@ -66,7 +66,7 @@ def highlight(code, full=False):
return cf.yellow("<None>")
if type(code) == list:
return repr([highlight(x) for x in code])
from transpiler.phases.typing import BaseType
from transpiler.phases.typing.types import BaseType
if isinstance(code, ast.AST):
return cf.italic_grey60(f"[{type(code).__name__}] ") + highlight(ast.unparse(code))
elif isinstance(code, BaseType):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment