1
0
mirror of https://github.com/exaloop/codon.git synced 2025-06-03 15:03:52 +08:00

Add static reflection methods (setattr; internal.static.*); refactor PyExt to python.codon; handle errors and kwargs in PyExt

This commit is contained in:
Ibrahim Numanagić 2023-03-02 21:59:42 -08:00
parent d0461d572f
commit 12d21ff5eb
15 changed files with 611 additions and 386 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@
build/
build_*/
install/
install_*/
extra/python/src/jit.cpp
extra/jupyter/build/

View File

@ -243,198 +243,6 @@ void Cache::populatePythonModule() {
LOG("[py] ====== module generation =======");
#define N std::make_shared
auto sctx = imports[MAIN_IMPORT].ctx;
auto getFn = [&](const std::string &canonicalName,
const std::string &className = "") -> ir::Func * {
auto fna = in(functions, canonicalName) ? functions[canonicalName].ast : nullptr;
std::vector<Param> params;
std::vector<ExprPtr> args;
bool isMethod = className.empty() ? false : fna->hasAttr(Attr::Method);
auto name =
fmt::format("{}{}", className.empty() ? "" : className + ".", canonicalName);
auto wrapArg = [&](ExprPtr po, int ai) {
if (fna && fna->args[ai].type) {
return N<CallExpr>(N<DotExpr>(fna->args[ai].type->clone(), "__from_py__"), po);
} else {
return N<CallExpr>(N<IdExpr>("pyobj"), po);
}
};
StmtPtr ret = nullptr;
ExprPtr retType = N<IdExpr>("cobj");
const int *p = nullptr;
LOG("[py] {}: {} => {} ({})", isMethod ? "method" : "classm", name, isMethod,
rev(canonicalName));
if (isMethod && in(std::set<std::string>{"__abs__", "__pos__", "__neg__",
"__invert__", "__int__", "__float__",
"__index__", "__repr__", "__str__"},
rev(canonicalName))) {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
ret = N<ReturnStmt>(N<CallExpr>(N<DotExpr>(
N<CallExpr>(N<IdExpr>(canonicalName),
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)}),
"__to_py__")));
} else if (isMethod &&
in(std::set<std::string>{"__len__", "__hash__"}, rev(canonicalName))) {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
ret = N<ReturnStmt>(
N<CallExpr>(N<IdExpr>(canonicalName),
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)}));
retType = N<IdExpr>("i64");
} else if (isMethod && rev(canonicalName) == "__bool__") {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
ret = N<ReturnStmt>(N<CallExpr>(
N<IdExpr>("i32"),
N<CallExpr>(N<IdExpr>(canonicalName),
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)})));
retType = N<IdExpr>("i32");
} else if (isMethod && rev(canonicalName) == "__del__") {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
ret = N<ExprStmt>(N<CallExpr>(
N<IdExpr>("i32"),
N<CallExpr>(N<IdExpr>(canonicalName),
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)})));
retType = N<IdExpr>("i32");
} else if (isMethod && rev(canonicalName) == "__contains__") {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")}};
ret = N<ReturnStmt>(N<CallExpr>(
N<IdExpr>("i32"),
N<CallExpr>(N<IdExpr>(canonicalName),
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0),
wrapArg(N<IdExpr>(params[1].name), 1)})));
retType = N<IdExpr>("i32");
} else if (isMethod && rev(canonicalName) == "__init__") {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("kwargs"), N<IdExpr>("cobj")}};
std::vector<ExprPtr> tupTypes;
for (size_t ai = 1; ai < fna->args.size(); ai++)
tupTypes.push_back(fna->args[ai].type ? fna->args[ai].type->clone()
: N<IdExpr>("pyobj"));
ExprPtr tup = N<InstantiateExpr>(
N<IdExpr>(fmt::format("Tuple.N{}", tupTypes.size())), tupTypes);
tup = N<CallExpr>(N<DotExpr>(tup, "__from_py__"), N<IdExpr>(params[1].name));
ret = N<SuiteStmt>(N<ExprStmt>(N<CallExpr>(N<IdExpr>(canonicalName),
wrapArg(N<IdExpr>(params[0].name), 0),
N<StarExpr>(tup))),
N<ReturnStmt>(N<CallExpr>(N<IdExpr>("i32"), N<IntExpr>(0))));
retType = N<IdExpr>("i32");
} else if (isMethod && rev(canonicalName) == "__call__") {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("kwargs"), N<IdExpr>("cobj")}};
std::vector<ExprPtr> tupTypes;
for (size_t ai = 1; ai < fna->args.size(); ai++)
tupTypes.push_back(fna->args[ai].type ? fna->args[ai].type->clone()
: N<IdExpr>("pyobj"));
ExprPtr tup = N<InstantiateExpr>(
N<IdExpr>(fmt::format("Tuple.N{}", tupTypes.size())), tupTypes);
tup = N<CallExpr>(N<DotExpr>(tup, "__from_py__"), N<IdExpr>(params[1].name));
ret = N<ReturnStmt>(N<CallExpr>(N<DotExpr>(
N<CallExpr>(N<IdExpr>(canonicalName), wrapArg(N<IdExpr>(params[0].name), 0),
N<StarExpr>(tup)),
"__to_py__")));
} else if (isMethod && in(std::set<std::string>{"__lt__", "__le__", "__eq__",
"__ne__", "__gt__", "__ge__"},
rev(canonicalName))) {
params = std::vector<Param>{
Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("other"), N<IdExpr>("cobj")}};
ret = N<ReturnStmt>(N<CallExpr>(N<DotExpr>(
N<CallExpr>(N<IdExpr>(canonicalName),
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0),
wrapArg(N<IdExpr>(params[1].name), 1)}),
"__to_py__")));
} else if (isMethod && rev(canonicalName) == "__setitem__") {
// TODO: return -1 if __delitem__ does not exist? right now it assumes it...
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("index"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("value"), N<IdExpr>("cobj")}};
retType = N<IdExpr>("i32");
ret = N<SuiteStmt>(std::vector<StmtPtr>{
N<IfStmt>(
N<BinaryExpr>(N<CallExpr>(N<IdExpr>("hasattr"), N<IdExpr>(className),
N<StringExpr>("__delitem__")),
"&&",
N<BinaryExpr>(N<IdExpr>(params[2].name),
"==", N<CallExpr>(N<IdExpr>("cobj")))),
N<ExprStmt>(N<CallExpr>(N<DotExpr>(N<IdExpr>(className), "__delitem__"),
wrapArg(N<IdExpr>(params[0].name), 0),
wrapArg(N<IdExpr>(params[1].name), 1))),
N<ExprStmt>(N<CallExpr>(N<IdExpr>(canonicalName),
wrapArg(N<IdExpr>(params[0].name), 0),
wrapArg(N<IdExpr>(params[1].name), 1),
wrapArg(N<IdExpr>(params[2].name), 2)))),
N<ReturnStmt>(N<CallExpr>(N<IdExpr>("i32"), N<IntExpr>(0)))});
} else if (isMethod && rev(canonicalName) == "__iter__") {
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
ret = N<ReturnStmt>(
N<CallExpr>(N<IdExpr>("std.internal.python._PyextIterWrap._init:0"),
N<IdExpr>(params[0].name), N<IdExpr>(className)));
} else {
// def wrapper(self: cobj, arg: cobj) -> cobj
// def wrapper(self: cobj, args: Ptr[cobj], nargs: int) -> cobj
// iter, iternext: todo
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")}};
if (fna->args.size() > 1 + isMethod) {
params.back().type = N<InstantiateExpr>(N<IdExpr>("Ptr"), params.back().type);
params.push_back(Param{sctx->generateCanonicalName("nargs"), N<IdExpr>("int")});
}
ExprPtr po = N<IdExpr>(params[0].name);
if (!className.empty())
po = N<CallExpr>(N<DotExpr>(N<IdExpr>(className), "__from_py__"), po);
if (isMethod)
args.push_back(po);
if (fna->args.size() > 1 + isMethod) {
for (size_t ai = isMethod; ai < fna->args.size(); ai++) {
ExprPtr po =
N<IndexExpr>(N<IdExpr>(params[1].name), N<IntExpr>(ai - isMethod));
if (fna->args[ai].type) {
po =
N<CallExpr>(N<DotExpr>(fna->args[ai].type->clone(), "__from_py__"), po);
} else {
po = N<CallExpr>(N<IdExpr>("pyobj"), po);
}
args.push_back(po);
}
} else if (fna->args.size() == 1 + isMethod) {
ExprPtr po = N<IdExpr>(params[1].name);
if (fna->args[isMethod].type) {
po = N<CallExpr>(N<DotExpr>(fna->args[isMethod].type->clone(), "__from_py__"),
po);
} else {
po = N<CallExpr>(N<IdExpr>("pyobj"), po);
}
args.push_back(po);
}
ret = N<ReturnStmt>(N<CallExpr>(
N<DotExpr>(N<CallExpr>(N<IdExpr>(canonicalName), args), "__to_py__")));
}
auto stubName = sctx->generateCanonicalName(fmt::format("_py.{}", name));
auto node = N<FunctionStmt>(stubName, retType, params, N<SuiteStmt>(ret),
Attr({Attr::ForceRealize}));
functions[node->name].ast = node;
auto tv = TypecheckVisitor(typeCtx);
auto tnode = tv.transform(node);
// LOG("=> {}", tnode->toString(2));
seqassertn(tnode, "blah");
seqassertn(typeCtx->forceFind(stubName) && typeCtx->forceFind(stubName)->type,
"bad type");
auto rtv = tv.realize(typeCtx->forceFind(stubName)->type);
seqassertn(rtv, "realization of {} failed", stubName);
auto pr = pendingRealizations; // copy it as it might be modified
for (auto &fn : pr)
TranslateVisitor(codegenCtx).transform(functions[fn.first].ast->clone());
auto f = functions[rtv->getFunc()->ast->name].realizations[rtv->realizedName()]->ir;
classes[className].methods[node->name] = node->name; // to allow getattr()
overloads[node->name] = std::vector<Overload>{{node->name, 0}};
return f;
};
if (!pyModule)
pyModule = std::make_shared<ir::PyModule>();
@ -443,6 +251,23 @@ void Cache::populatePythonModule() {
int oldAge = typeCtx->age;
typeCtx->age = 99999;
auto realizeIR = [&](const types::FuncTypePtr &fn,
const std::vector<types::TypePtr> &generics = {}) -> ir::Func * {
auto fnType = typeCtx->instantiate(fn);
types::Type::Unification u;
for (size_t i = 0; i < generics.size(); i++)
fnType->getFunc()->funcGenerics[i].type->unify(generics[i].get(), &u);
fnType = TypecheckVisitor(typeCtx).realize(fnType);
if (!fnType)
return nullptr;
auto pr = pendingRealizations; // copy it as it might be modified
for (auto &fn : pr)
TranslateVisitor(codegenCtx).transform(functions[fn.first].ast->clone());
return functions[fn->ast->name].realizations[fnType->realizedName()]->ir;
};
const std::string pyWrap = "std.internal.python._PyWrap";
for (const auto &[cn, c] : classes)
if (c.module.empty() && startswith(cn, "Pyx")) {
ir::PyType py{rev(cn), c.ast->getDocstr()};
@ -453,12 +278,12 @@ void Cache::populatePythonModule() {
tc = TypecheckVisitor(typeCtx).realize(tc);
seqassertn(tc, "cannot realize '{}'", cn);
// fix to_py / from_py
// 1. Replace to_py / from_py with _PyWrap.wrap_to_py/from_py
if (auto ofnn = in(c.methods, "__to_py__")) {
auto fnn = overloads[*ofnn].begin()->name; // default first overload!
auto &fna = functions[fnn].ast;
fna->getFunction()->suite = N<ReturnStmt>(N<CallExpr>(
N<IdExpr>("__internal__.to_py:0"), N<IdExpr>(fna->args[0].name)));
N<IdExpr>(pyWrap + ".wrap_to_py:0"), N<IdExpr>(fna->args[0].name)));
} else {
compilationError(fmt::format("class '{}' has no __to_py__"), rev(cn));
}
@ -466,7 +291,7 @@ void Cache::populatePythonModule() {
auto fnn = overloads[*ofnn].begin()->name; // default first overload!
auto &fna = functions[fnn].ast;
fna->getFunction()->suite =
N<ReturnStmt>(N<CallExpr>(N<IdExpr>("__internal__.from_py:0"),
N<ReturnStmt>(N<CallExpr>(N<IdExpr>(pyWrap + ".wrap_from_py:0"),
N<IdExpr>(fna->args[0].name), N<IdExpr>(cn)));
} else {
compilationError(fmt::format("class '{}' has no __from_py__"), rev(cn));
@ -488,23 +313,46 @@ void Cache::populatePythonModule() {
ir::util::call(functions[fnn].realizations.begin()->second->ir, args)));
}
}
for (auto &[rn, r] : functions["__internal__.py_type:0"].realizations) {
for (auto &[rn, r] : functions[pyWrap + ".py_type:0"].realizations) {
if (r->type->funcGenerics[0].type->unify(tc.get(), nullptr) >= 0) {
py.typePtrHook = r->ir;
break;
}
}
// 2. Handle methods
auto methods = c.methods;
for (const auto &[n, ofnn] : methods) {
auto fnn = overloads[ofnn].back().name; // last overload
auto &fna = functions[fnn].ast;
if (fna->hasAttr("autogenerated"))
auto canonicalName = overloads[ofnn].back().name;
if (overloads[ofnn].size() == 1 &&
functions[canonicalName].ast->hasAttr("autogenerated"))
continue;
auto f = getFn(fnn, cn);
auto fna = functions[canonicalName].ast;
bool isMethod = fna->hasAttr(Attr::Method);
std::string call = pyWrap + ".wrap_single";
if (fna->args.size() - isMethod > 1)
call = pyWrap + ".wrap_multiple";
bool isMagic = false;
if (startswith(n, "__") && endswith(n, "__")) {
if (auto i = in(classes[pyWrap].methods,
"wrap_magic_" + n.substr(2, n.size() - 4))) {
call = *i;
isMagic = true;
}
}
auto fnName = call + ":0";
seqassertn(in(functions, fnName), "bad name");
auto generics = std::vector<types::TypePtr>{tc};
if (!isMagic) {
generics.push_back(std::make_shared<types::StaticType>(this, n));
generics.push_back(std::make_shared<types::StaticType>(this, isMethod));
}
auto f = realizeIR(functions[fnName].type, generics);
if (!f)
continue;
if (n == "__repr__") {
py.repr = f;
} else if (n == "__add__") {
@ -610,13 +458,8 @@ void Cache::populatePythonModule() {
if (in(std::set<std::string>{"__lt__", "__le__", "__eq__", "__ne__", "__gt__",
"__ge__"},
m.name)) {
auto f = realizeFunction(
typeCtx->forceFind("__internal__.cmp_py:0")->type->getFunc(),
{typeCtx->forceFind("cobj")->type, typeCtx->forceFind("cobj")->type,
typeCtx->forceFind("cobj")->type, typeCtx->forceFind("i32")->type},
{tc,
std::make_shared<types::StaticType>(this, m.func->getUnmangledName())});
py.cmp = f;
py.cmp = realizeIR(
typeCtx->forceFind(pyWrap + ".wrap_cmp:0")->type->getFunc(), {tc});
break;
}
}
@ -626,75 +469,25 @@ void Cache::populatePythonModule() {
auto &r = c.realizations.begin()->second;
py.type = realizeType(r->type);
for (auto &[mn, mt] : r->fields) {
// This will be handled later
// py.members.push_back(ir::PyMember{mn, "",
// mt->is("int") ?
// ir::PyMember::Type::LONGLONG :
// mt->is("float")
// ? ir::PyMember::Type::DOUBLE
// : ir::PyMember::Type::OBJECT,
// true});
/// TODO: handle PyMember for tuples
// Generate getters & setters
std::vector<Param> params{
Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("closure"), N<IdExpr>("cobj")}};
ExprPtr retType = N<IdExpr>("cobj");
StmtPtr ret = N<ReturnStmt>(N<CallExpr>(
N<DotExpr>(N<DotExpr>(N<CallExpr>(N<DotExpr>(N<IdExpr>(cn), "__from_py__"),
N<IdExpr>(params[0].name)),
mn),
"__to_py__")));
auto gstub = sctx->generateCanonicalName(fmt::format("_py._get_{}", mn));
auto gnode = N<FunctionStmt>(gstub, retType, params, N<SuiteStmt>(ret),
Attr({Attr::ForceRealize}));
functions[gstub].ast = gnode;
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("what"), N<IdExpr>("cobj")},
Param{sctx->generateCanonicalName("closure"), N<IdExpr>("cobj")}};
retType = N<IdExpr>("i32");
ret = N<SuiteStmt>(
N<AssignMemberStmt>(
N<CallExpr>(N<DotExpr>(N<IdExpr>(cn), "__from_py__"),
N<IdExpr>(params[0].name)),
mn,
N<CallExpr>(N<DotExpr>(N<IdExpr>(mt->realizedName()), "__from_py__"),
N<IdExpr>(params[1].name))),
N<ReturnStmt>(N<CallExpr>(N<IdExpr>("i32"), N<IntExpr>(0))));
auto sstub = sctx->generateCanonicalName(fmt::format("_py._set_{}", mn));
auto snode = N<FunctionStmt>(sstub, retType, params, N<SuiteStmt>(ret),
Attr({Attr::ForceRealize}));
functions[sstub].ast = snode;
auto tv = TypecheckVisitor(typeCtx);
auto tnode = tv.transform(N<SuiteStmt>(gnode, snode));
seqassertn(tnode, "blah");
for (auto &stub : std::vector<std::string>{gstub, sstub}) {
seqassertn(typeCtx->forceFind(stub) && typeCtx->forceFind(stub)->type,
"bad type");
auto rtv = tv.realize(typeCtx->forceFind(stub)->type);
seqassertn(rtv, "realization of {} failed", stub);
}
auto pr = pendingRealizations; // copy it as it might be modified
for (auto &fn : pr)
TranslateVisitor(codegenCtx).transform(functions[fn.first].ast->clone());
py.getset.push_back({mn, "", functions[gstub].realizations.begin()->second->ir,
functions[sstub].realizations.begin()->second->ir});
LOG("[py] {}: {}.{} => {}, {}", "member", cn, mn, gstub, sstub);
auto generics = std::vector<types::TypePtr>{
tc, std::make_shared<types::StaticType>(this, mn)};
auto gf = realizeIR(functions[pyWrap + ".wrap_get:0"].type, generics);
auto sf = realizeIR(functions[pyWrap + ".wrap_set:0"].type, generics);
py.getset.push_back({mn, "", gf, sf});
LOG("[py] {}: {} . {}", "member", cn, mn);
}
pyModule->types.push_back(py);
}
// Handle __iternext__ wrappers
auto cin = "std.internal.python._PyextIterWrap";
auto cin = "std.internal.python._PyWrap.IterWrap";
for (auto &[cn, cr] : classes[cin].realizations) {
LOG("[py] iterfn: {}", cn);
ir::PyType py{cn, ""};
auto tc = cr->type;
for (auto &[rn, r] : functions["__internal__.py_type:0"].realizations) {
for (auto &[rn, r] : functions[pyWrap + ".py_type:0"].realizations) {
if (r->type->funcGenerics[0].type->unify(tc.get(), nullptr) >= 0) {
py.typePtrHook = r->ir;
break;
@ -703,7 +496,7 @@ void Cache::populatePythonModule() {
auto &methods = classes[cin].methods;
for (auto &n : std::vector<std::string>{"_iter", "_iternext"}) {
auto fnn = overloads[methods[n]].begin()->name; // default first overload!
auto fnn = overloads[methods[n]].begin()->name;
auto &fna = functions[fnn];
auto ft = typeCtx->instantiate(fna.type, tc->getClass());
auto rtv = TypecheckVisitor(typeCtx).realize(ft);
@ -721,14 +514,20 @@ void Cache::populatePythonModule() {
for (const auto &[fn, f] : functions)
if (f.isToplevel) {
auto fnn = overloads[f.rootName].back().name; // last overload
if (startswith(rev(fnn), "_"))
continue;
LOG("[py] functn {} => {}", rev(fn), fnn);
auto ir = getFn(fnn);
pyModule->functions.push_back(ir::PyFunction{rev(fn), f.ast->getDocstr(), ir,
ir::PyFunction::Type::TOPLEVEL,
int(f.ast->args.size())});
std::string call = pyWrap + ".wrap_single";
if (f.ast->args.size() > 1)
call = pyWrap + ".wrap_multiple";
auto fnName = call + ":0";
seqassertn(in(functions, fnName), "bad name");
LOG("<- {}", typeCtx->forceFind(".toplevel")->type);
auto generics = std::vector<types::TypePtr>{
typeCtx->forceFind(".toplevel")->type,
std::make_shared<types::StaticType>(this, rev(f.ast->name))};
if (auto ir = realizeIR(functions[fnName].type, generics)) {
pyModule->functions.push_back(ir::PyFunction{rev(fn), f.ast->getDocstr(), ir,
ir::PyFunction::Type::TOPLEVEL,
int(f.ast->args.size())});
}
}
typeCtx->age = oldAge;

View File

@ -257,6 +257,9 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) {
stmt->attributes.parentClass = ctx->getBase()->name;
// Add the method to the class' method list
ctx->cache->classes[ctx->getBase()->name].methods[stmt->name] = rootName;
} else {
// Hack so that we can later use same helpers for class overloads
ctx->cache->classes[".toplevel"].methods[stmt->name] = rootName;
}
// Handle captures. Add additional argument to the function for every capture.

View File

@ -90,7 +90,6 @@ void SimplifyVisitor::visit(ImportStmt *stmt) {
// `__` while the standard library is being loaded
auto c = i.second.front();
if (c->isConditional() && i.first.find('.') == std::string::npos) {
LOG("-> fix {} :: {}", import.moduleName, i.first);
c = import.ctx->findDominatingBinding(i.first);
}
// Imports should ignore noShadow property

View File

@ -86,6 +86,8 @@ SimplifyVisitor::apply(Cache *cache, const StmtPtr &node, const std::string &fil
// Prepare the code
auto suite = N<SuiteStmt>();
suite->stmts.push_back(N<ClassStmt>(".toplevel", std::vector<Param>{}, nullptr,
std::vector<ExprPtr>{N<IdExpr>(Attr::Internal)}));
for (auto &d : defines) {
// Load compile-time defines (e.g., codon run -DFOO=1 ...)
suite->stmts.push_back(

View File

@ -166,6 +166,12 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr,
return transform(N<StringExpr>(expr->expr->type->prettyString()));
return nullptr;
}
// Special case: expr.__is_static__
if (expr->member == "__is_static__") {
if (expr->expr->isDone())
return transform(N<BoolExpr>(expr->expr->isStatic()));
return nullptr;
}
// Special case: cls.__vtable_id__
if (expr->expr->isType() && expr->member == "__vtable_id__") {
if (auto c = realize(expr->expr->type))

View File

@ -543,6 +543,8 @@ std::pair<bool, ExprPtr> TypecheckVisitor::transformSpecialCall(CallExpr *expr)
return {true, transformHasAttr(expr)};
} else if (val == "getattr") {
return {true, transformGetAttr(expr)};
} else if (val == "setattr") {
return {true, transformSetAttr(expr)};
} else if (val == "type.__new__:0") {
return {true, transformTypeFn(expr)};
} else if (val == "compile_error") {
@ -553,6 +555,8 @@ std::pair<bool, ExprPtr> TypecheckVisitor::transformSpecialCall(CallExpr *expr)
return {true, transformRealizedFn(expr)};
} else if (val == "__static_print__") {
return {false, transformStaticPrintFn(expr)};
} else if (auto e = transformInternalStaticFn(expr)) {
return {true, e};
} else {
return {false, nullptr};
}
@ -827,6 +831,18 @@ ExprPtr TypecheckVisitor::transformGetAttr(CallExpr *expr) {
return transform(N<DotExpr>(expr->args[0].value, staticTyp->evaluate().getString()));
}
/// Transform setattr method to a AssignMemberStmt.
ExprPtr TypecheckVisitor::transformSetAttr(CallExpr *expr) {
auto funcTyp = expr->expr->type->getFunc();
auto staticTyp = funcTyp->funcGenerics[0].type->getStatic();
if (!staticTyp->canRealize())
return nullptr;
return transform(N<StmtExpr>(N<AssignMemberStmt>(expr->args[0].value,
staticTyp->evaluate().getString(),
expr->args[1].value),
N<NoneExpr>()));
}
/// Raise a compiler error.
ExprPtr TypecheckVisitor::transformCompileError(CallExpr *expr) {
auto funcTyp = expr->expr->type->getFunc();
@ -894,6 +910,66 @@ ExprPtr TypecheckVisitor::transformStaticPrintFn(CallExpr *expr) {
return nullptr;
}
// Transform internal.static calls
ExprPtr TypecheckVisitor::transformInternalStaticFn(CallExpr *expr) {
if (expr->expr->isId("std.internal.static.fn_can_call")) {
expr->staticValue.type = StaticValue::INT;
auto typ = expr->args[0].value->getType()->getClass();
if (!typ)
return nullptr;
auto fn = expr->args[0].value->type->getFunc();
if (!fn)
error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
auto inargs = unpackTupleTypes(expr->args[1].value);
auto kwargs = unpackTupleTypes(expr->args[2].value);
seqassert(inargs && kwargs, "bad call to fn_can_call");
std::vector<CallExpr::Arg> callArgs;
for (auto &a : *inargs) {
callArgs.push_back({a.first, std::make_shared<NoneExpr>()}); // dummy expression
callArgs.back().value->setType(a.second);
}
for (auto &a : *kwargs) {
callArgs.push_back({a.first, std::make_shared<NoneExpr>()}); // dummy expression
callArgs.back().value->setType(a.second);
}
return transform(N<BoolExpr>(canCall(fn, callArgs) >= 0));
} else if (expr->expr->isId("std.internal.static.fn_arg_has_type")) {
expr->staticValue.type = StaticValue::INT;
auto fn = expr->args[0].value->type->getFunc();
if (!fn)
error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
auto idx = ctx->getStaticInt(expr->expr->type->getFunc()->funcGenerics[0].type);
seqassert(idx, "expected a static integer");
auto &args = fn->getArgTypes();
return transform(
N<BoolExpr>(*idx >= 0 && *idx < args.size() && args[*idx]->canRealize()));
} else if (expr->expr->isId("std.internal.static.fn_arg_get_type")) {
auto fn = expr->args[0].value->type->getFunc();
if (!fn)
error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
auto idx = ctx->getStaticInt(expr->expr->type->getFunc()->funcGenerics[0].type);
seqassert(idx, "expected a static integer");
auto &args = fn->getArgTypes();
if (*idx < 0 || *idx >= args.size() || !args[*idx]->canRealize())
error("argument does not have type");
return transform(NT<IdExpr>(args[*idx]->realizedName()));
} else if (expr->expr->isId("std.internal.static.fn_args")) {
auto fn = expr->args[0].value->type->getFunc();
if (!fn)
error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
std::vector<ExprPtr> v;
for (size_t i = 0; i < fn->ast->args.size(); i++)
v.push_back(N<TupleExpr>(std::vector<ExprPtr>{
N<IntExpr>(i), N<StringExpr>(ctx->cache->rev(fn->ast->args[i].name))}));
return transform(N<TupleExpr>(v));
} else {
return nullptr;
}
}
/// Get the list that describes the inheritance hierarchy of a given type.
/// The first type in the list is the most recently inherited type.
std::vector<ClassTypePtr> TypecheckVisitor::getSuperTypes(const ClassTypePtr &cls) {

View File

@ -305,4 +305,44 @@ std::string TypeContext::debugInfo() {
getRealizationBase()->iteration, getSrcInfo());
}
std::shared_ptr<std::pair<std::vector<types::TypePtr>, std::vector<types::TypePtr>>>
TypeContext::getFunctionArgs(types::TypePtr t) {
if (!t->getFunc())
return nullptr;
auto fn = t->getFunc();
auto ret = std::make_shared<
std::pair<std::vector<types::TypePtr>, std::vector<types::TypePtr>>>();
for (auto &t : fn->funcGenerics)
ret->first.push_back(t.type);
for (auto &t : fn->generics[0].type->getRecord()->args)
ret->second.push_back(t);
return ret;
}
std::shared_ptr<std::string> TypeContext::getStaticString(types::TypePtr t) {
if (auto s = t->getStatic()) {
auto r = s->evaluate();
if (r.type == StaticValue::STRING)
return std::make_shared<std::string>(r.getString());
}
return nullptr;
}
std::shared_ptr<int64_t> TypeContext::getStaticInt(types::TypePtr t) {
if (auto s = t->getStatic()) {
auto r = s->evaluate();
if (r.type == StaticValue::INT)
return std::make_shared<int64_t>(r.getInt());
}
return nullptr;
}
types::FuncTypePtr TypeContext::extractFunction(types::TypePtr t) {
if (auto f = t->getFunc())
return f;
if (auto p = t->getPartial())
return p->func;
return nullptr;
}
} // namespace codon::ast

View File

@ -157,6 +157,13 @@ private:
void dump(int pad);
/// Pretty-print the current realization context.
std::string debugInfo();
public:
std::shared_ptr<std::pair<std::vector<types::TypePtr>, std::vector<types::TypePtr>>>
getFunctionArgs(types::TypePtr t);
std::shared_ptr<std::string> getStaticString(types::TypePtr t);
std::shared_ptr<int64_t> getStaticInt(types::TypePtr t);
types::FuncTypePtr extractFunction(types::TypePtr t);
};
} // namespace codon::ast

View File

@ -168,17 +168,22 @@ StmtPtr TypecheckVisitor::transformHeterogenousTupleFor(ForStmt *stmt) {
StmtPtr TypecheckVisitor::transformStaticForLoop(ForStmt *stmt) {
auto loopVar = ctx->cache->getTemporaryVar("loop");
auto fn = [&](const std::string &var, const ExprPtr &expr) {
bool staticInt = expr->isStatic();
auto t = NT<IndexExpr>(
N<IdExpr>("Static"),
N<IdExpr>(expr->staticValue.type == StaticValue::INT ? "int" : "str"));
auto brk = N<BreakStmt>();
brk->setDone(); // Avoid transforming this one to continue
// var [: Static] := expr; suite...
auto loop = N<WhileStmt>(N<IdExpr>(loopVar),
N<SuiteStmt>(N<AssignStmt>(N<IdExpr>(var), expr->clone(),
staticInt ? t : nullptr),
clone(stmt->suite), brk));
auto loop = N<WhileStmt>(
N<IdExpr>(loopVar),
N<SuiteStmt>(
expr ? N<AssignStmt>(N<IdExpr>(var), expr->clone(),
expr->isStatic()
? NT<IndexExpr>(N<IdExpr>("Static"),
N<IdExpr>(expr->staticValue.type ==
StaticValue::INT
? "int"
: "str"))
: nullptr)
: nullptr,
clone(stmt->suite), brk));
loop->gotoVar = loopVar;
return loop;
};
@ -212,6 +217,55 @@ StmtPtr TypecheckVisitor::transformStaticForLoop(ForStmt *stmt) {
E(Error::STATIC_RANGE_BOUNDS, iter, MAX_STATIC_ITER, ed);
for (int i = 0; i < ed; i++)
block->stmts.push_back(fn(var, N<IntExpr>(i)));
} else if (iter && startswith(iter->value, "std.internal.static.fn_overloads")) {
if (auto fna = ctx->getFunctionArgs(iter->type)) {
auto [generics, args] = *fna;
auto typ = generics[0]->getClass();
auto name = ctx->getStaticString(generics[1]);
seqassert(name, "bad static string");
if (auto n = in(ctx->cache->classes[typ->name].methods, *name)) {
for (auto &method : ctx->cache->overloads[*n]) {
if (endswith(method.name, ":dispatch") ||
!ctx->cache->functions[method.name].type)
continue;
if (method.age <= ctx->age)
block->stmts.push_back(fn(var, N<IdExpr>(method.name)));
}
}
} else {
error("bad call to fn_overloads");
}
} else if (iter && startswith(iter->value, "std.internal.static.fn_args")) {
auto &suiteVec = stmt->suite->getSuite()->stmts;
int validI = 0;
for (; validI < suiteVec.size(); validI++) {
if (auto a = suiteVec[validI]->getAssign())
if (auto i = suiteVec[0]->getAssign()->rhs->getIndex())
if (i->expr->isId(var))
continue;
break;
}
if (validI != 2)
error("fn_args needs two variables in for loop");
if (auto fna = ctx->getFunctionArgs(iter->type)) {
auto [generics, args] = *fna;
auto typ = ctx->extractFunction(args[0]);
if (!typ)
error("fn_args needs a function");
for (size_t i = 0; i < typ->ast->args.size(); i++) {
suiteVec[0]->getAssign()->rhs = N<IntExpr>(i);
suiteVec[0]->getAssign()->type =
NT<IndexExpr>(NT<IdExpr>("Static"), NT<IdExpr>("int"));
suiteVec[1]->getAssign()->rhs =
N<StringExpr>(ctx->cache->rev(typ->ast->args[i].name));
suiteVec[1]->getAssign()->type =
NT<IndexExpr>(NT<IdExpr>("Static"), NT<IdExpr>("str"));
block->stmts.push_back(fn("", nullptr));
}
} else {
error("bad call to fn_args");
}
} else {
return nullptr;
}
@ -226,6 +280,7 @@ StmtPtr TypecheckVisitor::transformStaticForLoop(ForStmt *stmt) {
transform(N<SuiteStmt>(N<AssignStmt>(N<IdExpr>(loopVar), N<BoolExpr>(true)),
N<WhileStmt>(N<IdExpr>(loopVar), block)));
ctx->blockLevel--;
// LOG("-> {} :: {}", getSrcInfo(), loop->toString(2));
return loop;
}

View File

@ -214,6 +214,70 @@ types::FuncTypePtr TypecheckVisitor::findBestMethod(
return m.empty() ? nullptr : m[0];
}
/// Check if a function can be called with the given arguments.
/// See @c reorderNamedArgs for details.
int TypecheckVisitor::canCall(const types::FuncTypePtr &fn,
const std::vector<CallExpr::Arg> &args) {
std::vector<std::pair<types::TypePtr, size_t>> reordered;
auto score = ctx->reorderNamedArgs(
fn.get(), args,
[&](int s, int k, const std::vector<std::vector<int>> &slots, bool _) {
for (int si = 0; si < slots.size(); si++) {
if (fn->ast->args[si].status == Param::Generic) {
if (slots[si].empty())
reordered.push_back({nullptr, 0});
else
reordered.push_back({args[slots[si][0]].value->type, slots[si][0]});
} else if (si == s || si == k || slots[si].size() != 1) {
// Ignore *args, *kwargs and default arguments
reordered.push_back({nullptr, 0});
} else {
reordered.push_back({args[slots[si][0]].value->type, slots[si][0]});
}
}
return 0;
},
[](error::Error, const SrcInfo &, const std::string &) { return -1; });
for (int ai = 0, mai = 0, gi = 0; score != -1 && ai < reordered.size(); ai++) {
auto expectTyp = fn->ast->args[ai].status == Param::Normal
? fn->getArgTypes()[mai++]
: fn->funcGenerics[gi++].type;
auto [argType, argTypeIdx] = reordered[ai];
if (!argType)
continue;
if (fn->ast->args[ai].status != Param::Normal) {
// Check if this is a good generic!
if (expectTyp && expectTyp->isStaticType()) {
if (!args[argTypeIdx].value->isStatic()) {
score = -1;
break;
} else {
argType = Type::makeStatic(ctx->cache, args[argTypeIdx].value);
}
} else {
/// TODO: check if these are real types or if traits are satisfied
continue;
}
}
try {
ExprPtr dummy = std::make_shared<IdExpr>("");
dummy->type = argType;
dummy->setDone();
wrapExpr(dummy, expectTyp, fn);
types::Type::Unification undo;
if (dummy->type->unify(expectTyp.get(), &undo) >= 0) {
undo.undo();
} else {
score = -1;
}
} catch (const exc::ParserException &) {
// Ignore failed wraps
score = -1;
}
}
return score;
}
/// Select the best method among the provided methods given the list of arguments.
/// See @c reorderNamedArgs for details.
std::vector<types::FuncTypePtr>
@ -226,63 +290,7 @@ TypecheckVisitor::findMatchingMethods(const types::ClassTypePtr &typ,
if (!mi)
continue; // avoid overloads that have not been seen yet
auto method = ctx->instantiate(mi, typ)->getFunc();
std::vector<std::pair<types::TypePtr, size_t>> reordered;
auto score = ctx->reorderNamedArgs(
method.get(), args,
[&](int s, int k, const std::vector<std::vector<int>> &slots, bool _) {
for (int si = 0; si < slots.size(); si++) {
if (method->ast->args[si].status == Param::Generic) {
if (slots[si].empty())
reordered.push_back({nullptr, 0});
else
reordered.push_back({args[slots[si][0]].value->type, slots[si][0]});
} else if (si == s || si == k || slots[si].size() != 1) {
// Ignore *args, *kwargs and default arguments
reordered.push_back({nullptr, 0});
} else {
reordered.push_back({args[slots[si][0]].value->type, slots[si][0]});
}
}
return 0;
},
[](error::Error, const SrcInfo &, const std::string &) { return -1; });
for (int ai = 0, mai = 0, gi = 0; score != -1 && ai < reordered.size(); ai++) {
auto expectTyp = method->ast->args[ai].status == Param::Normal
? method->getArgTypes()[mai++]
: method->funcGenerics[gi++].type;
auto [argType, argTypeIdx] = reordered[ai];
if (!argType)
continue;
if (method->ast->args[ai].status != Param::Normal) {
// Check if this is a good generic!
if (expectTyp && expectTyp->isStaticType()) {
if (!args[argTypeIdx].value->isStatic()) {
score = -1;
break;
} else {
argType = Type::makeStatic(ctx->cache, args[argTypeIdx].value);
}
} else {
/// TODO: check if these are real types or if traits are satisfied
continue;
}
}
try {
ExprPtr dummy = std::make_shared<IdExpr>("");
dummy->type = argType;
dummy->setDone();
wrapExpr(dummy, expectTyp, method);
types::Type::Unification undo;
if (dummy->type->unify(expectTyp.get(), &undo) >= 0) {
undo.undo();
} else {
score = -1;
}
} catch (const exc::ParserException &) {
// Ignore failed wraps
score = -1;
}
}
int score = canCall(method, args);
if (score != -1) {
results.push_back(mi);
}
@ -418,4 +426,32 @@ ExprPtr TypecheckVisitor::castToSuperClass(ExprPtr expr, ClassTypePtr superTyp,
dist, typExpr));
}
/// Unpack a Tuple or KwTuple expression into (name, type) vector.
/// Name is empty when handling Tuple; otherwise it matches names of KwTuple.
std::shared_ptr<std::vector<std::pair<std::string, types::TypePtr>>>
TypecheckVisitor::unpackTupleTypes(ExprPtr expr) {
auto ret = std::make_shared<std::vector<std::pair<std::string, types::TypePtr>>>();
if (auto tup = expr->origExpr->getTuple()) {
for (auto &a : tup->items) {
transform(a);
if (!a->getType()->getClass())
return nullptr;
ret->push_back({"", a->getType()});
}
} else if (auto kw = expr->origExpr->getCall()) { // origExpr?
auto kwCls = in(ctx->cache->classes, expr->getType()->getClass()->name);
seqassert(kwCls, "cannot find {}", expr->getType()->getClass()->name);
for (size_t i = 0; i < kw->args.size(); i++) {
auto &a = kw->args[i].value;
transform(a);
if (!a->getType()->getClass())
return nullptr;
ret->push_back({kwCls->fields[i].name, a->getType()});
}
} else {
return nullptr;
}
return ret;
}
} // namespace codon::ast

View File

@ -139,11 +139,13 @@ private: // Node typechecking rules
ExprPtr transformStaticLen(CallExpr *expr);
ExprPtr transformHasAttr(CallExpr *expr);
ExprPtr transformGetAttr(CallExpr *expr);
ExprPtr transformSetAttr(CallExpr *expr);
ExprPtr transformCompileError(CallExpr *expr);
ExprPtr transformTupleFn(CallExpr *expr);
ExprPtr transformTypeFn(CallExpr *expr);
ExprPtr transformRealizedFn(CallExpr *expr);
ExprPtr transformStaticPrintFn(CallExpr *expr);
ExprPtr transformInternalStaticFn(CallExpr *expr);
std::vector<types::ClassTypePtr> getSuperTypes(const types::ClassTypePtr &cls);
void addFunctionGenerics(const types::FuncType *t);
std::string generatePartialStub(const std::vector<char> &mask, types::FuncType *fn);
@ -213,6 +215,7 @@ private:
types::FuncTypePtr
findBestMethod(const types::ClassTypePtr &typ, const std::string &member,
const std::vector<std::pair<std::string, types::TypePtr>> &args);
int canCall(const types::FuncTypePtr &, const std::vector<CallExpr::Arg> &);
std::vector<types::FuncTypePtr>
findMatchingMethods(const types::ClassTypePtr &typ,
const std::vector<types::FuncTypePtr> &methods,
@ -228,6 +231,10 @@ public:
friend class Cache;
friend class types::CallableTrait;
friend class types::UnionType;
private: // Helpers
std::shared_ptr<std::vector<std::pair<std::string, types::TypePtr>>>
unpackTupleTypes(ExprPtr);
};
} // namespace codon::ast

View File

@ -178,6 +178,9 @@ def hasattr(obj, attr: Static[str]):
def getattr(obj, attr: Static[str]):
pass
def setattr(obj, attr: Static[str], what):
pass
def tuple(iterable):
pass

View File

@ -1515,66 +1515,239 @@ __pyenv__: Optional[pyobj] = None
def _____(): __pyenv__ # make it global!
@extend
class __internal__:
def py_type(T: type) -> Ptr[byte]:
return Ptr[byte]()
import internal.static as _S
class _PyWrap:
def _wrap(args, T: type, F: Static[str], map):
for fn in _S.fn_overloads(T, F):
if _S.fn_can_call(fn, *args):
try:
return map(fn, args)
except PyError:
pass
raise PyError("cannot dispatch " + F)
def to_py(o) -> Ptr[byte]:
pytype = __internal__.py_type(type(o))
obj = Ptr[PyWrapper[type(o)]](alloc_uncollectable(sizeof(type(o))))
obj[0] = PyWrapper(PyObject(1, pytype), o)
return obj.as_byte()
def _wrap_unary(obj: cobj, T: type, F: Static[str]) -> cobj:
return _PyWrap._wrap(
(pyobj(obj), ), T=T, F=F,
map=lambda f, a: f(*a).__to_py__()
)
def wrap_magic_abs(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__abs__")
def wrap_magic_pos(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__pos__")
def wrap_magic_neg(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__neg__")
def wrap_magic_invert(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__invert__")
def wrap_magic_int(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__int__")
def wrap_magic_float(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__float__")
def wrap_magic_index(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__index__")
def wrap_magic_repr(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__repr__")
def wrap_magic_str(obj: cobj, T: type):
return _PyWrap._wrap_unary(obj, T, "__str__")
def from_py(o: Ptr[byte], T: type) -> T:
obj = Ptr[PyWrapper[T]](o)[0]
pytype = __internal__.py_type(T)
if obj.head.pytype != pytype:
raise TypeError("Python object has incompatible type")
return obj.data
def _wrap_hash(obj: cobj, T: type, F: Static[str]) -> i64:
return _PyWrap._wrap(
(pyobj(obj), ), T=T, F=F,
map=lambda f, a: f(*a)
)
def wrap_magic_len(obj: cobj, T: type):
return _PyWrap._wrap_hash(obj, T, "__len__")
def wrap_magic_hash(obj: cobj, T: type):
return _PyWrap._wrap_hash(obj, T, "__hash__")
def cmp_py(obj: cobj, other: cobj, op: i32, C: type, name: Static[str]) -> cobj:
def wrap_magic_bool(obj: cobj, T: type) -> i32:
return _PyWrap._wrap(
(pyobj(obj), ), T=T, F="__bool__",
map=lambda f, a: i32(f(*a))
)
def wrap_magic_del(obj: cobj, T: type):
_PyWrap._wrap(
(pyobj(obj), ), T=T, F="__del__",
map=lambda f, a: f(*a)
)
def wrap_magic_contains(obj: cobj, arg: cobj, T: type) -> i32:
return _PyWrap._wrap(
(pyobj(obj), pyobj(arg)), T=T, F="__contains__",
map=lambda f, a: i32(f(*a))
)
def wrap_magic_init(obj: cobj, _args: cobj, _kwds: cobj, T: type) -> i32:
args = pyobj(_args)
kwds = pyobj(_kwds)
for fn in _S.fn_overloads(T, "__init__"):
try:
ai = -1
# TODO: default values do not work
a = tuple(
kwds[n] if n in kwds else args[(ai := ai + 1)]
for _, n in _S.fn_args(fn)
)
a = (pyobj(obj), *a)
if _S.fn_can_call(fn, *a):
fn(*a)
return i32(0)
except PyError:
pass
return i32(-1)
def wrap_magic_call(obj: cobj, _args: cobj, _kwds: cobj, T: type) -> cobj:
args = pyobj(_args)
kwds = pyobj(_kwds)
for fn in _S.fn_overloads(T, "__call__"):
try:
ai = -1
a = tuple( # TODO: default values do not work
kwds[n] if n in kwds else args[(ai := ai + 1)]
for _, n in _S.fn_args(fn)
)
a = (pyobj(obj), *a)
if _S.fn_can_call(fn, *a):
return fn(*a).__to_py__()
except PyError:
pass
raise PyError("cannot dispatch __call__")
def _wrap_cmp(obj: cobj, other: cobj, T: type, F: Static[str]) -> cobj:
return _PyWrap._wrap(
(pyobj(obj), pyobj(other)), T=T, F=F,
map=lambda f, a: f(*a).__to_py__()
)
def wrap_magic_lt(obj: cobj, other: cobj, T: type):
return _PyWrap._wrap_cmp(obj, other, T, "__lt__")
def wrap_magic_le(obj: cobj, other: cobj, T: type):
return _PyWrap._wrap_cmp(obj, other, T, "__le__")
def wrap_magic_eq(obj: cobj, other: cobj, T: type):
return _PyWrap._wrap_cmp(obj, other, T, "__eq__")
def wrap_magic_ne(obj: cobj, other: cobj, T: type):
return _PyWrap._wrap_cmp(obj, other, T, "__ne__")
def wrap_magic_gt(obj: cobj, other: cobj, T: type):
return _PyWrap._wrap_cmp(obj, other, T, "__gt__")
def wrap_magic_ge(obj: cobj, other: cobj, T: type):
return _PyWrap._wrap_cmp(obj, other, T, "__ge__")
def wrap_cmp(obj: cobj, other: cobj, op: i32, C: type) -> cobj:
if hasattr(C, "__lt__") and op == 0i32:
return getattr(C, name)(obj, other)
return _PyWrap.wrap_magic_lt(obj, other, C)
elif hasattr(C, "__le__") and op == 1i32:
return getattr(C, name)(obj, other)
return _PyWrap.wrap_magic_le(obj, other, C)
elif hasattr(C, "__eq__") and op == 2i32:
return getattr(C, name)(obj, other)
return _PyWrap.wrap_magic_eq(obj, other, C)
elif hasattr(C, "__ne__") and op == 3i32:
return getattr(C, name)(obj, other)
return _PyWrap.wrap_magic_ne(obj, other, C)
elif hasattr(C, "__gt__") and op == 4i32:
return getattr(C, name)(obj, other)
return _PyWrap.wrap_magic_gt(obj, other, C)
elif hasattr(C, "__ge__") and op == 5i32:
return getattr(C, name)(obj, other)
return _PyWrap.wrap_magic_ge(obj, other, C)
else:
Py_IncRef(Py_NotImplemented)
return Py_NotImplemented
class _PyextIterWrap:
_gen: cobj
T: type
def wrap_magic_setitem(obj: cobj, idx: cobj, val: cobj, T: type) -> i32:
if val == cobj():
try:
if hasattr(T, "__delitem__"):
T.__delitem__(pyobj(obj), pyobj(idx))
return 0
except PyError:
pass
return -1
try:
_PyWrap._wrap(
(pyobj(obj), pyobj(idx), pyobj(val)), T=T, F="__setitem__",
map=lambda f, a: f(*a).__to_py__()
)
return 0
except PyError:
return -1
def _init(obj: cobj, T: type) -> cobj:
return _PyextIterWrap(T.__from_py__(obj)).__to_py__()
class IterWrap:
_gen: cobj
T: type
@realize_without_self
def __init__(self, it: T):
self._gen = it.__iter__().__raw__()
def _init(obj: cobj, T: type) -> cobj:
return _PyWrap.IterWrap(T.__from_py__(obj)).__to_py__()
def _iter(obj: cobj) -> cobj:
T # need separate fn for each instantiation
return obj
@realize_without_self
def __init__(self, it: T):
self._gen = it.__iter__().__raw__()
def _iternext(self: cobj) -> cobj:
pt = _PyextIterWrap[T].__from_py__(self)
gt = type(T().__iter__())(pt._gen)
if gt.done():
return cobj()
else:
return gt.next().__to_py__()
def _iter(obj: cobj) -> cobj:
T # need separate fn for each instantiation
return obj
def __to_py__(self):
return __internal__.to_py(self)
def _iternext(self: cobj) -> cobj:
pt = _PyWrap.IterWrap[T].__from_py__(self)
gt = type(T().__iter__())(pt._gen)
if gt.done():
return cobj()
else:
return gt.next().__to_py__()
def __from_py__(obj: cobj):
return __internal__.from_py(obj, _PyextIterWrap[T])
def __to_py__(self):
return _PyWrap.wrap_to_py(self)
def __from_py__(obj: cobj):
return _PyWrap.wrap_from_py(obj, _PyWrap.IterWrap[T])
def wrap_magic_iter(obj: cobj, T: type) -> cobj:
return _PyWrap.IterWrap._init(obj, T)
def wrap_single(obj: cobj, arg: cobj, T: type, F: Static[str], method: Static[int]):
a = (pyobj(obj), pyobj(arg)) if method else (pyobj(arg),)
return _PyWrap._wrap(
a, T=T, F=F,
map=lambda f, a: f(*a).__to_py__()
)
def wrap_multiple(obj: cobj, args: Ptr[cobj], nargs: i32, T: type, F: Static[str], method: Static[int]):
def _err():
raise PyError("argument mismatch")
return pyobj()
a = (pyobj(obj), ) if method else ()
for fn in _S.fn_overloads(T, F):
try:
ai = -1
an = (
pyobj(args[i]) if i < nargs else _err()
for i, _ in _S.fn_args(fn)
)
if len(an) != nargs:
_err()
if _S.fn_can_call(fn, (*a, *an)):
return fn(*a, *an).__to_py__()
except PyError:
pass
PyError("cannot dispatch " + F)
def wrap_get(obj: cobj, closure: cobj, T: type, S: Static[str]):
return getattr(T.__from_py__(obj), S).__to_py__()
def wrap_set(obj: cobj, what: cobj, closure: cobj, T: type, S: Static[str]) -> i32:
try:
t = T.__from_py__(obj)
setattr(t, S, type(getattr(t, S)).__from_py__(what))
return i32(0)
except PyError:
return i32(-1)
def py_type(T: type) -> cobj:
return cobj()
def wrap_to_py(o) -> cobj:
pytype = _PyWrap.py_type(type(o))
obj = Ptr[PyWrapper[type(o)]](alloc_uncollectable(sizeof(type(o))))
obj[0] = PyWrapper(PyObject(1, pytype), o)
return obj.as_byte()
def wrap_from_py(o: cobj, T: type) -> T:
obj = Ptr[PyWrapper[T]](o)[0]
pytype = _PyWrap.py_type(T)
if obj.head.pytype != pytype:
raise TypeError("Python object has incompatible type")
return obj.data

View File

@ -0,0 +1,18 @@
def fn_overloads(T: type, F: Static[str]):
pass
def fn_args(F): # function: (i, name)
pass
def fn_arg_has_type(F, i: Static[int]):
pass
def fn_arg_get_type(F, i: Static[int]):
pass
def fn_can_call(F, *args, **kwargs):
pass
def class_args(T: type):
pass