Commit 95b8479f authored by Tom Niget's avatar Tom Niget

Few things work

parent ef37573d
from typing import Self, Generic, Protocol, Optional
assert 5
class object:
def __eq__(self, other: Self) -> bool: ...
def __ne__(self, other: Self) -> bool: ...
class int:
def __add__(self, other: Self) -> Self: ...
def __sub__(self, other: Self) -> Self: ...
def __mul__(self, other: Self) -> Self: ...
def __and__(self, other: Self) -> Self: ...
def __neg__(self) -> Self: ...
def __init__(self, x: object) -> None: ...
def __lt__(self, other: Self) -> bool: ...
def __gt__(self, other: Self) -> bool: ...
def __mod__(self, other: Self) -> Self: ...
def __ge__(self, other: Self) -> bool: ...
class float:
def __init__(self, x: object) -> None: ...
assert int.__add__
assert (5).__add__
class slice:
pass
class HasLen(Protocol):
def __len__(self) -> int: ...
def len(x: HasLen) -> int:
...
class Iterator[U](Protocol):
def __iter__(self) -> Self: ...
def __next__(self) -> U: ...
class Iterable[U](Protocol):
def __iter__(self) -> Iterator[U]: ...
class str:
def find(self, sub: Self) -> int: ...
def format(self, *args) -> Self: ...
def encode(self, encoding: Self) -> bytes: ...
def __len__(self) -> int: ...
def __add__(self, other: Self) -> Self: ...
def __mul__(self, other: int) -> Self: ...
def startswith(self, prefix: Self) -> bool: ...
def __getitem__(self, item: int | slice) -> Self: ...
def isspace(self) -> bool: ...
def __contains__(self, item: Self) -> bool: ...
assert len("a")
class bytes:
def decode(self, encoding: str) -> str: ...
def __len__(self) -> int: ...
class list[U]:
def __add__(self, other: Self) -> Self: ...
def __mul__(self, other: int) -> Self: ...
def __getitem__(self, index: int) -> U: ...
def __setitem__(self, index: int, value: U) -> None: ...
def pop(self, index: int = -1) -> U: ...
def __iter__(self) -> Iterator[U]: ...
def __len__(self) -> int: ...
def append(self, value: U) -> None: ...
def __contains__(self, item: U) -> bool: ...
def __init__(self, it: Iterator[U]) -> None: ...
assert [1, 2].__iter__()
assert list[int].__iter__
class dict[U, V]:
def __getitem__(self, key: U) -> V: ...
def __setitem__(self, key: U, value: V) -> None: ...
def __len__(self) -> int: ...
assert(len(["a"]))
#assert list.__getitem__
assert [].__getitem__
assert [4].__getitem__
assert [1, 2, 3][1]
def iter[U](x: Iterable[U]) -> Iterator[U]:
...
assert iter
def next[U](it: Iterator[U], default: Optional[U] = None) -> U:
...
# what happens with multiple functions
assert iter(["1", "2"])
def identity[U](x: U) -> U:
...
assert identity(1)
assert identity("a")
def identity_2[U, V](x: U, y: V) -> tuple[U, V]:
...
assert list.__add__
assert list.__add__([5], [[6][0]])
assert list[int].__add__
assert identity_2(1, "a")
assert lambda x, y: identity_2(x, y)
assert lambda x: identity_2(x, x)
def print(*args) -> None: ...
assert print
def input(prompt: str = "") -> str:
...
def range(*args) -> Iterator[int]: ...
assert [].__add__
assert [6].__add__
assert [True].__add__
assert lambda x: [x].__add__
assert next(range(6), None)
class file:
def read(self, size: int=0) -> Task[str]: ...
def close(self) -> Task[None]: ...
def __enter__(self) -> Self: ...
def __exit__(self) -> Task[bool]: ...
def open(filename: str, mode: str) -> Task[file]: ...
def __test_opt(x: int, y: int = 5) -> int:
...
assert __test_opt
assert __test_opt(5)
assert __test_opt(5, 6)
assert not __test_opt(5, 6, 7)
assert not __test_opt()
class __test_type:
def __init__(self) -> None: ...
def test_opt(self, x: int, y: int = 5) -> int:
...
assert __test_type().test_opt(5)
assert __test_type().test_opt(5, 6)
assert not __test_type().test_opt(5, 6, 7)
assert not __test_type().test_opt()
def exit(code: int | None = None) -> None: ...
class Exception:
def __init__(self, message: str) -> None: ...
\ No newline at end of file
from typing import Self, Protocol, Optional
assert 5
class object:
def __eq__(self, other: Self) -> bool: ...
def __ne__(self, other: Self) -> bool: ...
class int:
def __add__(self, other: Self) -> Self: ...
def __sub__(self, other: Self) -> Self: ...
def __mul__(self, other: Self) -> Self: ...
def __and__(self, other: Self) -> Self: ...
def __neg__(self) -> Self: ...
def __init__(self, x: object) -> None: ...
def __lt__(self, other: Self) -> bool: ...
def __gt__(self, other: Self) -> bool: ...
def __mod__(self, other: Self) -> Self: ...
def __ge__(self, other: Self) -> bool: ...
class float:
def __init__(self, x: object) -> None: ...
assert int.__add__
assert (5).__add__
class slice:
pass
class HasLen(Protocol):
def __len__(self) -> int: ...
def len(x: HasLen) -> int:
...
class Iterator[U](Protocol):
def __iter__(self) -> Self: ...
def __next__(self) -> U: ...
class Iterable[U](Protocol):
def __iter__(self) -> Iterator[U]: ...
class str:
def find(self, sub: Self) -> int: ...
def format(self, *args) -> Self: ...
def encode(self, encoding: Self) -> bytes: ...
def __len__(self) -> int: ...
def __add__(self, other: Self) -> Self: ...
def __mul__(self, other: int) -> Self: ...
def startswith(self, prefix: Self) -> bool: ...
def __getitem__(self, item: int | slice) -> Self: ...
def isspace(self) -> bool: ...
def __contains__(self, item: Self) -> bool: ...
assert len("a")
class bytes:
def decode(self, encoding: str) -> str: ...
def __len__(self) -> int: ...
class list[U]:
def __add__(self, other: Self) -> Self: ...
def __mul__(self, other: int) -> Self: ...
def __getitem__(self, index: int) -> U: ...
def __setitem__(self, index: int, value: U) -> None: ...
def pop(self, index: int = -1) -> U: ...
def __iter__(self) -> Iterator[U]: ...
def __len__(self) -> int: ...
def append(self, value: U) -> None: ...
def __contains__(self, item: U) -> bool: ...
def __init__(self, it: Iterator[U]) -> None: ...
assert [1, 2].__iter__()
assert list[int].__iter__
class dict[U, V]:
def __getitem__(self, key: U) -> V: ...
def __setitem__(self, key: U, value: V) -> None: ...
def __len__(self) -> int: ...
assert(len(["a"]))
#assert list.__getitem__
assert [].__getitem__
assert [4].__getitem__
assert [1, 2, 3][1]
def iter[U](x: Iterable[U]) -> Iterator[U]:
...
assert iter
def next[U](it: Iterator[U], default: Optional[U] = None) -> U:
...
# what happens with multiple functions
assert iter(["1", "2"])
def identity[U](x: U) -> U:
...
assert identity(1)
assert identity("a")
def identity_2[U, V](x: U, y: V) -> tuple[U, V]:
...
assert list.__add__
assert list.__add__([5], [[6][0]])
assert list[int].__add__
assert identity_2(1, "a")
assert lambda x, y: identity_2(x, y)
assert lambda x: identity_2(x, x)
def print(*args) -> None: ...
assert print
def input(prompt: str = "") -> str:
...
def range(*args) -> Iterator[int]: ...
assert [].__add__
assert [6].__add__
assert [True].__add__
assert lambda x: [x].__add__
assert next(range(6), None)
class file:
def read(self, size: int=0) -> Task[str]: ...
def close(self) -> Task[None]: ...
def __enter__(self) -> Self: ...
def __exit__(self) -> Task[bool]: ...
def open(filename: str, mode: str) -> Task[file]: ...
def __test_opt(x: int, y: int = 5) -> int:
...
assert __test_opt
assert __test_opt(5)
assert __test_opt(5, 6)
assert not __test_opt(5, 6, 7)
assert not __test_opt()
class __test_type:
def __init__(self) -> None: ...
def test_opt(self, x: int, y: int = 5) -> int:
...
assert __test_type().test_opt(5)
assert __test_type().test_opt(5, 6)
assert not __test_type().test_opt(5, 6, 7)
assert not __test_type().test_opt()
def exit(code: int | None = None) -> None: ...
class Exception:
def __init__(self, message: str) -> None: ...
\ No newline at end of file
# coding: utf-8 # coding: utf-8
Protocol = BuiltinFeature["Protocol"]
Self = BuiltinFeature["Self"]
Optional = BuiltinFeature["Optional"]
\ No newline at end of file
...@@ -6,12 +6,15 @@ import inspect ...@@ -6,12 +6,15 @@ import inspect
import os import os
import traceback import traceback
from pathlib import Path
#os.environ["TERM"] = "xterm-256" #os.environ["TERM"] = "xterm-256"
import colorama import colorama
from transpiler.phases.desugar_compare import DesugarCompare from transpiler.phases.desugar_compare import DesugarCompare
from transpiler.phases.desugar_op import DesugarOp from transpiler.phases.desugar_op import DesugarOp
from transpiler.phases.typing import PRELUDE
from transpiler.phases.typing.modules import parse_module
colorama.init() colorama.init()
...@@ -20,10 +23,9 @@ from transpiler.phases.desugar_with import DesugarWith ...@@ -20,10 +23,9 @@ from transpiler.phases.desugar_with import DesugarWith
#from transpiler.phases.emit_cpp.file import FileVisitor #from transpiler.phases.emit_cpp.file import FileVisitor
from transpiler.phases.if_main import IfMainVisitor from transpiler.phases.if_main import IfMainVisitor
#from transpiler.phases.typing.block import ScoperBlockVisitor #from transpiler.phases.typing.block import ScoperBlockVisitor
from transpiler.phases.typing.scope import Scope from transpiler.phases.typing.scope import Scope, ScopeKind
from transpiler.utils import highlight from transpiler.utils import highlight
from itertools import islice
import sys import sys
import colorful as cf import colorful as cf
...@@ -43,7 +45,7 @@ def exception_hook(exc_type, exc_value, tb): ...@@ -43,7 +45,7 @@ def exception_hook(exc_type, exc_value, tb):
local_vars = tb.tb_frame.f_locals local_vars = tb.tb_frame.f_locals
name = tb.tb_frame.f_code.co_name name = tb.tb_frame.f_code.co_name
if name == "transpile": if name in ("transpile", "parse_module"):
last_file = local_vars["path"] last_file = local_vars["path"]
if name == "visit" and (node := local_vars["node"]) and isinstance(node, ast.AST): if name == "visit" and (node := local_vars["node"]) and isinstance(node, ast.AST):
...@@ -66,7 +68,7 @@ def exception_hook(exc_type, exc_value, tb): ...@@ -66,7 +68,7 @@ def exception_hook(exc_type, exc_value, tb):
print() print()
tb = tb.tb_next tb = tb.tb_next
if last_node is not None: if last_node is not None and last_file is not None:
print() print()
if not hasattr(last_node, "lineno"): if not hasattr(last_node, "lineno"):
print(cf.red("Error: "), cf.white("No line number available")) print(cf.red("Error: "), cf.white("No line number available"))
...@@ -175,6 +177,11 @@ else: ...@@ -175,6 +177,11 @@ else:
pydevd.original_excepthook = sys.excepthook pydevd.original_excepthook = sys.excepthook
typon_std = Path(__file__).parent.parent / "stdlib"
#discover_module(typon_std, PRELUDE.child(ScopeKind.GLOBAL))
parse_module("builtins", typon_std, PRELUDE)
def transpile(source, name: str, path=None): def transpile(source, name: str, path=None):
__TB__ = f"transpiling module {cf.white(name)}" __TB__ = f"transpiling module {cf.white(name)}"
res = ast.parse(source, type_comments=True) res = ast.parse(source, type_comments=True)
......
import ast from transpiler.phases.typing.common import PRELUDE
from pathlib import Path from transpiler.phases.typing.scope import VarKind, VarDecl
from logging import debug from transpiler.phases.typing.types import TY_TASK, TY_CALLABLE, TY_OPTIONAL, TY_CPP_TYPE, TY_BUILTIN_FEATURE, TY_TUPLE, \
from transpiler.phases.typing.scope import VarKind, VarDecl, ScopeKind, Scope TY_DICT, TY_SET, TY_LIST, TY_COMPLEX, TY_BYTES, TY_STR, TY_FLOAT, TY_INT, TY_BOOL, TY_OBJECT
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, ResolvedConcreteType, \
MemberDef, TY_FLOAT, TY_BUILTIN_FEATURE, TY_TUPLE, TY_DICT, TY_SET, TY_LIST, TY_BYTES, TY_OBJECT, TY_CPP_TYPE, \
TY_OPTIONAL, UniqueTypeMixin, TY_CALLABLE, TY_TASK
# 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))),
# })
prelude_vars = { prelude_vars = {
"object": TY_OBJECT, "object": TY_OBJECT,
...@@ -46,50 +17,8 @@ prelude_vars = { ...@@ -46,50 +17,8 @@ prelude_vars = {
"tuple": TY_TUPLE, "tuple": TY_TUPLE,
"BuiltinFeature": TY_BUILTIN_FEATURE, "BuiltinFeature": TY_BUILTIN_FEATURE,
"CppType": TY_CPP_TYPE, "CppType": TY_CPP_TYPE,
"Optional": TY_OPTIONAL,
"Callable": TY_CALLABLE,
"Task": TY_TASK "Task": TY_TASK
} }
PRELUDE.vars.update({name: VarDecl(VarKind.LOCAL, ty.type_type()) for name, ty in prelude_vars.items()}) PRELUDE.vars.update({name: VarDecl(VarKind.LOCAL, ty.type_type()) for name, ty in prelude_vars.items()})
typon_std = Path(__file__).parent.parent.parent.parent / "stdlib"
def make_module(name: str, scope: Scope) -> ResolvedConcreteType:
class CreatedType(UniqueTypeMixin, ResolvedConcreteType):
def name(self):
return name
ty = CreatedType()
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
...@@ -43,10 +43,8 @@ class TypeAnnotationVisitor(NodeVisitorSeq): ...@@ -43,10 +43,8 @@ class TypeAnnotationVisitor(NodeVisitorSeq):
elif ty_op is TY_CPP_TYPE: elif ty_op is TY_CPP_TYPE:
assert len(args) == 1 assert len(args) == 1
return make_cpp_type(args[0]) return make_cpp_type(args[0])
# return ty_op(*args)
assert isinstance(ty_op, GenericType) assert isinstance(ty_op, GenericType)
return ty_op.instantiate(args) return ty_op.instantiate(args)
# return TypeOperator([self.visit(node.value)], self.visit(node.slice.value))
def visit_List(self, node: ast.List) -> BaseType: def visit_List(self, node: ast.List) -> BaseType:
return TypeListType([self.visit(elt) for elt in node.elts]) return TypeListType([self.visit(elt) for elt in node.elts])
......
...@@ -5,7 +5,7 @@ from typing import Dict, Optional ...@@ -5,7 +5,7 @@ from typing import Dict, Optional
from transpiler.utils import highlight from transpiler.utils import highlight
from transpiler.phases.typing.annotations import TypeAnnotationVisitor from transpiler.phases.typing.annotations import TypeAnnotationVisitor
from transpiler.phases.typing.scope import Scope, ScopeKind, VarDecl, VarKind from transpiler.phases.typing.scope import Scope, ScopeKind, VarDecl, VarKind
from transpiler.phases.typing.types import BaseType, TypeVariable, TY_NONE from transpiler.phases.typing.types import BaseType, TypeVariable, TY_NONE, BuiltinFeatureType
from transpiler.phases.utils import NodeVisitorSeq, AnnotationName from transpiler.phases.utils import NodeVisitorSeq, AnnotationName
PRELUDE = Scope.make_global() PRELUDE = Scope.make_global()
...@@ -121,4 +121,4 @@ def get_next(iter_type): ...@@ -121,4 +121,4 @@ def get_next(iter_type):
return next_type return next_type
def is_builtin(x, feature): def is_builtin(x, feature):
return isinstance(x, BuiltinFeature) and x.val == feature return isinstance(x, BuiltinFeatureType) and x.feature() == feature
\ No newline at end of file \ No newline at end of file
...@@ -180,9 +180,24 @@ class UnknownNameError(CompileError): ...@@ -180,9 +180,24 @@ class UnknownNameError(CompileError):
For example: For example:
{highlight('print(abcd)')} {highlight('print(abcd)')}
{highlight('import foobar')}
""" """
@dataclass
class UnknownModuleError(CompileError):
name: str
def __str__(self) -> str:
return f"Unknown module: {highlight(self.name)}"
def detail(self, last_node: ast.AST = None) -> str:
return f"""
This indicates that an attempt was made to import a module that does not exist.
For example:
{highlight('import abcd')}
"""
@dataclass @dataclass
class UnknownModuleMemberError(CompileError): class UnknownModuleMemberError(CompileError):
......
...@@ -4,12 +4,12 @@ import inspect ...@@ -4,12 +4,12 @@ import inspect
from itertools import zip_longest from itertools import zip_longest
from typing import List 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.common import ScoperVisitor, get_iter, get_next, is_builtin
from transpiler.phases.typing.exceptions import ArgumentCountMismatchError from transpiler.phases.typing.exceptions import ArgumentCountMismatchError, TypeMismatchKind, TypeMismatchError
from transpiler.phases.typing.types import BaseType, TY_STR, TY_BOOL, TY_INT, TY_COMPLEX, TY_FLOAT, TY_NONE, \ from transpiler.phases.typing.types import BaseType, TY_STR, TY_BOOL, TY_INT, TY_COMPLEX, TY_FLOAT, TY_NONE, \
ClassTypeType, ResolvedConcreteType, GenericType, CallableInstanceType, TY_LIST, TY_SET, TY_DICT, RuntimeValue, \ ClassTypeType, ResolvedConcreteType, GenericType, CallableInstanceType, TY_LIST, TY_SET, TY_DICT, RuntimeValue, \
TypeVariable, TY_LAMBDA, TypeListType, MethodType TypeVariable, TY_LAMBDA, TypeListType, MethodType
from transpiler.phases.typing.scope import ScopeKind, VarDecl, VarKind
from transpiler.utils import linenodata from transpiler.utils import linenodata
DUNDER = { DUNDER = {
...@@ -138,7 +138,8 @@ class ScoperExprVisitor(ScoperVisitor): ...@@ -138,7 +138,8 @@ class ScoperExprVisitor(ScoperVisitor):
raise ArgumentCountMismatchError(ftype, arguments) raise ArgumentCountMismatchError(ftype, arguments)
if a is None and ftype.is_variadic: if a is None and ftype.is_variadic:
break break
assert a.try_assign(b) if not a.try_assign(b):
raise TypeMismatchError(a, b, TypeMismatchKind.DIFFERENT_TYPE)
return ftype.return_type return ftype.return_type
# if isinstance(ftype, TypeType):# and isinstance(ftype.type_object, UserType): # if isinstance(ftype, TypeType):# and isinstance(ftype.type_object, UserType):
...@@ -222,7 +223,7 @@ class ScoperExprVisitor(ScoperVisitor): ...@@ -222,7 +223,7 @@ class ScoperExprVisitor(ScoperVisitor):
ty.python_func_used = True ty.python_func_used = True
if isinstance(ty, MethodType): if isinstance(ty, MethodType):
if bound and field.in_class_def and type(field.val) != RuntimeValue: if bound and field.in_class_def and type(field.val) != RuntimeValue:
return ty.remove_self() return ty.remove_self(ltype)
return ty return ty
......
import ast
from pathlib import Path
from logging import debug
from transpiler.phases.typing.scope import Scope, VarKind, VarDecl, ScopeKind
from transpiler.phases.typing.types import MemberDef, ResolvedConcreteType, UniqueTypeMixin
def make_module(name: str, scope: Scope) -> ResolvedConcreteType:
class CreatedType(UniqueTypeMixin, ResolvedConcreteType):
def name(self):
return name
ty = CreatedType()
for n, v in scope.vars.items():
ty.fields[n] = MemberDef(v.type, v.val, False)
return ty
visited_modules = {}
def parse_module(mod_name: str, python_path: Path, scope=None):
path = python_path / mod_name
if not path.exists():
path = path.with_suffix(".py")
if not path.exists():
path = path.with_stem(mod_name + "_")
if not path.exists():
raise FileNotFoundError(f"Could not find {path}")
if path.is_dir():
real_path = path / "__init__.py"
else:
real_path = path
if mod := visited_modules.get(real_path.as_posix()):
return mod
from transpiler import PRELUDE
mod_scope = scope or PRELUDE.child(ScopeKind.GLOBAL)
if real_path.suffix == ".py":
from transpiler.phases.typing.stdlib import StdlibVisitor
StdlibVisitor(python_path, mod_scope).visit(ast.parse(real_path.read_text()))
else:
raise NotImplementedError(f"Unsupported file type {path.suffix}")
mod = make_mod_decl(mod_name, mod_scope)
visited_modules[real_path.as_posix()] = mod
return mod
# def process_module(mod_path: Path, scope):
# if mod := visited_modules.get(mod_path.as_posix()):
# return mod
#
# if mod_path.is_dir():
# mod_scope = scope.child(ScopeKind.GLOBAL)
# discover_module(mod_path, mod_scope)
# mod = make_mod_decl(mod_path.name, mod_scope)
# scope.vars[mod_path.name] = mod
# elif mod_path.name == "__init__.py":
# StdlibVisitor(mod_path, scope).visit(ast.parse(mod_path.read_text()))
# mod = None
# debug(f"Visited {mod_path}")
# elif mod_path.suffix == ".py":
# mod_scope = scope.child(ScopeKind.GLOBAL)
# StdlibVisitor(mod_path, mod_scope).visit(ast.parse(mod_path.read_text()))
# if mod_path.stem[-1] == "_":
# mod_path = mod_path.with_name(mod_path.stem[:-1])
# mod = make_mod_decl(mod_path.name, mod_scope)
# scope.vars[mod_path.stem] = mod
# debug(f"Visited {mod_path}")
# return mod
#
# def discover_module(dir_path: Path, scope):
# mod = make_mod_decl(dir_path.name, scope)
# for child in sorted(dir_path.iterdir()):
# # if child.name == "__init__.py":
# # StdlibVisitor(mod_scope).visit(ast.parse(child.read_text()))
# # else:
# # process_module(child, mod_scope)
# child_mod = process_module(child, scope)
# visited_modules[child.as_posix()] = child_mod
# return mod
def make_mod_decl(child, mod_scope):
return VarDecl(VarKind.LOCAL, make_module(child, mod_scope), {k: v.type for k, v in mod_scope.vars.items()})
\ No newline at end of file
...@@ -2,9 +2,11 @@ import ast ...@@ -2,9 +2,11 @@ import ast
import dataclasses import dataclasses
from abc import ABCMeta from abc import ABCMeta
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional, List, Dict, Callable from typing import Optional, List, Dict, Callable
from logging import debug from logging import debug
from transpiler.phases.typing.modules import parse_module
from transpiler.utils import highlight from transpiler.utils import highlight
from transpiler.phases.typing.annotations import TypeAnnotationVisitor from transpiler.phases.typing.annotations import TypeAnnotationVisitor
from transpiler.phases.typing.common import PRELUDE, is_builtin from transpiler.phases.typing.common import PRELUDE, is_builtin
...@@ -17,7 +19,7 @@ from transpiler.phases.typing.types import BaseType, BuiltinGenericType, Builtin ...@@ -17,7 +19,7 @@ from transpiler.phases.typing.types import BaseType, BuiltinGenericType, Builtin
from transpiler.phases.utils import NodeVisitorSeq from transpiler.phases.utils import NodeVisitorSeq
def visit_generic_item( def visit_generic_item(
visit_nongeneric: Callable[[Scope, ConcreteType], None], visit_nongeneric: Callable[[Scope, ResolvedConcreteType], None],
node, node,
output_type: BuiltinGenericType, output_type: BuiltinGenericType,
scope: Scope, scope: Scope,
...@@ -59,8 +61,8 @@ def visit_generic_item( ...@@ -59,8 +61,8 @@ def visit_generic_item(
new_scope.declare_local(name, op_val.type_type()) new_scope.declare_local(name, op_val.type_type())
case ast.TypeVarTuple(name): case ast.TypeVarTuple(name):
new_scope.declare_local(name, TypeTupleType(list(args_iter)).type_type()) new_scope.declare_local(name, TypeTupleType(list(args_iter)).type_type())
# for a, b in constraints: for a, b in constraints:
# raise NotImplementedError() assert b.try_assign(a)
# todo # todo
new_output_type = instance_type() new_output_type = instance_type()
visit_nongeneric(new_scope, new_output_type) visit_nongeneric(new_scope, new_output_type)
...@@ -73,9 +75,20 @@ def visit_generic_item( ...@@ -73,9 +75,20 @@ def visit_generic_item(
@dataclass @dataclass
class StdlibVisitor(NodeVisitorSeq): class StdlibVisitor(NodeVisitorSeq):
python_path: Path
scope: Scope = field(default_factory=lambda: PRELUDE) scope: Scope = field(default_factory=lambda: PRELUDE)
cur_class: Optional[ResolvedConcreteType] = None cur_class: Optional[ResolvedConcreteType] = None
typevars: Dict[str, BaseType] = field(default_factory=dict)
def resolve_module_import(self, name: str) -> VarDecl:
# tries = [
# self.python_path.parent / f"{name}.py",
# self.python_path.parent / name
# ]
# for path in tries:
# if path.exists():
# return path
# raise FileNotFoundError(f"Could not find module {name}")
return parse_module(name, self.python_path)
def expr(self) -> ScoperExprVisitor: def expr(self) -> ScoperExprVisitor:
return ScoperExprVisitor(self.scope) return ScoperExprVisitor(self.scope)
...@@ -85,7 +98,7 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -85,7 +98,7 @@ class StdlibVisitor(NodeVisitorSeq):
self.visit(stmt) self.visit(stmt)
def visit_Assign(self, node: ast.Assign): def visit_Assign(self, node: ast.Assign):
self.scope.vars[node.targets[0].id] = VarDecl(VarKind.LOCAL, self.visit(node.value)) self.scope.vars[node.targets[0].id] = VarDecl(VarKind.LOCAL, self.anno().visit(node.value).type_type())
def visit_AnnAssign(self, node: ast.AnnAssign): def visit_AnnAssign(self, node: ast.AnnAssign):
ty = self.anno().visit(node.annotation) ty = self.anno().visit(node.annotation)
...@@ -95,10 +108,21 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -95,10 +108,21 @@ class StdlibVisitor(NodeVisitorSeq):
self.scope.vars[node.target.id] = VarDecl(VarKind.LOCAL, ty) self.scope.vars[node.target.id] = VarDecl(VarKind.LOCAL, ty)
def visit_ImportFrom(self, node: ast.ImportFrom): def visit_ImportFrom(self, node: ast.ImportFrom):
pass module = self.resolve_module_import(node.module)
node.module_obj = module.type
for alias in node.names:
thing = module.val.get(alias.name)
if not thing:
from transpiler.phases.typing.exceptions import UnknownModuleMemberError
raise UnknownModuleMemberError(node.module, alias.name)
alias.item_obj = thing
self.scope.vars[alias.asname or alias.name] = VarDecl(VarKind.LOCAL, thing)
def visit_Import(self, node: ast.Import): def visit_Import(self, node: ast.Import):
pass for alias in node.names:
mod = self.resolve_module_import(alias.name)
alias.module_obj = mod.type
self.scope.vars[alias.asname or alias.name] = mod
def visit_ClassDef(self, node: ast.ClassDef): def visit_ClassDef(self, node: ast.ClassDef):
if existing := self.scope.get(node.name): if existing := self.scope.get(node.name):
...@@ -109,10 +133,18 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -109,10 +133,18 @@ class StdlibVisitor(NodeVisitorSeq):
NewType = base_class(node.name) NewType = base_class(node.name)
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, NewType.type_type()) self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, NewType.type_type())
def visit_nongeneric(scope: Scope, output: ConcreteType): def visit_nongeneric(scope: Scope, output: ResolvedConcreteType):
cl_scope = scope.child(ScopeKind.CLASS) cl_scope = scope.child(ScopeKind.CLASS)
cl_scope.declare_local("Self", output.type_type()) cl_scope.declare_local("Self", output.type_type())
visitor = StdlibVisitor(cl_scope, output) visitor = StdlibVisitor(self.python_path, cl_scope, output)
bases = [self.anno().visit(base) for base in node.bases]
match bases:
case []:
pass
case [prot] if is_builtin(prot, "Protocol"):
output.is_protocol = True
case _:
raise NotImplementedError("parents not handled yet: " + ", ".join(map(ast.unparse, node.bases)))
for stmt in node.body: for stmt in node.body:
visitor.visit(stmt) visitor.visit(stmt)
...@@ -132,24 +164,17 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -132,24 +164,17 @@ class StdlibVisitor(NodeVisitorSeq):
@dataclass(eq=False, init=False) @dataclass(eq=False, init=False)
class InstanceType(CallableInstanceType): class InstanceType(CallableInstanceType):
def __init__(self): def __init__(self, **kwargs):
super().__init__([], None, 0) super().__init__(**{"parameters": None, "return_type": None, **kwargs})
def __str__(self): def __str__(self):
return f"{node.name}{super().__str__()}" return f"{node.name}{super().__str__()}"
'''
class arguments(__ast.AST):
""" arguments(arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, arg? kwarg, expr* defaults) """'''
args = node.args args = node.args
assert args.posonlyargs == [] assert args.posonlyargs == []
#assert args.vararg is None TODO
assert args.kwonlyargs == [] assert args.kwonlyargs == []
assert args.kw_defaults == [] assert args.kw_defaults == []
assert args.kwarg is None assert args.kwarg is None
#assert args.defaults == [] TODO
for i, arg in enumerate(args.args): for i, arg in enumerate(args.args):
arg: ast.arg arg: ast.arg
...@@ -159,11 +184,11 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -159,11 +184,11 @@ class StdlibVisitor(NodeVisitorSeq):
arg_name = "Self" arg_name = "Self"
else: else:
arg_name = f"AutoVar${hash(arg.arg)}" arg_name = f"AutoVar${hash(arg.arg)}"
node.type_params.append(ast.TypeVar(arg_name, None)) # todo: bounds node.type_params.append(ast.TypeVar(arg_name, None)) # todo: bounds
arg.annotation = ast.Name(arg_name, ast.Load()) arg.annotation = ast.Name(arg_name, ast.Load())
else: else:
if isinstance(arg.annotation, ast.Name) and ( if isinstance(arg.annotation, ast.Name) and (
arg.annotation.id == "Self" or #arg.annotation.id == "Self" or
any(k.name == arg.annotation.id for k in node.type_params) any(k.name == arg.annotation.id for k in node.type_params)
): ):
# annotation is type variable so we keep it # annotation is type variable so we keep it
...@@ -186,13 +211,13 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -186,13 +211,13 @@ class StdlibVisitor(NodeVisitorSeq):
return f"FuncTypeGen${node.name}" return f"FuncTypeGen${node.name}"
if cur_class_ref is not None: if cur_class_ref is not None:
def remove_self(self, new_return_type = None): def remove_self(self, self_type):
class BoundFuncType(UniqueTypeMixin, GenericType): class BoundFuncType(UniqueTypeMixin, GenericType):
def name(self) -> str: def name(self) -> str:
return f"BoundFuncType${node.name}" return f"BoundFuncType${node.name}"
def _instantiate(self, args: list[ConcreteType]) -> GenericInstanceType: def _instantiate(self, args: list[ConcreteType]) -> GenericInstanceType:
return NewType.instantiate(args).remove_self() return NewType.instantiate(args).remove_self(self_type)
def __str__(self): def __str__(self):
return str(self.instantiate_default()) return str(self.instantiate_default())
...@@ -213,25 +238,6 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -213,25 +238,6 @@ class StdlibVisitor(NodeVisitorSeq):
visit_generic_item(visit_nongeneric, node, NewType, self.scope, InstanceType, True) visit_generic_item(visit_nongeneric, node, NewType, self.scope, InstanceType, True)
# 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): def visit_Assert(self, node: ast.Assert):
if isinstance(node.test, ast.UnaryOp) and isinstance(node.test.op, ast.Not): if isinstance(node.test, ast.UnaryOp) and isinstance(node.test.op, ast.Not):
oper = node.test.operand oper = node.test.operand
......
import ast
import dataclasses
import enum import enum
import typing import typing
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
...@@ -32,14 +34,21 @@ class UnifyMode: ...@@ -32,14 +34,21 @@ class UnifyMode:
UnifyMode.NORMAL = UnifyMode() UnifyMode.NORMAL = UnifyMode()
UnifyMode.EXACT = UnifyMode(False, False) UnifyMode.EXACT = UnifyMode(False, False)
@dataclass
class BlockData[N]:
node: N
scope: "Scope"
@dataclass(eq=False) @dataclass(eq=False)
class BaseType(ABC): class BaseType(ABC):
block_data: Optional[BlockData] = field(default=None, init=False)
def resolve(self) -> "BaseType": def resolve(self) -> "BaseType":
return self return self
def type_type(self) -> "ClassTypeType": def type_type(self) -> "ClassTypeType":
return TY_TYPE.instantiate([self]) return TY_TYPE.instantiate([self.resolve()])
@abstractmethod @abstractmethod
def name(self) -> str: def name(self) -> str:
...@@ -69,12 +78,17 @@ class BaseType(ABC): ...@@ -69,12 +78,17 @@ class BaseType(ABC):
return (needle is haystack) or haystack.contains_internal(needle) return (needle is haystack) or haystack.contains_internal(needle)
def try_assign(self, other: "BaseType") -> bool: def try_assign(self, other: "BaseType") -> bool:
return self.resolve().try_assign_internal(other.resolve())
def try_assign_internal(self, other: "BaseType") -> bool:
try: try:
self.unify(other) self.unify(other)
return True return True
except: except:
return False return False
def deref(self): def deref(self):
return self return self
...@@ -144,6 +158,7 @@ class ResolvedConcreteType(ConcreteType): ...@@ -144,6 +158,7 @@ class ResolvedConcreteType(ConcreteType):
fields: Dict[str, "MemberDef"] = field(default_factory=dict, init=False) fields: Dict[str, "MemberDef"] = field(default_factory=dict, init=False)
parents: list["ResolvedConcreteType"] = field(default_factory=lambda: [TY_OBJECT], init=False) parents: list["ResolvedConcreteType"] = field(default_factory=lambda: [TY_OBJECT], init=False)
is_protocol: bool = field(default=False, init=False)
def get_mro(self): def get_mro(self):
""" """
...@@ -176,8 +191,26 @@ class ResolvedConcreteType(ConcreteType): ...@@ -176,8 +191,26 @@ class ResolvedConcreteType(ConcreteType):
def inherits(self, parent: BaseType): def inherits(self, parent: BaseType):
return self == parent or any(p.inherits(parent) for p in self.parents) return self == parent or any(p.inherits(parent) for p in self.parents)
def can_receive(self, value: BaseType): def try_assign_internal(self, other: BaseType) -> bool:
return self == value if self == other:
return True
if super().try_assign_internal(other):
return True
if self.is_protocol:
if isinstance(other, TypeVariable):
other.unify(self) # ? maybe, we'll see if it works
return True
assert isinstance(other, ResolvedConcreteType)
for name, member in self.fields.items():
corresponding = other.fields.get(name)
if corresponding is None:
#raise ProtocolMismatchError(self, protocol, f"missing method {name}")
return False
return member.type.deref().try_assign(corresponding.type.deref())
return False
class UniqueTypeMixin: class UniqueTypeMixin:
def unify_internal(self, other: "BaseType", mode: UnifyMode): def unify_internal(self, other: "BaseType", mode: UnifyMode):
...@@ -234,7 +267,9 @@ class GenericInstanceType(ResolvedConcreteType): ...@@ -234,7 +267,9 @@ class GenericInstanceType(ResolvedConcreteType):
if not isinstance(other, GenericInstanceType): if not isinstance(other, GenericInstanceType):
raise TypeMismatchError(self, other, TypeMismatchKind.DIFFERENT_TYPE) raise TypeMismatchError(self, other, TypeMismatchKind.DIFFERENT_TYPE)
if self.generic_parent != other.generic_parent: if self.generic_parent != other.generic_parent:
raise TypeMismatchError(self, other, TypeMismatchKind.DIFFERENT_TYPE) if not (isinstance(self, CallableInstanceType) and isinstance(other, CallableInstanceType)):
# methods have different generic parent types but we don't really care
raise TypeMismatchError(self, other, TypeMismatchKind.DIFFERENT_TYPE)
if len(self.generic_args) != len(other.generic_args): if len(self.generic_args) != len(other.generic_args):
raise TypeMismatchError(self, other, TypeMismatchKind.DIFFERENT_TYPE) raise TypeMismatchError(self, other, TypeMismatchKind.DIFFERENT_TYPE)
for a, b in zip(self.generic_args, other.generic_args): for a, b in zip(self.generic_args, other.generic_args):
...@@ -419,6 +454,9 @@ class TypeListType(ConcreteType): ...@@ -419,6 +454,9 @@ class TypeListType(ConcreteType):
class UnionInstanceType(GenericInstanceType): class UnionInstanceType(GenericInstanceType):
types: list[ConcreteType] types: list[ConcreteType]
def try_assign_internal(self, other: BaseType) -> bool:
return super().try_assign_internal(other) or any(t for t in self.types if t.try_assign(other))
class UnionType(UniqueTypeMixin, GenericType): class UnionType(UniqueTypeMixin, GenericType):
def name(self): def name(self):
return "Union" return "Union"
...@@ -440,7 +478,7 @@ TY_OPTIONAL = OptionalType() ...@@ -440,7 +478,7 @@ TY_OPTIONAL = OptionalType()
@typing.runtime_checkable @typing.runtime_checkable
class MethodType(typing.Protocol): class MethodType(typing.Protocol):
def remove_self(self, new_return_type = None) -> ...: def remove_self(self, self_type) -> ...:
raise NotImplementedError() raise NotImplementedError()
@dataclass(eq=False) @dataclass(eq=False)
...@@ -451,19 +489,31 @@ class CallableInstanceType(GenericInstanceType, MethodType): ...@@ -451,19 +489,31 @@ class CallableInstanceType(GenericInstanceType, MethodType):
is_variadic: bool = False is_variadic: bool = False
def __post_init__(self): def __post_init__(self):
if self.optional_at is None: if self.optional_at is None and self.parameters is not None:
self.optional_at = len(self.parameters) self.optional_at = len(self.parameters)
def remove_self(self, new_return_type = None): def remove_self(self, self_type):
res = CallableInstanceType(self.parameters[1:], new_return_type or self.return_type, self.optional_at - 1, self.is_variadic) assert self.parameters[0].try_assign(self_type)
res.generic_parent = self.generic_parent return dataclasses.replace(
res.generic_args = self.generic_args self,
return res parameters=self.parameters[1:],
#return self.generic_parent.instantiate([TypeListType(self.parameters[1:]), new_return_type or self.return_type]) optional_at=self.optional_at - 1,
)
def __str__(self): def __str__(self):
return f"({", ".join(map(str, self.parameters))}{", *args" if self.is_variadic else ""}) -> {self.return_type}" return f"({", ".join(map(str, self.parameters + ([", *args"] if self.is_variadic else [])))}) -> {self.return_type}"
def try_assign_internal(self, other: BaseType) -> bool:
if not isinstance(other, CallableInstanceType):
return False
self.unify(other)
return True
# @dataclass(eq=False)
# class UserFunctionInstance(CallableInstanceType):
# scope: "transpiler.phases.typing.scope.Scope" = None
# node: ast.FunctionDef = None
class CallableType(UniqueTypeMixin, GenericType): class CallableType(UniqueTypeMixin, GenericType):
def name(self): def name(self):
...@@ -494,15 +544,29 @@ class LambdaType(CallableType): ...@@ -494,15 +544,29 @@ class LambdaType(CallableType):
TY_LAMBDA = LambdaType() TY_LAMBDA = LambdaType()
def make_builtin_feature(name: str): class BuiltinFeatureType(BuiltinType):
class CreatedType(BuiltinType): @abstractmethod
def name(self): def feature(self):
return name pass
def feature(self): def __eq__(self, other):
return name return type(self) == type(other)
CreatedType.__name__ = f"BuiltinFeatureType${name}"
return CreatedType() def make_builtin_feature(name: str):
match name:
case "Optional":
return TY_OPTIONAL
case "Union":
return TY_UNION
case _:
class CreatedType(BuiltinFeatureType):
def name(self):
return name
def feature(self):
return name
CreatedType.__name__ = f"BuiltinFeatureType${name}"
return CreatedType()
def make_cpp_type(name: str): def make_cpp_type(name: str):
class CreatedType(BuiltinType): class CreatedType(BuiltinType):
...@@ -532,4 +596,3 @@ class ClassType(UniqueTypeMixin, GenericType): ...@@ -532,4 +596,3 @@ class ClassType(UniqueTypeMixin, GenericType):
return ClassTypeType(*args) return ClassTypeType(*args)
TY_TYPE = ClassType() TY_TYPE = ClassType()
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