From ec30ba9d02cb05c17e0a20c4bac29361509c02d9 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Tue, 30 Jul 2024 12:14:07 +0800 Subject: [PATCH] ... --- c_bindings/CMakeLists.txt | 23 -- c_bindings/test.c | 415 -------------------------- c_bindings/test_answers.txt | 112 ------- include/pocketpy/compiler/lexer.h | 3 +- include/pocketpy/objects/error.h | 7 +- include/pocketpy/objects/sourcedata.h | 24 +- include/pocketpy/pocketpy.h | 2 +- src/common/sourcedata.c | 33 +- src/compiler/compiler.c | 99 +++--- src/compiler/lexer.c | 46 +-- src/interpreter/vm.c | 1 + src/objects/error.c | 59 ---- src/public/error.c | 69 ----- src/public/modules.c | 37 ++- src/public/py_exception.c | 86 ++++++ src/public/values.c | 2 +- src/public/vm.c | 4 + 17 files changed, 240 insertions(+), 782 deletions(-) delete mode 100644 c_bindings/CMakeLists.txt delete mode 100644 c_bindings/test.c delete mode 100644 c_bindings/test_answers.txt delete mode 100644 src/objects/error.c delete mode 100644 src/public/error.c diff --git a/c_bindings/CMakeLists.txt b/c_bindings/CMakeLists.txt deleted file mode 100644 index 834102d2..00000000 --- a/c_bindings/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.17) - -project(test_c_bindings) - -set(CMAKE_C_STANDARD 11) - -option(PK_BUILD_STATIC_LIB "Build static library" ON) - -add_subdirectory( - ${CMAKE_CURRENT_LIST_DIR}/../ - ${CMAKE_CURRENT_LIST_DIR}/build/pocketpy/ -) - -include_directories( - ${CMAKE_CURRENT_LIST_DIR}/../include -) - -add_executable(${PROJECT_NAME} test.c) - -target_link_libraries( - ${PROJECT_NAME} - pocketpy -) \ No newline at end of file diff --git a/c_bindings/test.c b/c_bindings/test.c deleted file mode 100644 index eae28722..00000000 --- a/c_bindings/test.c +++ /dev/null @@ -1,415 +0,0 @@ -#include "pocketpy_c.h" -#include -#include - -//tests the c bindings for pocketpy - -void check_impl(pkpy_vm* vm, bool result, int lineno) { - if (!result) { - printf("ERROR: failed where it should have succeed at line %i\n", lineno); - if (!pkpy_clear_error(vm, NULL)) { - printf("clear error reported everything was fine\n"); - } - exit(1); - } -} - -void fail_impl(pkpy_vm* vm, bool result, int lineno) { - if (result) { - printf("ERROR: succeeded where it should have failed line %i\n", lineno); - exit(1); - } else { - char* message; - if (pkpy_clear_error(vm, &message)) { - printf("actually errored! line %i\n", lineno); - free(message); - exit(1); - } - } -} - -void error_impl(pkpy_vm* vm, bool result, int lineno) { - if (result) { - printf("ERROR: succeeded where it should have failed line %i\n", lineno); - exit(1); - } else { - char* message; - if (!pkpy_clear_error(vm, &message)){ - printf("clear error reported everything was fine\n"); - exit(1); - } else { - printf("successfully errored with this message: \n"); - printf("%s\n", message); - free(message); - } - } -} - - -#define check(r) check_impl(vm, (r), __LINE__) -#define fail(r) fail_impl(vm, (r), __LINE__) -#define error(r) error_impl(vm, (r), __LINE__) - -int test_binding(pkpy_vm* vm) { - pkpy_push_int(vm, 12); - return 1; -} - -int test_multiple_return(pkpy_vm* vm) { - pkpy_push_int(vm, 12); - pkpy_push_int(vm, 13); - return 2; -} - -int test_minus(pkpy_vm* vm) { - int a, b; - pkpy_to_int(vm, 0, &a); - pkpy_to_int(vm, 1, &b); - pkpy_push_int(vm, a - b); - return 1; -} - -int test_fib(pkpy_vm* vm) { - int n; - pkpy_to_int(vm, 0, &n); - if (n == 1) { - pkpy_push_int(vm, n); - } else { - pkpy_getglobal(vm, pkpy_name("test_fib")); - pkpy_push_null(vm); - pkpy_push_int(vm, n-1); - pkpy_vectorcall(vm, 1); - int r_int; - pkpy_to_int(vm, -1, &r_int); - pkpy_pop_top(vm); - pkpy_push_int(vm, r_int + n); - } - return 1; -} - -int test_default_argument(pkpy_vm* vm){ - int x; - pkpy_to_int(vm, -1, &x); - bool ok = x == 5; - pkpy_push_bool(vm, ok); - return 1; -} - -int test_return_none(pkpy_vm* vm) { - return 0; -} - -int test_error_propagate(pkpy_vm* vm) { - pkpy_error(vm, "NameError", pkpy_string("catch me")); - return 1; -} - -int test_nested_error(pkpy_vm* vm) { - pkpy_getglobal(vm, pkpy_name("error_from_python")); - pkpy_push_null(vm); - pkpy_vectorcall(vm, 0); - return 0; -} - -#define PRINT_TITLE(x) printf("\n====== %s ======\n", x) - -int main(int argc, char** argv) { - pkpy_vm* vm = pkpy_new_vm(true); - - PRINT_TITLE("test basic exec"); - check(pkpy_exec(vm, "print('hello world!')")); - fail(pkpy_getglobal(vm, pkpy_name("nonexistatn"))); - - // test int methods - PRINT_TITLE("test int methods"); - int r_int; - check(pkpy_push_int(vm, 11)); - pkpy_CName m_eleven = pkpy_name("eleven"); - check(pkpy_setglobal(vm, m_eleven)); - check(pkpy_exec(vm, "print(eleven)")); - check(pkpy_getglobal(vm, m_eleven)); - check(pkpy_is_int(vm, -1)); - check(pkpy_to_int(vm, -1, &r_int)); - printf("%i\n", r_int); // 11 - printf("%i\n", pkpy_stack_size(vm)); // 1 - - fail(pkpy_is_float(vm, -1)); - fail(pkpy_is_bool(vm, -1)); - fail(pkpy_is_string(vm, -1)); - fail(pkpy_is_none(vm, -1)); - fail(pkpy_is_voidp(vm, -1)); - - PRINT_TITLE("test float methods"); - double r_float; - check(pkpy_push_float(vm, 11.125)); - pkpy_CName m_elevenf = pkpy_name("elevenf"); - check(pkpy_setglobal(vm, m_elevenf)); - check(pkpy_exec(vm, "print(elevenf)")); - check(pkpy_getglobal(vm, m_elevenf)); - check(pkpy_is_float(vm, -1)); - check(pkpy_to_float(vm, -1, &r_float)); - printf("%.3f\n", r_float); - fail(pkpy_is_int(vm, -1)); - fail(pkpy_is_bool(vm, -1)); - fail(pkpy_is_string(vm, -1)); - fail(pkpy_is_none(vm, -1)); - fail(pkpy_is_voidp(vm, -1)); - - PRINT_TITLE("test bool methods"); - bool r_bool; - check(pkpy_push_bool(vm, false)); - pkpy_CName m_false_test = pkpy_name("false_test"); - check(pkpy_setglobal(vm, m_false_test)); - check(pkpy_exec(vm, "print(false_test)")); - check(pkpy_getglobal(vm, m_false_test)); - check(pkpy_is_bool(vm, -1)); - check(pkpy_to_bool(vm, -1, &r_bool)); - printf("%i\n", r_bool); - fail(pkpy_is_int(vm, -1)); - fail(pkpy_is_float(vm, -1)); - fail(pkpy_is_string(vm, -1)); - fail(pkpy_is_none(vm, -1)); - fail(pkpy_is_voidp(vm, -1)); - - PRINT_TITLE("test string methods"); - pkpy_CString r_string; - check(pkpy_push_string(vm, pkpy_string("hello!"))); - check(pkpy_setglobal(vm, pkpy_name("hello1"))); - check(pkpy_exec(vm, "print(hello1)")); - check(pkpy_push_string(vm, pkpy_string("hello!"))); - check(pkpy_is_string(vm, -1)); - check(pkpy_to_string(vm, -1, &r_string)); - - puts(r_string); - - fail(pkpy_is_int(vm, -1)); - fail(pkpy_is_float(vm, -1)); - fail(pkpy_is_bool(vm, -1)); - fail(pkpy_is_none(vm, -1)); - fail(pkpy_is_voidp(vm, -1)); - - PRINT_TITLE("test none methods"); - check(pkpy_push_none(vm)); - pkpy_CName m_none = pkpy_name("none"); - check(pkpy_setglobal(vm, m_none)); - check(pkpy_exec(vm, "print(none)")); - check(pkpy_getglobal(vm, m_none)); - check(pkpy_is_none(vm, -1)); - fail(pkpy_is_int(vm, -1)); - fail(pkpy_is_float(vm, -1)); - fail(pkpy_is_bool(vm, -1)); - fail(pkpy_is_string(vm, -1)); - fail(pkpy_is_voidp(vm, -1)); - - PRINT_TITLE("test voidp methods"); - void* vp = (void*) 123; - check(pkpy_push_voidp(vm, vp)); - check(pkpy_setglobal(vm, pkpy_name("vp"))); - check(pkpy_exec(vm, "print(vp)")); - check(pkpy_getglobal(vm, pkpy_name("vp"))); - check(pkpy_is_voidp(vm, -1)); - vp = NULL; - check(pkpy_to_voidp(vm, -1, &vp)); - printf("%i\n", (int) (intptr_t) vp); - fail(pkpy_is_int(vm, -1)); - fail(pkpy_is_float(vm, -1)); - fail(pkpy_is_bool(vm, -1)); - fail(pkpy_is_string(vm, -1)); - fail(pkpy_is_none(vm, -1)); - - PRINT_TITLE("test sizing and indexing"); - int stack_size = pkpy_stack_size(vm); - printf("stack size %i\n", stack_size); - check(pkpy_is_int(vm, 0)); - check(pkpy_is_float(vm, 1)); - check(pkpy_is_bool(vm, 2)); - check(pkpy_is_string(vm, 3)); - check(pkpy_is_none(vm, 4)); - check(pkpy_is_voidp(vm, 5)); - check(pkpy_is_int(vm, -6)); - check(pkpy_is_float(vm, -5)); - check(pkpy_is_bool(vm, -4)); - check(pkpy_is_string(vm, -3)); - check(pkpy_is_none(vm, -2)); - check(pkpy_is_voidp(vm, -1)); - - PRINT_TITLE("test error catching"); - error(pkpy_exec(vm, "let's make sure syntax errors get caught")); - //stack should be cleared after error is resolved - check(pkpy_stack_size(vm) == 0); - - PRINT_TITLE("test simple call"); - check(pkpy_exec(vm, "def x(x, y) : return x - y")); - check(pkpy_getglobal(vm, pkpy_name("x"))); - check(pkpy_push_null(vm)); - check(pkpy_push_int(vm, 2)); - check(pkpy_push_int(vm, 3)); - check(pkpy_vectorcall(vm, 2)); - check(pkpy_to_int(vm, -1, &r_int)); - printf("x : %i\n", r_int); - - PRINT_TITLE("test vararg call"); - check(pkpy_exec(vm, "def vararg_x(*x) : return sum(x)")); - check(pkpy_getglobal(vm, pkpy_name("vararg_x"))); - check(pkpy_push_null(vm)); - check(pkpy_push_int(vm, 1)); - check(pkpy_push_int(vm, 2)); - check(pkpy_push_int(vm, 3)); - check(pkpy_push_int(vm, 4)); - check(pkpy_push_int(vm, 5)); - check(pkpy_push_int(vm, 6)); - check(pkpy_vectorcall(vm, 6)); - check(pkpy_to_int(vm, -1, &r_int)); - printf("vararg_x : %i\n", r_int); - - PRINT_TITLE("test keyword call"); - check(pkpy_exec(vm, "def keyword_x(x=1, y=1) : return x+y")); - check(pkpy_getglobal(vm, pkpy_name("keyword_x"))); - check(pkpy_push_null(vm)); - check(pkpy_push_int(vm, 3)); - check(pkpy_vectorcall(vm, 1)); - check(pkpy_to_int(vm, -1, &r_int)); - printf("keyword_x : %i\n", r_int); // 3+1 - - check(pkpy_getglobal(vm, pkpy_name("keyword_x"))); - check(pkpy_push_null(vm)); - check(pkpy_vectorcall(vm, 0)); - check(pkpy_to_int(vm, -1, &r_int)); - printf("keyword_x : %i\n", r_int); // 1+1 - check(pkpy_stack_size(vm) == 4); - check(pkpy_pop(vm, 4)); // clear stack - - PRINT_TITLE("test return many"); - check(pkpy_exec(vm, "def retmany_x() : return 1, 2, 3")); - check(pkpy_getglobal(vm, pkpy_name("retmany_x"))); - check(pkpy_push_null(vm)); - check(pkpy_vectorcall(vm, 0)); - - check(pkpy_stack_size(vm) == 1); - check(pkpy_unpack_sequence(vm, 3)); - check(pkpy_stack_size(vm) == 3); - - check(pkpy_to_int(vm, -3, &r_int)); - printf("retmany_x : %i\n", r_int); - check(pkpy_to_int(vm, -2, &r_int)); - printf("retmany_x : %i\n", r_int); - check(pkpy_to_int(vm, -1, &r_int)); - printf("retmany_x : %i\n", r_int); - - // test argument error - check(pkpy_getglobal(vm, pkpy_name("x"))); - check(pkpy_push_null(vm)); - error(pkpy_vectorcall(vm, 0)); - - check(pkpy_exec(vm, "l = []")); - check(pkpy_getglobal(vm, pkpy_name("l"))); - check(pkpy_get_unbound_method(vm, pkpy_name("append"))); - check(pkpy_push_string(vm, pkpy_string("hello"))); - check(pkpy_vectorcall(vm, 1)); - check(pkpy_pop_top(vm)); // pop None returned by append() - check(pkpy_exec(vm, "print(l)")); - - PRINT_TITLE("test bindings"); - check(pkpy_push_function(vm, "test_binding()", test_binding)); - check(pkpy_setglobal(vm, pkpy_name("test_binding"))); - check(pkpy_exec(vm, "print(test_binding())")); - check(pkpy_stack_size(vm) == 0); - - check(pkpy_push_function(vm, "test_multiple_return()", test_multiple_return)); - check(pkpy_setglobal(vm, pkpy_name("test_multiple_return"))); - check(pkpy_stack_size(vm) == 0); - - check(pkpy_push_function(vm, "test_default_argument(x=5)", test_default_argument)); - check(pkpy_push_null(vm)); - check(pkpy_vectorcall(vm, 0)); - check(pkpy_stack_size(vm) == 1); - check(pkpy_is_bool(vm, -1) == true); - check(pkpy_to_bool(vm, -1, &r_bool)); - check(r_bool == true); - check(pkpy_pop_top(vm)); - check(pkpy_stack_size(vm) == 0); - - PRINT_TITLE("test bindings 2"); - check(pkpy_push_function(vm, "test_minus(a, b)", test_minus)); - check(pkpy_setglobal(vm, pkpy_name("test_minus"))); - check(pkpy_exec(vm, "print(test_minus(5, 3))")); - check(pkpy_exec(vm, "for i in range(5): print(test_minus(5, i))")); - check(pkpy_stack_size(vm) == 0); - - PRINT_TITLE("test bindings fib"); - check(pkpy_push_function(vm, "test_fib(n: int) -> int", test_fib)); - check(pkpy_setglobal(vm, pkpy_name("test_fib"))); - check(pkpy_exec(vm, "print(test_fib(10))")); - check(pkpy_stack_size(vm) == 0); - - PRINT_TITLE("test error propagate"); - check(pkpy_push_function(vm, "test_error_propagate()", test_error_propagate)); - check(pkpy_setglobal(vm, pkpy_name("test_error_propagate"))); - error(pkpy_exec(vm, "test_error_propagate()")); - - check(pkpy_getglobal(vm, pkpy_name("test_multiple_return"))); - check(pkpy_push_null(vm)); - check(pkpy_vectorcall(vm, 0)); - check(pkpy_stack_size(vm) == 1); - check(pkpy_unpack_sequence(vm, 2)); - check(pkpy_stack_size(vm) == 2); - check(pkpy_pop(vm, 2)); - check(pkpy_stack_size(vm) == 0); - - PRINT_TITLE("test other errors"); - check(pkpy_getglobal(vm, pkpy_name("test_error_propagate"))); - check(pkpy_pop_top(vm)); - fail(pkpy_getglobal(vm, pkpy_name("nonexistant"))); - error(pkpy_exec(vm, "raise NameError('testing error throwing from python')")); - - PRINT_TITLE("test TypeError"); - check(pkpy_push_float(vm, 2.0)); - error(pkpy_to_int(vm, -1, &r_int)); - - PRINT_TITLE("test complicated errors"); - pkpy_exec(vm, "test_error_propagate()"); - check(pkpy_check_error(vm)); - pkpy_clear_error(vm, NULL); - - //this should be catchable - check(pkpy_exec(vm, "try :\n test_error_propagate()\nexcept NameError : pass")); - error(pkpy_error(vm, "Exception", pkpy_string("test direct error mechanism"))); - - //more complicated error handling - check(pkpy_exec(vm, "def error_from_python() : raise NotImplementedError()")); - check(pkpy_push_function(vm, "test_nested_error()", test_nested_error)); - check(pkpy_setglobal(vm, pkpy_name("test_nested_error"))); - error(pkpy_exec(vm, "test_nested_error()")); - - PRINT_TITLE("test getattr/setattr"); - check(pkpy_stack_size(vm) == 0); - check(pkpy_exec(vm, "import math")); - check(pkpy_getglobal(vm, pkpy_name("math"))); - check(pkpy_getattr(vm, pkpy_name("pi"))); - check(pkpy_to_float(vm, -1, &r_float)); - printf("pi: %.2f\n", (float)r_float); - check(pkpy_pop(vm, 1)); - - // math.pi = 2 - check(pkpy_push_int(vm, 2)); - check(pkpy_eval(vm, "math")); - check(pkpy_setattr(vm, pkpy_name("pi"))); - check(pkpy_exec(vm, "print(math.pi)")); - - PRINT_TITLE("test eval"); - check(pkpy_eval(vm, "math.pi")); - check(pkpy_to_float(vm, -1, &r_float)); - printf("pi: %.2f\n", (float)r_float); - check(pkpy_pop(vm, 1)); - check(pkpy_stack_size(vm) == 0); - - PRINT_TITLE("test py_repr"); - check(pkpy_eval(vm, "['1', 2, (3, '4')]")); - check(pkpy_py_repr(vm)); - check(pkpy_to_string(vm, -1, &r_string)); - - puts(r_string); - check(pkpy_pop_top(vm)); - check(pkpy_stack_size(vm) == 0); - return 0; -} diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt deleted file mode 100644 index ee51443c..00000000 --- a/c_bindings/test_answers.txt +++ /dev/null @@ -1,112 +0,0 @@ - -====== test basic exec ====== -hello world! - -====== test int methods ====== -11 -11 -1 - -====== test float methods ====== -11.125 -11.125 - -====== test bool methods ====== -False -0 - -====== test string methods ====== -hello! -hello! - -====== test none methods ====== -None - -====== test voidp methods ====== - -123 - -====== test sizing and indexing ====== -stack size 6 - -====== test error catching ====== -successfully errored with this message: - File "main.py", line 1 - let's make sure syntax errors get caught -SyntaxError: EOL while scanning string literal - -====== test simple call ====== -x : -1 - -====== test vararg call ====== -vararg_x : 21 - -====== test keyword call ====== -keyword_x : 4 -keyword_x : 2 - -====== test return many ====== -retmany_x : 1 -retmany_x : 2 -retmany_x : 3 -successfully errored with this message: -TypeError: x() takes 2 positional arguments but 0 were given -['hello'] - -====== test bindings ====== -12 - -====== test bindings 2 ====== -2 -5 -4 -3 -2 -1 - -====== test bindings fib ====== -55 - -====== test error propagate ====== -successfully errored with this message: -Traceback (most recent call last): - File "main.py", line 1 - test_error_propagate() -NameError: catch me - -====== test other errors ====== -successfully errored with this message: -Traceback (most recent call last): - File "main.py", line 1 - raise NameError('testing error throwing from python') -NameError: testing error throwing from python - -====== test TypeError ====== -successfully errored with this message: -TypeError: expected 'int', got 'float' - -====== test complicated errors ====== -Traceback (most recent call last): - File "main.py", line 1 - test_error_propagate() -NameError: catch me -successfully errored with this message: -Traceback (most recent call last): -Exception: test direct error mechanism -successfully errored with this message: -Traceback (most recent call last): - File "main.py", line 1 - test_nested_error() - File "main.py", line 1, in error_from_python - def error_from_python() : raise NotImplementedError() -NotImplementedError - -====== test getattr/setattr ====== -pi: 3.14 -2 - -====== test eval ====== -pi: 2.00 - -====== test py_repr ====== -['1', 2, (3, '4')] diff --git a/include/pocketpy/compiler/lexer.h b/include/pocketpy/compiler/lexer.h index 8369561d..b9bea7fc 100644 --- a/include/pocketpy/compiler/lexer.h +++ b/include/pocketpy/compiler/lexer.h @@ -3,6 +3,7 @@ #include "pocketpy/common/str.h" #include "pocketpy/common/vector.h" #include "pocketpy/objects/sourcedata.h" +#include "pocketpy/objects/error.h" #include #ifdef __cplusplus @@ -87,8 +88,6 @@ enum Precedence { PREC_HIGHEST, }; -typedef struct Error Error; - typedef c11_array pk_TokenArray; Error* pk_Lexer__process(pk_SourceData_ src, pk_TokenArray* out_tokens); diff --git a/include/pocketpy/objects/error.h b/include/pocketpy/objects/error.h index 247c1c5a..99ec5704 100644 --- a/include/pocketpy/objects/error.h +++ b/include/pocketpy/objects/error.h @@ -11,14 +11,11 @@ extern "C" { #endif -struct Error{ - const char* type; +typedef struct{ pk_SourceData_ src; int lineno; - const char* cursor; char msg[100]; - int64_t userdata; -}; +} Error; void py_BaseException__set_lineno(py_Ref, int lineno, const CodeObject* code); int py_BaseException__get_lineno(py_Ref, const CodeObject* code); diff --git a/include/pocketpy/objects/sourcedata.h b/include/pocketpy/objects/sourcedata.h index 31816a09..d1ae0999 100644 --- a/include/pocketpy/objects/sourcedata.h +++ b/include/pocketpy/objects/sourcedata.h @@ -3,31 +3,41 @@ #include #include "pocketpy/pocketpy.h" #include "pocketpy/common/str.h" +#include "pocketpy/common/sstream.h" #include "pocketpy/common/vector.h" #ifdef __cplusplus extern "C" { #endif - struct pk_SourceData { RefCounted rc; enum py_CompileMode mode; bool is_precompiled; - bool is_dynamic; // for exec() and eval() + bool is_dynamic; // for exec() and eval() c11_string* filename; c11_string* source; - c11_vector/*T=const char* */ line_starts; - c11_vector/*T=c11_string* */ _precompiled_tokens; + c11_vector /*T=const char* */ line_starts; + c11_vector /*T=c11_string* */ _precompiled_tokens; }; typedef struct pk_SourceData* pk_SourceData_; -pk_SourceData_ pk_SourceData__rcnew(const char* source, const char* filename, enum py_CompileMode mode, bool is_dynamic); -bool pk_SourceData__get_line(const struct pk_SourceData* self, int lineno, const char** st, const char** ed); -c11_string* pk_SourceData__snapshot(const struct pk_SourceData *self, int lineno, const char *cursor, const char *name); +pk_SourceData_ pk_SourceData__rcnew(const char* source, + const char* filename, + enum py_CompileMode mode, + bool is_dynamic); +bool pk_SourceData__get_line(const struct pk_SourceData* self, + int lineno, + const char** st, + const char** ed); +void pk_SourceData__snapshot(const struct pk_SourceData* self, + c11_sbuf* ss, + int lineno, + const char* cursor, + const char* name); #ifdef __cplusplus } diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 764c7a9b..8ed07d7c 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -160,7 +160,7 @@ py_ObjectRef py_bind2(py_Ref obj, // old style argc-based bindings void py_bindmethod(py_Type type, const char* name, py_CFunction f); void py_bindmethod2(py_Type type, const char* name, py_CFunction f, enum BindType bt); -void py_bindnativefunc(py_Ref obj, const char* name, py_CFunction f); +void py_bindfunc(py_Ref obj, const char* name, py_CFunction f); /// Get the reference to the i-th register. /// All registers are located in a contiguous memory. diff --git a/src/common/sourcedata.c b/src/common/sourcedata.c index 1d6e495e..0cbdd760 100644 --- a/src/common/sourcedata.c +++ b/src/common/sourcedata.c @@ -71,41 +71,38 @@ bool pk_SourceData__get_line(const struct pk_SourceData* self, return true; } -c11_string* pk_SourceData__snapshot(const struct pk_SourceData* self, - int lineno, - const char* cursor, - const char* name) { - c11_sbuf ss; - c11_sbuf__ctor(&ss); - - pk_sprintf(&ss, " File \"%s\", line %d", self->filename->data, lineno); +void pk_SourceData__snapshot(const struct pk_SourceData* self, + c11_sbuf* ss, + int lineno, + const char* cursor, + const char* name) { + pk_sprintf(ss, " File \"%s\", line %d", self->filename->data, lineno); if(name && *name) { - c11_sbuf__write_cstr(&ss, ", in "); - c11_sbuf__write_cstr(&ss, name); + c11_sbuf__write_cstr(ss, ", in "); + c11_sbuf__write_cstr(ss, name); } if(!self->is_precompiled) { - c11_sbuf__write_char(&ss, '\n'); + c11_sbuf__write_char(ss, '\n'); const char *st = NULL, *ed; if(pk_SourceData__get_line(self, lineno, &st, &ed)) { while(st < ed && isblank(*st)) ++st; if(st < ed) { - c11_sbuf__write_cstr(&ss, " "); - c11_sbuf__write_cstrn(&ss, st, ed - st); + c11_sbuf__write_cstr(ss, " "); + c11_sbuf__write_cstrn(ss, st, ed - st); if(cursor && st <= cursor && cursor <= ed) { - c11_sbuf__write_cstr(&ss, "\n "); + c11_sbuf__write_cstr(ss, "\n "); for(int i = 0; i < (cursor - st); ++i) - c11_sbuf__write_char(&ss, ' '); - c11_sbuf__write_cstr(&ss, "^"); + c11_sbuf__write_char(ss, ' '); + c11_sbuf__write_cstr(ss, "^"); } } else { st = NULL; } } - if(!st) { c11_sbuf__write_cstr(&ss, " "); } + if(!st) { c11_sbuf__write_cstr(ss, " "); } } - return c11_sbuf__submit(&ss); } diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 55d5938f..924d1d6c 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1450,6 +1450,7 @@ static void Compiler__ctor(Compiler* self, pk_SourceData_ src, pk_TokenArray tok static void Compiler__dtor(Compiler* self) { pk_TokenArray__dtor(&self->tokens); + c11__foreach(Ctx, &self->contexts, ctx) Ctx__dtor(ctx); c11_vector__dtor(&self->contexts); } @@ -1458,7 +1459,6 @@ static void Compiler__dtor(Compiler* self) { #define prev() tk(self->i - 1) #define curr() tk(self->i) #define next() tk(self->i + 1) -// #define err() (self->i == self->tokens.count ? prev() : curr()) #define advance() self->i++ #define mode() self->src->mode @@ -1468,11 +1468,12 @@ static void Compiler__dtor(Compiler* self) { #define consume(expected) \ if(!match(expected)) \ - return SyntaxError("expected '%s', got '%s'", \ + return SyntaxError(self, \ + "expected '%s', got '%s'", \ pk_TokenSymbols[expected], \ pk_TokenSymbols[curr()->type]); #define consume_end_stmt() \ - if(!match_end_stmt(self)) return SyntaxError("expected statement end") + if(!match_end_stmt(self)) return SyntaxError(self, "expected statement end") #define check(B) \ if((err = B)) return err @@ -1483,9 +1484,17 @@ static NameScope name_scope(Compiler* self) { return s; } -Error* SyntaxError(const char* fmt, ...) { - c11__abort("%s", fmt); - return NULL; +Error* SyntaxError(Compiler* self, const char* fmt, ...) { + Error* err = malloc(sizeof(Error)); + err->src = self->src; + PK_INCREF(self->src); + Token* t = self->i == self->tokens.count ? prev() : curr(); + err->lineno = t->line; + va_list args; + va_start(args, fmt); + vsnprintf(err->msg, sizeof(err->msg), fmt, args); + va_end(args); + return err; } /* Matchers */ @@ -1522,7 +1531,7 @@ static bool match_end_stmt(Compiler* self) { static Error* parse_expression(Compiler* self, int precedence, bool allow_slice) { PrattCallback prefix = rules[curr()->type].prefix; if(!prefix || (curr()->type == TK_COLON && !allow_slice)) { - return SyntaxError("expected an expression, got %s", pk_TokenSymbols[curr()->type]); + return SyntaxError(self, "expected an expression, got %s", pk_TokenSymbols[curr()->type]); } advance(); Error* err; @@ -1612,9 +1621,11 @@ static Error* pop_context(Compiler* self) { // some check here c11_vector* codes = &co->codes; if(co->nlocals > PK_MAX_CO_VARNAMES) { - return SyntaxError("maximum number of local variables exceeded"); + return SyntaxError(self, "maximum number of local variables exceeded"); + } + if(co->consts.count > 65530) { + return SyntaxError(self, "maximum number of constants exceeded"); } - if(co->consts.count > 65530) { return SyntaxError("maximum number of constants exceeded"); } // pre-compute LOOP_BREAK and LOOP_CONTINUE for(int i = 0; i < codes->count; i++) { Bytecode* bc = c11__at(Bytecode, codes, i); @@ -1635,7 +1646,8 @@ static Error* pop_context(Compiler* self) { func->type = FuncType_GENERATOR; c11__foreach(Bytecode, &func->code.codes, bc) { if(bc->op == OP_RETURN_VALUE && bc->arg == BC_NOARG) { - return SyntaxError("'return' with argument inside generator function"); + return SyntaxError(self, + "'return' with argument inside generator function"); } } break; @@ -1962,7 +1974,7 @@ static Error* exprCall(Compiler* self) { } else { // positional argument if(e->kwargs.count > 0) { - return SyntaxError("positional argument follows keyword argument"); + return SyntaxError(self, "positional argument follows keyword argument"); } c11_vector__push(Expr*, &e->args, Ctx__s_popx(ctx())); } @@ -2052,7 +2064,7 @@ static Error* compile_block_body(Compiler* self, PrattCallback callback) { } bool consumed = match_newlines(); - if(!consumed) return SyntaxError("expected a new line after ':'"); + if(!consumed) return SyntaxError(self, "expected a new line after ':'"); consume(TK_INDENT); while(curr()->type != TK_DEDENT) { @@ -2119,7 +2131,7 @@ static Error* compile_for_loop(Compiler* self) { vtdelete(vars); if(!ok) { // this error occurs in `vars` instead of this line, but...nevermind - return SyntaxError("invalid syntax"); + return SyntaxError(self, "invalid syntax"); } check(compile_block_body(self, compile_stmt)); Ctx__emit_virtual(ctx(), OP_LOOP_CONTINUE, Ctx__get_loop(ctx()), BC_KEEPLINE, true); @@ -2147,9 +2159,9 @@ Error* try_compile_assignment(Compiler* self, bool* is_assign) { case TK_IOR: case TK_IXOR: { if(Ctx__s_top(ctx())->vt->is_starred) - return SyntaxError("can't use inplace operator with starred expression"); + return SyntaxError(self, "can't use inplace operator with starred expression"); if(ctx()->is_compiling_class) - return SyntaxError("can't use inplace operator in class definition"); + return SyntaxError(self, "can't use inplace operator in class definition"); advance(); // a[x] += 1; a and x should be evaluated only once // a.x += 1; a should be evaluated only once @@ -2159,14 +2171,14 @@ Error* try_compile_assignment(Compiler* self, bool* is_assign) { // [lhs] check(EXPR_TUPLE(self)); // [lhs, rhs] if(Ctx__s_top(ctx())->vt->is_starred) - return SyntaxError("can't use starred expression here"); + return SyntaxError(self, "can't use starred expression here"); BinaryExpr* e = BinaryExpr__new(line, op, true); e->rhs = Ctx__s_popx(ctx()); // [lhs] e->lhs = Ctx__s_popx(ctx()); // [] vtemit_((Expr*)e, ctx()); bool ok = vtemit_istore(e->lhs, ctx()); vtdelete((Expr*)e); - if(!ok) return SyntaxError("invalid syntax"); + if(!ok) return SyntaxError(self, "invalid syntax"); *is_assign = true; return NULL; } @@ -2182,11 +2194,11 @@ Error* try_compile_assignment(Compiler* self, bool* is_assign) { Ctx__emit_(ctx(), OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); for(int j = 0; j < n; j++) { if(Ctx__s_top(ctx())->vt->is_starred) - return SyntaxError("can't use starred expression here"); + return SyntaxError(self, "can't use starred expression here"); Expr* e = Ctx__s_top(ctx()); bool ok = vtemit_store(e, ctx()); Ctx__s_pop(ctx()); - if(!ok) return SyntaxError("invalid syntax"); + if(!ok) return SyntaxError(self, "invalid syntax"); } *is_assign = true; return NULL; @@ -2239,7 +2251,8 @@ static Error* read_literal(Compiler* self, py_Ref out) { py_TValue cpnts[4]; int count = 0; while(true) { - if(count == 4) return SyntaxError("default argument tuple exceeds 4 elements"); + if(count == 4) + return SyntaxError(self, "default argument tuple exceeds 4 elements"); check(read_literal(self, &cpnts[count])); count += 1; if(curr()->type == TK_RPAREN) break; @@ -2261,13 +2274,13 @@ static Error* _compile_f_args(Compiler* self, FuncDecl* decl, bool enable_type_h int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs Error* err; do { - if(state >= 3) return SyntaxError("**kwargs should be the last argument"); + if(state >= 3) return SyntaxError(self, "**kwargs should be the last argument"); match_newlines(); if(match(TK_MUL)) { if(state < 1) state = 1; else - return SyntaxError("*args should be placed before **kwargs"); + return SyntaxError(self, "*args should be placed before **kwargs"); } else if(match(TK_POW)) { state = 3; } @@ -2276,7 +2289,7 @@ static Error* _compile_f_args(Compiler* self, FuncDecl* decl, bool enable_type_h // check duplicate argument name if(FuncDecl__is_duplicated_arg(decl, name)) { - return SyntaxError("duplicate argument name"); + return SyntaxError(self, "duplicate argument name"); } // eat type hints @@ -2292,7 +2305,7 @@ static Error* _compile_f_args(Compiler* self, FuncDecl* decl, bool enable_type_h consume(TK_ASSIGN); py_TValue value; check(read_literal(self, &value)); - if(py_isnil(&value)) return SyntaxError("default argument must be a literal"); + if(py_isnil(&value)) return SyntaxError(self, "default argument must be a literal"); FuncDecl__add_kwarg(decl, name, &value); } break; case 3: @@ -2367,7 +2380,7 @@ static Error* compile_class(Compiler* self, int decorators) { Ctx__emit_(ctx(), OP_BEGIN_CLASS, name, BC_KEEPLINE); c11__foreach(Ctx, &self->contexts, it) { - if(it->is_compiling_class) return SyntaxError("nested class is not allowed"); + if(it->is_compiling_class) return SyntaxError(self, "nested class is not allowed"); } ctx()->is_compiling_class = true; check(compile_block_body(self, compile_stmt)); @@ -2384,7 +2397,7 @@ static Error* compile_decorated(Compiler* self) { do { check(EXPR(self)); count += 1; - if(!match_newlines()) return SyntaxError("expected a newline after '@'"); + if(!match_newlines()) return SyntaxError(self, "expected a newline after '@'"); } while(match(TK_DECORATOR)); if(match(TK_CLASS)) { @@ -2468,7 +2481,7 @@ __EAT_DOTS_END: if(match(TK_MUL)) { if(name_scope(self) != NAME_GLOBAL) - return SyntaxError("from import * can only be used in global scope"); + return SyntaxError(self, "from import * can only be used in global scope"); // pop the module and import __all__ Ctx__emit_(ctx(), OP_POP_IMPORT_STAR, BC_NOARG, prev()->line); consume_end_stmt(); @@ -2503,10 +2516,14 @@ static Error* compile_try_except(Compiler* self) { 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"); } + if(curr()->type == TK_FINALLY) { + return SyntaxError(self, "finally clause is not supported yet"); + } do { - if(patches_length == 8) { return SyntaxError("maximum number of except clauses reached"); } + if(patches_length == 8) { + return SyntaxError(self, "maximum number of except clauses reached"); + } py_Name as_name = 0; consume(TK_EXCEPT); if(is_expression(self, false)) { @@ -2553,24 +2570,24 @@ static Error* compile_stmt(Compiler* self) { int curr_loop_block = Ctx__get_loop(ctx()); switch(prev()->type) { case TK_BREAK: - if(curr_loop_block < 0) return SyntaxError("'break' outside loop"); + if(curr_loop_block < 0) return SyntaxError(self, "'break' outside loop"); Ctx__emit_(ctx(), OP_LOOP_BREAK, curr_loop_block, kw_line); consume_end_stmt(); break; case TK_CONTINUE: - if(curr_loop_block < 0) return SyntaxError("'continue' not properly in loop"); + if(curr_loop_block < 0) return SyntaxError(self, "'continue' not properly in loop"); Ctx__emit_(ctx(), OP_LOOP_CONTINUE, curr_loop_block, kw_line); consume_end_stmt(); break; case TK_YIELD: - if(self->contexts.count <= 1) return SyntaxError("'yield' outside function"); + if(self->contexts.count <= 1) return SyntaxError(self, "'yield' outside function"); check(EXPR_TUPLE(self)); Ctx__s_emit_top(ctx()); Ctx__emit_(ctx(), OP_YIELD_VALUE, BC_NOARG, kw_line); consume_end_stmt(); break; case TK_YIELD_FROM: - if(self->contexts.count <= 1) return SyntaxError("'yield from' outside function"); + if(self->contexts.count <= 1) return SyntaxError(self, "'yield from' outside function"); check(EXPR_TUPLE(self)); Ctx__s_emit_top(ctx()); Ctx__emit_(ctx(), OP_GET_ITER, BC_NOARG, kw_line); @@ -2582,7 +2599,7 @@ static Error* compile_stmt(Compiler* self) { consume_end_stmt(); break; case TK_RETURN: - if(self->contexts.count <= 1) return SyntaxError("'return' outside function"); + if(self->contexts.count <= 1) return SyntaxError(self, "'return' outside function"); if(match_end_stmt(self)) { Ctx__emit_(ctx(), OP_RETURN_VALUE, 1, kw_line); } else { @@ -2642,7 +2659,7 @@ static Error* compile_stmt(Compiler* self) { case TK_DEL: { check(EXPR_TUPLE(self)); Expr* e = Ctx__s_top(ctx()); - if(!vtemit_del(e, ctx())) return SyntaxError("invalid syntax"); + if(!vtemit_del(e, ctx())) return SyntaxError(self, "invalid syntax"); Ctx__s_pop(ctx()); consume_end_stmt(); } break; @@ -2660,7 +2677,7 @@ static Error* compile_stmt(Compiler* self) { // if(as_name) { // bool ok = as_name->emit_store(ctx()); // delete_expr(as_name); - // if(!ok) return SyntaxError(); + // if(!ok) return SyntaxError(self, ); // } else { // Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE); // } @@ -2671,16 +2688,18 @@ static Error* compile_stmt(Compiler* self) { /*************************************************/ case TK_EQ: { consume(TK_ID); - if(mode() != EXEC_MODE) return SyntaxError("'label' is only available in EXEC_MODE"); + if(mode() != EXEC_MODE) + return SyntaxError(self, "'label' is only available in EXEC_MODE"); c11_sv name = Token__sv(prev()); bool ok = Ctx__add_label(ctx(), py_namev(name)); - if(!ok) return SyntaxError("label %q already exists", name); + if(!ok) return SyntaxError(self, "label %q already exists", name); consume(TK_EQ); consume_end_stmt(); } break; case TK_ARROW: consume(TK_ID); - if(mode() != EXEC_MODE) return SyntaxError("'goto' is only available in EXEC_MODE"); + if(mode() != EXEC_MODE) + return SyntaxError(self, "'goto' is only available in EXEC_MODE"); py_Name name = py_namev(Token__sv(prev())); Ctx__emit_(ctx(), OP_GOTO, name, prev()->line); consume_end_stmt(); @@ -2710,7 +2729,7 @@ static Error* compile_stmt(Compiler* self) { check(try_compile_assignment(self, &is_assign)); if(!is_assign) { if(Ctx__s_size(ctx()) > 0 && Ctx__s_top(ctx())->vt->is_starred) { - return SyntaxError("can't use starred expression here"); + return SyntaxError(self, "can't use starred expression here"); } if(!is_typed_name) { Ctx__s_emit_top(ctx()); diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 8b9555c6..a0cd740a 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -192,13 +192,19 @@ static bool is_possible_number_char(char c){ } /******************************/ -static Error* SyntaxError(const char* fmt, ...){ - // va_list args; - // va_start(args, fmt); - // Error* err = _error(true, "SyntaxError", fmt, &args); - // va_end(args); - // return err; - return NULL; +static Error* SyntaxError(pk_Lexer* self, const char* fmt, ...){ + Error* err = malloc(sizeof(Error)); + err->src = self->src; + PK_INCREF(self->src); + err->lineno = self->current_line; + if(*self->curr_char == '\n') { + err->lineno--; + } + va_list args; + va_start(args, fmt); + vsnprintf(err->msg, sizeof(err->msg), fmt, args); + va_end(args); + return err; } static Error* eat_name(pk_Lexer* self){ @@ -206,7 +212,7 @@ static Error* eat_name(pk_Lexer* self){ while(true) { unsigned char c = *self->curr_char; int u8bytes = c11__u8_header(c, true); - if(u8bytes == 0) return SyntaxError("invalid char: %c", c); + if(u8bytes == 0) return SyntaxError(self, "invalid char: %c", c); if(u8bytes == 1) { if(isalnum(c) || c == '_') { self->curr_char++; @@ -238,7 +244,7 @@ static Error* eat_name(pk_Lexer* self){ } int length = (int)(self->curr_char - self->token_start); - if(length == 0) return SyntaxError("@id contains invalid char"); + if(length == 0) return SyntaxError(self, "@id contains invalid char"); c11_sv name = {self->token_start, length}; const char** KW_BEGIN = pk_TokenSymbols + TK_FALSE; @@ -271,11 +277,11 @@ static Error* eat_string_until(pk_Lexer* self, char quote, bool raw, c11_string* break; } if(c == '\0') { - return SyntaxError("EOL while scanning string literal"); + return SyntaxError(self, "EOL while scanning string literal"); } if(c == '\n') { if(!quote3) - return SyntaxError("EOL while scanning string literal"); + return SyntaxError(self, "EOL while scanning string literal"); else { c11_sbuf__write_char(&buff, c); continue; @@ -294,11 +300,11 @@ static Error* eat_string_until(pk_Lexer* self, char quote, bool raw, c11_string* char hex[3] = {eatchar(self), eatchar(self), '\0'}; int code; if(sscanf(hex, "%x", &code) != 1) { - return SyntaxError("invalid hex char"); + return SyntaxError(self, "invalid hex char"); } c11_sbuf__write_char(&buff, (char)code); } break; - default: return SyntaxError("invalid escape char"); + default: return SyntaxError(self, "invalid escape char"); } } else { c11_sbuf__write_char(&buff, c); @@ -357,7 +363,7 @@ static Error* eat_number(pk_Lexer* self){ add_token_with_value(self, TK_NUM, value); return NULL; case IntParsing_OVERFLOW: - return SyntaxError("int literal is too large"); + return SyntaxError(self, "int literal is too large"); case IntParsing_FAILURE: break; // do nothing } @@ -380,7 +386,7 @@ static Error* eat_number(pk_Lexer* self){ return NULL; } - return SyntaxError("invalid number literal"); + return SyntaxError(self, "invalid number literal"); } static Error* lex_one_token(pk_Lexer* self, bool* eof){ @@ -411,7 +417,7 @@ static Error* lex_one_token(pk_Lexer* self, bool* eof){ // line continuation character char c = eatchar_include_newline(self); if(c != '\n') { - return SyntaxError("expected newline after line continuation character"); + return SyntaxError(self, "expected newline after line continuation character"); } eat_spaces(self); return NULL; @@ -471,7 +477,7 @@ static Error* lex_one_token(pk_Lexer* self, bool* eof){ if(matchchar(self, '=')){ add_token(self, TK_NE); }else{ - Error* err = SyntaxError("expected '=' after '!'"); + Error* err = SyntaxError(self, "expected '=' after '!'"); if(err) return err; } break; @@ -494,7 +500,7 @@ static Error* lex_one_token(pk_Lexer* self, bool* eof){ case '\n': { add_token(self, TK_EOL); if(!eat_indentation(self)){ - return SyntaxError("unindent does not match any outer indentation level"); + return SyntaxError(self, "unindent does not match any outer indentation level"); } return NULL; } @@ -534,10 +540,10 @@ static Error* from_precompiled(pk_Lexer* self) { c11_sv version = TokenDeserializer__read_string(&deserializer, '\n'); if(c11_sv__cmp2(version, PK_VERSION) != 0) { - return SyntaxError("precompiled version mismatch"); + return SyntaxError(self, "precompiled version mismatch"); } if(TokenDeserializer__read_uint(&deserializer, '\n') != (int64_t)self->src->mode){ - return SyntaxError("precompiled mode mismatch"); + return SyntaxError(self, "precompiled mode mismatch"); } int count = TokenDeserializer__read_count(&deserializer); diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 0eef7f5c..654db5a0 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -577,6 +577,7 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) { void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) { // return; + if(frame == NULL) return; py_TValue* sp = self->stack.sp; c11_sbuf buf; diff --git a/src/objects/error.c b/src/objects/error.c deleted file mode 100644 index c19fc4bc..00000000 --- a/src/objects/error.c +++ /dev/null @@ -1,59 +0,0 @@ -// #include "pocketpy/objects/error.h" -// #include "pocketpy/common/strname.h" -// #include "pocketpy/common/sstream.h" - -// void pkpy_Exception__ctor(pkpy_Exception* self, py_Name type){ -// self->type = type; -// self->is_re = true; -// self->_ip_on_error = -1; -// self->_code_on_error = NULL; -// self->self = NULL; - -// py_Str__ctor(&self->msg, ""); -// c11_vector__ctor(&self->stacktrace, sizeof(pkpy_ExceptionFrame)); -// } - -// void pkpy_Exception__dtor(pkpy_Exception* self){ -// for(int i=0; istacktrace.count; i++){ -// pkpy_ExceptionFrame* frame = c11__at(pkpy_ExceptionFrame, &self->stacktrace, i); -// PK_DECREF(frame->src); -// py_Str__dtor(&frame->name); -// } -// py_Str__dtor(&self->msg); -// c11_vector__dtor(&self->stacktrace); -// } - -// void pkpy_Exception__stpush(pkpy_Exception* self, pk_SourceData_ src, int lineno, const char* cursor, const char* name){ -// if(self->stacktrace.count >= 7) return; -// PK_INCREF(src); -// pkpy_ExceptionFrame* frame = c11_vector__emplace(&self->stacktrace); -// frame->src = src; -// frame->lineno = lineno; -// frame->cursor = cursor; -// py_Str__ctor(&frame->name, name); -// } - -// py_Str pkpy_Exception__summary(pkpy_Exception* self){ -// c11_sbuf ss; -// c11_sbuf__ctor(&ss); - -// if(self->is_re){ -// c11_sbuf__write_cstr(&ss, "Traceback (most recent call last):\n"); -// } -// for(int i=self->stacktrace.count-1; i >= 0; i--) { -// pkpy_ExceptionFrame* frame = c11__at(pkpy_ExceptionFrame, &self->stacktrace, i); -// py_Str s = pk_SourceData__snapshot(frame->src, frame->lineno, frame->cursor, py_Str__data(&frame->name)); -// c11_sbuf__write_Str(&ss, &s); -// py_Str__dtor(&s); -// c11_sbuf__write_cstr(&ss, "\n"); -// } - -// const char* name = py_Name__rmap(self->type); -// c11_sbuf__write_cstr(&ss, name); - -// if(self->msg.size > 0){ -// c11_sbuf__write_cstr(&ss, ": "); -// c11_sbuf__write_Str(&ss, &self->msg); -// } -// return c11_sbuf__submit(&ss); -// } \ No newline at end of file diff --git a/src/public/error.c b/src/public/error.c deleted file mode 100644 index 32eb9584..00000000 --- a/src/public/error.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "pocketpy/common/utils.h" -#include "pocketpy/objects/base.h" -#include "pocketpy/pocketpy.h" -#include "pocketpy/common/sstream.h" -#include "pocketpy/interpreter/vm.h" - -#include - -bool py_checkexc() { - pk_VM* vm = pk_current_vm; - return !py_isnil(&vm->curr_exception); -} - -void py_printexc() { - pk_VM* vm = pk_current_vm; - if(py_isnil(&vm->curr_exception)) { - vm->_stdout("NoneType: None\n"); - } else { - const char* name = py_tpname(vm->curr_exception.type); - bool ok = py_str(&vm->curr_exception); - if(!ok) c11__abort("py_printexc(): failed to convert exception to string"); - const char* message = py_tostr(py_retval()); - vm->_stdout("%s: %s\n", name, message); - } -} - -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, ...) { - 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); - py_Ref message = py_pushtmp(); - py_newstrn(message, res->data, res->size); - c11_string__delete(res); - - 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); - if(!ok) c11__abort("py_exception(): failed to create exception object"); - py_pop(); - - 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; -} - -bool KeyError(py_Ref key){ - py_Ref cls = py_getdict(&pk_current_vm->builtins, py_name("KeyError")); - bool ok = py_call(cls, 1, key); - if(!ok) return false; - return py_raise(py_retval()); -} \ No newline at end of file diff --git a/src/public/modules.c b/src/public/modules.c index f7ef1616..aac9d084 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -204,18 +204,35 @@ static bool _py_NoneType__repr__(int argc, py_Ref argv) { return true; } +static bool _py_builtins__exec(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_str); + bool ok = py_exec(py_tostr(argv)); + py_newnone(py_retval()); + return ok; +} + +static bool _py_builtins__eval(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_str); + return py_eval(py_tostr(argv)); +} + py_TValue pk_builtins__register() { py_Ref builtins = py_newmodule("builtins", NULL); - py_bindnativefunc(builtins, "repr", _py_builtins__repr); - py_bindnativefunc(builtins, "exit", _py_builtins__exit); - py_bindnativefunc(builtins, "len", _py_builtins__len); - py_bindnativefunc(builtins, "reversed", _py_builtins__reversed); - py_bindnativefunc(builtins, "hex", _py_builtins__hex); - py_bindnativefunc(builtins, "iter", _py_builtins__iter); - py_bindnativefunc(builtins, "next", _py_builtins__next); - py_bindnativefunc(builtins, "hash", _py_builtins__hash); - py_bindnativefunc(builtins, "abs", _py_builtins__abs); - py_bindnativefunc(builtins, "sum", _py_builtins__sum); + py_bindfunc(builtins, "repr", _py_builtins__repr); + py_bindfunc(builtins, "exit", _py_builtins__exit); + py_bindfunc(builtins, "len", _py_builtins__len); + py_bindfunc(builtins, "reversed", _py_builtins__reversed); + py_bindfunc(builtins, "hex", _py_builtins__hex); + py_bindfunc(builtins, "iter", _py_builtins__iter); + py_bindfunc(builtins, "next", _py_builtins__next); + py_bindfunc(builtins, "hash", _py_builtins__hash); + py_bindfunc(builtins, "abs", _py_builtins__abs); + py_bindfunc(builtins, "sum", _py_builtins__sum); + + py_bindfunc(builtins, "exec", _py_builtins__exec); + py_bindfunc(builtins, "eval", _py_builtins__eval); py_bind(builtins, "print(*args, sep=' ', end='\\n')", _py_builtins__print); py_bind(builtins, "sorted(iterable, key=None, reverse=False)", _py_builtins__sorted); diff --git a/src/public/py_exception.c b/src/public/py_exception.c index a4cff1a8..6ca9c41b 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -111,4 +111,90 @@ py_Type pk_BaseException__register() { py_Type pk_Exception__register() { py_Type type = pk_newtype("Exception", tp_BaseException, NULL, NULL, false, true); return type; +} + +////////////////////////////////////////////////// +bool py_checkexc() { + pk_VM* vm = pk_current_vm; + return !py_isnil(&vm->curr_exception); +} + +void py_printexc() { + char* msg = py_formatexc(); + if(!msg) return; + pk_current_vm->_stdout("%s\n", msg); + free(msg); +} + +char* py_formatexc() { + pk_VM* vm = pk_current_vm; + if(py_isnil(&vm->curr_exception)) { return NULL; } + c11_sbuf ss; + c11_sbuf__ctor(&ss); + + if(true) { c11_sbuf__write_cstr(&ss, "Traceback (most recent call last):\n"); } + + BaseException* ud = py_touserdata(&vm->curr_exception); + + for(int i = ud->stacktrace.count - 1; i >= 0; i--) { + BaseExceptionFrame* frame = c11__at(BaseExceptionFrame, &ud->stacktrace, i); + pk_SourceData__snapshot(frame->src, + &ss, + frame->lineno, + NULL, + frame->name ? frame->name->data : NULL); + c11_sbuf__write_char(&ss, '\n'); + } + + const char* name = py_tpname(vm->curr_exception.type); + bool ok = py_str(&vm->curr_exception); + if(!ok) c11__abort("py_printexc(): failed to convert exception to string"); + const char* message = py_tostr(py_retval()); + + c11_sbuf__write_cstr(&ss, name); + c11_sbuf__write_cstr(&ss, ": "); + c11_sbuf__write_cstr(&ss, message); + + c11_string* res = c11_sbuf__submit(&ss); + char* dup = malloc(res->size + 1); + memcpy(dup, res->data, res->size); + dup[res->size] = '\0'; + c11_string__delete(res); + return dup; +} + +bool py_exception(const char* name, const char* fmt, ...) { + 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); + py_Ref message = py_pushtmp(); + py_newstrn(message, res->data, res->size); + c11_string__delete(res); + + 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); + if(!ok) c11__abort("py_exception(): failed to create exception object"); + py_pop(); + + 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; +} + +bool KeyError(py_Ref key) { + py_Ref cls = py_getdict(&pk_current_vm->builtins, py_name("KeyError")); + bool ok = py_call(cls, 1, key); + if(!ok) return false; + return py_raise(py_retval()); } \ No newline at end of file diff --git a/src/public/values.c b/src/public/values.c index 8b55b8ba..741adb4e 100644 --- a/src/public/values.c +++ b/src/public/values.c @@ -58,7 +58,7 @@ void py_bindmethod2(py_Type type, const char* name, py_CFunction f, enum BindTyp py_setdict(py_tpobject(type), py_name(name), &tmp); } -void py_bindnativefunc(py_Ref obj, const char* name, py_CFunction f) { +void py_bindfunc(py_Ref obj, const char* name, py_CFunction f) { py_TValue tmp; py_newnativefunc(&tmp, f); py_setdict(obj, py_name(name), &tmp); diff --git a/src/public/vm.c b/src/public/vm.c index 032a6bc2..be8aa005 100644 --- a/src/public/vm.c +++ b/src/public/vm.c @@ -179,7 +179,11 @@ static bool pk_SourceData_ src = pk_SourceData__rcnew(source, filename, mode, false); Error* err = pk_compile(src, &co); if(err) { + py_exception("SyntaxError", err->msg); + py_BaseException__stpush(&vm->curr_exception, src, err->lineno, NULL); + PK_DECREF(src); + free(err); return false; }