improve errors

This commit is contained in:
blueloveTH 2024-07-04 22:44:37 +08:00
parent facd1c0ce6
commit 3a8613b7ff
8 changed files with 121 additions and 49 deletions

View File

@ -116,11 +116,9 @@ bool py_issubclass(py_Type derived, py_Type base);
/************* References *************/ /************* References *************/
#define py_offset(p, i) (py_Ref)((char*)p + ((i) << 4)) #define py_offset(p, i) (py_Ref)((char*)p + ((i) << 4))
#define TypeError(...) false
#define py_arg(i) py_offset(argv, i) #define py_arg(i) py_offset(argv, i)
#define py_checkargc(n) \ #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); py_GlobalRef py_tpmagic(py_Type type, py_Name name);
#define py_bindmagic(type, __magic__, f) py_newnativefunc(py_tpmagic((type), __magic__), (f)) #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); void py_setupvalue(py_StackRef argv, const py_Ref val);
/// Gets the attribute of the object. /// Gets the attribute of the object.
bool py_getattr(const py_Ref self, py_Name name, py_Ref out); /// 1: success, 0: not found, -1: error
/// Gets the unbound method of the object. int py_getattr(const py_Ref self, py_Name name, py_Ref out);
bool py_getunboundmethod(const py_Ref self,
py_Name name,
bool fallback,
py_Ref out,
py_Ref out_self);
/// Sets the attribute of the object. /// Sets the attribute of the object.
bool py_setattr(py_Ref self, py_Name name, const py_Ref val); bool py_setattr(py_Ref self, py_Name name, const py_Ref val);
/// Deletes the attribute of the object. /// Deletes the attribute of the object.
bool py_delattr(py_Ref self, py_Name name); 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_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); 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); bool py_import(const char* name);
/************* Errors *************/ /************* Errors *************/
bool py_exception(const char* name, const char* fmt, ...);
/// Print the last error to the console. /// Print the last error to the console.
void py_printexc(); void py_printexc();
/// Format the last error to a string. /// Format the last error to a string.
void py_formatexc(char* out); 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 *************/ /************* Operators *************/
/// Equivalent to `bool(val)`. /// Equivalent to `bool(val)`.
/// Returns 1 if `val` is truthy, otherwise 0. /// 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. /// Return the reference or NULL if not found.
py_GlobalRef py_tpfindmagic(py_Type, py_Name name); 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. /// Get the type object of the given type.
py_GlobalRef py_tpobject(py_Type type); py_GlobalRef py_tpobject(py_Type type);
@ -333,28 +343,28 @@ enum py_MagicNames {
enum py_PredefinedTypes { enum py_PredefinedTypes {
tp_object = 1, tp_object = 1,
tp_type, tp_type, // py_Type
tp_int, tp_int,
tp_float, tp_float,
tp_bool, tp_bool,
tp_str, tp_str,
tp_list, tp_list, // c11_vector
tp_tuple, tp_tuple, // N slots
tp_slice, tp_slice, // 3 slots (start, stop, step)
tp_range, tp_range,
tp_module, tp_module,
tp_function, tp_function,
tp_nativefunc, tp_nativefunc,
tp_bound_method, tp_bound_method,
tp_super, tp_super, // 1 slot + py_Type
tp_exception, tp_exception,
tp_bytes, tp_bytes,
tp_mappingproxy, tp_mappingproxy,
tp_dict, tp_dict,
tp_property, tp_property, // 2 slots (getter + setter)
tp_star_wrapper, tp_star_wrapper,
tp_staticmethod, tp_staticmethod, // 1 slot
tp_classmethod, tp_classmethod, // 1 slot
tp_none_type, tp_none_type,
tp_not_implemented_type, tp_not_implemented_type,
tp_ellipsis, tp_ellipsis,

View File

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

View File

@ -5,12 +5,6 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include <stdbool.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); 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; goto __ERROR;
} }
case OP_LOAD_METHOD: { case OP_LOAD_METHOD: {
// `py_getunboundmethod` never fails on `fallback=true` // [self]
py_getunboundmethod(TOP(), byte.arg, true, TOP(), SP()); bool ok = py_getunboundmethod(TOP(), byte.arg, TOP(), SP());
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(); DISPATCH();
} }
case OP_LOAD_SUBSCR: { case OP_LOAD_SUBSCR: {
@ -276,7 +282,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
} }
DISPATCH(); DISPATCH();
} }
TypeError(); TypeError("'%t' object is not subscriptable", SECOND()->type);
goto __ERROR; goto __ERROR;
} }
case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH(); 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(); DISPATCH();
} }
TypeError(); TypeError("'%t' object does not support item assignment", SECOND()->type);
goto __ERROR; goto __ERROR;
} }
case OP_DELETE_FAST: { case OP_DELETE_FAST: {
py_Ref tmp = &frame->locals[byte.arg]; py_Ref tmp = &frame->locals[byte.arg];
if(py_isnull(tmp)) { 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; goto __ERROR;
} }
py_newnull(tmp); py_newnull(tmp);
@ -400,7 +407,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
} }
DISPATCH(); DISPATCH();
} }
TypeError(); TypeError("'%t' object does not support item deletion", SECOND()->type);
goto __ERROR; goto __ERROR;
} }
/*****************************************/ /*****************************************/
@ -536,7 +543,8 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
if(byte.arg) py_newbool(TOP(), !res); if(byte.arg) py_newbool(TOP(), !res);
DISPATCH(); DISPATCH();
} }
TypeError(); // TODO: fallback to __iter__?
TypeError("argument of type '%t' is not iterable", SECOND()->type);
goto __ERROR; 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__ // 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) { bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop) {

