diff --git a/c_bindings/main.c b/c_bindings/main.c index ff18459a..65670880 100644 --- a/c_bindings/main.c +++ b/c_bindings/main.c @@ -1,6 +1,8 @@ #include "pocketpy_c.h" #include +//tests the c bindings for pocketpy + void test_binding(pkpy_vm vm) { pkpy_push_int(vm, 12); @@ -15,22 +17,24 @@ int main(int argc, char** argv) { pkpy_push_int(vm, 11); pkpy_set_global(vm, "eleven"); - pkpy_push_cfunction(vm, test_binding); - pkpy_set_global(vm, "binding"); + //pkpy_push_cfunction(vm, test_binding); + //pkpy_set_global(vm, "binding"); pkpy_vm_exec(vm, "print(eleven)"); - pkpy_vm_exec(vm, "print(binding())"); + //pkpy_vm_exec(vm, "print(binding())"); - pkpy_vm_exec(vm, "x = lambda x : x + 1"); + pkpy_vm_exec(vm, "def x(x) : return x + 1"); pkpy_get_global(vm, "x"); - pkpy_push_null(vm); pkpy_push_int(vm, 1); pkpy_call(vm, 1); - int64_t r = pkpy_toint(vm, 1); + int r; + pkpy_to_int(vm, -1, &r); printf("%li\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 9e0ec369..d890eaa5 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -1,71 +1,180 @@ #include "pocketpy.h" #include "pocketpy_c.h" -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { - pkpy::VM* p = new pkpy::VM(use_stdio, enable_os); +using namespace pkpy; - return (pkpy_vm) p; + +#define ERRHANDLER_OPEN try { \ + 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(NULL); \ + return false; \ + } \ + } catch(...) { \ + std::cerr << "ERROR: a non pocketpy exeception was thrown " \ + << "this probably means pocketpy itself has a bug!\n"; \ + exit(2); \ + } + +bool pkpy_clear_error(pkpy_vm vm_handle, const char** message) { + VM* vm = (VM*) vm_handle; + + try { + if (vm->c_data.top() != nullptr) + return false; + + vm->c_data.pop(); + Str wrapped_message = CAST(Str&, vm->c_data.top()); + if (message != nullptr) + *message = wrapped_message.c_str_dup(); + else + std::cerr << "ERROR: " << wrapped_message << "\n"; + + vm->c_data.pop(); + //at this point the stack is clear + + return true; + + } catch(...) { + std::cerr << "ERROR: a non pocketpy exeception was thrown " + << "this probably means pocketpy itself has a bug!\n"; + exit(2); + } } -void pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { + VM* vm = new VM(use_stdio, enable_os); - vm->exec(source, "main.py", pkpy::EXEC_MODE); + return (pkpy_vm) vm; +} + +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); + + return true; + ERRHANDLER_CLOSE } void pkpy_vm_destroy(pkpy_vm vm_handle) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - + VM* vm = (VM*) vm_handle; delete vm; } -void pkpy_push_cfunction(pkpy_vm vm_handle, pkpy_cfunction f) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(VAR((pkpy::StackFunc) f)); +bool pkpy_push_function(pkpy_vm vm_handle, pkpy_function f) { + return true; } -void pkpy_push_int(pkpy_vm vm_handle, int64_t value) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(VAR(value)); +bool pkpy_push_int(pkpy_vm vm_handle, int value) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE } -void pkpy_push_null(pkpy_vm vm_handle) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(pkpy::PY_NULL); +bool pkpy_push_float(pkpy_vm vm_handle, double value) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE } +bool pkpy_set_global(pkpy_vm vm_handle, const char* name) { + VM* vm = (VM*) vm_handle; -void pkpy_push_float(pkpy_vm vm_handle, double value) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(VAR(value)); + ERRHANDLER_OPEN + + vm->_main->attr().set(name, vm->c_data.top()); + + vm->c_data.pop(); + + return true; + ERRHANDLER_CLOSE } -void pkpy_set_global(pkpy_vm vm_handle, const char* name) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +bool pkpy_get_global(pkpy_vm vm_handle, const char* name) { + VM* vm = (VM*) vm_handle; - vm->_main->attr().set(name, vm->s_data.top()); + ERRHANDLER_OPEN - vm->s_data.pop(); + PyObject* o = vm->_main->attr().try_get(name); + + vm->c_data.push(o); + + return true; + ERRHANDLER_CLOSE } -void pkpy_get_global(pkpy_vm vm_handle, const char* name) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +static void call_wrapper(VM* vm, int argc, bool method_call) { + int callable_index = vm->c_data.size() - argc - 1; - pkpy::PyObject* o = vm->_main->attr().try_get(name); + PyObject* callable = vm->c_data.get(callable_index); - vm->s_data.push(o); + 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); + + 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); + + //TODO unpack tuple? + vm->c_data.push(o); } -void pkpy_call(pkpy_vm vm_handle, int argc) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - pkpy::PyObject* o = vm->vectorcall(argc, 0, 0); - vm->s_data.push(o); +bool pkpy_call(pkpy_vm vm_handle, int argc) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + call_wrapper(vm, argc, false); + + return true; + ERRHANDLER_CLOSE } -int pkpy_toint(pkpy_vm vm_handle, int index) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +bool pkpy_call_method(pkpy_vm vm_handle, int argc) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + call_wrapper(vm, argc, true); - pkpy::PyObject* o = vm->s_data.peek(index); - return pkpy::py_cast(vm, o); + return true; + ERRHANDLER_CLOSE } +static int lua_to_cstack_index(int index, int size) { + if (index < 0) + index = size + index; + return index; +} + +bool pkpy_to_int(pkpy_vm vm_handle, int index, int* 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 +} diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index f800548f..e88d707e 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -10,23 +10,52 @@ extern "C" { typedef struct pkpy_vm_handle* pkpy_vm; +//we mostly follow the lua api for these bindings +//the key difference being each method returns 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) +//NOTE : you need to free the message that is passed back after you are done using it +//or else pass in null +bool pkpy_clear_error(pkpy_vm, const char** message); + + + pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); -void pkpy_vm_exec(pkpy_vm vm_handle, const char* source); +bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source); void pkpy_vm_destroy(pkpy_vm vm); -////////binding a c function to pocketpy -typedef int (*pkpy_cfunction)(pkpy_vm); -void pkpy_push_cfunction(pkpy_vm, pkpy_cfunction); -void pkpy_push_int(pkpy_vm, int64_t); -void pkpy_push_float(pkpy_vm, double); -void pkpy_push_null(pkpy_vm); +typedef int (*pkpy_function)(pkpy_vm); -void pkpy_set_global(pkpy_vm, const char* name); -void pkpy_get_global(pkpy_vm vm_handle, const char* name); +bool pkpy_push_function(pkpy_vm, pkpy_function); +bool pkpy_push_int(pkpy_vm, int); +bool pkpy_push_float(pkpy_vm, double); -void pkpy_call(pkpy_vm vm_handle, int argc); -int pkpy_toint(pkpy_vm vm_handle, int index); +bool pkpy_set_global(pkpy_vm, const char* name); +bool pkpy_get_global(pkpy_vm vm_handle, const char* name); + +//first push callable you want to call +//then push the arguments to send +//argc is the number of arguments +bool pkpy_call(pkpy_vm vm_handle, 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 +bool pkpy_call_method(pkpy_vm vm_handle, 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); diff --git a/src/error.h b/src/error.h index bd7182cd..a216b8a9 100644 --- a/src/error.h +++ b/src/error.h @@ -101,4 +101,4 @@ public: } }; -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/frame.h b/src/frame.h index 91ee5bda..0240a6ac 100644 --- a/src/frame.h +++ b/src/frame.h @@ -101,14 +101,13 @@ struct ValueStack { //stack for working with c bindings struct CVirtualStack { static const size_t MAX_SIZE = 256; - // We allocate 512 more bytes to keep `_sp` valid when `is_overflow() == true`. PyObject* _begin[MAX_SIZE]; PyObject** _sp; CVirtualStack(): _sp(_begin) {} PyObject* top() const { return _sp[-1]; } - PyObject* peek(int n) const { return _sp[-n]; } + PyObject* get(int index) const { return _begin[index]; } void push(PyObject* v){ *_sp++ = v; } void pop(){ --_sp; } void shrink(int n){ _sp -= n; } @@ -116,18 +115,12 @@ struct CVirtualStack { bool empty() const { return _sp == _begin; } PyObject** begin() { return _begin; } PyObject** end() { return _sp; } - void reset(PyObject** sp) { -#if DEBUG_EXTRA_CHECK - if(sp < _begin || sp > _begin + MAX_SIZE) FATAL_ERROR(); -#endif - _sp = sp; - } void clear() { _sp = _begin; } - ValueStack(const ValueStack&) = delete; - ValueStack(ValueStack&&) = delete; - ValueStack& operator=(const ValueStack&) = delete; - ValueStack& operator=(ValueStack&&) = delete; + CVirtualStack(const CVirtualStack&) = delete; + CVirtualStack(CVirtualStack&&) = delete; + CVirtualStack& operator=(const CVirtualStack&) = delete; + CVirtualStack& operator=(CVirtualStack&&) = delete; }; struct Frame { diff --git a/src/main.cpp b/src/main.cpp index 282d4522..2e7f94a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,21 +6,21 @@ #ifndef __EMSCRIPTEN__ int main(int argc, char** argv){ - pkpy::VM* vm = pkpy_new_vm(); + pkpy::VM* vm = new pkpy::VM(); vm->bind_builtin_func<0>("input", [](pkpy::VM* vm, pkpy::ArgsView args){ return VAR(pkpy::getline()); }); if(argc == 1){ - pkpy::REPL* repl = pkpy_new_repl(vm); + pkpy::REPL* repl = new pkpy::REPL(vm); bool need_more_lines = false; while(true){ (*vm->_stdout) << (need_more_lines ? "... " : ">>> "); bool eof = false; std::string line = pkpy::getline(&eof); if(eof) break; - need_more_lines = pkpy_repl_input(repl, line.c_str()); + need_more_lines = repl->input(line.c_str()); } - pkpy_delete(vm); + delete vm; return 0; } @@ -47,7 +47,7 @@ int main(int argc, char** argv){ pkpy::PyObject* ret = nullptr; ret = vm->exec(src.c_str(), argv_1, pkpy::EXEC_MODE); - pkpy_delete(vm); + delete vm; return ret != nullptr ? 0 : 1; } @@ -56,4 +56,4 @@ __HELP: return 0; } -#endif \ No newline at end of file +#endif diff --git a/src/str.h b/src/str.h index bbac8fb4..72a6235d 100644 --- a/src/str.h +++ b/src/str.h @@ -434,4 +434,4 @@ const StrName __and__ = StrName::get("__and__"); const StrName __or__ = StrName::get("__or__"); const StrName __xor__ = StrName::get("__xor__"); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy