Bläddra i källkod

improve errors

blueloveTH 1 år sedan
förälder
incheckning
3a8613b7ff

+ 28 - 18
include/pocketpy/pocketpy.h

@@ -116,11 +116,9 @@ bool py_issubclass(py_Type derived, py_Type base);
 
 /************* References *************/
 #define py_offset(p, i) (py_Ref)((char*)p + ((i) << 4))
-
-#define TypeError(...) false
 #define py_arg(i) py_offset(argv, i)
 #define py_checkargc(n)                                                                            \
-    if(argc != n) return TypeError()
+    if(argc != n) return TypeError("expected %d arguments, got %d", n, argc)
 
 py_GlobalRef py_tpmagic(py_Type type, py_Name name);
 #define py_bindmagic(type, __magic__, f) py_newnativefunc(py_tpmagic((type), __magic__), (f))
@@ -157,17 +155,14 @@ py_TmpRef py_getupvalue(py_StackRef argv);
 void py_setupvalue(py_StackRef argv, const py_Ref val);
 
 /// Gets the attribute of the object.
-bool py_getattr(const py_Ref self, py_Name name, py_Ref out);
-/// Gets the unbound method of the object.
-bool py_getunboundmethod(const py_Ref self,
-                         py_Name name,
-                         bool fallback,
-                         py_Ref out,
-                         py_Ref out_self);
+/// 1: success, 0: not found, -1: error
+int py_getattr(const py_Ref self, py_Name name, py_Ref out);
 /// 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.
 bool py_delattr(py_Ref self, py_Name name);
+/// Gets the unbound method of the object.
+bool py_getunboundmethod(py_Ref self, py_Name name, py_Ref out, py_Ref out_self);
 
 bool py_getitem(const py_Ref self, const py_Ref key, py_Ref out);
 bool py_setitem(py_Ref self, const py_Ref key, const py_Ref val);
@@ -226,11 +221,22 @@ py_TmpRef py_getmodule(const char* name);
 bool py_import(const char* name);
 
 /************* Errors *************/
+bool py_exception(const char* name, const char* fmt, ...);
 /// Print the last error to the console.
 void py_printexc();
 /// Format the last error to a string.
 void py_formatexc(char* out);
 
+#define KeyError(q) py_exception("KeyError", "'%q'", (q))
+#define NameError(n) py_exception("NameError", "name '%n' is not defined", (n))
+#define TypeError(...) py_exception("TypeError", __VA_ARGS__)
+#define ValueError(...) py_exception("ValueError", __VA_ARGS__)
+#define IndexError(...) py_exception("IndexError", __VA_ARGS__)
+#define AttributeError(self, n)                                                                    \
+    py_exception("AttributeError", "'%t' object has no attribute '%n'", (self)->type, (n))
+#define UnboundLocalError(n)                                                                       \
+    py_exception("UnboundLocalError", "local variable '%n' referenced before assignment", (n))
+
 /************* Operators *************/
 /// Equivalent to `bool(val)`.
 /// Returns 1 if `val` is truthy, otherwise 0.
@@ -301,6 +307,10 @@ pk_TypeInfo* pk_tpinfo(const py_Ref self);
 /// Return the reference or NULL if not found.
 py_GlobalRef py_tpfindmagic(py_Type, py_Name name);
 
+/// Search the name from the given type to the base type.
+/// Return the reference or NULL if not found.
+py_GlobalRef py_tpfindname(py_Type, py_Name name);
+
 /// Get the type object of the given type.
 py_GlobalRef py_tpobject(py_Type type);
 
