mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
improve errors
This commit is contained in:
parent
facd1c0ce6
commit
3a8613b7ff
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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){
|
||||
@ -14,3 +17,22 @@ 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;
|
||||
}
|
@ -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; }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user