Merge branch 'starred_kwargs'

This commit is contained in:
blueloveTH 2023-06-09 22:39:55 +08:00
commit d5500c246c
17 changed files with 384 additions and 114 deletions

View File

@ -20,17 +20,16 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
## Unimplemented features ## Unimplemented features
1. `**kwargs` in function definition. 1. `__getattr__` and `__setattr__`.
2. `__getattr__` and `__setattr__`. 2. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
3. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented. 3. `__slots__` in class definition.
4. `__slots__` in class definition. 4. One element tuple. `(1,)` is not supported.
5. One element tuple. `(1,)` is not supported. 5. Unpacking in `list` and `dict` literals, e.g. `[1, 2, *a]`.
6. Unpacking in `list` and `dict` literals, e.g. `[1, 2, *a]`. 6. Access the exception object in try..except.
7. Access the exception object in try..except. 7. `else` clause in try..except.
8. `else` clause in try..except. 8. Inplace methods like `__iadd__` and `__imul__`.
9. Inplace methods like `__iadd__` and `__imul__`. 9. `__del__` in class definition.
10. `__del__` in class definition. 10. Multiple inheritance.
11. Multiple inheritance.
## Different behaviors ## Different behaviors

View File

@ -91,7 +91,7 @@ __NEXT_STEP:;
TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH(); TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH();
TARGET(LOAD_FUNCTION) { TARGET(LOAD_FUNCTION) {
FuncDecl_ decl = co->func_decls[byte.arg]; FuncDecl_ decl = co->func_decls[byte.arg];
bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator; bool is_simple = decl->starred_kwarg==-1 && decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator;
int argc = decl->args.size(); int argc = decl->args.size();
PyObject* obj; PyObject* obj;
if(decl->nested){ if(decl->nested){
@ -236,6 +236,11 @@ __NEXT_STEP:;
} }
DISPATCH(); DISPATCH();
/*****************************************/ /*****************************************/
TARGET(BUILD_TUPLE)
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
STACK_SHRINK(byte.arg);
PUSH(_0);
DISPATCH();
TARGET(BUILD_LIST) TARGET(BUILD_LIST)
_0 = VAR(STACK_VIEW(byte.arg).to_list()); _0 = VAR(STACK_VIEW(byte.arg).to_list());
STACK_SHRINK(byte.arg); STACK_SHRINK(byte.arg);
@ -263,11 +268,6 @@ __NEXT_STEP:;
_0 = POPX(); // start _0 = POPX(); // start
PUSH(VAR(Slice(_0, _1, _2))); PUSH(VAR(Slice(_0, _1, _2)));
DISPATCH(); DISPATCH();
TARGET(BUILD_TUPLE)
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
STACK_SHRINK(byte.arg);
PUSH(_0);
DISPATCH();
TARGET(BUILD_STRING) { TARGET(BUILD_STRING) {
std::stringstream ss; std::stringstream ss;
ArgsView view = STACK_VIEW(byte.arg); ArgsView view = STACK_VIEW(byte.arg);
@ -276,6 +276,40 @@ __NEXT_STEP:;
PUSH(VAR(ss.str())); PUSH(VAR(ss.str()));
} DISPATCH(); } DISPATCH();
/*****************************************/ /*****************************************/
TARGET(BUILD_TUPLE_UNPACK) {
auto _lock = heap.gc_scope_lock();
List list;
_unpack_as_list(STACK_VIEW(byte.arg), list);
STACK_SHRINK(byte.arg);
_0 = VAR(Tuple(std::move(list)));
PUSH(_0);
} DISPATCH();
TARGET(BUILD_LIST_UNPACK) {
auto _lock = heap.gc_scope_lock();
List list;
_unpack_as_list(STACK_VIEW(byte.arg), list);
STACK_SHRINK(byte.arg);
_0 = VAR(std::move(list));
PUSH(_0);
} DISPATCH();
TARGET(BUILD_DICT_UNPACK) {
auto _lock = heap.gc_scope_lock();
Dict dict(this);
_unpack_as_dict(STACK_VIEW(byte.arg), dict);
STACK_SHRINK(byte.arg);
_0 = VAR(std::move(dict));
PUSH(_0);
} DISPATCH();
TARGET(BUILD_SET_UNPACK) {
auto _lock = heap.gc_scope_lock();
List list;
_unpack_as_list(STACK_VIEW(byte.arg), list);
STACK_SHRINK(byte.arg);
_0 = VAR(std::move(list));
_0 = call(builtins->attr(set), _0);
PUSH(_0);
} DISPATCH();
/*****************************************/
#define PREDICT_INT_OP(op) \ #define PREDICT_INT_OP(op) \
if(is_both_int(TOP(), SECOND())){ \ if(is_both_int(TOP(), SECOND())){ \
_1 = POPX(); \ _1 = POPX(); \
@ -426,9 +460,6 @@ __NEXT_STEP:;
frame->jump_abs_break(index); frame->jump_abs_break(index);
} DISPATCH(); } DISPATCH();
/*****************************************/ /*****************************************/
TARGET(BEGIN_CALL)
PUSH(PY_BEGIN_CALL);
DISPATCH();
TARGET(CALL) TARGET(CALL)
_0 = vectorcall( _0 = vectorcall(
byte.arg & 0xFFFF, // ARGC byte.arg & 0xFFFF, // ARGC
@ -438,6 +469,23 @@ __NEXT_STEP:;
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL(); if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
PUSH(_0); PUSH(_0);
DISPATCH(); DISPATCH();
TARGET(CALL_TP)
// [callable, <self>, args: tuple, kwargs: dict]
_2 = POPX();
_1 = POPX();
for(PyObject* obj: _CAST(Tuple&, _1)) PUSH(obj);
_CAST(Dict&, _2).apply([this](PyObject* k, PyObject* v){
PUSH(VAR(StrName(CAST(Str&, k)).index));
PUSH(v);
});
_0 = vectorcall(
_CAST(Tuple&, _1).size(), // ARGC
_CAST(Dict&, _2).size(), // KWARGC
true
);
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
PUSH(_0);
DISPATCH();
TARGET(RETURN_VALUE) TARGET(RETURN_VALUE)
_0 = POPX(); _0 = POPX();
_pop_frame(); _pop_frame();
@ -471,6 +519,9 @@ __NEXT_STEP:;
TARGET(UNARY_NOT) TARGET(UNARY_NOT)
TOP() = VAR(!py_bool(TOP())); TOP() = VAR(!py_bool(TOP()));
DISPATCH(); DISPATCH();
TARGET(UNARY_STAR)
TOP() = VAR(StarWrapper(byte.arg, TOP()));
DISPATCH();
/*****************************************/ /*****************************************/
TARGET(GET_ITER) TARGET(GET_ITER)
TOP() = py_iter(TOP()); TOP() = py_iter(TOP());
@ -528,15 +579,6 @@ __NEXT_STEP:;
} }
PUSH(VAR(extras)); PUSH(VAR(extras));
} DISPATCH(); } DISPATCH();
TARGET(UNPACK_UNLIMITED) {
auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!!
_0 = py_iter(POPX());
_1 = py_next(_0);
while(_1 != StopIteration){
PUSH(_1);
_1 = py_next(_0);
}
} DISPATCH();
/*****************************************/ /*****************************************/
TARGET(BEGIN_CLASS) TARGET(BEGIN_CLASS)
_name = StrName(byte.arg); _name = StrName(byte.arg);