@@ -333,28 +343,28 @@ enum py_MagicNames {
 
 enum py_PredefinedTypes {
     tp_object = 1,
-    tp_type,
+    tp_type,  // py_Type
     tp_int,
     tp_float,
     tp_bool,
     tp_str,
-    tp_list,
-    tp_tuple,
-    tp_slice,
+    tp_list,   // c11_vector
+    tp_tuple,  // N slots
+    tp_slice,  // 3 slots (start, stop, step)
     tp_range,
     tp_module,
     tp_function,
     tp_nativefunc,
     tp_bound_method,
-    tp_super,
+    tp_super,  // 1 slot + py_Type
     tp_exception,
     tp_bytes,
     tp_mappingproxy,
     tp_dict,
-    tp_property,
+    tp_property,  // 2 slots (getter + setter)
     tp_star_wrapper,
-    tp_staticmethod,
-    tp_classmethod,
+    tp_staticmethod,  // 1 slot
+    tp_classmethod,   // 1 slot
     tp_none_type,
     tp_not_implemented_type,
     tp_ellipsis,

+ 1 - 1
src/common/sstream.c

@@ -142,7 +142,7 @@ c11_string* c11_sbuf__submit(c11_sbuf* self) {
 }
 
 void pk_vsprintf(c11_sbuf* ss, const char* fmt, va_list args) {
-    while(fmt) {
+    while(*fmt) {
         char c = *fmt;
         if(c != '%') {
             c11_sbuf__write_char(ss, c);

+ 23 - 15
src/interpreter/ceval.c

@@ -5,12 +5,6 @@
 #include "pocketpy/pocketpy.h"
 #include <stdbool.h>
 
-int UnboundLocalError(py_Name name) { return -1; }
-
-int NameError(py_Name name) { return -1; }
-
-#define AttributeError(obj, name) false
-#define BinaryOptError(op) false
 
 static bool stack_binaryop(pk_VM* self, py_Name op, py_Name rop);
 
@@ -255,9 +249,21 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 goto __ERROR;
             }
             case OP_LOAD_METHOD: {
-                // `py_getunboundmethod` never fails on `fallback=true`
-                py_getunboundmethod(TOP(), byte.arg, true, TOP(), SP());
-                SP()++;
+                // [self]
+                bool ok = py_getunboundmethod(TOP(), byte.arg, TOP(), SP());
+                if(ok){
+                    // [unbound, self]
+                    SP()++;
+                }else{
+                    // fallback to getattr
+                    int res = py_getattr(TOP(), byte.arg, TOP());
+                    if(res != 1){
+                        if(res == 0){
+                            AttributeError(TOP(), byte.arg);
+                        }
+                        goto __ERROR;
+                    }
+                }
                 DISPATCH();
             }
             case OP_LOAD_SUBSCR: {
@@ -276,7 +282,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     }
                     DISPATCH();
                 }
-                TypeError();
+                TypeError("'%t' object is not subscriptable", SECOND()->type);
                 goto __ERROR;
             }
             case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH();
@@ -330,13 +336,14 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     }
                     DISPATCH();
                 }
-                TypeError();
+                TypeError("'%t' object does not support item assignment", SECOND()->type);
                 goto __ERROR;
             }
             case OP_DELETE_FAST: {
                 py_Ref tmp = &frame->locals[byte.arg];
                 if(py_isnull(tmp)) {
-                    UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
+                    py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg);
+                    UnboundLocalError(name);
                     goto __ERROR;
                 }
                 py_newnull(tmp);
@@ -400,7 +407,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     }
                     DISPATCH();
                 }
-                TypeError();
+                TypeError("'%t' object does not support item deletion", SECOND()->type);
                 goto __ERROR;
             }
                 /*****************************************/
@@ -536,7 +543,8 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     if(byte.arg) py_newbool(TOP(), !res);
                     DISPATCH();
                 }
-                TypeError();
+                // TODO: fallback to __iter__?
+                TypeError("argument of type '%t' is not iterable", SECOND()->type);
                 goto __ERROR;
             }
                 /*****************************************/
@@ -718,7 +726,7 @@ static bool stack_binaryop(pk_VM* self, py_Name op, py_Name rop) {
         }
     }
     // eq/ne op never fails due to object.__eq__
