add function unpack call

This commit is contained in:
blueloveTH 2023-02-24 00:56:41 +08:00
parent b2b56316b2
commit 5e15d526a0
6 changed files with 79 additions and 6 deletions

View File

@ -209,17 +209,33 @@ PyVar VM::run_frame(Frame* frame){
frame->push(obj); frame->push(obj);
} continue; } continue;
case OP_DUP_TOP_VALUE: frame->push(frame->top_value(this)); continue; case OP_DUP_TOP_VALUE: frame->push(frame->top_value(this)); continue;
case OP_CALL: { case OP_UNARY_STAR: {
if(byte.arg > 0){ // rvalue
frame->top() = PyStarWrapper({frame->top_value(this), true});
}else{
PyRef_AS_C(frame->top()); // check ref
frame->top() = PyStarWrapper({frame->top(), false});
}
} continue;
case OP_CALL_KWARGS_UNPACK: case OP_CALL_KWARGS: {
int ARGC = byte.arg & 0xFFFF; int ARGC = byte.arg & 0xFFFF;
int KWARGC = (byte.arg >> 16) & 0xFFFF; int KWARGC = (byte.arg >> 16) & 0xFFFF;
pkpy::Args kwargs(0); pkpy::Args kwargs = frame->pop_n_values_reversed(this, KWARGC*2);
if(KWARGC > 0) kwargs = frame->pop_n_values_reversed(this, KWARGC*2);
pkpy::Args args = frame->pop_n_values_reversed(this, ARGC); pkpy::Args args = frame->pop_n_values_reversed(this, ARGC);
if(byte.op == OP_CALL_KWARGS_UNPACK) unpack_args(args);
PyVar callable = frame->pop_value(this); PyVar callable = frame->pop_value(this);
PyVar ret = call(callable, std::move(args), kwargs, true); PyVar ret = call(callable, std::move(args), kwargs, true);
if(ret == _py_op_call) return ret; if(ret == _py_op_call) return ret;
frame->push(std::move(ret)); frame->push(std::move(ret));
} continue; } continue;
case OP_CALL_UNPACK: case OP_CALL: {
pkpy::Args args = frame->pop_n_values_reversed(this, byte.arg);
if(byte.op == OP_CALL_UNPACK) unpack_args(args);
PyVar callable = frame->pop_value(this);
PyVar ret = call(callable, std::move(args), pkpy::no_arg(), true);
if(ret == _py_op_call) return ret;
frame->push(std::move(ret));
} continue;
case OP_JUMP_ABSOLUTE: frame->jump_abs(byte.arg); continue; case OP_JUMP_ABSOLUTE: frame->jump_abs(byte.arg); continue;
case OP_SAFE_JUMP_ABSOLUTE: frame->jump_abs_safe(byte.arg); continue; case OP_SAFE_JUMP_ABSOLUTE: frame->jump_abs_safe(byte.arg); continue;
case OP_GOTO: { case OP_GOTO: {

View File

@ -500,7 +500,7 @@ private:
switch (op) { switch (op) {
case TK("-"): emit(OP_UNARY_NEGATIVE); break; case TK("-"): emit(OP_UNARY_NEGATIVE); break;
case TK("not"): emit(OP_UNARY_NOT); break; case TK("not"): emit(OP_UNARY_NOT); break;
case TK("*"): SyntaxError("cannot use '*' as unary operator"); break; case TK("*"): emit(OP_UNARY_STAR, co()->_rvalue); break;
default: UNREACHABLE(); default: UNREACHABLE();
} }
} }
@ -594,6 +594,7 @@ __LISTCOMP:
void exprCall() { void exprCall() {
int ARGC = 0; int ARGC = 0;
int KWARGC = 0; int KWARGC = 0;
bool need_unpack = false;
do { do {
match_newlines(mode()==REPL_MODE); match_newlines(mode()==REPL_MODE);
if (peek() == TK(")")) break; if (peek() == TK(")")) break;
@ -607,12 +608,19 @@ __LISTCOMP:
} else{ } else{
if(KWARGC > 0) SyntaxError("positional argument follows keyword argument"); if(KWARGC > 0) SyntaxError("positional argument follows keyword argument");
co()->_rvalue += 1; EXPR(); co()->_rvalue -= 1; co()->_rvalue += 1; EXPR(); co()->_rvalue -= 1;
if(co()->codes.back().op == OP_UNARY_STAR) need_unpack = true;
ARGC++; ARGC++;
} }
match_newlines(mode()==REPL_MODE); match_newlines(mode()==REPL_MODE);
} while (match(TK(","))); } while (match(TK(",")));
consume(TK(")")); consume(TK(")"));
emit(OP_CALL, (KWARGC << 16) | ARGC); if(ARGC > 32767) SyntaxError("too many positional arguments");
if(KWARGC > 32767) SyntaxError("too many keyword arguments");
if(KWARGC > 0){
emit(need_unpack ? OP_CALL_KWARGS_UNPACK : OP_CALL_KWARGS, (KWARGC << 16) | ARGC);
}else{
emit(need_unpack ? OP_CALL_UNPACK : OP_CALL, ARGC);
}
} }
void exprName(){ _exprName(false); } void exprName(){ _exprName(false); }

