mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-25 22:10:17 +00:00
Compare commits
2 Commits
8de566f875
...
3e7ca3cddd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e7ca3cddd | ||
|
|
f2363a8657 |
@ -44,7 +44,7 @@ typedef struct pk_VM {
|
||||
void (*_stderr)(const char*, ...);
|
||||
|
||||
py_TValue last_retval;
|
||||
py_TValue last_exception;
|
||||
py_TValue curr_exception;
|
||||
bool is_stopiteration;
|
||||
|
||||
py_TValue reg[8]; // users' registers
|
||||
|
||||
@ -2,38 +2,15 @@
|
||||
|
||||
#include "pocketpy/common/str.h"
|
||||
#include "pocketpy/common/strname.h"
|
||||
#include "pocketpy/objects/codeobject.h"
|
||||
#include "pocketpy/objects/sourcedata.h"
|
||||
#include "pocketpy/objects/object.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// typedef struct pkpy_ExceptionFrame {
|
||||
// pk_SourceData_ src;
|
||||
// int lineno;
|
||||
// const char* cursor;
|
||||
// c11_string* name;
|
||||
// } pkpy_ExceptionFrame;
|
||||
|
||||
// typedef struct pkpy_Exception {
|
||||
// py_Name type;
|
||||
// c11_string* msg;
|
||||
// bool is_re;
|
||||
|
||||
// int _ip_on_error;
|
||||
// void* _code_on_error;
|
||||
|
||||
// PyObject* self; // weak reference
|
||||
|
||||
// c11_vector/*T=pkpy_ExceptionFrame*/ stacktrace;
|
||||
// } pkpy_Exception;
|
||||
|
||||
// void pkpy_Exception__ctor(pkpy_Exception* self, py_Name type);
|
||||
// void pkpy_Exception__dtor(pkpy_Exception* self);
|
||||
// void pkpy_Exception__stpush(pkpy_Exception* self, pk_SourceData_ src, int lineno, const char* cursor, const char* name);
|
||||
// py_Str pkpy_Exception__summary(pkpy_Exception* self);
|
||||
|
||||
struct Error{
|
||||
const char* type;
|
||||
pk_SourceData_ src;
|
||||
@ -43,6 +20,10 @@ struct Error{
|
||||
int64_t userdata;
|
||||
};
|
||||
|
||||
void py_BaseException__set_lineno(py_Ref, int lineno, const CodeObject* code);
|
||||
int py_BaseException__get_lineno(py_Ref, const CodeObject* code);
|
||||
void py_BaseException__stpush(py_Ref, pk_SourceData_ src, int lineno, const char* func_name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -241,12 +241,15 @@ py_TmpRef py_getmodule(const char* name);
|
||||
bool py_import(const char* name);
|
||||
|
||||
/************* Errors *************/
|
||||
/// Raise an exception by name and message. Always returns false.
|
||||
bool py_exception(const char* name, const char* fmt, ...);
|
||||
/// Raise an expection object. Always returns false.
|
||||
bool py_raise(py_Ref);
|
||||
/// Print the last error to the console.
|
||||
void py_printexc();
|
||||
/// Format the last error to a string.
|
||||
void py_formatexc(char* out);
|
||||
/// Check if an error is set.
|
||||
char* py_formatexc();
|
||||
/// Check if an exception is raised.
|
||||
bool py_checkexc();
|
||||
|
||||
#define NameError(n) py_exception("NameError", "name '%n' is not defined", (n))
|
||||
|
||||
@ -105,6 +105,7 @@ OPCODE(EXCEPTION_MATCH)
|
||||
OPCODE(RAISE)
|
||||
OPCODE(RAISE_ASSERT)
|
||||
OPCODE(RE_RAISE)
|
||||
OPCODE(PUSH_EXCEPTION)
|
||||
OPCODE(POP_EXCEPTION)
|
||||
/**************************/
|
||||
OPCODE(FSTRING_EVAL)
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "pocketpy/common/config.h"
|
||||
#include "pocketpy/common/memorypool.h"
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* expr.h */
|
||||
typedef struct Expr Expr;
|
||||
@ -2492,7 +2493,53 @@ __EAT_DOTS_END:
|
||||
}
|
||||
|
||||
static Error* compile_try_except(Compiler* self) {
|
||||
assert(false);
|
||||
Error* err;
|
||||
Ctx__enter_block(ctx(), CodeBlockType_TRY_EXCEPT);
|
||||
Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line);
|
||||
check(compile_block_body(self, compile_stmt));
|
||||
|
||||
int patches[8];
|
||||
int patches_length = 0;
|
||||
|
||||
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
||||
Ctx__exit_block(ctx());
|
||||
|
||||
if(curr()->type == TK_FINALLY) { return SyntaxError("finally clause is not supported yet"); }
|
||||
|
||||
do {
|
||||
if(patches_length == 8) { return SyntaxError("maximum number of except clauses reached"); }
|
||||
py_Name as_name = 0;
|
||||
consume(TK_EXCEPT);
|
||||
if(is_expression(self, false)) {
|
||||
check(EXPR(self)); // push assumed type on to the stack
|
||||
Ctx__s_emit_top(ctx());
|
||||
Ctx__emit_(ctx(), OP_EXCEPTION_MATCH, BC_NOARG, prev()->line);
|
||||
if(match(TK_AS)) {
|
||||
consume(TK_ID);
|
||||
as_name = py_namev(Token__sv(prev()));
|
||||
}
|
||||
} else {
|
||||
Ctx__emit_(ctx(), OP_LOAD_TRUE, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);
|
||||
// on match
|
||||
if(as_name) {
|
||||
Ctx__emit_(ctx(), OP_PUSH_EXCEPTION, BC_NOARG, BC_KEEPLINE);
|
||||
Ctx__emit_store_name(ctx(), name_scope(self), as_name, BC_KEEPLINE);
|
||||
}
|
||||
// pop the exception
|
||||
Ctx__emit_(ctx(), OP_POP_EXCEPTION, BC_NOARG, BC_KEEPLINE);
|
||||
check(compile_block_body(self, compile_stmt));
|
||||
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
||||
Ctx__patch_jump(ctx(), patch);
|
||||
} while(curr()->type == TK_EXCEPT);
|
||||
|
||||
// no match, re-raise
|
||||
Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE);
|
||||
|
||||
// no exception or no match, jump to the end
|
||||
for(int i = 0; i < patches_length; i++)
|
||||
Ctx__patch_jump(ctx(), patches[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
#include "pocketpy/common/config.h"
|
||||
#include "pocketpy/common/utils.h"
|
||||
#include "pocketpy/interpreter/frame.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
#include "pocketpy/common/memorypool.h"
|
||||
#include "pocketpy/common/sstream.h"
|
||||
#include "pocketpy/objects/codeobject.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include "pocketpy/objects/error.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
static bool stack_unpack_sequence(pk_VM* self, uint16_t arg);
|
||||
@ -850,6 +853,30 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
|
||||
DISPATCH();
|
||||
}
|
||||
///////////
|
||||
case OP_TRY_ENTER: {
|
||||
Frame__set_unwind_target(frame, SP());
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_EXCEPTION_MATCH: {
|
||||
if(!py_checktype(TOP(), tp_type)) goto __ERROR;
|
||||
bool ok = py_isinstance(TOP(), py_totype(&self->curr_exception));
|
||||
POP();
|
||||
py_newbool(TOP(), ok);
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_RAISE: {
|
||||
// [exception]
|
||||
if(py_istype(TOP(), tp_type)) {
|
||||
if(!py_tpcall(py_totype(TOP()), 0, NULL)) goto __ERROR;
|
||||
py_assign(TOP(), py_retval());
|
||||
}
|
||||
if(!py_isinstance(TOP(), tp_BaseException)) {
|
||||
TypeError("exceptions must derive from BaseException");
|
||||
goto __ERROR;
|
||||
}
|
||||
py_raise(TOP());
|
||||
goto __ERROR;
|
||||
}
|
||||
case OP_RAISE_ASSERT: {
|
||||
if(byte.arg) {
|
||||
if(!py_str(TOP())) goto __ERROR;
|
||||
@ -860,6 +887,20 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
|
||||
}
|
||||
goto __ERROR;
|
||||
}
|
||||
case OP_RE_RAISE: {
|
||||
py_raise(&self->curr_exception);
|
||||
goto __ERROR_RE_RAISE;
|
||||
}
|
||||
case OP_PUSH_EXCEPTION: {
|
||||
assert(self->curr_exception.type);
|
||||
PUSH(&self->curr_exception);
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_POP_EXCEPTION: {
|
||||
assert(self->curr_exception.type);
|
||||
self->curr_exception = *py_NIL;
|
||||
DISPATCH();
|
||||
}
|
||||
//////////////////
|
||||
case OP_FSTRING_EVAL: {
|
||||
py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||
@ -879,13 +920,28 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
|
||||
default: c11__unreachedable();
|
||||
}
|
||||
|
||||
assert(false); // should never reach here
|
||||
c11__unreachedable();
|
||||
|
||||
__ERROR:
|
||||
// 1. Exception can be handled inside the current frame
|
||||
// 2. Exception need to be propagated to the upper frame
|
||||
pk_print_stack(self, frame, (Bytecode){OP_NO_OP, 0});
|
||||
py_BaseException__set_lineno(&self->curr_exception, Frame__lineno(frame), frame->co);
|
||||
__ERROR_RE_RAISE:
|
||||
|
||||
printf("error.op: %s, line: %d\n", pk_opname(byte.op), Frame__lineno(frame));
|
||||
return RES_ERROR;
|
||||
int lineno = py_BaseException__get_lineno(&self->curr_exception, frame->co);
|
||||
py_BaseException__stpush(&self->curr_exception,
|
||||
frame->co->src,
|
||||
lineno < 0 ? Frame__lineno(frame) : lineno,
|
||||
frame->function ? frame->co->name->data : NULL);
|
||||
|
||||
int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
|
||||
if(target >= 0) {
|
||||
// 1. Exception can be handled inside the current frame
|
||||
DISPATCH_JUMP_ABSOLUTE(target);
|
||||
} else {
|
||||
// 2. Exception need to be propagated to the upper frame
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return RES_RETURN;
|
||||
|
||||
@ -71,10 +71,8 @@ int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) {
|
||||
iblock = block->parent;
|
||||
}
|
||||
if(iblock < 0) return -1;
|
||||
py_TValue obj = *--_s->sp; // pop exception object
|
||||
UnwindTarget* uw = Frame__find_unwind_target(self, iblock);
|
||||
_s->sp = (self->locals + uw->offset); // unwind the stack
|
||||
*(_s->sp++) = obj; // push it back
|
||||
return c11__at(CodeBlock, &self->co->blocks, iblock)->end;
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ static void pk_TypeInfo__ctor(pk_TypeInfo* self,
|
||||
py_Name name,
|
||||
py_Type index,
|
||||
py_Type base,
|
||||
const py_TValue* module) {
|
||||
py_TValue module) {
|
||||
memset(self, 0, sizeof(pk_TypeInfo));
|
||||
|
||||
self->name = name;
|
||||
@ -47,7 +47,7 @@ static void pk_TypeInfo__ctor(pk_TypeInfo* self,
|
||||
._obj = typeobj,
|
||||
};
|
||||
|
||||
self->module = module ? *module : *py_NIL;
|
||||
self->module = module;
|
||||
c11_vector__ctor(&self->annotated_fields, sizeof(py_Name));
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ void pk_VM__ctor(pk_VM* self) {
|
||||
self->_stderr = pk_default_stderr;
|
||||
|
||||
self->last_retval = *py_NIL;
|
||||
self->last_exception = *py_NIL;
|
||||
self->curr_exception = *py_NIL;
|
||||
self->is_stopiteration = false;
|
||||
|
||||
self->__curr_class = NULL;
|
||||
@ -161,6 +161,30 @@ void pk_VM__ctor(pk_VM* self) {
|
||||
py_setdict(&self->builtins, ti->name, py_tpobject(t));
|
||||
}
|
||||
|
||||
// inject some builtin expections
|
||||
const char** builtin_exceptions = (const char*[]){
|
||||
"StackOverflowError",
|
||||
"IOError",
|
||||
"NotImplementedError",
|
||||
"TypeError",
|
||||
"IndexError",
|
||||
"ValueError",
|
||||
"RuntimeError",
|
||||
"ZeroDivisionError",
|
||||
"NameError",
|
||||
"UnboundLocalError",
|
||||
"AttributeError",
|
||||
"ImportError",
|
||||
"AssertionError",
|
||||
// "KeyError",
|
||||
NULL, // sentinel
|
||||
};
|
||||
const char** it = builtin_exceptions;
|
||||
while(*it){
|
||||
py_Type type = pk_newtype(*it, tp_Exception, &self->builtins, NULL, false, true);
|
||||
py_setdict(&self->builtins, py_name(*it), py_tpobject(type));
|
||||
}
|
||||
|
||||
py_TValue tmp;
|
||||
py_newnotimplemented(&tmp);
|
||||
py_setdict(&self->builtins, py_name("NotImplemented"), &tmp);
|
||||
@ -266,7 +290,7 @@ py_Type pk_newtype(const char* name,
|
||||
c11_vector* types = &pk_current_vm->types;
|
||||
py_Type index = types->count;
|
||||
pk_TypeInfo* ti = c11_vector__emplace(types);
|
||||
pk_TypeInfo__ctor(ti, py_name(name), index, base, module);
|
||||
pk_TypeInfo__ctor(ti, py_name(name), index, base, module ? *module : *py_NIL);
|
||||
ti->dtor = dtor;
|
||||
ti->is_python = is_python;
|
||||
ti->is_sealed = is_sealed;
|
||||
@ -540,14 +564,14 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
|
||||
}
|
||||
// mark vm's registers
|
||||
mark_value(&vm->last_retval);
|
||||
mark_value(&vm->last_exception);
|
||||
mark_value(&vm->curr_exception);
|
||||
for(int i = 0; i < c11__count_array(vm->reg); i++) {
|
||||
mark_value(&vm->reg[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) {
|
||||
return;
|
||||
// return;
|
||||
|
||||
py_TValue* sp = self->stack.sp;
|
||||
c11_sbuf buf;
|
||||
|
||||
@ -46,4 +46,17 @@ bool py_checktype(py_Ref self, py_Type type) {
|
||||
return TypeError("expected %t, got %t", type, self->type);
|
||||
}
|
||||
|
||||
bool py_isinstance(py_Ref obj, py_Type type){
|
||||
return py_issubclass(obj->type, type);
|
||||
}
|
||||
|
||||
bool py_issubclass(py_Type derived, py_Type base){
|
||||
pk_TypeInfo* types = pk_current_vm->types.data;
|
||||
do {
|
||||
if(derived == base) return true;
|
||||
derived = types[derived].base;
|
||||
} while(derived);
|
||||
return false;
|
||||
}
|
||||
|
||||
py_Type py_typeof(py_Ref self) { return self->type; }
|
||||
@ -1,3 +1,4 @@
|
||||
#include "pocketpy/objects/base.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include "pocketpy/common/sstream.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
@ -6,29 +7,31 @@
|
||||
|
||||
bool py_checkexc() {
|
||||
pk_VM* vm = pk_current_vm;
|
||||
return !py_isnil(&vm->last_exception);
|
||||
return !py_isnil(&vm->curr_exception);
|
||||
}
|
||||
|
||||
void py_printexc() {
|
||||
pk_VM* vm = pk_current_vm;
|
||||
if(py_isnil(&vm->last_exception)) {
|
||||
if(py_isnil(&vm->curr_exception)) {
|
||||
vm->_stdout("NoneType: None\n");
|
||||
} else {
|
||||
const char* name = py_tpname(vm->last_exception.type);
|
||||
bool ok = py_str(&vm->last_exception);
|
||||
const char* name = py_tpname(vm->curr_exception.type);
|
||||
bool ok = py_str(&vm->curr_exception);
|
||||
if(!ok) abort();
|
||||
const char* message = py_tostr(py_retval());
|
||||
vm->_stdout("%s: %s\n", name, message);
|
||||
}
|
||||
}
|
||||
|
||||
void py_formatexc(char* out) {}
|
||||
char* py_formatexc() {
|
||||
pk_VM* vm = pk_current_vm;
|
||||
if(py_isnil(&vm->curr_exception)) {
|
||||
return NULL;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool py_exception(const char* name, const char* fmt, ...) {
|
||||
pk_VM* vm = pk_current_vm;
|
||||
// an error is already set
|
||||
assert(py_isnil(&vm->last_exception));
|
||||
|
||||
c11_sbuf buf;
|
||||
c11_sbuf__ctor(&buf);
|
||||
va_list args;
|
||||
@ -41,11 +44,16 @@ bool py_exception(const char* name, const char* fmt, ...) {
|
||||
py_newstrn(message, res->data, res->size);
|
||||
c11_string__delete(res);
|
||||
bool ok = py_tpcall(tp_Exception, 1, message);
|
||||
if(!ok) abort();
|
||||
py_pop();
|
||||
|
||||
if(!ok) abort();
|
||||
vm->last_exception = *py_retval();
|
||||
return py_raise(py_retval());
|
||||
}
|
||||
|
||||
bool py_raise(py_Ref exc) {
|
||||
assert(py_isinstance(exc, tp_BaseException));
|
||||
pk_VM* vm = pk_current_vm;
|
||||
vm->curr_exception = *exc;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#include "pocketpy/objects/codeobject.h"
|
||||
#include "pocketpy/objects/error.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
|
||||
#include "pocketpy/common/utils.h"
|
||||
@ -12,20 +14,47 @@ typedef struct BaseExceptionFrame {
|
||||
} BaseExceptionFrame;
|
||||
|
||||
typedef struct BaseException {
|
||||
int ip_backup;
|
||||
CodeObject* code_backup;
|
||||
int lineno_backup;
|
||||
const CodeObject* code_backup;
|
||||
c11_vector /*T=BaseExceptionFrame*/ stacktrace;
|
||||
} BaseException;
|
||||
|
||||
void py_BaseException__set_lineno(py_Ref self, int lineno, const CodeObject* code){
|
||||
BaseException* ud = py_touserdata(self);
|
||||
ud->lineno_backup = lineno;
|
||||
ud->code_backup = code;
|
||||
}
|
||||
|
||||
int py_BaseException__get_lineno(py_Ref self, const CodeObject* code){
|
||||
BaseException* ud = py_touserdata(self);
|
||||
if(code != ud->code_backup) return -1;
|
||||
return ud->lineno_backup;
|
||||
}
|
||||
|
||||
void py_BaseException__stpush(py_Ref self, pk_SourceData_ src, int lineno, const char *func_name){
|
||||
BaseException* ud = py_touserdata(self);
|
||||
if(ud->stacktrace.count >= 7) return;
|
||||
BaseExceptionFrame* frame = c11_vector__emplace(&ud->stacktrace);
|
||||
PK_INCREF(src);
|
||||
frame->src = src;
|
||||
frame->lineno = lineno;
|
||||
frame->name = func_name ? c11_string__new(func_name) : NULL;
|
||||
}
|
||||
|
||||
static void BaseException__dtor(void* ud) {
|
||||
BaseException* self = (BaseException*)ud;
|
||||
c11__foreach(BaseExceptionFrame, &self->stacktrace, it) {
|
||||
PK_DECREF(it->src);
|
||||
if(it->name) c11_string__delete(it->name);
|
||||
}
|
||||
c11_vector__dtor(&self->stacktrace);
|
||||
}
|
||||
|
||||
static bool _py_BaseException__new__(int argc, py_Ref argv) {
|
||||
py_Type cls = py_totype(argv);
|
||||
BaseException* ud = py_newobject(py_retval(), cls, 1, sizeof(BaseException));
|
||||
ud->ip_backup = -1;
|
||||
c11_vector__ctor(&ud->stacktrace, sizeof(BaseExceptionFrame));
|
||||
ud->lineno_backup = -1;
|
||||
ud->code_backup = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
try:
|
||||
raise 1
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
assert False
|
||||
exit(1)
|
||||
except AssertionError:
|
||||
pass
|
||||
|
||||
try:
|
||||
raise 1
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
for i in range(5):
|
||||
raise KeyError(i)
|
||||
@ -134,6 +134,7 @@ except SyntaxError as e:
|
||||
ok = True
|
||||
assert ok
|
||||
|
||||
"""
|
||||
# finally, only
|
||||
def finally_only():
|
||||
try:
|
||||
@ -195,3 +196,4 @@ except KeyError:
|
||||
exit(0)
|
||||
|
||||
exit(1)
|
||||
"""
|
||||
Loading…
x
Reference in New Issue
Block a user