-    return BinaryOptError(byte.arg);
+    return py_exception("TypeError", "unsupported operand type(s) for '%n'", op);
 }
 
 bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop) {

+ 0 - 2
src/interpreter/py_number.c

@@ -44,8 +44,6 @@ DEF_NUM_BINARY_OP(__ge__, >=, py_newbool, py_newbool)
 
 #undef DEF_NUM_BINARY_OP
 
-static bool ValueError(const char* fmt, ...) { return false; }
-
 static bool _py_int__neg__(int argc, py_Ref argv) {
     py_checkargc(1);
     int64_t val = py_toint(&argv[0]);

+ 2 - 2
src/interpreter/vm.c

@@ -364,7 +364,7 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t ARGC, uint16_t KWARGC, bo
     }
 
     // handle `__call__` overload
-    if(py_getunboundmethod(p0, __call__, false, p0, p0 + 1)) {
+    if(py_getunboundmethod(p0, __call__, p0, p0 + 1)) {
         // [__call__, self, args..., kwargs...]
         pk_FrameResult res = pk_VM__vectorcall(self, ARGC, KWARGC, false);
         if(res == RES_ERROR) return RES_ERROR;
@@ -400,4 +400,4 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
     // vm->obj_gc_mark(vm->__c.error);
     // vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
     // if(self->_gc_marker_ex) self->_gc_marker_ex((pkpy_VM*)vm);
-}
+}

+ 22 - 0
src/public/error.c

@@ -1,6 +1,9 @@
 #include "pocketpy/pocketpy.h"
+#include "pocketpy/common/sstream.h"
 #include "pocketpy/interpreter/vm.h"
 
+#include <stdarg.h>
+
 void py_printexc(){
     pk_VM* vm = pk_current_vm;
     if(vm->has_error){
@@ -13,4 +16,23 @@ void py_printexc(){
 
 void py_formatexc(char *out){
 
+}
+
+bool py_exception(const char* name, const char* fmt, ...){
+    pk_VM* vm = pk_current_vm;
+    assert(!vm->has_error);     // an error is already set
+    vm->has_error = true;
+
+    c11_sbuf buf;
+    c11_sbuf__ctor(&buf);
+    va_list args;
+    va_start(args, fmt);
+    pk_vsprintf(&buf, fmt, args);
+    va_end(args);
+
+    c11_string* res = c11_sbuf__submit(&buf);
+    // vm->last_retval = py_newexception(name, res->data);
+    vm->_stderr("%s: %s\n", name, res->data);
+    c11_string__delete(res);
+    return false;
 }

+ 3 - 3
src/public/py_ops.c

@@ -21,11 +21,11 @@ int py_bool(const py_Ref val) { return 1; }
 
 bool py_hash(const py_Ref val, int64_t* out) { return 0; }
 
-bool py_getattr(const py_Ref self, py_Name name, py_Ref out) { return true; }
+int py_getattr(const py_Ref self, py_Name name, py_Ref out) { return -1; }
 
-bool py_setattr(py_Ref self, py_Name name, const py_Ref val) { return -1; }
+bool py_setattr(py_Ref self, py_Name name, const py_Ref val) { return false; }
 
-bool py_delattr(py_Ref self, py_Name name) { return -1; }
+bool py_delattr(py_Ref self, py_Name name) { return false; }
 
 bool py_getitem(const py_Ref self, const py_Ref key, py_Ref out) { return -1; }
 

+ 42 - 8
src/public/vm.c

@@ -26,7 +26,7 @@ void py_finalize() {
     pk_MemoryPools__finalize();
 }
 
-const char* pk_opname(Opcode op){
+const char* pk_opname(Opcode op) {
     const static char* OP_NAMES[] = {
 #define OPCODE(name) #name,
 #include "pocketpy/xmacros/opcodes.h"
@@ -195,11 +195,34 @@ bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
 
 py_Ref py_retval() { return &pk_current_vm->last_retval; }
 
-bool py_getunboundmethod(const py_Ref self,
-                         py_Name name,
-                         bool fallback,
-                         py_Ref out,
-                         py_Ref out_self) {
+bool py_getunboundmethod(py_Ref self, py_Name name, py_Ref out, py_Ref out_self) {
+    // NOTE: `out` and `out_self` may overlap with `self`
+    py_Type type;
+    // handle super() proxy
+    if(py_istype(self, tp_super)) {
+        self = py_getslot(self, 0);
+        type = *(py_Type*)py_touserdata(self);
+    } else {
+        type = self->type;
+    }
+
+    py_Ref cls_var = py_tpfindname(type, name);
+    if(cls_var != NULL) {
+        switch(cls_var->type) {
+            case tp_function: *out_self = *self; break;
+            case tp_nativefunc: *out_self = *self; break;
+            case tp_staticmethod:
+                py_newnull(self);
+                *out = *py_getslot(cls_var, 0);
+                break;
+            case tp_classmethod:
+                *out_self = c11__getitem(pk_TypeInfo, &pk_current_vm->types, type).self;
+                *out = *py_getslot(cls_var, 0);
+                break;
+        }
+        return true;
+    }
+    // TODO: __getattr__ fallback
     return false;
 }
 
@@ -209,7 +232,6 @@ pk_TypeInfo* pk_tpinfo(const py_Ref self) {
 }
 
 py_Ref py_tpfindmagic(py_Type t, py_Name name) {
-    assert(t);
     assert(py_ismagicname(name));
     pk_TypeInfo* types = (pk_TypeInfo*)pk_current_vm->types.data;
     do {
@@ -220,6 +242,16 @@ py_Ref py_tpfindmagic(py_Type t, py_Name name) {
     return NULL;
 }
 
+py_Ref py_tpfindname(py_Type t, py_Name name) {
+    pk_TypeInfo* types = (pk_TypeInfo*)pk_current_vm->types.data;
+    do {
+        py_Ref res = py_getdict(&types[t].self, name);
+        if(res) return res;
+        t = types[t].base;
+    } while(t);
+    return NULL;
+}
+
 py_Ref py_tpmagic(py_Type type, py_Name name) {
     assert(py_ismagicname(name));
     pk_VM* vm = pk_current_vm;
@@ -241,7 +273,9 @@ bool py_callmagic(py_Name name, int argc, py_Ref argv) {
     assert(argc >= 1);
     assert(py_ismagicname(name));
     py_Ref tmp = py_tpfindmagic(argv->type, name);
-    if(!tmp) return TypeError(name);
+    if(!tmp){
+        return AttributeError(argv, name);
+    }
     if(tmp->type == tp_nativefunc) return tmp->_cfunc(argc, argv);
     return py_call(tmp, argc, argv);
 }