View File

@ -52,6 +52,12 @@ struct Range {
i64 step = 1; i64 step = 1;
}; };
struct StarWrapper {
PyVar obj;
bool rvalue;
StarWrapper(const PyVar& obj, bool rvalue): obj(obj), rvalue(rvalue) {}
};
struct Slice { struct Slice {
int start = 0; int start = 0;
int stop = 0x7fffffff; int stop = 0x7fffffff;

View File

@ -4,6 +4,9 @@ OPCODE(NO_OP)
OPCODE(POP_TOP) OPCODE(POP_TOP)
OPCODE(DUP_TOP_VALUE) OPCODE(DUP_TOP_VALUE)
OPCODE(CALL) OPCODE(CALL)
OPCODE(CALL_UNPACK)
OPCODE(CALL_KWARGS)
OPCODE(CALL_KWARGS_UNPACK)
OPCODE(RETURN_VALUE) OPCODE(RETURN_VALUE)
OPCODE(BINARY_OP) OPCODE(BINARY_OP)
@ -14,6 +17,7 @@ OPCODE(CONTAINS_OP)
OPCODE(UNARY_NEGATIVE) OPCODE(UNARY_NEGATIVE)
OPCODE(UNARY_NOT) OPCODE(UNARY_NOT)
OPCODE(UNARY_STAR)
OPCODE(BUILD_LIST) OPCODE(BUILD_LIST)
OPCODE(BUILD_MAP) OPCODE(BUILD_MAP)

View File

@ -519,7 +519,7 @@ public:
Type tp_list, tp_tuple; Type tp_list, tp_tuple;
Type tp_function, tp_native_function, tp_native_iterator, tp_bound_method; Type tp_function, tp_native_function, tp_native_iterator, tp_bound_method;
Type tp_slice, tp_range, tp_module, tp_ref; Type tp_slice, tp_range, tp_module, tp_ref;
Type tp_super, tp_exception; Type tp_super, tp_exception, tp_star_wrapper;
template<typename P> template<typename P>
inline PyVarRef PyRef(P&& value) { inline PyVarRef PyRef(P&& value) {
@ -592,6 +592,7 @@ public:
DEF_NATIVE(Range, pkpy::Range, tp_range) DEF_NATIVE(Range, pkpy::Range, tp_range)
DEF_NATIVE(Slice, pkpy::Slice, tp_slice) DEF_NATIVE(Slice, pkpy::Slice, tp_slice)
DEF_NATIVE(Exception, pkpy::Exception, tp_exception) DEF_NATIVE(Exception, pkpy::Exception, tp_exception)
DEF_NATIVE(StarWrapper, pkpy::StarWrapper, tp_star_wrapper)
// there is only one True/False, so no need to copy them! // there is only one True/False, so no need to copy them!
inline bool PyBool_AS_C(const PyVar& obj){return obj == True;} inline bool PyBool_AS_C(const PyVar& obj){return obj == True;}
@ -619,6 +620,7 @@ public:
tp_range = _new_type_object("range"); tp_range = _new_type_object("range");
tp_module = _new_type_object("module"); tp_module = _new_type_object("module");
tp_ref = _new_type_object("_ref"); tp_ref = _new_type_object("_ref");
tp_star_wrapper = _new_type_object("_star_wrapper");
tp_function = _new_type_object("function"); tp_function = _new_type_object("function");
tp_native_function = _new_type_object("native_function"); tp_native_function = _new_type_object("native_function");
@ -743,6 +745,22 @@ public:
return OBJ_GET(T, obj); return OBJ_GET(T, obj);
} }
void unpack_args(pkpy::Args& args){
pkpy::List unpacked;
for(int i=0; i<args.size(); i++){
if(is_type(args[i], tp_star_wrapper)){
auto& star = PyStarWrapper_AS_C(args[i]);
if(!star.rvalue) UNREACHABLE();
PyVar list = asList(star.obj);
pkpy::List& list_c = PyList_AS_C(list);
unpacked.insert(unpacked.end(), list_c.begin(), list_c.end());
}else{
unpacked.push_back(args[i]);
}
}
args = std::move(unpacked);
}
~VM() { ~VM() {
if(!use_stdio){ if(!use_stdio){
delete _stdout; delete _stdout;

21
tests/_star.py Normal file
View File

@ -0,0 +1,21 @@
def f(a, b, *args):
return a + b + sum(args)
assert f(1, 2, 3, 4) == 10
a = [5, 6, 7, 8]
assert f(*a) == 26
def g(*args):
return f(*args)
assert g(1, 2, 3, 4) == 10
assert g(*a) == 26
def f(a, b, *args, c=16):
return a + b + sum(args) + c
assert f(1, 2, 3, 4) == 26
assert f(1, 2, 3, 4, c=32) == 42
assert f(*a, c=-26) == 0