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 *************/
|
/************* 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,
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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]);
|
||||||
|
@ -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;
|
||||||
@ -400,4 +400,4 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
|
|||||||
// vm->obj_gc_mark(vm->__c.error);
|
// vm->obj_gc_mark(vm->__c.error);
|
||||||
// vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
|
// vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
|
||||||
// if(self->_gc_marker_ex) self->_gc_marker_ex((pkpy_VM*)vm);
|
// if(self->_gc_marker_ex) self->_gc_marker_ex((pkpy_VM*)vm);
|
||||||
}
|
}
|
||||||
|
@ -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){
|
||||||
@ -13,4 +16,23 @@ 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;
|
||||||
}
|
}
|
@ -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; }
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user