mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
Merge branch 'starred_kwargs'
This commit is contained in:
commit
d5500c246c
@ -20,17 +20,16 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
|
||||
|
||||
## Unimplemented features
|
||||
|
||||
1. `**kwargs` in function definition.
|
||||
2. `__getattr__` and `__setattr__`.
|
||||
3. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
|
||||
4. `__slots__` in class definition.
|
||||
5. One element tuple. `(1,)` is not supported.
|
||||
6. Unpacking in `list` and `dict` literals, e.g. `[1, 2, *a]`.
|
||||
7. Access the exception object in try..except.
|
||||
8. `else` clause in try..except.
|
||||
9. Inplace methods like `__iadd__` and `__imul__`.
|
||||
10. `__del__` in class definition.
|
||||
11. Multiple inheritance.
|
||||
1. `__getattr__` and `__setattr__`.
|
||||
2. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
|
||||
3. `__slots__` in class definition.
|
||||
4. One element tuple. `(1,)` is not supported.
|
||||
5. Unpacking in `list` and `dict` literals, e.g. `[1, 2, *a]`.
|
||||
6. Access the exception object in try..except.
|
||||
7. `else` clause in try..except.
|
||||
8. Inplace methods like `__iadd__` and `__imul__`.
|
||||
9. `__del__` in class definition.
|
||||
10. Multiple inheritance.
|
||||
|
||||
## Different behaviors
|
||||
|
||||
|
78
src/ceval.h
78
src/ceval.h
@ -91,7 +91,7 @@ __NEXT_STEP:;
|
||||
TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH();
|
||||
TARGET(LOAD_FUNCTION) {
|
||||
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();
|
||||
PyObject* obj;
|
||||
if(decl->nested){
|
||||
@ -236,6 +236,11 @@ __NEXT_STEP:;
|
||||
}
|
||||
DISPATCH();
|
||||
/*****************************************/
|
||||
TARGET(BUILD_TUPLE)
|
||||
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
|
||||
STACK_SHRINK(byte.arg);
|
||||
PUSH(_0);
|
||||
DISPATCH();
|
||||
TARGET(BUILD_LIST)
|
||||
_0 = VAR(STACK_VIEW(byte.arg).to_list());
|
||||
STACK_SHRINK(byte.arg);
|
||||
@ -263,11 +268,6 @@ __NEXT_STEP:;
|
||||
_0 = POPX(); // start
|
||||
PUSH(VAR(Slice(_0, _1, _2)));
|
||||
DISPATCH();
|
||||
TARGET(BUILD_TUPLE)
|
||||
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
|
||||
STACK_SHRINK(byte.arg);
|
||||
PUSH(_0);
|
||||
DISPATCH();
|
||||
TARGET(BUILD_STRING) {
|
||||
std::stringstream ss;
|
||||
ArgsView view = STACK_VIEW(byte.arg);
|
||||
@ -276,6 +276,40 @@ __NEXT_STEP:;
|
||||
PUSH(VAR(ss.str()));
|
||||
} 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) \
|
||||
if(is_both_int(TOP(), SECOND())){ \
|
||||
_1 = POPX(); \
|
||||
@ -426,9 +460,6 @@ __NEXT_STEP:;
|
||||
frame->jump_abs_break(index);
|
||||
} DISPATCH();
|
||||
/*****************************************/
|
||||
TARGET(BEGIN_CALL)
|
||||
PUSH(PY_BEGIN_CALL);
|
||||
DISPATCH();
|
||||
TARGET(CALL)
|
||||
_0 = vectorcall(
|
||||
byte.arg & 0xFFFF, // ARGC
|
||||
@ -438,6 +469,23 @@ __NEXT_STEP:;
|
||||
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
|
||||
PUSH(_0);
|
||||
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)
|
||||
_0 = POPX();
|
||||
_pop_frame();
|
||||
@ -471,6 +519,9 @@ __NEXT_STEP:;
|
||||
TARGET(UNARY_NOT)
|
||||
TOP() = VAR(!py_bool(TOP()));
|
||||
DISPATCH();
|
||||
TARGET(UNARY_STAR)
|
||||
TOP() = VAR(StarWrapper(byte.arg, TOP()));
|
||||
DISPATCH();
|
||||
/*****************************************/
|
||||
TARGET(GET_ITER)
|
||||
TOP() = py_iter(TOP());
|
||||
@ -528,15 +579,6 @@ __NEXT_STEP:;
|
||||
}
|
||||
PUSH(VAR(extras));
|
||||
} 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)
|
||||
_name = StrName(byte.arg);
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include <variant>
|
||||
#include <type_traits>
|
||||
|
||||
#define PK_VERSION "1.0.1"
|
||||
#define PK_VERSION "1.0.2"
|
||||
|
||||
// debug macros
|
||||
#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()!");
|
||||
#endif
|
||||
|
||||
#define PK_ASSERT(x) if(!(x)) FATAL_ERROR();
|
||||
|
||||
inline const float kInstAttrLoadFactor = 0.67f;
|
||||
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
|
||||
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_YIELD = (PyObject*)0b110011;
|
||||
|
||||
|
@ -90,7 +90,7 @@ class Compiler {
|
||||
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_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 };
|
||||
@ -280,7 +280,10 @@ class Compiler {
|
||||
ctx()->s_expr.push(make_expr<NegatedExpr>(ctx()->s_expr.popx()));
|
||||
break;
|
||||
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;
|
||||
default: FATAL_ERROR();
|
||||
}
|
||||
@ -319,7 +322,6 @@ class Compiler {
|
||||
if (curr().type == TK("]")) break;
|
||||
EXPR();
|
||||
items.push_back(ctx()->s_expr.popx());
|
||||
if(items.back()->is_starred()) SyntaxError();
|
||||
match_newlines_repl();
|
||||
if(items.size()==1 && match(TK("for"))){
|
||||
_consume_comp<ListCompExpr>(std::move(items[0]));
|
||||
@ -341,19 +343,24 @@ class Compiler {
|
||||
match_newlines_repl();
|
||||
if (curr().type == TK("}")) break;
|
||||
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){
|
||||
auto dict_item = make_expr<DictItemExpr>();
|
||||
if(star_level == 2){
|
||||
dict_item->key = nullptr;
|
||||
dict_item->value = ctx()->s_expr.popx();
|
||||
}else{
|
||||
consume(TK(":"));
|
||||
EXPR();
|
||||
auto dict_item = make_expr<DictItemExpr>();
|
||||
dict_item->key = ctx()->s_expr.popx();
|
||||
dict_item->value = ctx()->s_expr.popx();
|
||||
if(dict_item->key->is_starred()) SyntaxError();
|
||||
if(dict_item->value->is_starred()) SyntaxError();
|
||||
}
|
||||
items.push_back(std::move(dict_item));
|
||||
}else{
|
||||
items.push_back(ctx()->s_expr.popx());
|
||||
if(items.back()->is_starred()) SyntaxError();
|
||||
}
|
||||
match_newlines_repl();
|
||||
if(items.size()==1 && match(TK("for"))){
|
||||
@ -387,10 +394,16 @@ class Compiler {
|
||||
EXPR();
|
||||
e->kwargs.push_back({key, ctx()->s_expr.popx()});
|
||||
} else{
|
||||
if(!e->kwargs.empty()) SyntaxError("positional argument follows keyword argument");
|
||||
EXPR();
|
||||
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();
|
||||
} while (match(TK(",")));
|
||||
consume(TK(")"));
|
||||
@ -876,6 +889,7 @@ __SUBSCR_END:
|
||||
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
|
||||
do {
|
||||
if(state > 3) SyntaxError();
|
||||
if(state == 3) SyntaxError("**kwargs should be the last argument");
|
||||
match_newlines();
|
||||
if(match(TK("*"))){
|
||||
@ -894,14 +908,17 @@ __SUBSCR_END:
|
||||
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){
|
||||
if(decl->code->varnames[kv.key] == 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
|
||||
if(enable_type_hints && match(TK(":"))) consume_type_hints();
|
||||
@ -924,7 +941,10 @@ __SUBSCR_END:
|
||||
}
|
||||
decl->kwargs.push_back(FuncDecl::KwArg{index, value});
|
||||
} break;
|
||||
case 3: SyntaxError("**kwargs is not supported yet"); break;
|
||||
case 3:
|
||||
decl->starred_kwarg = index;
|
||||
state+=1;
|
||||
break;
|
||||
}
|
||||
} while (match(TK(",")));
|
||||
}
|
||||
|
@ -124,6 +124,14 @@ struct Dict{
|
||||
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(){
|
||||
memset(_items, 0, _capacity * sizeof(Item));
|
||||
_size = 0;
|
||||
|
91
src/expr.h
91
src/expr.h
@ -19,11 +19,12 @@ struct Expr{
|
||||
virtual void emit(CodeEmitContext* ctx) = 0;
|
||||
virtual std::string str() const = 0;
|
||||
|
||||
virtual bool is_starred() const { return false; }
|
||||
virtual bool is_literal() const { return false; }
|
||||
virtual bool is_json_object() const { return false; }
|
||||
virtual bool is_attrib() 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
|
||||
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
|
||||
@ -183,24 +184,25 @@ struct NameExpr: Expr{
|
||||
};
|
||||
|
||||
struct StarredExpr: Expr{
|
||||
int level;
|
||||
Expr_ child;
|
||||
StarredExpr(Expr_&& child): child(std::move(child)) {}
|
||||
std::string str() const override { return "Starred()"; }
|
||||
StarredExpr(int level, Expr_&& child): level(level), child(std::move(child)) {}
|
||||
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 {
|
||||
child->emit(ctx);
|
||||
ctx->emit(OP_UNPACK_UNLIMITED, BC_NOARG, line);
|
||||
ctx->emit(OP_UNARY_STAR, level, line);
|
||||
}
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
if(level != 1) return false;
|
||||
// simply proxy to child
|
||||
return child->emit_store(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct NotExpr: Expr{
|
||||
Expr_ child;
|
||||
NotExpr(Expr_&& child): child(std::move(child)) {}
|
||||
@ -265,16 +267,13 @@ struct LiteralExpr: Expr{
|
||||
if(std::holds_alternative<i64>(value)){
|
||||
return std::to_string(std::get<i64>(value));
|
||||
}
|
||||
|
||||
if(std::holds_alternative<f64>(value)){
|
||||
return std::to_string(std::get<f64>(value));
|
||||
}
|
||||
|
||||
if(std::holds_alternative<Str>(value)){
|
||||
Str s = std::get<Str>(value).escape();
|
||||
return s.str();
|
||||
}
|
||||
|
||||
FATAL_ERROR();
|
||||
}
|
||||
|
||||
@ -367,15 +366,22 @@ struct SliceExpr: Expr{
|
||||
};
|
||||
|
||||
struct DictItemExpr: Expr{
|
||||
Expr_ key;
|
||||
Expr_ key; // maybe nullptr if it is **kwargs
|
||||
Expr_ value;
|
||||
std::string str() const override { return "DictItem()"; }
|
||||
|
||||
int star_level() const override { return value->star_level(); }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
if(is_starred()){
|
||||
PK_ASSERT(key == nullptr);
|
||||
value->emit(ctx);
|
||||
}else{
|
||||
value->emit(ctx);
|
||||
key->emit(ctx); // reverse order
|
||||
ctx->emit(OP_BUILD_TUPLE, 2, line);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SequenceExpr: Expr{
|
||||
@ -392,7 +398,11 @@ struct SequenceExpr: Expr{
|
||||
struct ListExpr: SequenceExpr{
|
||||
using SequenceExpr::SequenceExpr;
|
||||
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; }
|
||||
};
|
||||
@ -400,7 +410,10 @@ struct ListExpr: SequenceExpr{
|
||||
struct DictExpr: SequenceExpr{
|
||||
using SequenceExpr::SequenceExpr;
|
||||
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; }
|
||||
};
|
||||
@ -408,13 +421,19 @@ struct DictExpr: SequenceExpr{
|
||||
struct SetExpr: SequenceExpr{
|
||||
using SequenceExpr::SequenceExpr;
|
||||
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{
|
||||
using SequenceExpr::SequenceExpr;
|
||||
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 {
|
||||
// TOS is an iterable
|
||||
@ -644,38 +663,56 @@ struct AttribExpr: Expr{
|
||||
struct CallExpr: Expr{
|
||||
Expr_ callable;
|
||||
std::vector<Expr_> args;
|
||||
// **a will be interpreted as a special keyword argument: {"**": a}
|
||||
std::vector<std::pair<Str, Expr_>> kwargs;
|
||||
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 {
|
||||
VM* vm = ctx->vm;
|
||||
if(need_unpack()) ctx->emit(OP_BEGIN_CALL, BC_NOARG, line);
|
||||
bool vargs = false;
|
||||
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_attrib()){
|
||||
auto p = static_cast<AttribExpr*>(callable.get());
|
||||
p->emit_method(ctx);
|
||||
p->emit_method(ctx); // OP_LOAD_METHOD
|
||||
}else{
|
||||
callable->emit(ctx);
|
||||
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
// emit args
|
||||
|
||||
if(vargs || vkwargs){
|
||||
for(auto& item: args) item->emit(ctx);
|
||||
// emit kwargs
|
||||
ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line);
|
||||
|
||||
for(auto& item: kwargs){
|
||||
int index = StrName::get(item.first.sv()).index;
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(index)), line);
|
||||
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();
|
||||
if(need_unpack()) ARGC = 0xFFFF;
|
||||
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct BinaryExpr: Expr{
|
||||
|
@ -238,6 +238,7 @@ struct MemoryPool{
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: make them thread-safe
|
||||
inline MemoryPool<64> pool64;
|
||||
inline MemoryPool<128> pool128;
|
||||
|
||||
|
19
src/obj.h
19
src/obj.h
@ -60,8 +60,9 @@ struct FuncDecl {
|
||||
};
|
||||
CodeObject_ code; // code object of this function
|
||||
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
|
||||
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
|
||||
void _gc_mark() const;
|
||||
};
|
||||
@ -101,6 +102,12 @@ struct Range {
|
||||
i64 step = 1;
|
||||
};
|
||||
|
||||
struct StarWrapper{
|
||||
int level; // either 1 or 2
|
||||
PyObject* obj;
|
||||
StarWrapper(int level, PyObject* obj) : level(level), obj(obj) {}
|
||||
};
|
||||
|
||||
struct Bytes{
|
||||
std::vector<char> _data;
|
||||
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<>
|
||||
struct Py_<Property> final: PyObject {
|
||||
Property _value;
|
||||
|
@ -38,13 +38,18 @@ OPCODE(DELETE_GLOBAL)
|
||||
OPCODE(DELETE_ATTR)
|
||||
OPCODE(DELETE_SUBSCR)
|
||||
/**************************/
|
||||
OPCODE(BUILD_TUPLE)
|
||||
OPCODE(BUILD_LIST)
|
||||
OPCODE(BUILD_DICT)
|
||||
OPCODE(BUILD_SET)
|
||||
OPCODE(BUILD_SLICE)
|
||||
OPCODE(BUILD_TUPLE)
|
||||
OPCODE(BUILD_STRING)
|
||||
/**************************/
|
||||
OPCODE(BUILD_TUPLE_UNPACK)
|
||||
OPCODE(BUILD_LIST_UNPACK)
|
||||
OPCODE(BUILD_DICT_UNPACK)
|
||||
OPCODE(BUILD_SET_UNPACK)
|
||||
/**************************/
|
||||
OPCODE(BINARY_TRUEDIV)
|
||||
OPCODE(BINARY_POW)
|
||||
|
||||
@ -81,8 +86,8 @@ OPCODE(LOOP_CONTINUE)
|
||||
OPCODE(LOOP_BREAK)
|
||||
OPCODE(GOTO)
|
||||
/**************************/
|
||||
OPCODE(BEGIN_CALL)
|
||||
OPCODE(CALL)
|
||||
OPCODE(CALL_TP)
|
||||
OPCODE(RETURN_VALUE)
|
||||
OPCODE(YIELD_VALUE)
|
||||
/**************************/
|
||||
@ -92,6 +97,7 @@ OPCODE(SET_ADD)
|
||||
/**************************/
|
||||
OPCODE(UNARY_NEGATIVE)
|
||||
OPCODE(UNARY_NOT)
|
||||
OPCODE(UNARY_STAR)
|
||||
/**************************/
|
||||
OPCODE(GET_ITER)
|
||||
OPCODE(FOR_ITER)
|
||||
@ -102,7 +108,6 @@ OPCODE(IMPORT_STAR)
|
||||
/**************************/
|
||||
OPCODE(UNPACK_SEQUENCE)
|
||||
OPCODE(UNPACK_EX)
|
||||
OPCODE(UNPACK_UNLIMITED)
|
||||
/**************************/
|
||||
OPCODE(BEGIN_CLASS)
|
||||
OPCODE(END_CLASS)
|
||||
|
22
src/str.h
22
src/str.h
@ -124,19 +124,37 @@ struct Str{
|
||||
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 {
|
||||
int ret = strncmp(data, other.data, std::min(size, other.size));
|
||||
if(ret != 0) return ret < 0;
|
||||
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()));
|
||||
if(ret != 0) return ret < 0;
|
||||
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;
|
||||
}
|
||||
|
||||
|
75
src/vm.h
75
src/vm.h
@ -134,7 +134,7 @@ public:
|
||||
Type tp_function, tp_native_func, tp_bound_method;
|
||||
Type tp_slice, tp_range, tp_module;
|
||||
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;
|
||||
|
||||
@ -637,6 +637,8 @@ public:
|
||||
#if DEBUG_CEVAL_STEP
|
||||
void _log_s_data(const char* title = nullptr);
|
||||
#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);
|
||||
CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope=false);
|
||||
PyObject* py_negate(PyObject* obj);
|
||||
@ -686,6 +688,7 @@ DEF_NATIVE_2(Bytes, tp_bytes)
|
||||
DEF_NATIVE_2(MappingProxy, tp_mappingproxy)
|
||||
DEF_NATIVE_2(Dict, tp_dict)
|
||||
DEF_NATIVE_2(Property, tp_property)
|
||||
DEF_NATIVE_2(StarWrapper, tp_star_wrapper)
|
||||
|
||||
#undef DEF_NATIVE_2
|
||||
|
||||
@ -1063,7 +1066,6 @@ inline void VM::_log_s_data(const char* title) {
|
||||
if(sp_bases[p] > 0) ss << " ";
|
||||
PyObject* obj = *p;
|
||||
if(obj == nullptr) ss << "(nil)";
|
||||
else if(obj == PY_BEGIN_CALL) ss << "BEGIN_CALL";
|
||||
else if(obj == PY_NULL) ss << "NULL";
|
||||
else if(is_int(obj)) ss << CAST(i64, 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_dict = _new_type_object("dict");
|
||||
tp_property = _new_type_object("property");
|
||||
tp_star_wrapper = _new_type_object("_star_wrapper");
|
||||
|
||||
this->None = heap._new<Dummy>(_new_type_object("NoneType"), {});
|
||||
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
|
||||
@ -1154,21 +1157,47 @@ inline void VM::init_builtin_types(){
|
||||
this->_main = new_module("__main__");
|
||||
}
|
||||
|
||||
inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
||||
bool is_varargs = ARGC == 0xFFFF;
|
||||
PyObject** p0;
|
||||
PyObject** p1 = s_data._sp - KWARGC*2;
|
||||
if(is_varargs){
|
||||
p0 = p1 - 1;
|
||||
while(*p0 != PY_BEGIN_CALL) p0--;
|
||||
// [BEGIN_CALL, callable, <self>, args..., kwargs...]
|
||||
// ^p0 ^p1 ^_sp
|
||||
ARGC = p1 - (p0 + 3);
|
||||
// `heap.gc_scope_lock();` needed before calling this function
|
||||
inline void VM::_unpack_as_list(ArgsView args, List& list){
|
||||
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 != 1) TypeError("expected level 1 star wrapper");
|
||||
PyObject* _0 = py_iter(w.obj);
|
||||
PyObject* _1 = py_next(_0);
|
||||
while(_1 != StopIteration){
|
||||
list.push_back(_1);
|
||||
_1 = py_next(_0);
|
||||
}
|
||||
}else{
|
||||
p0 = p1 - ARGC - 2 - (int)is_varargs;
|
||||
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)];
|
||||
bool method_call = p1[-(ARGC + 1)] != PY_NULL;
|
||||
|
||||
@ -1249,12 +1278,28 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
||||
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){
|
||||
StrName key = CAST(int, kwargs[i]);
|
||||
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){
|
||||
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){
|
||||
s_data.reset(p0);
|
||||
|
@ -75,3 +75,12 @@ assert a == [8, 2, 4, 2, 9]
|
||||
b = [(1, 2), (3, 3), (5, 1)]
|
||||
b.sort(key=lambda x:x[1])
|
||||
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]
|
@ -6,3 +6,12 @@ a,b = b,a
|
||||
assert a == 2
|
||||
assert b == 1
|
||||
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)
|
@ -53,3 +53,17 @@ assert a.pop(1) == 2
|
||||
assert a == {3: 4}
|
||||
assert a.pop(3) == 4
|
||||
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}
|
@ -77,3 +77,12 @@ assert {1,2}.issubset({1,2,3})
|
||||
assert {1,2,3}.issuperset({1,2})
|
||||
assert {1,2,3}.isdisjoint({4,5,6})
|
||||
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}
|
@ -47,6 +47,9 @@ assert f(10, 1, 2, 3) == 18
|
||||
def f(a, b, *c, d=2, e=5):
|
||||
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, 5, 6, 7, 8, 9, 10) == 62
|
||||
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, 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
|
||||
b = 2
|
||||
|
||||
@ -67,3 +77,18 @@ def f():
|
||||
f()
|
||||
assert a == 3
|
||||
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}
|
@ -53,3 +53,14 @@ try:
|
||||
exit(1)
|
||||
except TypeError:
|
||||
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}
|
Loading…
x
Reference in New Issue
Block a user