mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
finalized initial c api and built tests for it
This commit is contained in:
parent
8244a8a1a4
commit
d4b9d354ed
@ -1,41 +0,0 @@
|
|||||||
#include "pocketpy_c.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
//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;
|
|
||||||
}
|
|
@ -3,52 +3,82 @@
|
|||||||
|
|
||||||
using namespace pkpy;
|
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 { \
|
try { \
|
||||||
if (vm->c_data.top() == nullptr) \
|
if (vm->c_data.top() == nullptr) \
|
||||||
return false; \
|
return false; \
|
||||||
|
|
||||||
#define ERRHANDLER_CLOSE \
|
#define ERRHANDLER_CLOSE \
|
||||||
} catch( Exception e ) { \
|
} catch( Exception e ) { \
|
||||||
vm->c_data.clear(); \
|
vm->c_data.push(VAR(e)); \
|
||||||
vm->c_data.push(VAR(e.summary())); \
|
|
||||||
vm->c_data.push(NULL); \
|
vm->c_data.push(NULL); \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
} catch(...) { \
|
SAFEGUARD_CLOSE \
|
||||||
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) {
|
|
||||||
|
//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;
|
VM* vm = (VM*) vm_handle;
|
||||||
|
SAFEGUARD_OPEN
|
||||||
|
|
||||||
try {
|
|
||||||
if (vm->c_data.top() != nullptr)
|
if (vm->c_data.top() != nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
vm->c_data.pop();
|
vm->c_data.pop();
|
||||||
Str wrapped_message = CAST(Str&, vm->c_data.top());
|
Exception& e = CAST(Exception&, vm->c_data.top());
|
||||||
if (message != nullptr)
|
if (message != nullptr)
|
||||||
*message = wrapped_message.c_str_dup();
|
*message = e.summary().c_str_dup();
|
||||||
else
|
else
|
||||||
std::cerr << "ERROR: " << wrapped_message << "\n";
|
std::cerr << "ERROR: " << e.summary() << "\n";
|
||||||
|
|
||||||
vm->c_data.pop();
|
|
||||||
//at this point the stack is clear
|
|
||||||
|
|
||||||
|
vm->c_data.clear();
|
||||||
|
vm->callstack.clear();
|
||||||
|
vm->s_data.clear();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch(...) {
|
SAFEGUARD_CLOSE
|
||||||
std::cerr << "ERROR: a non pocketpy exeception was thrown "
|
|
||||||
<< "this probably means pocketpy itself has a bug!\n";
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
VM* vm = new VM(use_stdio, enable_os);
|
||||||
|
|
||||||
return (pkpy_vm) vm;
|
return (pkpy_vm) vm;
|
||||||
@ -58,17 +88,55 @@ bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source) {
|
|||||||
VM* vm = (VM*) vm_handle;
|
VM* vm = (VM*) vm_handle;
|
||||||
ERRHANDLER_OPEN
|
ERRHANDLER_OPEN
|
||||||
|
|
||||||
vm->exec(source, "main.py", EXEC_MODE);
|
CodeObject_ code = vm->compile(source, "<c-bound>", EXEC_MODE);
|
||||||
|
PyObject* result = vm->_exec(code, vm->_main);
|
||||||
|
unpack_return(vm, result);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
ERRHANDLER_CLOSE
|
ERRHANDLER_CLOSE
|
||||||
}
|
}
|
||||||
|
|
||||||
void pkpy_vm_destroy(pkpy_vm vm_handle) {
|
void pkpy_delete_vm(pkpy_vm vm_handle) {
|
||||||
VM* vm = (VM*) vm_handle;
|
VM* vm = (VM*) vm_handle;
|
||||||
delete vm;
|
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) {
|
PyObject* c_function_wrapper(VM* vm, ArgsView args) {
|
||||||
LuaStyleFuncC f = CAST(NativeFunc&, args[-2])._lua_f;
|
LuaStyleFuncC f = CAST(NativeFunc&, args[-2])._lua_f;
|
||||||
|
|
||||||
@ -81,10 +149,20 @@ PyObject* c_function_wrapper(VM* vm, ArgsView args) {
|
|||||||
int retc = f(vm);
|
int retc = f(vm);
|
||||||
|
|
||||||
PyObject* ret = vm->None;
|
PyObject* ret = vm->None;
|
||||||
|
propagate_if_errored(vm);
|
||||||
|
|
||||||
//TODO handle tuple packing for multiple returns
|
if (retc == 1)
|
||||||
if (retc > 0)
|
|
||||||
ret = vm->c_data.top();
|
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.clear();
|
||||||
vm->c_data.restore(stored);
|
vm->c_data.restore(stored);
|
||||||
@ -127,6 +205,50 @@ bool pkpy_push_float(pkpy_vm vm_handle, double value) {
|
|||||||
ERRHANDLER_CLOSE
|
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) {
|
bool pkpy_set_global(pkpy_vm vm_handle, const char* name) {
|
||||||
VM* vm = (VM*) vm_handle;
|
VM* vm = (VM*) vm_handle;
|
||||||
|
|
||||||
@ -140,12 +262,18 @@ bool pkpy_set_global(pkpy_vm vm_handle, const char* name) {
|
|||||||
ERRHANDLER_CLOSE
|
ERRHANDLER_CLOSE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get global will also get bulitins
|
||||||
bool pkpy_get_global(pkpy_vm vm_handle, const char* name) {
|
bool pkpy_get_global(pkpy_vm vm_handle, const char* name) {
|
||||||
VM* vm = (VM*) vm_handle;
|
VM* vm = (VM*) vm_handle;
|
||||||
|
|
||||||
ERRHANDLER_OPEN
|
ERRHANDLER_OPEN
|
||||||
|
|
||||||
PyObject* o = vm->_main->attr().try_get(name);
|
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);
|
vm->c_data.push(o);
|
||||||
|
|
||||||
@ -153,15 +281,16 @@ bool pkpy_get_global(pkpy_vm vm_handle, const char* name) {
|
|||||||
ERRHANDLER_CLOSE
|
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;
|
int callable_index = vm->c_data.size() - argc - 1;
|
||||||
|
|
||||||
PyObject* callable = vm->c_data.get(callable_index);
|
PyObject* callable = vm->c_data.get(callable_index);
|
||||||
|
|
||||||
vm->s_data.push(callable);
|
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++)
|
for (int i = 0; i < argc; i++)
|
||||||
@ -169,30 +298,41 @@ static void call_wrapper(VM* vm, int argc, bool method_call) {
|
|||||||
|
|
||||||
PyObject* o = vm->vectorcall(argc);
|
PyObject* o = vm->vectorcall(argc);
|
||||||
|
|
||||||
vm->c_data.shrink(argc + 1 + (int) method_call);
|
vm->c_data.shrink(argc + 1);
|
||||||
|
|
||||||
//TODO unpack tuple?
|
unpack_return(vm, o);
|
||||||
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);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
ERRHANDLER_CLOSE
|
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;
|
VM* vm = (VM*) vm_handle;
|
||||||
ERRHANDLER_OPEN
|
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;
|
return true;
|
||||||
ERRHANDLER_CLOSE
|
ERRHANDLER_CLOSE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int lua_to_cstack_index(int index, int size) {
|
static int lua_to_cstack_index(int index, int size) {
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
index = size + index;
|
index = size + index;
|
||||||
@ -212,3 +352,99 @@ bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret) {
|
|||||||
return true;
|
return true;
|
||||||
ERRHANDLER_CLOSE
|
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<double>(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<bool>(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -9,22 +9,30 @@ extern "C" {
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct pkpy_vm_handle* pkpy_vm;
|
typedef struct pkpy_vm_handle* pkpy_vm;
|
||||||
|
typedef struct pkpy_repl_hande* pkpy_repl;
|
||||||
|
|
||||||
//we mostly follow the lua api for these bindings
|
//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
|
//the key difference being most methods return a bool,
|
||||||
//false if it did not
|
//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 a method returns false call the pkpy_clear_error 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 pkpy_clear_error returns false it means that no error was set, and it takes no action
|
||||||
//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 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
|
//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
|
//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);
|
pkpy_vm pkpy_new_vm(bool use_stdio, bool enable_os);
|
||||||
bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source);
|
bool pkpy_vm_exec(pkpy_vm, const char* source);
|
||||||
void pkpy_vm_destroy(pkpy_vm vm);
|
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);
|
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_function(pkpy_vm, pkpy_function);
|
||||||
bool pkpy_push_int(pkpy_vm, int);
|
bool pkpy_push_int(pkpy_vm, int);
|
||||||
bool pkpy_push_float(pkpy_vm, double);
|
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_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
|
//first push callable you want to call
|
||||||
//then push the arguments to send
|
//then push the arguments to send
|
||||||
//argc is the number of arguments that was pushed (not counting the callable)
|
//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)
|
//first push the object the method belongs to (self)
|
||||||
//then push the callable you want to call
|
|
||||||
//then push the the argments
|
//then push the the argments
|
||||||
//argc is the number of arguments that was pushed (not counting the callable or self)
|
//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
|
//we will break with the lua api here
|
||||||
//lua uses 1 as the index to the first pushed element for all of these functions
|
//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
|
//but we will start counting at zero to match python
|
||||||
//we will allow negative numbers to count backwards from the top
|
//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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
262
c_bindings/test.c
Normal file
262
c_bindings/test.c
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#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);
|
||||||
|
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;
|
||||||
|
}
|
49
c_bindings/test_answers.txt
Normal file
49
c_bindings/test_answers.txt
Normal file
@ -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 "<c-bound>", 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
|
24
run_c_binding_test.sh
Normal file
24
run_c_binding_test.sh
Normal file
@ -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
|
||||||
|
|
@ -117,6 +117,7 @@ struct CVirtualStack {
|
|||||||
PyObject** begin() { return _begin + offset; }
|
PyObject** begin() { return _begin + offset; }
|
||||||
PyObject** end() { return _sp; }
|
PyObject** end() { return _sp; }
|
||||||
void clear() { _sp = _begin + offset;}
|
void clear() { _sp = _begin + offset;}
|
||||||
|
int remaining() { return MAX_SIZE - (_sp - _begin); }
|
||||||
|
|
||||||
size_t store() { size_t ret = offset; offset = _sp - _begin; return ret; }
|
size_t store() { size_t ret = offset; offset = _sp - _begin; return ret; }
|
||||||
void restore(size_t stored) { offset = stored; }
|
void restore(size_t stored) { offset = stored; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user