View File

@ -29,7 +29,7 @@
#include <variant> #include <variant>
#include <type_traits> #include <type_traits>
#define PK_VERSION "1.0.1" #define PK_VERSION "1.0.2"
// debug macros // debug macros
#define DEBUG_NO_BUILTIN_MODULES 0 #define DEBUG_NO_BUILTIN_MODULES 0
@ -153,6 +153,8 @@ struct Type {
#define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!"); #define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!");
#endif #endif
#define PK_ASSERT(x) if(!(x)) FATAL_ERROR();
inline const float kInstAttrLoadFactor = 0.67f; inline const float kInstAttrLoadFactor = 0.67f;
inline const float kTypeAttrLoadFactor = 0.5f; inline const float kTypeAttrLoadFactor = 0.5f;
@ -173,7 +175,6 @@ inline bool is_both_int(PyObject* a, PyObject* b) noexcept {
// special singals, is_tagged() for them is true // special singals, is_tagged() for them is true
inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null
inline PyObject* const PY_BEGIN_CALL = (PyObject*)0b010011;
inline PyObject* const PY_OP_CALL = (PyObject*)0b100011; inline PyObject* const PY_OP_CALL = (PyObject*)0b100011;
inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011; inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011;

View File

@ -90,7 +90,7 @@ class Compiler {
rules[TK("*")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_FACTOR }; rules[TK("*")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_FACTOR };
rules[TK("/")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR }; rules[TK("/")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
rules[TK("//")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR }; rules[TK("//")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
rules[TK("**")] = { nullptr, METHOD(exprBinaryOp), PREC_EXPONENT }; rules[TK("**")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_EXPONENT };
rules[TK(">")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION }; rules[TK(">")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
rules[TK("<")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION }; rules[TK("<")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION }; rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
@ -280,7 +280,10 @@ class Compiler {
ctx()->s_expr.push(make_expr<NegatedExpr>(ctx()->s_expr.popx())); ctx()->s_expr.push(make_expr<NegatedExpr>(ctx()->s_expr.popx()));
break; break;
case TK("*"): case TK("*"):
ctx()->s_expr.push(make_expr<StarredExpr>(ctx()->s_expr.popx())); ctx()->s_expr.push(make_expr<StarredExpr>(1, ctx()->s_expr.popx()));
break;
case TK("**"):
ctx()->s_expr.push(make_expr<StarredExpr>(2, ctx()->s_expr.popx()));
break; break;
default: FATAL_ERROR(); default: FATAL_ERROR();
} }
@ -319,7 +322,6 @@ class Compiler {
if (curr().type == TK("]")) break; if (curr().type == TK("]")) break;
EXPR(); EXPR();
items.push_back(ctx()->s_expr.popx()); items.push_back(ctx()->s_expr.popx());
if(items.back()->is_starred()) SyntaxError();
match_newlines_repl(); match_newlines_repl();
if(items.size()==1 && match(TK("for"))){ if(items.size()==1 && match(TK("for"))){
_consume_comp<ListCompExpr>(std::move(items[0])); _consume_comp<ListCompExpr>(std::move(items[0]));
@ -341,19 +343,24 @@ class Compiler {
match_newlines_repl(); match_newlines_repl();
if (curr().type == TK("}")) break; if (curr().type == TK("}")) break;
EXPR(); EXPR();
if(curr().type == TK(":")) parsing_dict = true; int star_level = ctx()->s_expr.top()->star_level();
if(star_level==2 || curr().type == TK(":")){
parsing_dict = true;
}
if(parsing_dict){ if(parsing_dict){
consume(TK(":"));
EXPR();
auto dict_item = make_expr<DictItemExpr>(); auto dict_item = make_expr<DictItemExpr>();
dict_item->key = ctx()->s_expr.popx(); if(star_level == 2){
dict_item->value = ctx()->s_expr.popx(); dict_item->key = nullptr;
if(dict_item->key->is_starred()) SyntaxError(); dict_item->value = ctx()->s_expr.popx();
if(dict_item->value->is_starred()) SyntaxError(); }else{
consume(TK(":"));
EXPR();
dict_item->key = ctx()->s_expr.popx();
dict_item->value = ctx()->s_expr.popx();
}
items.push_back(std::move(dict_item)); items.push_back(std::move(dict_item));
}else{ }else{
items.push_back(ctx()->s_expr.popx()); items.push_back(ctx()->s_expr.popx());
if(items.back()->is_starred()) SyntaxError();
} }
match_newlines_repl(); match_newlines_repl();
if(items.size()==1 && match(TK("for"))){ if(items.size()==1 && match(TK("for"))){
@ -387,9 +394,15 @@ class Compiler {
EXPR(); EXPR();
e->kwargs.push_back({key, ctx()->s_expr.popx()}); e->kwargs.push_back({key, ctx()->s_expr.popx()});
} else{ } else{
if(!e->kwargs.empty()) SyntaxError("positional argument follows keyword argument");
EXPR(); EXPR();
e->args.push_back(ctx()->s_expr.popx()); if(ctx()->s_expr.top()->star_level() == 2){
// **kwargs
e->kwargs.push_back({"**", ctx()->s_expr.popx()});
}else{
// positional argument
if(!e->kwargs.empty()) SyntaxError("positional argument follows keyword argument");
e->args.push_back(ctx()->s_expr.popx());
}
} }
match_newlines_repl(); match_newlines_repl();
} while (match(TK(","))); } while (match(TK(",")));
@ -876,6 +889,7 @@ __SUBSCR_END:
void _compile_f_args(FuncDecl_ decl, bool enable_type_hints){ void _compile_f_args(FuncDecl_ decl, bool enable_type_hints){
int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs
do { do {
if(state > 3) SyntaxError();
if(state == 3) SyntaxError("**kwargs should be the last argument"); if(state == 3) SyntaxError("**kwargs should be the last argument");
match_newlines(); match_newlines();
if(match(TK("*"))){ if(match(TK("*"))){
@ -894,14 +908,17 @@ __SUBSCR_END:
SyntaxError("duplicate argument name"); SyntaxError("duplicate argument name");
} }
} }
if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){
SyntaxError("duplicate argument name");
}
for(auto& kv: decl->kwargs){ for(auto& kv: decl->kwargs){
if(decl->code->varnames[kv.key] == name){ if(decl->code->varnames[kv.key] == name){
SyntaxError("duplicate argument name"); SyntaxError("duplicate argument name");
} }
} }
if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){
SyntaxError("duplicate argument name");
}
if(decl->starred_kwarg!=-1 && decl->code->varnames[decl->starred_kwarg] == name){
SyntaxError("duplicate argument name");
}
// eat type hints // eat type hints
if(enable_type_hints && match(TK(":"))) consume_type_hints(); if(enable_type_hints && match(TK(":"))) consume_type_hints();
@ -924,7 +941,10 @@ __SUBSCR_END:
} }
decl->kwargs.push_back(FuncDecl::KwArg{index, value}); decl->kwargs.push_back(FuncDecl::KwArg{index, value});
} break; } break;
case 3: SyntaxError("**kwargs is not supported yet"); break; case 3:
decl->starred_kwarg = index;
state+=1;
break;
} }
} while (match(TK(","))); } while (match(TK(",")));
} }

View File

@ -124,6 +124,14 @@ struct Dict{
return v; return v;
} }
template<typename __Func>
void apply(__Func f) const {
for(int i=0; i<_capacity; i++){
if(_items[i].first == nullptr) continue;
f(_items[i].first, _items[i].second);
}
}
void clear(){ void clear(){
memset(_items, 0, _capacity * sizeof(Item)); memset(_items, 0, _capacity * sizeof(Item));
_size = 0; _size = 0;

View File

@ -19,11 +19,12 @@ struct Expr{
virtual void emit(CodeEmitContext* ctx) = 0; virtual void emit(CodeEmitContext* ctx) = 0;
virtual std::string str() const = 0; virtual std::string str() const = 0;
virtual bool is_starred() const { return false; }
virtual bool is_literal() const { return false; } virtual bool is_literal() const { return false; }
virtual bool is_json_object() const { return false; } virtual bool is_json_object() const { return false; }
virtual bool is_attrib() const { return false; } virtual bool is_attrib() const { return false; }
virtual bool is_compare() const { return false; } virtual bool is_compare() const { return false; }
virtual int star_level() const { return 0; }
bool is_starred() const { return star_level() > 0; }
// for OP_DELETE_XXX // for OP_DELETE_XXX
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; } [[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
@ -183,24 +184,25 @@ struct NameExpr: Expr{
}; };
struct StarredExpr: Expr{ struct StarredExpr: Expr{
int level;
Expr_ child; Expr_ child;
StarredExpr(Expr_&& child): child(std::move(child)) {} StarredExpr(int level, Expr_&& child): level(level), child(std::move(child)) {}
std::string str() const override { return "Starred()"; } std::string str() const override { return fmt("Starred(level=", level, ")"); }
bool is_starred() const override { return true; } int star_level() const override { return level; }
void emit(CodeEmitContext* ctx) override { void emit(CodeEmitContext* ctx) override {
child->emit(ctx); child->emit(ctx);
ctx->emit(OP_UNPACK_UNLIMITED, BC_NOARG, line); ctx->emit(OP_UNARY_STAR, level, line);
} }
bool emit_store(CodeEmitContext* ctx) override { bool emit_store(CodeEmitContext* ctx) override {
if(level != 1) return false;
// simply proxy to child // simply proxy to child
return child->emit_store(ctx); return child->emit_store(ctx);
} }
}; };
struct NotExpr: Expr{ struct NotExpr: Expr{
Expr_ child; Expr_ child;
NotExpr(Expr_&& child): child(std::move(child)) {} NotExpr(Expr_&& child): child(std::move(child)) {}
@ -265,16 +267,13 @@ struct LiteralExpr: Expr{
if(std::holds_alternative<i64>(value)){ if(std::holds_alternative<i64>(value)){
return std::to_string(std::get<i64>(value)); return std::to_string(std::get<i64>(value));
} }
if(std::holds_alternative<f64>(value)){ if(std::holds_alternative<f64>(value)){
return std::to_string(std::get<f64>(value)); return std::to_string(std::get<f64>(value));
} }
if(std::holds_alternative<Str>(value)){ if(std::holds_alternative<Str>(value)){
Str s = std::get<Str>(value).escape(); Str s = std::get<Str>(value).escape();
return s.str(); return s.str();
} }
FATAL_ERROR(); FATAL_ERROR();
} }
@ -367,14 +366,21 @@ struct SliceExpr: Expr{
}; };
struct DictItemExpr: Expr{ struct DictItemExpr: Expr{
Expr_ key; Expr_ key; // maybe nullptr if it is **kwargs
Expr_ value; Expr_ value;
std::string str() const override { return "DictItem()"; } std::string str() const override { return "DictItem()"; }
int star_level() const override { return value->star_level(); }
void emit(CodeEmitContext* ctx) override { void emit(CodeEmitContext* ctx) override {
value->emit(ctx); if(is_starred()){
key->emit(ctx); // reverse order PK_ASSERT(key == nullptr);
ctx->emit(OP_BUILD_TUPLE, 2, line); value->emit(ctx);
}else{
value->emit(ctx);
key->emit(ctx); // reverse order
ctx->emit(OP_BUILD_TUPLE, 2, line);
}
} }
}; };
@ -392,7 +398,11 @@ struct SequenceExpr: Expr{
struct ListExpr: SequenceExpr{ struct ListExpr: SequenceExpr{
using SequenceExpr::SequenceExpr; using SequenceExpr::SequenceExpr;
std::string str() const override { return "List()"; } std::string str() const override { return "List()"; }
Opcode opcode() const override { return OP_BUILD_LIST; }
Opcode opcode() const override {
for(auto& e: items) if(e->is_starred()) return OP_BUILD_LIST_UNPACK;
return OP_BUILD_LIST;
}
bool is_json_object() const override { return true; } bool is_json_object() const override { return true; }
}; };
@ -400,7 +410,10 @@ struct ListExpr: SequenceExpr{
struct DictExpr: SequenceExpr{ struct DictExpr: SequenceExpr{
using SequenceExpr::SequenceExpr; using SequenceExpr::SequenceExpr;
std::string str() const override { return "Dict()"; } std::string str() const override { return "Dict()"; }
Opcode opcode() const override { return OP_BUILD_DICT; } Opcode opcode() const override {
for(auto& e: items) if(e->is_starred()) return OP_BUILD_DICT_UNPACK;
return OP_BUILD_DICT;
}
bool is_json_object() const override { return true; } bool is_json_object() const override { return true; }
}; };
@ -408,13 +421,19 @@ struct DictExpr: SequenceExpr{
struct SetExpr: SequenceExpr{ struct SetExpr: SequenceExpr{
using SequenceExpr::SequenceExpr; using SequenceExpr::SequenceExpr;
std::string str() const override { return "Set()"; } std::string str() const override { return "Set()"; }
Opcode opcode() const override { return OP_BUILD_SET; } Opcode opcode() const override {
for(auto& e: items) if(e->is_starred()) return OP_BUILD_SET_UNPACK;
return OP_BUILD_SET;
}
}; };
struct TupleExpr: SequenceExpr{ struct TupleExpr: SequenceExpr{
using SequenceExpr::SequenceExpr; using SequenceExpr::SequenceExpr;
std::string str() const override { return "Tuple()"; } std::string str() const override { return "Tuple()"; }
Opcode opcode() const override { return OP_BUILD_TUPLE; } Opcode opcode() const override {
for(auto& e: items) if(e->is_starred()) return OP_BUILD_TUPLE_UNPACK;
return OP_BUILD_TUPLE;
}
bool emit_store(CodeEmitContext* ctx) override { bool emit_store(CodeEmitContext* ctx) override {
// TOS is an iterable // TOS is an iterable
@ -644,37 +663,55 @@ struct AttribExpr: Expr{
struct CallExpr: Expr{ struct CallExpr: Expr{
Expr_ callable; Expr_ callable;
std::vector<Expr_> args; std::vector<Expr_> args;
// **a will be interpreted as a special keyword argument: {"**": a}
std::vector<std::pair<Str, Expr_>> kwargs; std::vector<std::pair<Str, Expr_>> kwargs;
std::string str() const override { return "Call()"; } std::string str() const override { return "Call()"; }
bool need_unpack() const {
for(auto& item: args) if(item->is_starred()) return true;
return false;
}
void emit(CodeEmitContext* ctx) override { void emit(CodeEmitContext* ctx) override {
VM* vm = ctx->vm; bool vargs = false;
if(need_unpack()) ctx->emit(OP_BEGIN_CALL, BC_NOARG, line); bool vkwargs = false;
for(auto& arg: args) if(arg->is_starred()) vargs = true;
for(auto& item: kwargs) if(item.second->is_starred()) vkwargs = true;
// if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy // if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy
if(callable->is_attrib()){ if(callable->is_attrib()){
auto p = static_cast<AttribExpr*>(callable.get()); auto p = static_cast<AttribExpr*>(callable.get());
p->emit_method(ctx); p->emit_method(ctx); // OP_LOAD_METHOD
}else{ }else{
callable->emit(ctx); callable->emit(ctx);
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
} }
// emit args
for(auto& item: args) item->emit(ctx); if(vargs || vkwargs){
// emit kwargs for(auto& item: args) item->emit(ctx);
for(auto& item: kwargs){ ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line);
int index = StrName::get(item.first.sv()).index;
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(index)), line); for(auto& item: kwargs){
item.second->emit(ctx); if(item.second->is_starred()){
if(item.second->star_level() != 2) FATAL_ERROR();
item.second->emit(ctx);
}else{
// k=v
int index = ctx->add_const(py_var(ctx->vm, item.first));
ctx->emit(OP_LOAD_CONST, index, line);
item.second->emit(ctx);
ctx->emit(OP_BUILD_TUPLE, 2, line);
}
}
ctx->emit(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line);
ctx->emit(OP_CALL_TP, BC_NOARG, line);
}else{
// vectorcall protocal
for(auto& item: args) item->emit(ctx);
for(auto& item: kwargs){
int index = StrName(item.first.sv()).index;
ctx->emit(OP_LOAD_INTEGER, index, line);
item.second->emit(ctx);
}
int KWARGC = (int)kwargs.size();
int ARGC = (int)args.size();
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
} }
int KWARGC = (int)kwargs.size();
int ARGC = (int)args.size();
if(need_unpack()) ARGC = 0xFFFF;
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
} }
}; };

View File

@ -238,6 +238,7 @@ struct MemoryPool{
} }
}; };
// TODO: make them thread-safe
inline MemoryPool<64> pool64; inline MemoryPool<64> pool64;
inline MemoryPool<128> pool128; inline MemoryPool<128> pool128;

View File

@ -60,8 +60,9 @@ struct FuncDecl {
}; };
CodeObject_ code; // code object of this function CodeObject_ code; // code object of this function
pod_vector<int> args; // indices in co->varnames pod_vector<int> args; // indices in co->varnames
int starred_arg = -1; // index in co->varnames, -1 if no *arg
pod_vector<KwArg> kwargs; // indices in co->varnames pod_vector<KwArg> kwargs; // indices in co->varnames
int starred_arg = -1; // index in co->varnames, -1 if no *arg
int starred_kwarg = -1; // index in co->varnames, -1 if no **kwarg
bool nested = false; // whether this function is nested bool nested = false; // whether this function is nested
void _gc_mark() const; void _gc_mark() const;
}; };
@ -101,6 +102,12 @@ struct Range {
i64 step = 1; i64 step = 1;
}; };
struct StarWrapper{
int level; // either 1 or 2
PyObject* obj;
StarWrapper(int level, PyObject* obj) : level(level), obj(obj) {}
};
struct Bytes{ struct Bytes{
std::vector<char> _data; std::vector<char> _data;
bool _ok; bool _ok;
@ -335,6 +342,16 @@ struct Py_<BoundMethod> final: PyObject {
} }
}; };
template<>
struct Py_<StarWrapper> final: PyObject {
StarWrapper _value;
void* value() override { return &_value; }
Py_(Type type, StarWrapper val): PyObject(type), _value(val) {}
void _obj_gc_mark() override {
OBJ_MARK(_value.obj);
}
};
template<> template<>
struct Py_<Property> final: PyObject { struct Py_<Property> final: PyObject {
Property _value; Property _value;

View File

@ -38,13 +38,18 @@ OPCODE(DELETE_GLOBAL)
OPCODE(DELETE_ATTR) OPCODE(DELETE_ATTR)
OPCODE(DELETE_SUBSCR) OPCODE(DELETE_SUBSCR)
/**************************/ /**************************/
OPCODE(BUILD_TUPLE)
OPCODE(BUILD_LIST) OPCODE(BUILD_LIST)
OPCODE(BUILD_DICT) OPCODE(BUILD_DICT)
OPCODE(BUILD_SET) OPCODE(BUILD_SET)
OPCODE(BUILD_SLICE) OPCODE(BUILD_SLICE)
OPCODE(BUILD_TUPLE)
OPCODE(BUILD_STRING) OPCODE(BUILD_STRING)
/**************************/ /**************************/
OPCODE(BUILD_TUPLE_UNPACK)
OPCODE(BUILD_LIST_UNPACK)
OPCODE(BUILD_DICT_UNPACK)
OPCODE(BUILD_SET_UNPACK)
/**************************/
OPCODE(BINARY_TRUEDIV) OPCODE(BINARY_TRUEDIV)
OPCODE(BINARY_POW) OPCODE(BINARY_POW)
@ -81,8 +86,8 @@ OPCODE(LOOP_CONTINUE)
OPCODE(LOOP_BREAK) OPCODE(LOOP_BREAK)
OPCODE(GOTO) OPCODE(GOTO)
/**************************/ /**************************/
OPCODE(BEGIN_CALL)
OPCODE(CALL) OPCODE(CALL)
OPCODE(CALL_TP)
OPCODE(RETURN_VALUE) OPCODE(RETURN_VALUE)
OPCODE(YIELD_VALUE) OPCODE(YIELD_VALUE)
/**************************/ /**************************/
@ -92,6 +97,7 @@ OPCODE(SET_ADD)
/**************************/ /**************************/
OPCODE(UNARY_NEGATIVE) OPCODE(UNARY_NEGATIVE)
OPCODE(UNARY_NOT) OPCODE(UNARY_NOT)
OPCODE(UNARY_STAR)
/**************************/ /**************************/
OPCODE(GET_ITER) OPCODE(GET_ITER)
OPCODE(FOR_ITER) OPCODE(FOR_ITER)
@ -102,7 +108,6 @@ OPCODE(IMPORT_STAR)
/**************************/ /**************************/
OPCODE(UNPACK_SEQUENCE) OPCODE(UNPACK_SEQUENCE)
OPCODE(UNPACK_EX) OPCODE(UNPACK_EX)
OPCODE(UNPACK_UNLIMITED)
/**************************/ /**************************/
OPCODE(BEGIN_CLASS) OPCODE(BEGIN_CLASS)
OPCODE(END_CLASS) OPCODE(END_CLASS)

View File

@ -124,19 +124,37 @@ struct Str{
return memcmp(data, other.data, size) != 0; return memcmp(data, other.data, size) != 0;
} }
bool operator==(const std::string_view other) const {
if(size != (int)other.size()) return false;
return memcmp(data, other.data(), size) == 0;
}
bool operator!=(const std::string_view other) const {
if(size != (int)other.size()) return true;
return memcmp(data, other.data(), size) != 0;
}
bool operator==(const char* p) const {
return *this == std::string_view(p);
}
bool operator!=(const char* p) const {
return *this != std::string_view(p);
}
bool operator<(const Str& other) const { bool operator<(const Str& other) const {
int ret = strncmp(data, other.data, std::min(size, other.size)); int ret = strncmp(data, other.data, std::min(size, other.size));
if(ret != 0) return ret < 0; if(ret != 0) return ret < 0;
return size < other.size; return size < other.size;
} }
bool operator<(const std::string_view& other) const { bool operator<(const std::string_view other) const {
int ret = strncmp(data, other.data(), std::min(size, (int)other.size())); int ret = strncmp(data, other.data(), std::min(size, (int)other.size()));
if(ret != 0) return ret < 0; if(ret != 0) return ret < 0;
return size < (int)other.size(); return size < (int)other.size();
} }
friend bool operator<(const std::string_view& other, const Str& str){ friend bool operator<(const std::string_view other, const Str& str){
return str > other; return str > other;
} }

View File

@ -134,7 +134,7 @@ public:
Type tp_function, tp_native_func, tp_bound_method; Type tp_function, tp_native_func, tp_bound_method;
Type tp_slice, tp_range, tp_module; Type tp_slice, tp_range, tp_module;
Type tp_super, tp_exception, tp_bytes, tp_mappingproxy; Type tp_super, tp_exception, tp_bytes, tp_mappingproxy;
Type tp_dict, tp_property; Type tp_dict, tp_property, tp_star_wrapper;
const bool enable_os; const bool enable_os;
@ -637,6 +637,8 @@ public:
#if DEBUG_CEVAL_STEP #if DEBUG_CEVAL_STEP
void _log_s_data(const char* title = nullptr); void _log_s_data(const char* title = nullptr);
#endif #endif
void _unpack_as_list(ArgsView args, List& list);
void _unpack_as_dict(ArgsView args, Dict& dict);
PyObject* vectorcall(int ARGC, int KWARGC=0, bool op_call=false); PyObject* vectorcall(int ARGC, int KWARGC=0, bool op_call=false);
CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope=false); CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope=false);
PyObject* py_negate(PyObject* obj); PyObject* py_negate(PyObject* obj);
@ -686,6 +688,7 @@ DEF_NATIVE_2(Bytes, tp_bytes)
DEF_NATIVE_2(MappingProxy, tp_mappingproxy) DEF_NATIVE_2(MappingProxy, tp_mappingproxy)
DEF_NATIVE_2(Dict, tp_dict) DEF_NATIVE_2(Dict, tp_dict)
DEF_NATIVE_2(Property, tp_property) DEF_NATIVE_2(Property, tp_property)
DEF_NATIVE_2(StarWrapper, tp_star_wrapper)
#undef DEF_NATIVE_2 #undef DEF_NATIVE_2
@ -1063,7 +1066,6 @@ inline void VM::_log_s_data(const char* title) {
if(sp_bases[p] > 0) ss << " "; if(sp_bases[p] > 0) ss << " ";
PyObject* obj = *p; PyObject* obj = *p;
if(obj == nullptr) ss << "(nil)"; if(obj == nullptr) ss << "(nil)";
else if(obj == PY_BEGIN_CALL) ss << "BEGIN_CALL";
else if(obj == PY_NULL) ss << "NULL"; else if(obj == PY_NULL) ss << "NULL";
else if(is_int(obj)) ss << CAST(i64, obj); else if(is_int(obj)) ss << CAST(i64, obj);
else if(is_float(obj)) ss << CAST(f64, obj); else if(is_float(obj)) ss << CAST(f64, obj);
@ -1121,6 +1123,7 @@ inline void VM::init_builtin_types(){
tp_mappingproxy = _new_type_object("mappingproxy"); tp_mappingproxy = _new_type_object("mappingproxy");
tp_dict = _new_type_object("dict"); tp_dict = _new_type_object("dict");
tp_property = _new_type_object("property"); tp_property = _new_type_object("property");
tp_star_wrapper = _new_type_object("_star_wrapper");
this->None = heap._new<Dummy>(_new_type_object("NoneType"), {}); this->None = heap._new<Dummy>(_new_type_object("NoneType"), {});
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {}); this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
@ -1154,21 +1157,47 @@ inline void VM::init_builtin_types(){
this->_main = new_module("__main__"); this->_main = new_module("__main__");
} }
inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ // `heap.gc_scope_lock();` needed before calling this function
bool is_varargs = ARGC == 0xFFFF; inline void VM::_unpack_as_list(ArgsView args, List& list){
PyObject** p0; for(PyObject* obj: args){
PyObject** p1 = s_data._sp - KWARGC*2; if(is_non_tagged_type(obj, tp_star_wrapper)){
if(is_varargs){ const StarWrapper& w = _CAST(StarWrapper&, obj);
p0 = p1 - 1; // maybe this check should be done in the compile time
while(*p0 != PY_BEGIN_CALL) p0--; if(w.level != 1) TypeError("expected level 1 star wrapper");
// [BEGIN_CALL, callable, <self>, args..., kwargs...] PyObject* _0 = py_iter(w.obj);
// ^p0 ^p1 ^_sp PyObject* _1 = py_next(_0);
ARGC = p1 - (p0 + 3); while(_1 != StopIteration){
}else{ list.push_back(_1);
p0 = p1 - ARGC - 2 - (int)is_varargs; _1 = py_next(_0);
// [callable, <self>, args..., kwargs...] }
// ^p0 ^p1 ^_sp }else{
list.push_back(obj);
}
} }
}
// `heap.gc_scope_lock();` needed before calling this function
inline void VM::_unpack_as_dict(ArgsView args, Dict& dict){
for(PyObject* obj: args){
if(is_non_tagged_type(obj, tp_star_wrapper)){
const StarWrapper& w = _CAST(StarWrapper&, obj);
// maybe this check should be done in the compile time
if(w.level != 2) TypeError("expected level 2 star wrapper");
const Dict& other = CAST(Dict&, w.obj);
dict.update(other);
}else{
const Tuple& t = CAST(Tuple&, obj);
if(t.size() != 2) TypeError("expected tuple of length 2");
dict.set(t[0], t[1]);
}
}
}
inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
PyObject** p1 = s_data._sp - KWARGC*2;
PyObject** p0 = p1 - ARGC - 2;
// [callable, <self>, args..., kwargs...]
// ^p0 ^p1 ^_sp
PyObject* callable = p1[-(ARGC + 2)]; PyObject* callable = p1[-(ARGC + 2)];
bool method_call = p1[-(ARGC + 1)] != PY_NULL; bool method_call = p1[-(ARGC + 1)] != PY_NULL;
@ -1249,11 +1278,27 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
if(i < args.size()) TypeError(fmt("too many arguments", " (", fn.decl->code->name, ')')); if(i < args.size()) TypeError(fmt("too many arguments", " (", fn.decl->code->name, ')'));
} }
PyObject* vkwargs;
if(fn.decl->starred_kwarg != -1){
vkwargs = VAR(Dict(this));
buffer[fn.decl->starred_kwarg] = vkwargs;
}else{
vkwargs = nullptr;
}
for(int i=0; i<kwargs.size(); i+=2){ for(int i=0; i<kwargs.size(); i+=2){
StrName key = CAST(int, kwargs[i]); StrName key = CAST(int, kwargs[i]);
int index = co->varnames_inv.try_get(key); int index = co->varnames_inv.try_get(key);
if(index<0) TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()")); if(index < 0){
buffer[index] = kwargs[i+1]; if(vkwargs == nullptr){
TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
}else{
Dict& dict = _CAST(Dict&, vkwargs);
dict.set(VAR(key.sv()), kwargs[i+1]);
}
}else{
buffer[index] = kwargs[i+1];
}
} }
if(co->is_generator){ if(co->is_generator){

View File

@ -74,4 +74,13 @@ assert a == [8, 2, 4, 2, 9]
b = [(1, 2), (3, 3), (5, 1)] b = [(1, 2), (3, 3), (5, 1)]
b.sort(key=lambda x:x[1]) b.sort(key=lambda x:x[1])
assert b == [(5, 1), (1, 2), (3,3)] assert b == [(5, 1), (1, 2), (3,3)]
# unpack expression
a = [1, 2, 3]
b = [*a, 4, 5]
assert b == [1, 2, 3, 4, 5]
a = []
b = [*a, 1, 2, 3, *a, *a]
assert b == [1, 2, 3]

View File

@ -5,4 +5,13 @@ assert b == 2
a,b = b,a a,b = b,a
assert a == 2 assert a == 2
assert b == 1 assert b == 1
assert len(tup) == 6 assert len(tup) == 6
# unpack expression
a = 1, 2, 3
b = *a, 4, 5
assert b == (1, 2, 3, 4, 5)
a = tuple([])
b = *a, 1, 2, 3, *a, *a
assert b == (1, 2, 3)

View File

@ -52,4 +52,18 @@ assert a == {1: 2, 3: 4}
assert a.pop(1) == 2 assert a.pop(1) == 2
assert a == {3: 4} assert a == {3: 4}
assert a.pop(3) == 4 assert a.pop(3) == 4
assert a == {} assert a == {}
# unpack expression
a = {1:2, 3:4}
b = {**a, 5:6, **a}
assert b == {1: 2, 3: 4, 5: 6}
a = {}
b = {**a, 1:2, 3:4}
assert b == {1: 2, 3: 4}
a = {1:2, 3:4, 7:8}
b = {**a, 1:5, 3:6}
c = {**a, **b}
assert c == {1: 5, 3: 6, 7: 8}

View File

@ -76,4 +76,13 @@ assert type({}) is dict
assert {1,2}.issubset({1,2,3}) assert {1,2}.issubset({1,2,3})
assert {1,2,3}.issuperset({1,2}) assert {1,2,3}.issuperset({1,2})
assert {1,2,3}.isdisjoint({4,5,6}) assert {1,2,3}.isdisjoint({4,5,6})
assert not {1,2,3}.isdisjoint({2,3,4}) assert not {1,2,3}.isdisjoint({2,3,4})
# unpack expression
a = {1, 2, 3}
b = {*a, 4, 5, *a, *a}
assert b == {1, 2, 3, 4, 5}
a = set()
b = {*a, 1, 2, 3, *a, *a}
assert b == {1, 2, 3}

View File

@ -47,6 +47,9 @@ assert f(10, 1, 2, 3) == 18
def f(a, b, *c, d=2, e=5): def f(a, b, *c, d=2, e=5):
return a + b + d + e + sum(c) return a + b + d + e + sum(c)
def g(*args, **kwargs):
return f(*args, **kwargs)
assert f(1, 2, 3, 4) == 17 assert f(1, 2, 3, 4) == 17
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 62 assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 62
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1, e=2) == 58 assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1, e=2) == 58
@ -56,6 +59,13 @@ assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, e=1) == 58
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) == 217 assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) == 217
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, d=1, e=2) == 213 assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, d=1, e=2) == 213
assert g(1, 2, 3, 4) == 17
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 62
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1, e=2) == 58
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, e=1, d=2) == 58
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1) == 61
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, e=1) == 58
a = 1 a = 1
b = 2 b = 2
@ -66,4 +76,19 @@ def f():
f() f()
assert a == 3 assert a == 3
assert b == 4 assert b == 4
def g(a, b, *args, c=1, d=2, **kwargs):
S = a + b + c + d + sum(args)
return S, kwargs
S, kwargs = g(1, 2, 3, 4, 5, c=4, e=5, f=6)
# a = 1
# b = 2
# c = 4
# d = 2
# sum(args) = 3 + 4 + 5 = 12
# S = 1 + 2 + 4 + 2 + 12 = 21
assert S == 21
assert kwargs == {'e': 5, 'f': 6}

View File

@ -52,4 +52,15 @@ try:
x = f1(*[1, 2, 3, 4]) x = f1(*[1, 2, 3, 4])
exit(1) exit(1)
except TypeError: except TypeError:
pass pass
def g(*args, **kwargs):
return args, kwargs
def f(a, b, *args, c=1, **kwargs):
return g(a, b, *args, c=c, **kwargs)
args, kwargs = f(1, 2, 3, 4, c=5, d=6, e=-6.0)
assert args == (1, 2, 3, 4)
assert kwargs == {'c': 5, 'd': 6, 'e': -6.0}