diff --git a/c_bindings/main.c b/c_bindings/main.c deleted file mode 100644 index d66759f8..00000000 --- a/c_bindings/main.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "pocketpy_c.h" -#include - -//tests the c bindings for pocketpy - - -int test_binding(pkpy_vm vm) { - pkpy_push_int(vm, 12); - return 1; -} - -int main(int argc, char** argv) { - - pkpy_vm vm = pkpy_vm_create(true, true); - - pkpy_vm_exec(vm, "print('hello world!')"); - - pkpy_push_int(vm, 11); - pkpy_set_global(vm, "eleven"); - - pkpy_push_function(vm, test_binding); - pkpy_set_global(vm, "binding"); - - pkpy_vm_exec(vm, "print(eleven)"); - pkpy_vm_exec(vm, "print(binding())"); - - pkpy_vm_exec(vm, "def x(x) : return x + 1"); - - pkpy_get_global(vm, "x"); - pkpy_push_int(vm, 1); - pkpy_call(vm, 1); - - int r; - pkpy_to_int(vm, -1, &r); - printf("%i\n", r); - - pkpy_clear_error(vm, NULL); - - pkpy_vm_destroy(vm); - return 0; -} diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 8a01971a..8a271d6d 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -3,52 +3,82 @@ using namespace pkpy; +#define SAFEGUARD_OPEN try { \ -#define ERRHANDLER_OPEN try { \ +#define SAFEGUARD_CLOSE \ + } catch(std::exception& e) { \ + std::cerr << "ERROR: a std::exception " \ + << "this probably means pocketpy itself has a bug!\n" \ + << e.what() << "\n"; \ + exit(2); \ + } catch(...) { \ + std::cerr << "ERROR: a unknown exception was thrown " \ + << "this probably means pocketpy itself has a bug!\n"; \ + exit(2); \ + } + + +#define ERRHANDLER_OPEN SAFEGUARD_OPEN \ try { \ if (vm->c_data.top() == nullptr) \ return false; \ #define ERRHANDLER_CLOSE \ } catch( Exception e ) { \ - vm->c_data.clear(); \ - vm->c_data.push(VAR(e.summary())); \ + vm->c_data.push(VAR(e)); \ vm->c_data.push(NULL); \ return false; \ } \ - } catch(...) { \ - std::cerr << "ERROR: a non pocketpy exeception was thrown " \ - << "this probably means pocketpy itself has a bug!\n"; \ - exit(2); \ - } + SAFEGUARD_CLOSE \ -bool pkpy_clear_error(pkpy_vm vm_handle, const char** message) { + +//for now I will unpack a tuple automatically, we may not want to handle +//it this way, not sure +//it is more lua like, but maybe not python like +static void unpack_return(VM* vm, PyObject* ret) { + if (is_type(ret, vm->tp_tuple)) { + Tuple& t = CAST(Tuple&, ret); + for (int i = 0; i < t.size(); i++) + vm->c_data.push(t[i]); + } else if (ret == vm->None) { + //do nothing here + //having to pop the stack after every call that returns none is annoying + //lua does not do this + // + //so for now we will not push none on the stack when it is the sole thing returned + //if this becomes a problem we can change it + // + //you can still check if it returned none by comparing stack size before + //and after if you have too + } else + vm->c_data.push(ret); + +} + + +bool pkpy_clear_error(pkpy_vm vm_handle, char** message) { VM* vm = (VM*) vm_handle; + SAFEGUARD_OPEN - try { if (vm->c_data.top() != nullptr) return false; vm->c_data.pop(); - Str wrapped_message = CAST(Str&, vm->c_data.top()); + Exception& e = CAST(Exception&, vm->c_data.top()); if (message != nullptr) - *message = wrapped_message.c_str_dup(); + *message = e.summary().c_str_dup(); else - std::cerr << "ERROR: " << wrapped_message << "\n"; - - vm->c_data.pop(); - //at this point the stack is clear + std::cerr << "ERROR: " << e.summary() << "\n"; + vm->c_data.clear(); + vm->callstack.clear(); + vm->s_data.clear(); return true; - } catch(...) { - std::cerr << "ERROR: a non pocketpy exeception was thrown " - << "this probably means pocketpy itself has a bug!\n"; - exit(2); - } + SAFEGUARD_CLOSE } -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { +pkpy_vm pkpy_new_vm(bool use_stdio, bool enable_os) { VM* vm = new VM(use_stdio, enable_os); return (pkpy_vm) vm; @@ -58,17 +88,55 @@ bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { VM* vm = (VM*) vm_handle; ERRHANDLER_OPEN - vm->exec(source, "main.py", EXEC_MODE); + CodeObject_ code = vm->compile(source, "", EXEC_MODE); + PyObject* result = vm->_exec(code, vm->_main); + unpack_return(vm, result); return true; ERRHANDLER_CLOSE } -void pkpy_vm_destroy(pkpy_vm vm_handle) { +void pkpy_delete_vm(pkpy_vm vm_handle) { VM* vm = (VM*) vm_handle; delete vm; } +pkpy_repl pkpy_new_repl(pkpy_vm vm_handle) { + VM* vm = (VM*) vm_handle; + REPL* repl = new REPL(vm); + return (pkpy_repl) repl; +} + +bool pkpy_repl_input(pkpy_repl repl_handle, const char* line) { + REPL* repl = (REPL*) repl_handle; + return repl->input(line); +} + +void pkpy_delete_repl(pkpy_repl repl_handle) { + REPL* repl = (REPL*) repl_handle; + delete repl; +} + +static void propagate_if_errored(VM* vm) { + try { + if (vm->c_data.top() != nullptr) + return; + + vm->c_data.pop(); + Exception& e = CAST(Exception&, vm->c_data.top()); + vm->c_data.pop(); + + throw e; + } catch(Exception& e) { + throw; + } catch(...) { + std::cerr << "ERROR: a non pocketpy exeception was thrown " + << "this probably means pocketpy itself has a bug!\n"; + exit(2); + } +} + + PyObject* c_function_wrapper(VM* vm, ArgsView args) { LuaStyleFuncC f = CAST(NativeFunc&, args[-2])._lua_f; @@ -81,10 +149,20 @@ PyObject* c_function_wrapper(VM* vm, ArgsView args) { int retc = f(vm); PyObject* ret = vm->None; + propagate_if_errored(vm); - //TODO handle tuple packing for multiple returns - if (retc > 0) + if (retc == 1) ret = vm->c_data.top(); + else if (retc > 1) { + Tuple t = Tuple(retc); + + for (int i = 0; i < retc; i++) { + int stack_index = (vm->c_data.size() - retc) + i; + t[i] = vm->c_data.get(stack_index); + } + + ret = VAR(t); + } vm->c_data.clear(); vm->c_data.restore(stored); @@ -127,6 +205,50 @@ bool pkpy_push_float(pkpy_vm vm_handle, double value) { ERRHANDLER_CLOSE } +bool pkpy_push_bool(pkpy_vm vm_handle, bool value) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_push_string(pkpy_vm vm_handle, const char* value) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_push_stringn(pkpy_vm vm_handle, const char* value, int length) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + + Str s = Str(value, length); + vm->c_data.push(VAR(s)); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_push_none(pkpy_vm vm_handle) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(vm->None); + + return true; + ERRHANDLER_CLOSE +} + + + bool pkpy_set_global(pkpy_vm vm_handle, const char* name) { VM* vm = (VM*) vm_handle; @@ -140,12 +262,18 @@ bool pkpy_set_global(pkpy_vm vm_handle, const char* name) { ERRHANDLER_CLOSE } +//get global will also get bulitins bool pkpy_get_global(pkpy_vm vm_handle, const char* name) { VM* vm = (VM*) vm_handle; ERRHANDLER_OPEN PyObject* o = vm->_main->attr().try_get(name); + if (o == nullptr) { + o = vm->builtins->attr().try_get(name); + if (o == nullptr) + throw Exception("AttributeError", "could not find requested global"); + } vm->c_data.push(o); @@ -153,46 +281,58 @@ bool pkpy_get_global(pkpy_vm vm_handle, const char* name) { ERRHANDLER_CLOSE } -static void call_wrapper(VM* vm, int argc, bool method_call) { + +bool pkpy_call(pkpy_vm vm_handle, int argc) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + int callable_index = vm->c_data.size() - argc - 1; PyObject* callable = vm->c_data.get(callable_index); vm->s_data.push(callable); - if (method_call) - vm->s_data.push(vm->c_data.get(callable_index - 1)); - else - vm->s_data.push(PY_NULL); + vm->s_data.push(PY_NULL); for (int i = 0; i < argc; i++) vm->s_data.push(vm->c_data.get(callable_index + i + 1)); PyObject* o = vm->vectorcall(argc); - vm->c_data.shrink(argc + 1 + (int) method_call); + vm->c_data.shrink(argc + 1); - //TODO unpack tuple? - vm->c_data.push(o); -} - -bool pkpy_call(pkpy_vm vm_handle, int argc) { - VM* vm = (VM*) vm_handle; - ERRHANDLER_OPEN - call_wrapper(vm, argc, false); + unpack_return(vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_call_method(pkpy_vm vm_handle, int argc) { +bool pkpy_call_method(pkpy_vm vm_handle, const char* name, int argc) { VM* vm = (VM*) vm_handle; ERRHANDLER_OPEN - call_wrapper(vm, argc, true); + + int self_index = vm->c_data.size() - argc - 1; + PyObject* self = vm->c_data.get(self_index); + + PyObject* callable = vm->get_unbound_method(self, name, &self); + + vm->s_data.push(callable); + vm->s_data.push(self); + + for (int i = 0; i < argc; i++) + vm->s_data.push(vm->c_data.get(self_index + i + 1)); + + PyObject* o = vm->vectorcall(argc); + + vm->c_data.shrink(argc + 1); + + unpack_return(vm, o); return true; ERRHANDLER_CLOSE } + + static int lua_to_cstack_index(int index, int size) { if (index < 0) index = size + index; @@ -212,3 +352,99 @@ bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret) { return true; ERRHANDLER_CLOSE } + +bool pkpy_to_float(pkpy_vm vm_handle, int index, double* ret) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, vm->c_data.size()); + + PyObject* o = vm->c_data.get(index); + if (ret != nullptr) + *ret = py_cast(vm, o); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_to_bool(pkpy_vm vm_handle, int index, bool* ret) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, vm->c_data.size()); + + PyObject* o = vm->c_data.get(index); + if (ret != nullptr) + *ret = py_cast(vm, o); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_to_string(pkpy_vm vm_handle, int index, char** ret) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, vm->c_data.size()); + + PyObject* o = vm->c_data.get(index); + if (ret != nullptr) { + Str& s = CAST(Str&, o); + *ret = s.c_str_dup(); + } + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_is_int(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_int); +} +bool pkpy_is_float(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_float); +} +bool pkpy_is_bool(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_bool); +} +bool pkpy_is_string(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_str); +} +bool pkpy_is_none(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return o == vm->None; +} + +bool pkpy_check_stack(pkpy_vm vm_handle, int free) { + VM* vm = (VM*) vm_handle; + return free <= vm->c_data.remaining(); +} + +int pkpy_stack_size(pkpy_vm vm_handle) { + VM* vm = (VM*) vm_handle; + return vm->c_data.size(); +} + diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 786756a4..6c56742f 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -9,22 +9,30 @@ extern "C" { #include typedef struct pkpy_vm_handle* pkpy_vm; +typedef struct pkpy_repl_hande* pkpy_repl; -//we mostly follow the lua api for these bindings -//the key difference being most methods return a bool, true if it succeeded -//false if it did not +//we we take a lot of inspiration from the lua api for these bindings +//the key difference being most methods return a bool, +//true if it succeeded false if it did not -//if a method returns false call this next method to check the error and clear it -//if this method returns false it means that no error was set, and no action is taken -//if it returns true it means there was an error and it was cleared, it will provide a string summary of the error in the message parameter (if it is not NULL) +//if a method returns false call the pkpy_clear_error method to check the error and clear it +//if pkpy_clear_error returns false it means that no error was set, and it takes no action +//if pkpy_clear_error returns true it means there was an error and it was cleared, +//it will provide a string summary of the error in the message parameter (if it is not NULL) //NOTE : you need to free the message that is passed back after you are done using it //or else pass in null as message, and it will just print the message to stderr -bool pkpy_clear_error(pkpy_vm, const char** message); +bool pkpy_clear_error(pkpy_vm, char** message); -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); -bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source); -void pkpy_vm_destroy(pkpy_vm vm); +pkpy_vm pkpy_new_vm(bool use_stdio, bool enable_os); +bool pkpy_vm_exec(pkpy_vm, const char* source); +void pkpy_delete_vm(pkpy_vm); + +pkpy_repl pkpy_new_repl(pkpy_vm); +//the repl does its own error management, so this method doesn't follow the ame +//error semantics, it is just a thin wrapper around the REPL classes input method +bool pkpy_repl_input(pkpy_repl, const char* line); +void pkpy_delete_repl(pkpy_repl); typedef int (*pkpy_function)(pkpy_vm); @@ -32,29 +40,52 @@ typedef int (*pkpy_function)(pkpy_vm); bool pkpy_push_function(pkpy_vm, pkpy_function); bool pkpy_push_int(pkpy_vm, int); bool pkpy_push_float(pkpy_vm, double); +bool pkpy_push_bool(pkpy_vm, bool); +bool pkpy_push_string(pkpy_vm, const char*); +bool pkpy_push_stringn(pkpy_vm, const char*, int length); +bool pkpy_push_none(pkpy_vm); bool pkpy_set_global(pkpy_vm, const char* name); -bool pkpy_get_global(pkpy_vm vm_handle, const char* name); +bool pkpy_get_global(pkpy_vm, const char* name); //first push callable you want to call //then push the arguments to send //argc is the number of arguments that was pushed (not counting the callable) -bool pkpy_call(pkpy_vm vm_handle, int argc); +bool pkpy_call(pkpy_vm, int argc); //first push the object the method belongs to (self) -//then push the callable you want to call //then push the the argments //argc is the number of arguments that was pushed (not counting the callable or self) -bool pkpy_call_method(pkpy_vm vm_handle, int argc); +//name is the name of the method to call on the object +bool pkpy_call_method(pkpy_vm, const char* name, int argc); //we will break with the lua api here //lua uses 1 as the index to the first pushed element for all of these functions //but we will start counting at zero to match python //we will allow negative numbers to count backwards from the top -bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret); +bool pkpy_to_int(pkpy_vm, int index, int* ret); +bool pkpy_to_float(pkpy_vm, int index, double* ret); +bool pkpy_to_bool(pkpy_vm, int index, bool* ret); +//you have to free ret after you are done using it +bool pkpy_to_string(pkpy_vm, int index, char** ret); +//these do not follow the same error semantics as above, their return values +//just say whether the check succeeded or not, or else return the value asked for + +bool pkpy_is_int(pkpy_vm, int index); +bool pkpy_is_float(pkpy_vm, int index); +bool pkpy_is_bool(pkpy_vm, int index); +bool pkpy_is_string(pkpy_vm, int index); +bool pkpy_is_none(pkpy_vm, int index); + +//will return true if at least free empty slots remain on the stack +bool pkpy_check_stack(pkpy_vm, int free); + +//returns the number of elements on the stack +int pkpy_stack_size(pkpy_vm); + #ifdef __cplusplus } #endif diff --git a/c_bindings/test.c b/c_bindings/test.c new file mode 100644 index 00000000..42df6048 --- /dev/null +++ b/c_bindings/test.c @@ -0,0 +1,262 @@ +#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); + char* message; + if (!pkpy_clear_error(vm, &message)) { + printf("clear error reported everything was fine\n"); + exit(1); + } + + printf("%s\n", message); + free(message); + 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!\n"); + 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"); + 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_return_none(pkpy_vm vm) { + return 0; +} + +int test_error_propagate(pkpy_vm vm) { + pkpy_get_global(vm, "does not exist"); + return 1; +} + + +pkpy_vm vm; + +void cleanup(void) { + pkpy_delete_vm(vm); +} + +int main(int argc, char** argv) { + + vm = pkpy_new_vm(true, true); + atexit(cleanup); + + //test exec + check(pkpy_vm_exec(vm, "print('hello world!')")); + + error(pkpy_get_global(vm, "nonexistatn")); + + printf("\ntesting int methods\n"); + int r_int; + check(pkpy_push_int(vm, 11)); + check(pkpy_set_global(vm, "eleven")); + check(pkpy_vm_exec(vm, "print(eleven)")); + check(pkpy_get_global(vm, "eleven")); + check(pkpy_is_int(vm, -1)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("%i\n", r_int); + fail(pkpy_is_float(vm, -1)); + fail(pkpy_is_bool(vm, -1)); + fail(pkpy_is_string(vm, -1)); + fail(pkpy_is_none(vm, -1)); + + printf("\ntesting float methods\n"); + double r_float; + check(pkpy_push_float(vm, 11.11)); + check(pkpy_set_global(vm, "elevenf")); + check(pkpy_vm_exec(vm, "print(elevenf)")); + check(pkpy_get_global(vm, "elevenf")); + check(pkpy_is_float(vm, -1)); + check(pkpy_to_float(vm, -1, &r_float)); + printf("%f\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)); + + printf("\ntesting bool methods\n"); + bool r_bool; + check(pkpy_push_bool(vm, false)); + check(pkpy_set_global(vm, "false_test")); + check(pkpy_vm_exec(vm, "print(false_test)")); + check(pkpy_get_global(vm, "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)); + + printf("\ntesting string methods\n"); + char* r_string; + check(pkpy_push_string(vm, "hello!")); + check(pkpy_set_global(vm, "hello1")); + check(pkpy_vm_exec(vm, "print(hello1)")); + check(pkpy_push_stringn(vm, "hello!", 5)); + check(pkpy_is_string(vm, -1)); + check(pkpy_to_string(vm, -1, &r_string)); + printf("%s\n", 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)); + free(r_string); + + printf("\ntesting None methods\n"); + check(pkpy_push_none(vm)); + check(pkpy_set_global(vm, "none")); + check(pkpy_vm_exec(vm, "print(none)")); + check(pkpy_get_global(vm, "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)); + + printf("\ntesting sizing and indexing\n"); + int stack_size = pkpy_stack_size(vm); + printf("stack size %i\n", stack_size); + check(pkpy_check_stack(vm, 10)); + check(pkpy_check_stack(vm, 251)); + fail(pkpy_check_stack(vm, 252)); + 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_int(vm, -5)); + check(pkpy_is_float(vm, -4)); + check(pkpy_is_bool(vm, -3)); + check(pkpy_is_string(vm, -2)); + check(pkpy_is_none(vm, -1)); + + printf("\ntesting error catching\n"); + error(pkpy_vm_exec(vm, "let's make sure syntax errors get caught")); + check(pkpy_stack_size(vm) == 0); //stack should be cleared after error is resolved + + printf("\ntesting calls\n"); + + check(pkpy_vm_exec(vm, "def x(x, y) : return x - y")); + check(pkpy_vm_exec(vm, "def vararg_x(*x) : return sum(x)")); + check(pkpy_vm_exec(vm, "def keyword_x(x=1, y=1) : return x+y")); + check(pkpy_vm_exec(vm, "def retmany_x() : return 1, 2, 3")); + + check(pkpy_get_global(vm, "x")); + check(pkpy_push_int(vm, 2)); + check(pkpy_push_int(vm, 3)); + check(pkpy_call(vm, 2)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("x : %i\n", r_int); + + check(pkpy_get_global(vm, "vararg_x")); + 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_call(vm, 6)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("vararg_x : %i\n", r_int); + + check(pkpy_get_global(vm, "keyword_x")); + check(pkpy_push_int(vm, 3)); + check(pkpy_call(vm, 1)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("keyword_x : %i\n", r_int); + + check(pkpy_get_global(vm, "keyword_x")); + check(pkpy_call(vm, 0)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("keyword_x : %i\n", r_int); + + check(pkpy_stack_size(vm) == 4); + + check(pkpy_get_global(vm, "retmany_x")); + check(pkpy_call(vm, 0)); + check(pkpy_stack_size(vm) == 7); + 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); + + check(pkpy_get_global(vm, "x")); + error(pkpy_call(vm, 0)); + + check(pkpy_vm_exec(vm, "l = []")); + + check(pkpy_get_global(vm, "l")); + check(pkpy_push_string(vm, "hello")); + check(pkpy_call_method(vm, "append", 1)); + check(pkpy_vm_exec(vm, "print(l)")); + + + printf("\ntesting pushing functions\n"); + + check(pkpy_push_function(vm, test_binding)); + check(pkpy_set_global(vm, "test_binding")); + check(pkpy_vm_exec(vm, "print(test_binding())")); + + check(pkpy_push_function(vm, test_multiple_return)); + check(pkpy_set_global(vm, "test_multiple_return")); + check(pkpy_vm_exec(vm, "test_multiple_return()")); + check(pkpy_stack_size(vm) == 2); + + check(pkpy_push_function(vm, test_error_propagate)); + check(pkpy_set_global(vm, "test_error_propagate")); + error(pkpy_vm_exec(vm, "test_error_propagate()")); + + check(pkpy_get_global(vm, "test_multiple_return")); + check(pkpy_call(vm, 0)); + check(pkpy_stack_size(vm) == 2); + + return 0; +} diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt new file mode 100644 index 00000000..66be2403 --- /dev/null +++ b/c_bindings/test_answers.txt @@ -0,0 +1,49 @@ +hello world! +successfully errored with this message: +Traceback (most recent call last): +AttributeError: could not find requested global + +testing int methods +11 +11 + +testing float methods +11.11 +11.110000 + +testing bool methods +False +0 + +testing string methods +hello! +hello + +testing None methods +None + +testing sizing and indexing +stack size 5 + +testing error catching +successfully errored with this message: + File "", line 1 + let's make sure syntax errors get caught +SyntaxError: EOL while scanning string literal + +testing calls +x : -1 +vararg_x : 21 +keyword_x : 4 +keyword_x : 2 +retmany_x : 1 +retmany_x : 2 +retmany_x : 3 +successfully errored with this message: +TypeError: expected 2 positional arguments, but got 0 (x) +['hello'] + +testing pushing functions +12 +ERROR: failed where it should have succeed at line 251 +clear error reported everything was fine diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh new file mode 100644 index 00000000..e92b1439 --- /dev/null +++ b/run_c_binding_test.sh @@ -0,0 +1,24 @@ +python3 preprocess.py + +if [ ! -f "pocketpy_c.o" ] +then + echo "compiling c++ lib" + clang++ -c -o pocketpy_c.o c_bindings/pocketpy_c.cpp -Wfatal-errors --std=c++17 -O2 -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti -stdlib=libc++ -I src/ -fsanitize=address -g +else + echo "DETECTED PREVIOUS COMPILATION USING IT" +fi + +echo "compiling c executable" +clang -c -o test.o c_bindings/test.c -Wfatal-errors -O2 -Wall -Wno-sign-compare -Wno-unused-variable -I src/ -fsanitize=address -g +echo "linking" +clang++ -o c_binding_test test.o pocketpy_c.o -stdlib=libc++ -fsanitize=address -g +echo "running, no weird output should show up" +./c_binding_test > binding_test_scratch +echo "checking results (they should be identical)" +diff -s binding_test_scratch c_bindings/test_answers.txt + +echo "cleaning up" +rm pocketpy_c.o +rm test.o +rm binding_test_scratch + diff --git a/src/frame.h b/src/frame.h index ee8c1789..d0facbae 100644 --- a/src/frame.h +++ b/src/frame.h @@ -117,6 +117,7 @@ struct CVirtualStack { PyObject** begin() { return _begin + offset; } PyObject** end() { return _sp; } void clear() { _sp = _begin + offset;} + int remaining() { return MAX_SIZE - (_sp - _begin); } size_t store() { size_t ret = offset; offset = _sp - _begin; return ret; } void restore(size_t stored) { offset = stored; }