View File

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

View File

@ -364,7 +364,7 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t ARGC, uint16_t KWARGC, bo
} }
// handle `__call__` overload // handle `__call__` overload
if(py_getunboundmethod(p0, __call__, false, p0, p0 + 1)) { if(py_getunboundmethod(p0, __call__, p0, p0 + 1)) {
// [__call__, self, args..., kwargs...] // [__call__, self, args..., kwargs...]
pk_FrameResult res = pk_VM__vectorcall(self, ARGC, KWARGC, false); pk_FrameResult res = pk_VM__vectorcall(self, ARGC, KWARGC, false);
if(res == RES_ERROR) return RES_ERROR; if(res == RES_ERROR) return RES_ERROR;

View File

@ -1,6 +1,9 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include <stdarg.h>
void py_printexc(){ void py_printexc(){
pk_VM* vm = pk_current_vm; pk_VM* vm = pk_current_vm;
if(vm->has_error){ if(vm->has_error){
@ -14,3 +17,22 @@ void py_printexc(){
void py_formatexc(char *out){ 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;
}

View File

@ -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_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; } bool py_getitem(const py_Ref self, const py_Ref key, py_Ref out) { return -1; }

View File

@ -26,7 +26,7 @@ void py_finalize() {
pk_MemoryPools__finalize(); pk_MemoryPools__finalize();
} }
const char* pk_opname(Opcode op){ const char* pk_opname(Opcode op) {
const static char* OP_NAMES[] = { const static char* OP_NAMES[] = {
#define OPCODE(name) #name, #define OPCODE(name) #name,
#include "pocketpy/xmacros/opcodes.h" #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; } py_Ref py_retval() { return &pk_current_vm->last_retval; }
bool py_getunboundmethod(const py_Ref self, bool py_getunboundmethod(py_Ref self, py_Name name, py_Ref out, py_Ref out_self) {
py_Name name, // NOTE: `out` and `out_self` may overlap with `self`
bool fallback, py_Type type;
py_Ref out, // handle super() proxy
py_Ref out_self) { 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; return false;
} }
@ -209,7 +232,6 @@ pk_TypeInfo* pk_tpinfo(const py_Ref self) {
} }
py_Ref py_tpfindmagic(py_Type t, py_Name name) { py_Ref py_tpfindmagic(py_Type t, py_Name name) {
assert(t);
assert(py_ismagicname(name)); assert(py_ismagicname(name));
pk_TypeInfo* types = (pk_TypeInfo*)pk_current_vm->types.data; pk_TypeInfo* types = (pk_TypeInfo*)pk_current_vm->types.data;
do { do {
@ -220,6 +242,16 @@ py_Ref py_tpfindmagic(py_Type t, py_Name name) {
return NULL; 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) { py_Ref py_tpmagic(py_Type type, py_Name name) {
assert(py_ismagicname(name)); assert(py_ismagicname(name));
pk_VM* vm = pk_current_vm; 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(argc >= 1);
assert(py_ismagicname(name)); assert(py_ismagicname(name));
py_Ref tmp = py_tpfindmagic(argv->type, 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); if(tmp->type == tp_nativefunc) return tmp->_cfunc(argc, argv);
return py_call(tmp, argc, argv); return py_call(tmp, argc, argv);
} }