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

+ 0 - 23
c_bindings/CMakeLists.txt

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

+ 0 - 415
c_bindings/test.c

@@ -1,415 +0,0 @@
-#include "pocketpy_c.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-//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;
-}

+ 0 - 112
c_bindings/test_answers.txt

@@ -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 ======
-<void* at 0x7b>
-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')]

+ 1 - 2
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 <stdint.h>
 
 #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);

+ 2 - 5
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);

+ 17 - 7
include/pocketpy/objects/sourcedata.h

@@ -3,31 +3,41 @@
 #include <stdbool.h>
 #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
 }

+ 1 - 1
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.

+ 15 - 18
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);
 }

+ 59 - 40
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 <module> import * can only be used in global scope");
+            return SyntaxError(self, "from <module> 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());

+ 26 - 20
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);

+ 1 - 0
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;

+ 0 - 59
src/objects/error.c

@@ -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; i<self->stacktrace.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);
-// }

+ 0 - 69
src/public/error.c

@@ -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 <stdarg.h>
-
-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());
-}

+ 27 - 10
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);

+ 86 - 0
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());
 }

+ 1 - 1
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);

+ 4 - 0
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;
     }