blueloveTH 1 год назад
Родитель
Сommit
9d9674d171

+ 1 - 0
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);

+ 2 - 3
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.

+ 1 - 1
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

+ 6 - 0
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);

+ 0 - 1
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__; }

+ 1 - 1
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;
 }
 

+ 170 - 23
src/interpreter/ceval.c

@@ -7,6 +7,7 @@
 #include <stdbool.h>
 
 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>", 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;
+}

+ 1 - 1
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;

+ 1 - 1
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;
 }
 

+ 96 - 3
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);
+    }
+
+    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_setattr(py_Ref self, py_Name name, const py_Ref val) { 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_delattr(py_Ref self, py_Name name) { return false; }
+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);

+ 1 - 1
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;
 }
 

+ 11 - 6
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) {