From 20cd2064d474d2379dd07dfe88a80f3ada03af47 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 14 Jul 2024 16:24:13 +0800 Subject: [PATCH] ... --- include/pocketpy/pocketpy.h | 3 + src/compiler/compiler.c | 41 ++++++++----- src/interpreter/ceval.c | 95 ++++++++++++++++-------------- src/public/py_dict.c | 10 ++++ tests/00_tmp.py | 6 -- tests/16_functions.py | 18 ++++++ tests/{19_cmath.py => 80_cmath.py} | 0 tests/{23_long.py => 80_long.py} | 0 8 files changed, 108 insertions(+), 65 deletions(-) delete mode 100644 tests/00_tmp.py rename tests/{19_cmath.py => 80_cmath.py} (100%) rename tests/{23_long.py => 80_long.py} (100%) diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index c1303c83..f49aebae 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -361,6 +361,9 @@ py_TmpRef py_dict__getitem(const py_Ref self, const py_Ref key); void py_dict__setitem(py_Ref self, const py_Ref key, const py_Ref val); bool py_dict__contains(const py_Ref self, const py_Ref key); int py_dict__len(const py_Ref self); +bool py_dict__apply(const py_Ref self, + bool (*f)(const py_Ref key, const py_Ref val, void* ctx), + void* ctx); /// Search the magic method from the given type to the base type. /// Return the reference or NULL if not found. diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 9cd780ee..7c3183ce 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1385,6 +1385,7 @@ static void Ctx__emit_store_name(Ctx* self, NameScope scope, py_Name name, int l // emit top -> pop -> delete static void Ctx__s_emit_top(Ctx* self) { + assert(self->s_expr.count); Expr* top = c11_vector__back(Expr*, &self->s_expr); vtemit_(top, self); vtdelete(top); @@ -1395,13 +1396,17 @@ static void Ctx__s_emit_top(Ctx* self) { static void Ctx__s_push(Ctx* self, Expr* expr) { c11_vector__push(Expr*, &self->s_expr, expr); } // top -static Expr* Ctx__s_top(Ctx* self) { return c11_vector__back(Expr*, &self->s_expr); } +static Expr* Ctx__s_top(Ctx* self) { + assert(self->s_expr.count); + return c11_vector__back(Expr*, &self->s_expr); +} // size static int Ctx__s_size(Ctx* self) { return self->s_expr.count; } // pop -> delete static void Ctx__s_pop(Ctx* self) { + assert(self->s_expr.count); Expr* top = c11_vector__back(Expr*, &self->s_expr); vtdelete(top); c11_vector__pop(&self->s_expr); @@ -1409,6 +1414,7 @@ static void Ctx__s_pop(Ctx* self) { // pop move static Expr* Ctx__s_popx(Ctx* self) { + assert(self->s_expr.count); Expr* top = c11_vector__back(Expr*, &self->s_expr); c11_vector__pop(&self->s_expr); return top; @@ -1475,7 +1481,11 @@ static NameScope name_scope(Compiler* self) { return s; } -#define SyntaxError(...) NULL +Error* SyntaxError(const char* fmt, ...) { + printf("%s\n", fmt); + abort(); + return NULL; +} /* Matchers */ static bool is_expression(Compiler* self, bool allow_slice) { @@ -2108,7 +2118,7 @@ static Error* compile_for_loop(Compiler* self) { vtdelete(vars); if(!ok) { // this error occurs in `vars` instead of this line, but...nevermind - return SyntaxError(); + return SyntaxError("invalid syntax"); } check(compile_block_body(self, compile_stmt)); Ctx__emit_virtual(ctx(), OP_LOOP_CONTINUE, Ctx__get_loop(ctx()), BC_KEEPLINE, true); @@ -2135,10 +2145,10 @@ Error* try_compile_assignment(Compiler* self, bool* is_assign) { case TK_IAND: case TK_IOR: case TK_IXOR: { - if(Ctx__s_top(ctx())->vt->is_starred) return SyntaxError(); - if(ctx()->is_compiling_class) { + if(Ctx__s_top(ctx())->vt->is_starred) + return SyntaxError("can't use inplace operator with starred expression"); + if(ctx()->is_compiling_class) return SyntaxError("can't use inplace operator in class definition"); - } advance(); // a[x] += 1; a and x should be evaluated only once // a.x += 1; a should be evaluated only once @@ -2147,14 +2157,15 @@ Error* try_compile_assignment(Compiler* self, bool* is_assign) { TokenIndex op = (TokenIndex)(prev()->type - 1); // [lhs] check(EXPR_TUPLE(self)); // [lhs, rhs] - if(Ctx__s_top(ctx())->vt->is_starred) return SyntaxError(); + if(Ctx__s_top(ctx())->vt->is_starred) + return SyntaxError("can't use starred expression here"); BinaryExpr* e = BinaryExpr__new(line, op, true); e->rhs = Ctx__s_popx(ctx()); // [lhs] e->lhs = Ctx__s_popx(ctx()); // [] vtemit_((Expr*)e, ctx()); bool ok = vtemit_istore(e->lhs, ctx()); vtdelete((Expr*)e); - if(!ok) return SyntaxError(); + if(!ok) return SyntaxError("invalid syntax"); *is_assign = true; return NULL; } @@ -2169,11 +2180,12 @@ Error* try_compile_assignment(Compiler* self, bool* is_assign) { for(int j = 1; j < n; j++) Ctx__emit_(ctx(), OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); for(int j = 0; j < n; j++) { - if(Ctx__s_top(ctx())->vt->is_starred) return SyntaxError(); + if(Ctx__s_top(ctx())->vt->is_starred) + return SyntaxError("can't use starred expression here"); Expr* e = Ctx__s_top(ctx()); bool ok = vtemit_store(e, ctx()); Ctx__s_pop(ctx()); - if(!ok) return SyntaxError(); + if(!ok) return SyntaxError("invalid syntax"); } *is_assign = true; return NULL; @@ -2213,7 +2225,7 @@ static Error* read_literal(Compiler* self, py_Ref out) { } else if(value->index == TokenValue_F64) { py_newfloat(out, negated ? -value->_f64 : value->_f64); } else { - return SyntaxError(); + c11__unreachedable(); } return NULL; } @@ -2248,8 +2260,7 @@ static Error* _compile_f_args(Compiler* self, FuncDecl* decl, bool enable_type_h int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs Error* err; do { - if(state > 3) return SyntaxError(); - if(state == 3) return SyntaxError("**kwargs should be the last argument"); + if(state >= 3) return SyntaxError("**kwargs should be the last argument"); match_newlines(); if(match(TK_MUL)) { if(state < 1) @@ -2554,7 +2565,7 @@ static Error* compile_stmt(Compiler* self) { case TK_DEL: { check(EXPR_TUPLE(self)); Expr* e = Ctx__s_top(ctx()); - if(!vtemit_del(e, ctx())) return SyntaxError(); + if(!vtemit_del(e, ctx())) return SyntaxError("invalid syntax"); Ctx__s_pop(ctx()); consume_end_stmt(); } break; @@ -2622,7 +2633,7 @@ static Error* compile_stmt(Compiler* self) { check(try_compile_assignment(self, &is_assign)); if(!is_assign) { if(Ctx__s_size(ctx()) > 0 && Ctx__s_top(ctx())->vt->is_starred) { - return SyntaxError(); + return SyntaxError("can't use starred expression here"); } if(!is_typed_name) { Ctx__s_emit_top(ctx()); diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index bfc38883..e07d357b 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -59,6 +59,18 @@ static bool stack_unpack_sequence(pk_VM* self, uint16_t arg); } \ } while(0) +static bool unpack_dict_to_buffer(const py_Ref key, const py_Ref val, void* ctx) { + py_TValue** p = ctx; + if(py_isstr(key)) { + py_Name name = py_namev(py_tosv(key)); + py_newint(*p, name); + py_assign(*p + 1, val); + (*p) += 2; + return true; + } + return TypeError("keywords must be strings, not '%t'", key->type); +} + pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { Frame* frame = self->top_frame; const Frame* base_frame = frame; @@ -624,63 +636,58 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { DISPATCH(); } case OP_CALL_VARGS: { + // [_0, _1, _2 | k1, v1, k2, v2] uint16_t argc = byte.arg & 0xFF; uint16_t kwargc = byte.arg >> 8; - int size = 0; + + int n = 0; + py_TValue* sp = SP(); + py_TValue* p1 = sp - kwargc * 2; + py_TValue* base = p1 - argc; py_TValue* buf = self->__vectorcall_buffer; - // if(size == PK_MAX_CO_VARNAMES) { - // ValueError("**kwargs is too large to unpack"); - // goto __ERROR; - // } - - uint16_t new_argc = argc; - uint16_t new_kwargc = kwargc; - - // pop kwargc - for(int i = 0; i < kwargc; i++) { - // [k1, v1, k2, v2, ...] -> reversed - py_TValue value = POPX(); - py_TValue key = POPX(); - if(value.type == tp_star_wrapper) { - value = *py_getslot(&value, 0); - // unpack dict - if(value.type != tp_dict) { - TypeError("**kwargs should be a dict, got '%t'", value.type); - goto __ERROR; - } - new_kwargc += (0) - 1; + for(py_TValue* curr = base; curr != p1; curr++) { + if(curr->type != tp_star_wrapper) { + buf[n++] = *curr; } else { - buf[size++] = value; - buf[size++] = key; - } - } - // pop argc - for(int i = 0; i < argc; i++) { - py_TValue value = POPX(); - if(value.type == tp_star_wrapper) { - value = *py_getslot(&value, 0); + py_TValue* args = py_getslot(curr, 0); int length; - py_TValue* p = pk_arrayview(&value, &length); - if(!p) { - TypeError("*args should be a list or tuple, got '%t'", value.type); + py_TValue* p = pk_arrayview(args, &length); + if(p) { + for(int j = 0; j < length; j++) { + buf[n++] = p[j]; + } + argc += length - 1; + } else { + TypeError("*args must be a list or tuple, got '%t'", args->type); goto __ERROR; } - for(int j = 0; j < length; j++) { - buf[size++] = p[j]; - } - new_argc += length - 1; - } else { - buf[size++] = value; } } - // push everything back in reversed order - for(int i = size - 1; i >= 0; i--) { - PUSH(buf + i); + for(py_TValue* curr = p1; curr != sp; curr += 2) { + if(curr[1].type != tp_star_wrapper) { + buf[n++] = curr[0]; + buf[n++] = curr[1]; + } else { + assert(py_toint(&curr[0]) == 0); + py_TValue* kwargs = py_getslot(&curr[1], 0); + if(kwargs->type == tp_dict) { + py_TValue* p = buf + n; + if(!py_dict__apply(kwargs, unpack_dict_to_buffer, &p)) goto __ERROR; + n = p - buf; + kwargc += py_dict__len(kwargs) - 1; + } else { + TypeError("*kwargs must be a dict, got '%t'", kwargs->type); + goto __ERROR; + } + } } - vectorcall_opcall(new_argc, new_kwargc); + memcpy(base, buf, n * sizeof(py_TValue)); + SP() = base + n; + + vectorcall_opcall(argc, kwargc); DISPATCH(); } case OP_RETURN_VALUE: { diff --git a/src/public/py_dict.c b/src/public/py_dict.c index 8af5db62..a6d19ef9 100644 --- a/src/public/py_dict.c +++ b/src/public/py_dict.c @@ -520,6 +520,16 @@ int py_dict__len(const py_Ref self) { return ud->length; } +bool py_dict__apply(const py_Ref self, bool (*f)(const py_Ref, const py_Ref, void *), void *ctx){ + Dict* ud = py_touserdata(self); + for(int i = 0; i < ud->entries.count; i++) { + DictEntry* entry = c11__at(DictEntry, &ud->entries, i); + if(py_isnil(&entry->key)) continue; + if(!f(&entry->key, &entry->val, ctx)) return false; + } + return true; +} + void pk_dict__mark(void* ud, void (*marker)(py_TValue*)) { Dict* self = ud; for(int i = 0; i < self->entries.count; i++) { diff --git a/tests/00_tmp.py b/tests/00_tmp.py deleted file mode 100644 index 7b5b8e94..00000000 --- a/tests/00_tmp.py +++ /dev/null @@ -1,6 +0,0 @@ -# dict delete test -data = [] -j = 6 -for i in range(65535): - j = ((j*5+1) % 65535) - data.append(str(j)) diff --git a/tests/16_functions.py b/tests/16_functions.py index 53d0fe63..82fcb847 100644 --- a/tests/16_functions.py +++ b/tests/16_functions.py @@ -31,6 +31,23 @@ assert f(b=5) == 6 assert f(a=5) == 4 assert f(b=5, a=5) == 10 +# test args unpack +def f(a, b, *args): + assert a == 1 + assert b == 2 + assert args == (3, 4) + +f(1, 2, 3, 4) + +# test kwargs unpack +def f(a=1, b=2, **kwargs): + assert a == 10 + assert b == 2 + assert kwargs == {'c': 3, 'd': 4} + +f(10, c=3, d=4) +f(a=10, c=3, d=4) + def f(*args): return 10 * sum(args) @@ -61,6 +78,7 @@ assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, assert g(1, 2, 3, 4) == 17 assert g(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 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 diff --git a/tests/19_cmath.py b/tests/80_cmath.py similarity index 100% rename from tests/19_cmath.py rename to tests/80_cmath.py diff --git a/tests/23_long.py b/tests/80_long.py similarity index 100% rename from tests/23_long.py rename to tests/80_long.py