Commit 4dda1cb4 authored by Tom Niget's avatar Tom Niget

Python ext works

parent fe2492d6
...@@ -318,7 +318,7 @@ struct ValueTypeEx { ...@@ -318,7 +318,7 @@ struct ValueTypeEx {
// has ::iterator type // has ::iterator type
template <typename T> struct ValueType { template <typename T> struct ValueType {
using type = typename T::iterator::value_type; using type = decltype(*std::declval<T>().begin());
}; };
template <typename T> template <typename T>
......
...@@ -180,8 +180,10 @@ struct TyDict__oo : classtype<_Base0, TyDict__oo<>> { ...@@ -180,8 +180,10 @@ struct TyDict__oo : classtype<_Base0, TyDict__oo<>> {
Obj(std::unordered_map<K, V> &&m) Obj(std::unordered_map<K, V> &&m)
: _m(std::move( : _m(std::move(
std::make_shared<std::unordered_map<K, V>>(std::move(m)))) {}*/ std::make_shared<std::unordered_map<K, V>>(std::move(m)))) {}*/
Obj(std::initializer_list<typename map_type::value_type> &&m) /*Obj(std::initializer_list<typename map_type::value_type> &&m)
: _m(std::make_shared<map_type>(std::move(m))) {} : _m(std::make_shared<map_type>(std::move(m))) {}*/
Obj(std::initializer_list<std::pair<K, V>> && init)
: _m(std::make_shared<std::unordered_map<K, V>>(init.begin(), init.end())) {}
Obj() : _m(std::make_shared<map_type>()) {} Obj() : _m(std::make_shared<map_type>()) {}
template <typename... Args> template <typename... Args>
......
...@@ -24,9 +24,17 @@ struct TyList__oo : classtype<_Base0, TyList__oo<>> { ...@@ -24,9 +24,17 @@ struct TyList__oo : classtype<_Base0, TyList__oo<>> {
auto operator()(auto self, auto other) const { auto operator()(auto self, auto other) const {
self->_v.reserve(self->_v.size() + other.size()); self->_v.reserve(self->_v.size() + other.size());
self->_v.insert(self->_v.end(), other.begin(), other.end()); self->_v.insert(self->_v.end(), other.begin(), other.end());
return None;
} }
} static constexpr extend{}; } static constexpr extend{};
// append
struct : method {
auto operator()(auto self, auto value) const { self->_v->push_back(value);
return None;
}
} static constexpr append{};
struct : method { struct : method {
auto operator()(auto self, auto other) const { auto operator()(auto self, auto other) const {
auto result = TyList__oo<>{}(self->_v); auto result = TyList__oo<>{}(self->_v);
......
#ifndef TYPON_DATACLASSES_HPP
#define TYPON_DATACLASSES_HPP
#include "builtins.hpp"
namespace py_dataclasses {
template <typename _Unused = void>
struct dataclasses__oo : referencemodel::moduletype<dataclasses__oo<>> {
};
dataclasses__oo<> all;
} // namespace py_dataclasses
#endif //TYPON_DATACLASSES_HPP
\ No newline at end of file
#ifndef TYPON_TYPON_HPP
#define TYPON_TYPON_HPP
#include "builtins.hpp"
namespace py_typon {
template <typename _Unused = void>
struct typon__oo : referencemodel::moduletype<typon__oo<>> {
};
typon__oo<> all;
} // namespace py_typon
#endif //TYPON_TYPON_HPP
\ No newline at end of file
def is_cpp() -> bool: def is_cpp() -> bool:
return False return False
export: BuiltinFeature["PybindExport"]
\ No newline at end of file
...@@ -90,7 +90,7 @@ def run_test(path, quiet=True): ...@@ -90,7 +90,7 @@ def run_test(path, quiet=True):
if args.compile: if args.compile:
return TestStatus.SUCCESS return TestStatus.SUCCESS
execute_str = "true" if (execute and not args.generate) else "false" execute_str = "true" if (execute and not args.generate) else "false"
name_bin = path.with_suffix("").as_posix() + ("$(python3.12-config --extension-suffix)" if extension else ".exe") name_bin = path.with_suffix("").as_posix() + ("\\$(python3.12-config --extension-suffix)" if extension else ".exe")
if exec_cmd(f'bash -c "export PYTHONPATH=stdlib; if {execute_str}; then echo python3.12 ./{path.as_posix()}; fi"') != 0: if exec_cmd(f'bash -c "export PYTHONPATH=stdlib; if {execute_str}; then echo python3.12 ./{path.as_posix()}; fi"') != 0:
return TestStatus.PYTHON_ERROR return TestStatus.PYTHON_ERROR
if compile and (alt := environ.get("ALT_RUNNER")): if compile and (alt := environ.get("ALT_RUNNER")):
......
import pyext import pyext
p = pyext.Person("jean", 123) # p = pyext.Person("jean", 123)
print("Imported:", pyext.add(5, 3), pyext.fibo(10), pyext.squares(), p) print("Imported:", pyext.add(5, 3), pyext.fibo(10), pyext.squares())
print(p.afficher("Bonjour")) # print(p.afficher("Bonjour"))
print(p.name, p.age) # print(p.name, p.age)
\ No newline at end of file \ No newline at end of file
# coding: utf-8 # coding: utf-8
# extension # extension
from typon import export
import numpy as np import numpy as np
from dataclasses import dataclass # from dataclasses import dataclass
@dataclass # @dataclass
class Person: # class Person:
name: str # name: str
age: int # age: int
#
def afficher(self, msg: str): # def afficher(self, msg: str):
print(msg, ",", self.name, self.age) # print(msg, ",", self.name, self.age)
return 123 # return 123
@export([int, int])
def add(x, y): def add(x, y):
return x + y return x + y
@export([int])
def fibo(n): def fibo(n):
res = [0, 1] res = [0, 1]
for i in range(2, n): for i in range(2, n):
res.append(res[i - 1] + res[i - 2]) res.append(res[i - 1] + res[i - 2])
return res return res
@export([])
def squares() -> list[int]: def squares() -> list[int]:
return np.square([x for x in range(5)]) return np.square([x for x in range(5)])
if __name__ == "__main__": if __name__ == "__main__":
p = Person("jean", 123) # p = Person("jean", 123)
print("Python:", add(5, 3), fibo(10), squares(), p) print("Python:", add(5, 3), fibo(10), squares())
p.afficher("Bonjour") # p.afficher("Bonjour")
\ No newline at end of file \ No newline at end of file
...@@ -31,7 +31,6 @@ def handle_connection(connfd): ...@@ -31,7 +31,6 @@ def handle_connection(connfd):
else: else:
http_pos = buf.find("HTTP/1.1\r\n") http_pos = buf.find("HTTP/1.1\r\n")
s = "str(" + buf[12:http_pos-1] + ")" s = "str(" + buf[12:http_pos-1] + ")"
#resp = eval(s, {"req": buf})
context = {"req": buf} context = {"req": buf}
resp = eval(s, context) resp = eval(s, context)
response = response_fmt.format(len(resp), resp) response = response_fmt.format(len(resp), resp)
......
...@@ -7,7 +7,7 @@ from transpiler.phases.emit_cpp.function import emit_function, BlockVisitor ...@@ -7,7 +7,7 @@ from transpiler.phases.emit_cpp.function import emit_function, BlockVisitor
from transpiler.phases.emit_cpp.visitors import NodeVisitor, CoroutineMode from transpiler.phases.emit_cpp.visitors import NodeVisitor, CoroutineMode
from transpiler.phases.typing.modules import ModuleType, TyponModuleType, PythonModuleType from transpiler.phases.typing.modules import ModuleType, TyponModuleType, PythonModuleType
from transpiler.phases.typing.types import CallableInstanceType, ClassTypeType, TypeVariable, BaseType, GenericType, \ from transpiler.phases.typing.types import CallableInstanceType, ClassTypeType, TypeVariable, BaseType, GenericType, \
GenericInstanceType, UserGenericType, RuntimeValue GenericInstanceType, UserGenericType, RuntimeValue, BuiltinFeatureType
from transpiler.utils import linenodata from transpiler.utils import linenodata
...@@ -83,8 +83,8 @@ def emit_module(mod: ModuleType) -> Iterable[str]: ...@@ -83,8 +83,8 @@ def emit_module(mod: ModuleType) -> Iterable[str]:
yield from emit(node.module_obj) yield from emit(node.module_obj)
prefix = "python_" if isinstance(node.module_obj, PythonModuleType) else "" prefix = "python_" if isinstance(node.module_obj, PythonModuleType) else ""
for alias in names: for alias in names:
if isinstance(node.module_obj, PythonModuleType): # if isinstance(node.module_obj, PythonModuleType):
if isinstance(node.module_obj.fields[alias.name].type.resolve(), TypeVariable): if isinstance(node.module_obj.fields[alias.name].type.resolve(), (TypeVariable, BuiltinFeatureType)):
continue # unused function continue # unused function
incl_vars.append(f"auto& {alias.asname or alias.name} = py_{prefix}{node.module_obj.name()}::all.{alias.name};") incl_vars.append(f"auto& {alias.asname or alias.name} = py_{prefix}{node.module_obj.name()}::all.{alias.name};")
yield "namespace PROGRAMNS {" yield "namespace PROGRAMNS {"
......
import ast import ast
import copy import copy
import dataclasses
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 logging import debug from logging import debug
from pathlib import Path
from typing import Optional, Callable
from transpiler.phases.typing.modules import parse_module
from transpiler.utils import highlight, linenodata
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
from transpiler.phases.typing.expr import ScoperExprVisitor from transpiler.phases.typing.expr import ScoperExprVisitor
from transpiler.phases.typing.modules import parse_module
from transpiler.phases.typing.scope import Scope, VarDecl, VarKind, ScopeKind from transpiler.phases.typing.scope import Scope, VarDecl, VarKind, ScopeKind
from transpiler.phases.typing.types import BaseType, BuiltinGenericType, BuiltinType, create_builtin_generic_type, \ from transpiler.phases.typing.types import BaseType, BuiltinGenericType, BuiltinType, create_builtin_generic_type, \
create_builtin_type, ConcreteType, GenericInstanceType, TypeListType, TypeTupleType, GenericParameter, \ create_builtin_type, ConcreteType, GenericInstanceType, TypeListType, TypeTupleType, GenericParameter, \
GenericParameterKind, TypeVariable, ResolvedConcreteType, MemberDef, ClassTypeType, CallableInstanceType, \ GenericParameterKind, TypeVariable, ResolvedConcreteType, MemberDef, ClassTypeType, CallableInstanceType, \
MethodType, UniqueTypeMixin, GenericType, BlockData, TY_TASK, UserGenericType, UserType, BoundFuncTypeBase MethodType, GenericType, BlockData, TY_TASK, UserGenericType, UserType, BoundFuncTypeBase
from transpiler.phases.utils import NodeVisitorSeq from transpiler.phases.utils import NodeVisitorSeq
from transpiler.utils import highlight, linenodata
def visit_generic_item( def visit_generic_item(
visit_nongeneric: Callable[[Scope, ResolvedConcreteType], None], visit_nongeneric: Callable[[Scope, ResolvedConcreteType], None],
node, node,
output_type: BuiltinGenericType, output_type: BuiltinGenericType,
scope: Scope, scope: Scope,
instance_type = None, instance_type=None,
force_generic = False): force_generic=False):
if force_generic or node.type_params: if force_generic or node.type_params:
output_type.parameters = [] output_type.parameters = []
for param in node.type_params: for param in node.type_params:
...@@ -41,6 +40,7 @@ def visit_generic_item( ...@@ -41,6 +40,7 @@ def visit_generic_item(
if instance_type is None: if instance_type is None:
class instance_type(GenericInstanceType): class instance_type(GenericInstanceType):
pass pass
instance_type.__name__ = f"GenericInstance${node.name}" instance_type.__name__ = f"GenericInstance${node.name}"
def instantiate(args: list[ConcreteType]) -> GenericInstanceType: def instantiate(args: list[ConcreteType]) -> GenericInstanceType:
...@@ -64,12 +64,13 @@ def visit_generic_item( ...@@ -64,12 +64,13 @@ def visit_generic_item(
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:
assert b.try_assign(a) assert b.try_assign(a)
# todo #  todo
new_output_type = instance_type() new_output_type = instance_type()
new_output_type.generic_parent = output_type new_output_type.generic_parent = output_type
new_output_type.generic_args = args new_output_type.generic_args = args
visit_nongeneric(new_scope, new_output_type) visit_nongeneric(new_scope, new_output_type)
return new_output_type return new_output_type
output_type.constraints_ = [] output_type.constraints_ = []
output_type.instantiate_ = instantiate output_type.instantiate_ = instantiate
else: else:
...@@ -112,7 +113,7 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -112,7 +113,7 @@ class StdlibVisitor(NodeVisitorSeq):
ty = self.anno().visit(node.annotation) ty = self.anno().visit(node.annotation)
if self.cur_class: if self.cur_class:
assert isinstance(self.cur_class, ResolvedConcreteType) assert isinstance(self.cur_class, ResolvedConcreteType)
self.cur_class.fields[node.target.id] = MemberDef(ty) self.cur_class.fields[node.target.id] = MemberDef(ty, in_class_def=True, from_node=node)
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):
...@@ -139,7 +140,8 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -139,7 +140,8 @@ class StdlibVisitor(NodeVisitorSeq):
NewType = existing.type.inner_type NewType = existing.type.inner_type
else: else:
if node.type_params or force_generic: if node.type_params or force_generic:
base_class, base_type = create_builtin_generic_type, (BuiltinGenericType if self.is_native else UserGenericType) base_class, base_type = create_builtin_generic_type, (
BuiltinGenericType if self.is_native else UserGenericType)
else: else:
base_class, base_type = create_builtin_type, (BuiltinType if self.is_native else UserType) base_class, base_type = create_builtin_type, (BuiltinType if self.is_native else UserType)
NewType = base_class(node.name, NewType = base_class(node.name,
...@@ -163,6 +165,36 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -163,6 +165,36 @@ class StdlibVisitor(NodeVisitorSeq):
raise NotImplementedError("parents not handled yet: " + ", ".join(map(ast.unparse, node.bases))) 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)
for deco_node in node.decorator_list:
deco = self.expr().visit(deco_node)
match deco:
case dc if is_builtin(dc, "dataclass"):
real_fields = {k: m for k, m in output.fields.items() if
not isinstance(m.from_node, ast.FunctionDef)}
generated_init = ast.FunctionDef(
name="__init__",
args=ast.arguments(
posonlyargs=[],
args=[ast.arg(arg="self", annotation=None)] +
[ast.arg(arg=k, annotation=None) for k, m in real_fields.items()],
vararg=None, kwonlyargs=[],
kw_defaults=[], kwarg=None,
defaults=[]), body=[
ast.Assign(
targets=[
ast.Attribute(value=ast.Name("self", ast.Load()), attr=k, ctx=ast.Store())
],
value=ast.Name(k, ast.Load()),
type_comment=None,
**linenodata(node)
) for k in real_fields],
decorator_list=[],
returns=None,
type_params=[],
**linenodata(node))
visitor.visit(generated_init)
case _:
raise NotImplementedError(f"Decorator {deco} not handled yet")
if "__init__" not in output.fields: if "__init__" not in output.fields:
visitor.visit(ast.FunctionDef( visitor.visit(ast.FunctionDef(
name="__init__", name="__init__",
...@@ -230,7 +262,7 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -230,7 +262,7 @@ class StdlibVisitor(NodeVisitorSeq):
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
...@@ -252,6 +284,7 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -252,6 +284,7 @@ class StdlibVisitor(NodeVisitorSeq):
cur_class_ref = self.cur_class cur_class_ref = self.cur_class
if cur_class_ref is not None: if cur_class_ref is not None:
bases.append(MethodType) bases.append(MethodType)
class FuncType(*bases): class FuncType(*bases):
def name(self): def name(self):
return f"FuncTypeGen${node.name}" return f"FuncTypeGen${node.name}"
...@@ -278,9 +311,26 @@ class StdlibVisitor(NodeVisitorSeq): ...@@ -278,9 +311,26 @@ class StdlibVisitor(NodeVisitorSeq):
NewType = base_class() NewType = base_class()
FuncType.__name__ = NewType.name() FuncType.__name__ = NewType.name()
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, NewType, is_item_decl=True) for deco_node in copy.deepcopy(node.decorator_list):
if isinstance(deco_node, ast.Call):
deco_args = deco_node.args
deco_node = deco_node.func
else:
deco_args = []
deco = self.expr().visit(deco_node)
match deco:
case dc if is_builtin(dc, "PybindExport"):
assert len(deco_args) == 1
export = deco_args[0]
assert isinstance(export, ast.List)
exports = [self.anno().visit(e) for e in export.elts]
NewType.pybind_exports = exports
case _:
raise NotImplementedError(f"Decorator {deco} not handled yet")
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, NewType, is_item_decl=True, from_node=node)
if self.cur_class is not None: if self.cur_class is not None:
self.cur_class.fields[node.name] = MemberDef(NewType, node, in_class_def=True) self.cur_class.fields[node.name] = MemberDef(NewType, node, in_class_def=True, from_node=node)
visit_generic_item(visit_nongeneric, node, NewType, self.scope, InstanceType, True) visit_generic_item(visit_nongeneric, node, NewType, self.scope, InstanceType, True)
......
...@@ -13,6 +13,7 @@ from transpiler.phases.desugar_op import DesugarOp ...@@ -13,6 +13,7 @@ from transpiler.phases.desugar_op import DesugarOp
from transpiler.phases.desugar_subscript import DesugarSubscript from transpiler.phases.desugar_subscript import DesugarSubscript
from transpiler.phases.desugar_with import DesugarWith from transpiler.phases.desugar_with import DesugarWith
from transpiler.phases.emit_cpp.module import emit_module from transpiler.phases.emit_cpp.module import emit_module
from transpiler.phases.emit_cpp.visitors import NodeVisitor
from transpiler.phases.if_main import IfMainVisitor from transpiler.phases.if_main import IfMainVisitor
from transpiler.phases.typing import PRELUDE from transpiler.phases.typing import PRELUDE
from transpiler.phases.typing.modules import parse_module from transpiler.phases.typing.modules import parse_module
...@@ -54,12 +55,33 @@ def transpile(source, name: str, path: Path): ...@@ -54,12 +55,33 @@ def transpile(source, name: str, path: Path):
def main_module(): def main_module():
yield from emit_module(module) yield from emit_module(module)
yield "#ifdef TYPON_EXTENSION" yield "#ifdef TYPON_EXTENSION"
# yield f"PYBIND11_MODULE({self.module_name}, m) {{" yield f"PYBIND11_MODULE({module.name()}, m) {{"
# yield f"m.doc() = \"Typon extension module '{self.module_name}'\";" yield f"m.doc() = \"Typon extension module '{module.name()}'\";"
for n, f in module.fields.items():
if not f.in_class_def:
continue
node = f.from_node
if getattr(node, "is_main", False):
continue
if isinstance(node, ast.FunctionDef):
if (exports := getattr(f.type, "pybind_exports", None)) is not None:
yield f'm.def("{n}", []('
for i, ty in enumerate(exports):
if i != 0:
yield ","
yield from NodeVisitor().visit_BaseType(ty)
yield f"arg{i}"
yield ") {"
yield f"return PROGRAMNS::{module.name()}.{node.name}("
for i, _ in enumerate(exports):
if i != 0:
yield ","
yield f"arg{i}"
yield ").call(); });"
# visitor = ModuleVisitorExt(self.scope) # visitor = ModuleVisitorExt(self.scope)
# code = [line for stmt in node.body for line in visitor.visit(stmt)] # code = [line for stmt in node.body for line in visitor.visit(stmt)]
# yield from code # yield from code
# yield "}" yield "}"
yield "#else" yield "#else"
yield "typon::Root root() {" yield "typon::Root root() {"
yield f"co_await dot(PROGRAMNS::{module.name()}, main)();" yield f"co_await dot(PROGRAMNS::{module.name()}, main)();"
......
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