From 9d9674d1718d3a9856a34771826256cd07b00430 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 18 Jul 2024 11:48:32 +0800 Subject: [PATCH] ... --- include/pocketpy/common/sstream.h | 1 + include/pocketpy/pocketpy.h | 5 +- include/pocketpy/xmacros/opcodes.h | 2 +- src/common/sstream.c | 6 + src/common/strname.c | 1 - src/compiler/compiler.c | 2 +- src/interpreter/ceval.c | 193 +++++++++++++++++++++++++---- src/interpreter/vm.c | 2 +- src/public/py_object.c | 2 +- src/public/py_ops.c | 99 ++++++++++++++- src/public/py_str.c | 2 +- src/public/stack_ops.c | 17 ++- 12 files changed, 291 insertions(+), 41 deletions(-) diff --git a/include/pocketpy/common/sstream.h b/include/pocketpy/common/sstream.h index 0bf8e4db..08e9ca4f 100644 --- a/include/pocketpy/common/sstream.h +++ b/include/pocketpy/common/sstream.h @@ -21,6 +21,7 @@ void c11_sbuf__write_int(c11_sbuf* self, int); void c11_sbuf__write_i64(c11_sbuf* self, int64_t); void c11_sbuf__write_f64(c11_sbuf* self, double, int precision); void c11_sbuf__write_char(c11_sbuf* self, char); +void c11_sbuf__write_pad(c11_sbuf* self, int count, char pad); void c11_sbuf__write_sv(c11_sbuf* self, c11_sv); void c11_sbuf__write_cstr(c11_sbuf* self, const char*); void c11_sbuf__write_cstrn(c11_sbuf* self, const char*, int); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index d2b16d8e..8b19d635 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -83,7 +83,7 @@ const char* py_name2str(py_Name); py_Name py_namev(c11_sv name); c11_sv py_name2sv(py_Name); -bool py_ismagicname(py_Name); +#define py_ismagicname(name) (name <= __missing__) // opaque types void py_newdict(py_Ref); @@ -195,8 +195,7 @@ py_TmpRef py_getupvalue(py_StackRef argv); void py_setupvalue(py_StackRef argv, const py_Ref val); /// Gets the attribute of the object. -/// 1: success, 0: not found, -1: error -int py_getattr(const py_Ref self, py_Name name, py_Ref out); +bool py_getattr(py_Ref self, py_Name name); /// Sets the attribute of the object. bool py_setattr(py_Ref self, py_Name name, const py_Ref val); /// Deletes the attribute of the object. diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index d5d36f6b..b41cf867 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -68,7 +68,6 @@ OPCODE(LOOP_BREAK) OPCODE(JUMP_ABSOLUTE_TOP) OPCODE(GOTO) /**************************/ -OPCODE(FSTRING_EVAL) OPCODE(REPR) OPCODE(CALL) OPCODE(CALL_VARGS) @@ -108,6 +107,7 @@ OPCODE(RAISE_ASSERT) OPCODE(RE_RAISE) OPCODE(POP_EXCEPTION) /**************************/ +OPCODE(FSTRING_EVAL) OPCODE(FORMAT_STRING) /**************************/ #endif diff --git a/src/common/sstream.c b/src/common/sstream.c index 37ee3e64..122eea98 100644 --- a/src/common/sstream.c +++ b/src/common/sstream.c @@ -20,6 +20,12 @@ void c11_sbuf__dtor(c11_sbuf* self) { c11_vector__dtor(&self->data); } void c11_sbuf__write_char(c11_sbuf* self, char c) { c11_vector__push(char, &self->data, c); } +void c11_sbuf__write_pad(c11_sbuf* self, int count, char pad) { + for(int i = 0; i < count; i++) { + c11_sbuf__write_char(self, pad); + } +} + void c11_sbuf__write_int(c11_sbuf* self, int i) { // len('-2147483648') == 11 c11_vector__reserve(&self->data, self->data.count + 11 + 1); diff --git a/src/common/strname.c b/src/common/strname.c index e948c1bb..284e640d 100644 --- a/src/common/strname.c +++ b/src/common/strname.c @@ -62,4 +62,3 @@ c11_sv py_name2sv(py_Name index) { return (c11_sv){p, strlen(p)}; } -bool py_ismagicname(py_Name name) { return name <= __missing__; } diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 5c07c924..2a6873c2 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1126,7 +1126,7 @@ bool AttribExpr__emit_del(Expr* self_, Ctx* ctx) { bool AttribExpr__emit_store(Expr* self_, Ctx* ctx) { AttribExpr* self = (AttribExpr*)self_; vtemit_(self->child, ctx); - Ctx__emit_(ctx, OP_STORE_ATTR, BC_NOARG, self->line); + Ctx__emit_(ctx, OP_STORE_ATTR, self->name, self->line); return true; } diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index cf3366dc..e0db36fe 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -7,6 +7,7 @@ #include static bool stack_unpack_sequence(pk_VM* self, uint16_t arg); +static bool format_object(py_Ref obj, c11_sv spec); #define DISPATCH() \ do { \ @@ -231,22 +232,22 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { goto __ERROR; } case OP_LOAD_ATTR: { - int res = py_getattr(TOP(), byte.arg, TOP()); - if(res == -1) goto __ERROR; - if(!res) { - AttributeError(TOP(), byte.arg); + if(py_getattr(TOP(), byte.arg)) { + py_assign(TOP(), py_retval()); + } else { goto __ERROR; } DISPATCH(); } case OP_LOAD_CLASS_GLOBAL: { py_Name name = byte.arg; - if(py_getattr(self->__curr_class, name, SP())) { - SP()++; + py_Ref tmp = py_getdict(self->__curr_class, name); + if(tmp) { + PUSH(tmp); DISPATCH(); } // load global if attribute not found - py_Ref tmp = py_getdict(&frame->module, name); + tmp = py_getdict(&frame->module, name); if(tmp) { PUSH(tmp); DISPATCH(); @@ -267,9 +268,9 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { SP()++; } else { // fallback to getattr - int res = py_getattr(TOP(), byte.arg, TOP()); - if(res != 1) { - if(res == 0) { AttributeError(TOP(), byte.arg); } + if(py_getattr(TOP(), byte.arg)) { + py_assign(TOP(), py_retval()); + } else { goto __ERROR; } } @@ -322,6 +323,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { DISPATCH(); } case OP_STORE_ATTR: { + // [val, a] -> a.b = val if(!py_setattr(TOP(), byte.arg, SECOND())) goto __ERROR; STACK_SHRINK(2); DISPATCH(); @@ -616,19 +618,20 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { POP(); DISPATCH_JUMP_ABSOLUTE(target); } - // case OP_GOTO: { - // py_Name _name(byte.arg); - // int target = c11_smallmap_n2i__get(&frame->co->labels, byte.arg, -1); - // if(target < 0) RuntimeError(_S("label ", _name.escape(), " not found")); - // frame->prepare_jump_break(&s_data, target); - // DISPATCH_JUMP_ABSOLUTE(target) - // } - /*****************************************/ - case OP_FSTRING_EVAL: { - assert(false); + case OP_GOTO: { + int target = c11_smallmap_n2i__get(&frame->co->labels, byte.arg, -1); + if(target < 0) { + RuntimeError("label '%n' not found", byte.arg); + goto __ERROR; + } + Frame__prepare_jump_break(frame, &self->stack, target); + DISPATCH_JUMP_ABSOLUTE(target); } + /*****************************************/ case OP_REPR: { - assert(false); + if(!py_repr(TOP())) goto __ERROR; + py_assign(TOP(), py_retval()); + DISPATCH(); } case OP_CALL: { pk_ManagedHeap__collect_if_needed(&self->heap); @@ -678,7 +681,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { n = p - buf; kwargc += py_dict__len(kwargs) - 1; } else { - TypeError("*kwargs must be a dict, got '%t'", kwargs->type); + TypeError("**kwargs must be a dict, got '%t'", kwargs->type); goto __ERROR; } } @@ -806,7 +809,8 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { base = py_totype(TOP()); } POP(); - py_Type type = py_newtype(py_name2str(name), base, &frame->module, NULL); + py_Type type = + pk_newtype(py_name2str(name), base, &frame->module, NULL, true, false); PUSH(py_tpobject(type)); self->__curr_class = TOP(); DISPATCH(); @@ -857,6 +861,22 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { } goto __ERROR; } + ////////////////// + case OP_FSTRING_EVAL: { + py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg); + const char* string = py_tostr(tmp); + // TODO: optimize this + if(!py_exec2(string, "", EVAL_MODE)) goto __ERROR; + PUSH(py_retval()); + DISPATCH(); + } + case OP_FORMAT_STRING: { + py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg); + bool ok = format_object(TOP(), py_tosv(spec)); + if(!ok) goto __ERROR; + py_assign(TOP(), py_retval()); + DISPATCH(); + } default: c11__unreachedable(); } @@ -922,3 +942,130 @@ static bool stack_unpack_sequence(pk_VM* self, uint16_t arg) { } return true; } + +static bool format_object(py_Ref val, c11_sv spec) { + if(spec.size == 0) return py_str(val); + + char type; + switch(spec.data[spec.size - 1]) { + case 'f': + case 'd': + case 's': + type = spec.data[spec.size - 1]; + spec.size--; // remove last char + break; + default: type = ' '; break; + } + + char pad_c = ' '; + if(strchr("0-=*#@!~", spec.data[0])) { + pad_c = spec.data[0]; + spec = c11_sv__slice(spec, 1); + } + + char align; + if(spec.data[0] == '^') { + align = '^'; + spec = c11_sv__slice(spec, 1); + } else if(spec.data[0] == '>') { + align = '>'; + spec = c11_sv__slice(spec, 1); + } else if(spec.data[0] == '<') { + align = '<'; + spec = c11_sv__slice(spec, 1); + } else { + align = (py_isint(val) || py_isfloat(val)) ? '>' : '<'; + } + + int dot = c11_sv__index(spec, '.'); + py_i64 width, precision; + + if(dot >= 0) { + if(dot == 0) { + // {.2f} + width = -1; + } else { + // {10.2f} + IntParsingResult res = c11__parse_uint(c11_sv__slice2(spec, 0, dot), &width, 10); + if(res != IntParsing_SUCCESS) return ValueError("invalid format specifer"); + } + IntParsingResult res = c11__parse_uint(c11_sv__slice(spec, dot + 1), &precision, 10); + if(res != IntParsing_SUCCESS) return ValueError("invalid format specifer"); + } else { + // {10s} + IntParsingResult res = c11__parse_uint(spec, &width, 10); + if(res != IntParsing_SUCCESS) return ValueError("invalid format specifer"); + precision = -1; + } + + if(type != 'f' && dot >= 0) { + return ValueError("precision not allowed in the format specifier"); + } + + c11_sbuf buf; + c11_sbuf__ctor(&buf); + + if(type == 'f') { + py_f64 x; + if(!py_castfloat(val, &x)) { + c11_sbuf__dtor(&buf); + return false; + } + if(precision < 0) precision = 6; + c11_sbuf__write_f64(&buf, x, precision); + } else if(type == 'd') { + if(!py_checkint(val)) { + c11_sbuf__dtor(&buf); + return false; + } + c11_sbuf__write_i64(&buf, py_toint(val)); + } else if(type == 's') { + if(!py_checkstr(val)) { + c11_sbuf__dtor(&buf); + return false; + } + c11_sbuf__write_sv(&buf, py_tosv(val)); + } else { + if(!py_str(val)) { + c11_sbuf__dtor(&buf); + return false; + } + c11_sbuf__write_sv(&buf, py_tosv(py_retval())); + } + + c11_string* body = c11_sbuf__submit(&buf); + int length = c11_sv__u8_length(c11_string__sv(body)); + c11_sbuf__ctor(&buf); // reinit sbuf + + if(width != -1 && width > length) { + switch(align) { + case '>': { + c11_sbuf__write_pad(&buf, width - length, pad_c); + c11_sbuf__write_sv(&buf, c11_string__sv(body)); + break; + } + case '<': { + c11_sbuf__write_sv(&buf, c11_string__sv(body)); + c11_sbuf__write_pad(&buf, width - length, pad_c); + break; + } + case '^': { + int pad_left = (width - length) / 2; + int pad_right = (width - length) - pad_left; + c11_sbuf__write_pad(&buf, pad_left, pad_c); + c11_sbuf__write_sv(&buf, c11_string__sv(body)); + c11_sbuf__write_pad(&buf, pad_right, pad_c); + break; + } + default: c11__unreachedable(); + } + } else { + c11_sbuf__write_sv(&buf, c11_string__sv(body)); + } + + c11_string__delete(body); + c11_string* res = c11_sbuf__submit(&buf); + py_newstrn(py_retval(), res->data, res->size); + c11_string__delete(res); + return true; +} \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index a26170cd..b351608c 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -547,7 +547,7 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) { } void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) { - // return; + return; py_TValue* sp = self->stack.sp; c11_sbuf buf; diff --git a/src/public/py_object.c b/src/public/py_object.c index 92ad38b6..eac38063 100644 --- a/src/public/py_object.c +++ b/src/public/py_object.c @@ -9,7 +9,7 @@ static bool _py_object__new__(int argc, py_Ref argv) { if(!ti->is_python) { return TypeError("object.__new__(%t) is not safe, use %t.__new__()", cls, cls); } - py_newobject(py_retval(), cls, 0, 0); + py_newobject(py_retval(), cls, -1, 0); return true; } diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 86c89c1c..fc07e25e 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -77,11 +77,104 @@ int py_next(const py_Ref val) { return vm->is_stopiteration ? 0 : -1; } -int py_getattr(const py_Ref self, py_Name name, py_Ref out) { return -1; } +bool py_getattr(py_Ref self, py_Name name) { + // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance + py_Type type = self->type; + // handle super() proxy + if(py_istype(self, tp_super)) { + self = py_getslot(self, 0); + type = *(py_Type*)py_touserdata(self); + } -bool py_setattr(py_Ref self, py_Name name, const py_Ref val) { return false; } + py_Ref cls_var = py_tpfindname(type, name); + if(cls_var) { + // handle descriptor + if(py_istype(cls_var, tp_property)) { + py_Ref getter = py_getslot(cls_var, 0); + return py_call(getter, 1, self); + } + } + // handle instance __dict__ + if(self->is_ptr && self->_obj->slots == -1) { + if(!py_istype(self, tp_type)) { + py_Ref res = py_getdict(self, name); + if(res) { + py_assign(py_retval(), res); + return true; + } + } else { + py_Type* inner_type = py_touserdata(self); + py_Ref res = py_tpfindname(*inner_type, name); + if(res) { + if(py_istype(res, tp_staticmethod)) { + res = py_getslot(res, 0); + } else if(py_istype(res, tp_classmethod)) { + // TODO: make a closure + assert(false); + } + py_assign(py_retval(), res); + return true; + } + } + } -bool py_delattr(py_Ref self, py_Name name) { return false; } + if(cls_var) { + // bound method is non-data descriptor + switch(cls_var->type) { + case tp_function: assert(false); + case tp_nativefunc: assert(false); + case tp_staticmethod: assert(false); + case tp_classmethod: assert(false); + default: { + py_assign(py_retval(), cls_var); + return true; + } + } + } + + return AttributeError(self, name); +} + +bool py_setattr(py_Ref self, py_Name name, const py_Ref val) { + py_Type type = self->type; + // handle super() proxy + if(py_istype(self, tp_super)) { + self = py_getslot(self, 0); + type = *(py_Type*)py_touserdata(self); + } + + py_Ref cls_var = py_tpfindname(type, name); + if(cls_var) { + // handle descriptor + if(py_istype(cls_var, tp_property)) { + py_Ref setter = py_getslot(cls_var, 1); + if(!py_isnone(setter)) { + py_push(setter); + py_push(self); + py_push(val); + return py_vectorcall(1, 0); + } else { + return TypeError("readonly attribute: '%n'", name); + } + } + } + + // handle instance __dict__ + if(self->is_ptr && self->_obj->slots == -1) { + py_setdict(self, name, val); + return true; + } + + return TypeError("cannot set attribute"); +} + +bool py_delattr(py_Ref self, py_Name name) { + if(self->is_ptr && self->_obj->slots == -1) { + if(py_deldict(self, name)) return true; + return AttributeError(self, name); + } + return TypeError("cannot delete attribute"); +} bool py_getitem(const py_Ref self, const py_Ref key) { py_push(self); diff --git a/src/public/py_str.c b/src/public/py_str.c index e9a28a06..a5ec3f93 100644 --- a/src/public/py_str.c +++ b/src/public/py_str.c @@ -83,7 +83,7 @@ static bool _py_str__hash__(int argc, py_Ref argv) { static bool _py_str__len__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_string* self = py_touserdata(&argv[0]); - py_newint(py_retval(), self->size); + py_newint(py_retval(), c11_sv__u8_length((c11_sv){self->data, self->size})); return true; } diff --git a/src/public/stack_ops.c b/src/public/stack_ops.c index 2615fa34..1bfb860b 100644 --- a/src/public/stack_ops.c +++ b/src/public/stack_ops.c @@ -8,30 +8,35 @@ py_Ref py_reg(int i) { return pk_current_vm->reg + i; } py_Ref py_getdict(const py_Ref self, py_Name name) { assert(self && self->is_ptr); - if(self->type == tp_type && py_ismagicname(name)) { + if(!py_ismagicname(name) || self->type != tp_type) { + return pk_NameDict__try_get(PyObject__dict(self->_obj), name); + } else { py_Type* ud = py_touserdata(self); py_Ref slot = py_tpmagic(*ud, name); return py_isnil(slot) ? NULL : slot; } - return pk_NameDict__try_get(PyObject__dict(self->_obj), name); } void py_setdict(py_Ref self, py_Name name, const py_Ref val) { assert(self && self->is_ptr); - if(self->type == tp_type && py_ismagicname(name)) { + if(!py_ismagicname(name) || self->type != tp_type) { + pk_NameDict__set(PyObject__dict(self->_obj), name, *val); + } else { py_Type* ud = py_touserdata(self); *py_tpmagic(*ud, name) = *val; } - pk_NameDict__set(PyObject__dict(self->_obj), name, *val); } bool py_deldict(py_Ref self, py_Name name) { assert(self && self->is_ptr); - if(self->type == tp_type && py_ismagicname(name)) { + if(!py_ismagicname(name) || self->type != tp_type) { + return pk_NameDict__del(PyObject__dict(self->_obj), name); + + } else { py_Type* ud = py_touserdata(self); py_newnil(py_tpmagic(*ud, name)); + return true; } - return pk_NameDict__del(PyObject__dict(self->_obj), name); } py_Ref py_getslot(const py_Ref self, int i) {