From 50ec46fe83a83a15341d1bf6e9b7c88f51626b08 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 5 Aug 2024 14:20:39 +0800 Subject: [PATCH] ... --- include/pocketpy/pocketpy.h | 42 ++++++++++---- src/interpreter/ceval.c | 6 +- src/interpreter/vm.c | 60 ++++++++++---------- src/public/internal.c | 2 +- src/public/modules.c | 23 +++++++- src/public/py_exception.c | 23 ++++---- tests/{46_star.py => 44_star.py} | 0 tests/{47_reflection.py => 50_reflection.py} | 0 tests/{44_eval.py => 51_eval.py} | 0 tests/{45_yield.py => 52_yield.py} | 0 10 files changed, 98 insertions(+), 58 deletions(-) rename tests/{46_star.py => 44_star.py} (100%) rename tests/{47_reflection.py => 50_reflection.py} (100%) rename tests/{44_eval.py => 51_eval.py} (100%) rename tests/{45_yield.py => 52_yield.py} (100%) diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 5a910b8b..c5bdbfc2 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -281,7 +281,7 @@ int py_import(const char* path) PY_RAISE; /************* Errors *************/ /// Raise an exception by name and message. Always returns false. -bool py_exception(const char* name, const char* fmt, ...) PY_RAISE; +bool py_exception(py_Type type, const char* fmt, ...) PY_RAISE; /// Raise an expection object. Always returns false. bool py_raise(py_Ref) PY_RAISE; /// Print the current exception. @@ -290,22 +290,24 @@ void py_printexc(); char* py_formatexc(); /// Check if an exception is raised. bool py_checkexc(); +/// Check if the exception is an instance of the given type. +bool py_matchexc(py_Type type); /// Clear the current exception. void py_clearexc(py_StackRef p0); -#define IOError(...) py_exception("IOError", __VA_ARGS__) -#define OSError(...) py_exception("OSError", __VA_ARGS__) -#define NameError(n) py_exception("NameError", "name '%n' is not defined", (n)) -#define TypeError(...) py_exception("TypeError", __VA_ARGS__) -#define RuntimeError(...) py_exception("RuntimeError", __VA_ARGS__) -#define ValueError(...) py_exception("ValueError", __VA_ARGS__) -#define IndexError(...) py_exception("IndexError", __VA_ARGS__) -#define ImportError(...) py_exception("ImportError", __VA_ARGS__) -#define NotImplementedError() py_exception("NotImplementedError", "") +#define IOError(...) py_exception(tp_IOError, __VA_ARGS__) +#define OSError(...) py_exception(tp_OSError, __VA_ARGS__) +#define NameError(n) py_exception(tp_NameError, "name '%n' is not defined", (n)) +#define TypeError(...) py_exception(tp_TypeError, __VA_ARGS__) +#define RuntimeError(...) py_exception(tp_RuntimeError, __VA_ARGS__) +#define ValueError(...) py_exception(tp_ValueError, __VA_ARGS__) +#define IndexError(...) py_exception(tp_IndexError, __VA_ARGS__) +#define ImportError(...) py_exception(tp_ImportError, __VA_ARGS__) +#define NotImplementedError() py_exception(tp_NotImplementedError, "") #define AttributeError(self, n) \ - py_exception("AttributeError", "'%t' object has no attribute '%n'", (self)->type, (n)) + py_exception(tp_AttributeError, "'%t' object has no attribute '%n'", (self)->type, (n)) #define UnboundLocalError(n) \ - py_exception("UnboundLocalError", "local variable '%n' referenced before assignment", (n)) + py_exception(tp_UnboundLocalError, "local variable '%n' referenced before assignment", (n)) bool StopIteration(); bool KeyError(py_Ref key) PY_RAISE; @@ -429,6 +431,22 @@ enum py_PredefinedTypes { tp_ellipsis, tp_SyntaxError, tp_StopIteration, + /* builtin exceptions */ + tp_StackOverflowError, + tp_IOError, + tp_OSError, + tp_NotImplementedError, + tp_TypeError, + tp_IndexError, + tp_ValueError, + tp_RuntimeError, + tp_ZeroDivisionError, + tp_NameError, + tp_UnboundLocalError, + tp_AttributeError, + tp_ImportError, + tp_AssertionError, + tp_KeyError, }; #ifdef __cplusplus diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 4d7bb989..3d2b4264 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -909,9 +909,9 @@ FrameResult VM__run_top_frame(VM* self) { if(byte.arg) { if(!py_str(TOP())) goto __ERROR; POP(); - py_exception("AssertionError", "%s", py_tostr(py_retval())); + py_exception(tp_AssertionError, "%s", py_tostr(py_retval())); } else { - py_exception("AssertionError", ""); + py_exception(tp_AssertionError, ""); } goto __ERROR; } @@ -1010,7 +1010,7 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) { py_newbool(py_retval(), res); return true; } - return py_exception("TypeError", "unsupported operand type(s) for '%n'", op); + return TypeError("unsupported operand type(s) for '%n'", op); } bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) { diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 222e4606..ced3829c 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -129,10 +129,36 @@ void VM__ctor(VM* self) { validate(tp_SyntaxError, pk_newtype("SyntaxError", tp_Exception, NULL, NULL, false, true)); validate(tp_StopIteration, pk_newtype("StopIteration", tp_Exception, NULL, NULL, false, true)); -#undef validate self->builtins = pk_builtins__register(); + // inject some builtin expections +#define INJECT_BUILTIN_EXC(name) \ + do { \ + py_Type type = pk_newtype(#name, tp_Exception, &self->builtins, NULL, false, true); \ + py_setdict(&self->builtins, py_name(#name), py_tpobject(type)); \ + validate(tp_##name, type); \ + } while(0) + + INJECT_BUILTIN_EXC(StackOverflowError); + INJECT_BUILTIN_EXC(IOError); + INJECT_BUILTIN_EXC(OSError); + INJECT_BUILTIN_EXC(NotImplementedError); + INJECT_BUILTIN_EXC(TypeError); + INJECT_BUILTIN_EXC(IndexError); + INJECT_BUILTIN_EXC(ValueError); + INJECT_BUILTIN_EXC(RuntimeError); + INJECT_BUILTIN_EXC(ZeroDivisionError); + INJECT_BUILTIN_EXC(NameError); + INJECT_BUILTIN_EXC(UnboundLocalError); + INJECT_BUILTIN_EXC(AttributeError); + INJECT_BUILTIN_EXC(ImportError); + INJECT_BUILTIN_EXC(AssertionError); + INJECT_BUILTIN_EXC(KeyError); + +#undef INJECT_BUILTIN_EXC +#undef validate + /* Setup Public Builtin Types */ py_Type public_types[] = {tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str, tp_list, tp_tuple, @@ -146,35 +172,7 @@ void VM__ctor(VM* self) { py_setdict(&self->builtins, ti->name, py_tpobject(t)); } - // inject some builtin expections - const char** builtin_exceptions = (const char*[]){ - "StackOverflowError", - "IOError", - "OSError", - "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)); - it++; - } - - py_TValue tmp; - py_newnotimplemented(&tmp); - py_setdict(&self->builtins, py_name("NotImplemented"), &tmp); + py_newnotimplemented(py_emplacedict(&self->builtins, py_name("NotImplemented"))); // add modules pk__add_module_pkpy(); @@ -387,7 +385,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall /*****************_py_call*****************/ // check stack overflow if(self->stack.sp > self->stack.end) { - py_exception("StackOverflowError", ""); + py_exception(tp_StackOverflowError, ""); return RES_ERROR; } diff --git a/src/public/internal.c b/src/public/internal.c index 025dc3b5..90e33005 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -83,7 +83,7 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, SourceData_ src = SourceData__rcnew(source, filename, mode, false); Error* err = pk_compile(src, &co); if(err) { - py_exception("SyntaxError", err->msg); + py_exception(tp_SyntaxError, err->msg); py_BaseException__stpush(&vm->curr_exception, src, err->lineno, NULL); PK_DECREF(src); diff --git a/src/public/modules.c b/src/public/modules.c index 45d6ab08..88f49bc8 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -218,7 +218,7 @@ static bool builtins__next(int argc, py_Ref argv) { int res = py_next(argv); if(res == -1) return false; if(res) return true; - return py_exception("StopIteration", ""); + return py_exception(tp_StopIteration, ""); } static bool builtins__sorted(int argc, py_Ref argv) { @@ -355,6 +355,25 @@ static bool builtins__issubclass(int argc, py_Ref argv) { return true; } +static bool builtins__getattr(int argc, py_Ref argv) { + PY_CHECK_ARG_TYPE(1, tp_str); + py_Name name = py_namev(py_tosv(py_arg(1))); + if(argc == 2) { + return py_getattr(py_arg(0), name); + } else if(argc == 3) { + py_StackRef p0 = py_peek(0); + bool ok = py_getattr(py_arg(0), name); + if(!ok && py_matchexc(tp_AttributeError)) { + py_clearexc(p0); + return py_arg(2); // default value + } + return ok; + } else { + return TypeError("getattr() expected 2 or 3 arguments"); + } + return true; +} + py_TValue pk_builtins__register() { py_Ref builtins = py_newmodule("builtins"); py_bindfunc(builtins, "repr", builtins__repr); @@ -377,6 +396,8 @@ py_TValue pk_builtins__register() { py_bindfunc(builtins, "isinstance", builtins__isinstance); py_bindfunc(builtins, "issubclass", builtins__issubclass); + py_bindfunc(builtins, "getattr", builtins__getattr); + // None __repr__ py_bindmagic(tp_NoneType, __repr__, NoneType__repr__); return *builtins; diff --git a/src/public/py_exception.c b/src/public/py_exception.c index 9786fb29..5f7934e3 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -115,6 +115,12 @@ bool py_checkexc() { return !py_isnil(&vm->curr_exception); } +bool py_matchexc(py_Type type) { + VM* vm = pk_current_vm; + if(py_isnil(&vm->curr_exception)) return false; + return py_issubclass(vm->curr_exception.type, type); +} + void py_clearexc(py_StackRef p0) { VM* vm = pk_current_vm; vm->last_retval = *py_NIL; @@ -145,10 +151,10 @@ char* py_formatexc() { for(int i = ud->stacktrace.count - 1; i >= 0; i--) { BaseExceptionFrame* frame = c11__at(BaseExceptionFrame, &ud->stacktrace, i); SourceData__snapshot(frame->src, - &ss, - frame->lineno, - NULL, - frame->name ? frame->name->data : NULL); + &ss, + frame->lineno, + NULL, + frame->name ? frame->name->data : NULL); c11_sbuf__write_char(&ss, '\n'); } @@ -169,7 +175,7 @@ char* py_formatexc() { return dup; } -bool py_exception(const char* name, const char* fmt, ...) { +bool py_exception(py_Type type, const char* fmt, ...) { c11_sbuf buf; c11_sbuf__ctor(&buf); va_list args; @@ -180,9 +186,7 @@ bool py_exception(const char* name, const char* fmt, ...) { py_Ref message = py_pushtmp(); c11_sbuf__py_submit(&buf, message); - py_Ref exc_type = py_getdict(&pk_current_vm->builtins, py_name(name)); - if(exc_type == NULL) c11__abort("py_exception(): '%s' not found", name); - bool ok = py_call(exc_type, 1, message); + bool ok = py_tpcall(type, 1, message); if(!ok) c11__abort("py_exception(): failed to create exception object"); py_pop(); @@ -197,8 +201,7 @@ bool py_raise(py_Ref exc) { } bool KeyError(py_Ref key) { - py_Ref cls = py_getdict(&pk_current_vm->builtins, py_name("KeyError")); - bool ok = py_call(cls, 1, key); + bool ok = py_tpcall(tp_KeyError, 1, key); if(!ok) return false; return py_raise(py_retval()); } \ No newline at end of file diff --git a/tests/46_star.py b/tests/44_star.py similarity index 100% rename from tests/46_star.py rename to tests/44_star.py diff --git a/tests/47_reflection.py b/tests/50_reflection.py similarity index 100% rename from tests/47_reflection.py rename to tests/50_reflection.py diff --git a/tests/44_eval.py b/tests/51_eval.py similarity index 100% rename from tests/44_eval.py rename to tests/51_eval.py diff --git a/tests/45_yield.py b/tests/52_yield.py similarity index 100% rename from tests/45_yield.py rename to tests/52_yield.py