blueloveTH 1 год назад
Родитель
Сommit
f2363a8657

+ 1 - 1
include/pocketpy/interpreter/vm.h

@@ -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

+ 5 - 25
include/pocketpy/objects/error.h

@@ -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,9 @@ struct Error{
     int64_t userdata;
 };
 
+void py_BaseException__record(py_Ref, const Bytecode* ip, const CodeObject* code);
+void py_BaseException__stpush(py_Ref, pk_SourceData_ src, int lineno, const char* func_name);
+
 #ifdef __cplusplus
 }
 #endif

+ 5 - 2
include/pocketpy/pocketpy.h

@@ -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))

+ 1 - 0
include/pocketpy/xmacros/opcodes.h

@@ -105,6 +105,7 @@ OPCODE(EXCEPTION_MATCH)
 OPCODE(RAISE)
 OPCODE(RAISE_ASSERT)
 OPCODE(RE_RAISE)
+OPCODE(PUSH_EXCEPTION)
 OPCODE(POP_EXCEPTION)
 /**************************/
 OPCODE(FSTRING_EVAL)

+ 48 - 1
src/compiler/compiler.c

@@ -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;
 }
 

+ 60 - 5
src/interpreter/ceval.c

@@ -1,10 +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 <stdbool.h>
+#include "pocketpy/objects/error.h"
 
 static bool stack_unpack_sequence(pk_VM* self, uint16_t arg);
 static bool format_object(py_Ref obj, c11_sv spec);
@@ -850,6 +852,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 +886,20 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 }
                 goto __ERROR;
             }
+            case OP_RE_RAISE: {
+                py_raise(&self->curr_exception);
+                goto __ERROR;
+            }
+            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 +919,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
         printf("error.op: %s, line: %d\n", pk_opname(byte.op), Frame__lineno(frame));
-        return RES_ERROR;
+        py_BaseException__record(&self->curr_exception, frame->ip, frame->co);
+        // TODO: lineno bug on re-raise
+        int lineno = Frame__lineno(frame);
+        py_BaseException__stpush(
+            &self->curr_exception,
+            frame->co->src,
+            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;

+ 3 - 3
src/interpreter/vm.c

@@ -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;
@@ -540,14 +540,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;

+ 13 - 0
src/public/cast.c

@@ -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; }

+ 19 - 11
src/public/error.c

@@ -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) {}
-
-bool py_exception(const char* name, const char* fmt, ...) {
+char* py_formatexc() {
     pk_VM* vm = pk_current_vm;
-    // an error is already set
-    assert(py_isnil(&vm->last_exception));
+    if(py_isnil(&vm->curr_exception)) {
+        return NULL;
+    }
+    assert(false);
+}
 
+bool py_exception(const char* name, const char* fmt, ...) {
     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;
 }
 

+ 26 - 3
src/public/py_exception.c

@@ -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,41 @@ typedef struct BaseExceptionFrame {
 } BaseExceptionFrame;
 
 typedef struct BaseException {
-    int ip_backup;
-    CodeObject* code_backup;
+    const Bytecode* ip_backup;
+    const CodeObject* code_backup;
     c11_vector /*T=BaseExceptionFrame*/ stacktrace;
 } BaseException;
 
+void py_BaseException__record(py_Ref self, const Bytecode* ip, const CodeObject* code) {
+    BaseException* ud = py_touserdata(self);
+    ud->ip_backup = ip;
+    ud->code_backup = code;
+}
+
+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->ip_backup = NULL;
     ud->code_backup = NULL;
     return true;
 }

+ 0 - 0
tests/41_exception.py → tests/28_exception.py


+ 0 - 0
tests/28_iter.py → tests/29_iter.py