diff --git a/docs/2_0_0.md b/docs/2_0.md similarity index 99% rename from docs/2_0_0.md rename to docs/2_0.md index eff995ae..ec717008 100644 --- a/docs/2_0_0.md +++ b/docs/2_0.md @@ -1,6 +1,6 @@ --- icon: log -title: 'Upgrade to v2.0.0' +title: 'Upgrade to v2.0' order: 25 --- diff --git a/docs/C-API/call.md b/docs/C-API/call.md deleted file mode 100644 index e52fe926..00000000 --- a/docs/C-API/call.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Call -icon: dot -order: 6 ---- - -### `bool pkpy_vectorcall(pkpy_vm*, int argc)` - -Wraps `vm->vectorcall(argc)`. This function is used to call a function with a fixed number of arguments. The arguments are popped from the stack. The return value is pushed onto the stack. - -1. First push the function to call. -2. Push `self` argument if it is a method call. Otherwise, call `pkpy_push_null`. -3. Push arguments from left to right. - -!!! -Unlike lua, a python function always returns a value. -If the function returns `void`, it will push `None` onto the stack. -You can call `pkpy_pop_top` to discard the return value. -!!! \ No newline at end of file diff --git a/docs/C-API/error.md b/docs/C-API/error.md deleted file mode 100644 index ca38a353..00000000 --- a/docs/C-API/error.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Error Handling -icon: dot -order: 5 ---- - -#### `bool pkpy_clear_error(pkpy_vm*, char** message)` - -+ 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`. - -!!! -You are responsible for freeing `message`. -!!! - -#### `bool pkpy_check_error(pkpy_vm*)` - -Return true if the vm is currently in an error state. - -#### `bool pkpy_error(pkpy_vm*, const char* name, pkpy_CString message)` - -Set the error state of the vm. It is almost equivalent to `raise` in python. diff --git a/docs/C-API/index.yml b/docs/C-API/index.yml index 15195ecd..eb2d085b 100644 --- a/docs/C-API/index.yml +++ b/docs/C-API/index.yml @@ -1,3 +1,3 @@ label: C-API icon: code -order: 1 \ No newline at end of file +order: 15 \ No newline at end of file diff --git a/docs/C-API/specials.md b/docs/C-API/specials.md deleted file mode 100644 index ce6f3bf9..00000000 --- a/docs/C-API/specials.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Specials -icon: dot -order: 6 ---- - -+ `void pkpy_free(void* p)` - - Wraps `free(p)` in C++. - -+ `pkpy_CString pkpy_string(const char*)` - - Construct a `pkpy_CString` from a null-terminated C string. - -+ `pkpy_CName pkpy_name(const char*)` - - Construct a `pkpy_CName` from a null-terminated C string. You should cache the result of this function if you are going to use it multiple times. - -+ `pkpy_CString pkpy_name_to_string(pkpy_CName)` - - Convert a `pkpy_CName` to a `pkpy_CString`. \ No newline at end of file diff --git a/docs/bindings.md b/docs/bindings.md index b1600bf6..f3dfc697 100644 --- a/docs/bindings.md +++ b/docs/bindings.md @@ -6,294 +6,13 @@ order: 18 In order to use a C/C++ library in python, you need to write bindings for it. -pkpy uses an universal signature to wrap a function pointer as a python function or method that can be called in python code, i.e `NativeFuncC`. +pkpy uses an universal signature to wrap a C function pointer as a python function or method, i.e `py_CFunction`. -```cpp -typedef PyVar (*NativeFuncC)(VM*, ArgsView); +```c +typedef bool (*py_CFunction)(int argc, py_Ref argv); ``` -+ The first argument is the pointer of `VM` instance. -+ The second argument is an array-like object indicates the arguments list. You can use `[]` operator to get the element and call `size()` to get the length of the array. -+ The return value is a `PyVar`, which should not be `nullptr`. If there is no return value, return `vm->None`. ++ `argc` is the number of arguments passed to the function. ++ `argv` is the pointer to the first argument. -## Bind a function or method - -Use `vm->bind` to bind a function or method. - -+ `PyVar bind(PyVar, const char* sig, NativeFuncC)` -+ `PyVar bind(PyVar, const char* sig, const char* docstring, NativeFuncC)` - -```cpp - -vm->bind(obj, "add(a: int, b: int) -> int", [](VM* vm, ArgsView args){ - int a = py_cast(vm, args[0]); - int b = py_cast(vm, args[1]); - return py_var(vm, a + b); -}); - -// or you can provide a docstring -vm->bind(obj, - "add(a: int, b: int) -> int", - "add two integers", [](VM* vm, ArgsView args){ - int a = py_cast(vm, args[0]); - int b = py_cast(vm, args[1]); - return py_var(vm, a + b); -}); -``` - -### How to capture something - -By default, the lambda being bound is a C function pointer, -you cannot capture anything! The following example does not compile. - -```cpp -int x = 1; -vm->bind(obj, "f() -> int", [x](VM* vm, ArgsView args){ - // error: cannot capture 'x' - return py_var(vm, x); -}); -``` - -I do not encourage you to capture something in a lambda being bound -because: -1. Captured lambda runs slower and causes "code-bloat". -2. Captured values are unsafe, especially for `PyVar` as they could leak by accident. - -However, there are 3 ways to capture something when you really need to. -The most safe and elegant way is to subclass `VM` and add a member variable. - -```cpp -class YourVM : public VM{ -public: - int x; - YourVM() : VM() {} -}; - -int main(){ - YourVM* vm = new YourVM(); - vm->x = 1; - vm->bind(obj, "f() -> int", [](VM* _vm, ArgsView args){ - // do a static_cast and you can get any extra members of YourVM - YourVM* vm = static_cast(_vm); - return py_var(vm, vm->x); - }); - return 0; -} -``` - -The 2nd way is to use `vm->bind`'s last parameter `userdata`, you can store an `pkpy::any` object. -And use `lambda_get_userdata(args.begin())` to get it inside the lambda body. - -```cpp -int x = 1; -vm->bind(obj, "f() -> int", [](VM* vm, ArgsView args){ - // get the userdata - int x = lambda_get_userdata(args.begin()); - return py_var(vm, x); -}, x); // capture x -``` - -## Bind a class or struct - -Assume you have a struct `Point` declared as follows. - -```cpp -struct Point{ - int x; - int y; -} -``` - -You can create a `test` module and use `vm->register_user_class<>` to bind the class to the test module. - -```cpp -int main(){ - VM* vm = new VM(); - PyObject* mod = vm->new_module("test"); - vm->register_user_class(mod, "Point", - [](VM* vm, PyVar mod, PyVar type){ - // wrap field x - vm->bind_field(type, "x", &Point::x); - // wrap field y - vm->bind_field(type, "y", &Point::y); - - // __init__ method - vm->bind(type, "__init__(self, x, y)", [](VM* vm, ArgsView args){ - Point& self = _py_cast(vm, args[0]); - self.x = py_cast(vm, args[1]); - self.y = py_cast(vm, args[2]); - return vm->None; - }); - }); - - // use the Point class - vm->exec("import test"); - vm->exec("a = test.Point(1, 2)"); - vm->exec("print(a.x)"); // 1 - vm->exec("print(a.y)"); // 2 - - delete vm; - return 0; -} -``` - -### Handle gc for container types - -If your custom type stores `PyVar` in its fields, you need to handle gc for them. - -```cpp -struct Container{ - PyVar a; - std::vector b; - // ... -} -``` - -Add a magic method `_gc_mark(VM*) const` to your custom type. - -```cpp -struct Container{ - PyVar a; - std::vector b; - // ... - - void _gc_mark(VM* vm) const{ - // mark a - vm->obj_gc_mark(a); - - // mark elements in b - for(PyVar obj : b){ - vm->obj_gc_mark(obj); - } - } -} -``` - -For global objects, use the callback in `vm->heap`. -```cpp -void (*_gc_marker_ex)(VM*) = nullptr; -``` -It will be invoked before a GC starts. So you can mark objects inside the callback to keep them alive. - -## Others - -For some magic methods, we provide specialized binding function. -They do not take universal function pointer as argument. -You need to provide the detailed `Type` object and the corresponding function pointer. - -```cpp -PyVar f_add(VM* vm, PyVar lhs, PyVar rhs){ - int a = py_cast(vm, lhs); - int b = py_cast(vm, rhs); - return py_var(vm, a + b); -} - -vm->bind__add__(vm->tp_int, f_add); -``` - -This specialized binding function has optimizations and result in better performance when calling from python code. - -For example, `vm->bind__add__` is preferred over `vm->bind_func(type, "__add__", 2, f_add)`. - - -## Further reading - -See [random.cpp](https://github.com/pocketpy/pocketpy/blob/main/src/random.cpp) for an example used by `random` module. - -See [collections.cpp](https://github.com/pocketpy/pocketpy/blob/main/src/collections.cpp) for a modern implementation of `collections.deque`. - -## Reuse Lua bindings - -pkpy provides a lua bridge to reuse lua bindings. -It allows you to run lua code and call lua functions in python -by embedding a lua virtual machine. - -Add `lua_bridge.hpp` and `lua_bridge.cpp` in [3rd/lua_bridge](https://github.com/pocketpy/pocketpy/tree/main/3rd/lua_bridge) to your project. -Make sure `lua.h`, `lualib.h` and `lauxlib.h` are in your include path -because `lua_bridge.hpp` needs them. - -The lua bridge is based on lua 5.1.5 for maximum compatibility. -lua 5.2 or higher should also work. - -### Setup - -Use `initialize_lua_bridge(VM*, lua_State*)` to initialize the lua bridge. -This creates a new module `lua` in your python virtual machine. - -You can use `lua.dostring` to execute lua code and get the result. -And use `lua.Table()` to create a lua table. -A `lua.Table` instance in python is a dict-like object which provides a bunch of -magic methods to access the underlying lua table. - -```python -class Table: - def keys(self) -> list: - """Return a list of keys in the table.""" - - def values(self) -> list: - """Return a list of values in the table.""" - - def items(self) -> list[tuple]: - """Return a list of (key, value) pairs in the table.""" - - def __len__(self) -> int: - """Return the length of the table.""" - - def __contains__(self, key) -> bool: - """Return True if the table contains the key.""" - - def __getitem__(self, key): ... - def __setitem__(self, key, value): ... - def __delitem__(self, key): ... - def __getattr__(self, key): ... - def __setattr__(self, key, value): ... - def __delattr__(self, key): ... -``` - -Only basic types can be passed between python and lua. -The following table shows the type mapping. -If you pass an unsupported type, an exception will be raised. - -| Python type | Lua type | Allow create in Python? | Reference? | -| ----------- | -------- | ---------------------- | --------- | -| `None` | `nil` | YES | NO | -| `bool` | `boolean` | YES | NO | -| `int` | `number` | YES | NO | -| `float` | `number` | YES | NO | -| `str` | `string` | YES | NO | -| `tuple` | `table` | YES | NO | -| `list` | `table` | YES | NO | -| `dict` | `table` | YES | NO | -| `lua.Table` | `table` | YES | YES | -| `lua.Function`| `function`| NO | YES | - -### Example -```cpp -#include "lua_bridge.hpp" - -using namespace pkpy; - -int main(){ - VM* vm = new VM(); - - // create lua state - lua_State* L = luaL_newstate(); - luaL_openlibs(L); - - // initialize lua bridge - initialize_lua_bridge(vm, L); - - // dostring to get _G - vm->exec("import lua"); - vm->exec("g = lua.dostring('return _G')"); - - // create a table - vm->exec("t = lua.Table()"); - vm->exec("t.a = 1"); - vm->exec("t.b = 2"); - - // call lua function - vm->exec("g.print(t.a + t.b)"); // 3 - - return 0; -} -``` \ No newline at end of file +If successful, the function should return `true` and set the return value in `py_retval()`. In case there is no return value, you should use `py_newnone(py_retval())`. +If an error occurs, the function should raise an exception and return `false`. diff --git a/docs/cheatsheet.md b/docs/cheatsheet.md deleted file mode 100644 index d9896949..00000000 --- a/docs/cheatsheet.md +++ /dev/null @@ -1,333 +0,0 @@ ---- -icon: log -title: 'Cheatsheet' -order: 22 ---- - -## Basics - -Setup pocketpy - -```cpp -#include "pocketpy.h" -using namespace pkpy; -``` - -Create a python virtual machine - -```cpp -VM* vm = new VM(); -``` - -Dispose a python virtual machine - -```cpp -delete vm; -``` - -Execute a source string - -```cpp -vm->exec("print('Hello!')"); -``` - -Evaluate a source string - -```cpp -PyVar obj = vm->eval("123"); -std::cout << py_cast(vm, obj); // 123 -``` - -Compile a source string into a code object - -```cpp -CodeObject_ co = vm->compile("print('Hello!')", "main.py", EXEC_MODE); -``` - -Execute a compiled code object - -```cpp -try{ - vm->_exec(co); // may throw -}catch(TopLevelException e){ - std::cerr << e.summary() << std::endl; -} -``` - -## Interop with native types - -Create primitive objects - -```cpp -PyVar obj; -obj = py_var(vm, 1); // create a int -obj = py_var(vm, 1.0); // create a float -obj = py_var(vm, "123"); // create a string -obj = py_var(vm, true); // create a bool -``` - -Create a tuple object - -```cpp -// obj = (1, 1.0, '123') -Tuple t(3); -t[0] = py_var(vm, 1); -t[1] = py_var(vm, 1.0); -t[2] = py_var(vm, "123"); -PyVar obj = py_var(vm, std::move(t)); -``` - -Create a list object - -```cpp -// obj = [1, 1.0, '123'] -List t; -t.push_back(py_var(vm, 1)); -t.push_back(py_var(vm, 1.0)); -t.push_back(py_var(vm, "123")); -PyVar obj = py_var(vm, std::move(t)); -``` - -Create a dict object - -```cpp -// obj = {'x': 1, 'y': '123'} -Dict d(vm); -d.set(py_var(vm, "x"), py_var(vm, 1)); -d.set(py_var(vm, "y"), py_var(vm, "123")); -PyVar obj = py_var(vm, std::move(d)); -``` - -Get native types from python objects - -```cpp -PyVar obj; -i64 a = py_cast(vm, obj); -f64 b = py_cast(vm, obj); -Str& c = py_cast(vm, obj); // reference cast -bool d = py_cast(vm, obj); - -Tuple& e = py_cast(vm, obj); // reference cast -List& f = py_cast(vm, obj); // reference cast -Dict& g = py_cast(vm, obj); // reference cast -``` - -Get native types without type checking - -```cpp -// unsafe version 1 (for int object you must use `_py_cast`) -i64 a = _py_cast(vm, obj); -f64 b = _py_cast(vm, obj); -Tuple& c = _py_cast(vm, obj); -// unsafe version 2 (for others, you can also use `PK_OBJ_GET` macro) -Str& a_ = PK_OBJ_GET(Str, obj); -List& b_ = PK_OBJ_GET(List, obj); -Tuple& c_ = PK_OBJ_GET(Tuple, obj); -``` - -## Access python types - -Access built-in python types - -```cpp -PyVar int_t = vm->_t(VM::tp_int); -PyVar float_t = vm->_t(VM::tp_float); -PyVar object_t = vm->_t(VM::tp_object); -PyVar tuple_t = vm->_t(VM::tp_tuple); -PyVar list_t = vm->_t(VM::tp_list); -``` - -Access user registered types - -```cpp -Type voidp_t = vm->_tp_user(); -``` - -Check if an object is a python type - -```cpp -PyVar obj; -bool ok = is_type(obj, VM::tp_int); // check if obj is an int -``` - -Get the type of a python object - -```cpp -PyVar obj = py_var(vm, 1); -PyVar t = vm->_t(obj); // -Type type = vm->_tp(obj); // VM::tp_int -``` - -Convert a type object into a type index - -```cpp -PyVar int_t = vm->_t(VM::tp_int); -Type t = PK_OBJ_GET(Type, int_t); -// t == VM::tp_int -``` - -## Access attributes - -Check an object supports attribute access - -```cpp -PyVar obj; -bool ok = !is_tagged(obj) && obj->is_attr_valid(); -``` - -```python -class MyClass: - def __init__(self, x, y): - self.x = x - self.y = y - - def sum(self): - return self.x + self.y -``` - -Get and set attributes - -```cpp -PyVar obj = vm->exec("MyClass(1, 2)"); -PyVar x = vm->getattr(obj, "x"); // obj.x -vm->setattr(obj, "x", py_var(vm, 3)); // obj.x = 3 -``` - -## Call python functions - -```python -def add(a, b): - return a + b -``` - -Call a function - -```cpp -PyVar f_add = vm->eval("add"); -PyVar ret = vm->call(f_add, py_var(vm, 1), py_var(vm, 2)); -std::cout << py_cast(vm, ret); // 3 -``` - -Call a method - -```cpp -PyVar obj = vm->exec("MyClass(1, 2)"); -PyVar ret = vm->call_method(obj, "sum"); -std::cout << CAST(i64, ret); // 3 -``` - -Cache the name of a function or method to avoid string-based lookup - -```cpp -// cache the name "add" to avoid string-based lookup -const static StrName m_sum("sum"); -PyVar ret = vm->call_method(obj, m_sum); -``` - -## Special operations - -Compare two python objects - -```cpp -PyVar obj1 = py_var(vm, 1); -PyVar obj2 = py_var(vm, 2); -bool ok = vm->py_eq(obj1, obj2); -``` - -Convert a python object to string - -```cpp -PyVar obj = py_var(vm, 123); -std::cout << vm->py_str(obj); // 123 -``` - -Get the string representation of a python object - -```cpp -PyVar obj = py_var(vm, "123"); -std::cout << vm->py_repr(obj); // "'123'" -``` - -Get the JSON representation of a python object - -```cpp -PyVar obj = py_var(vm, 123); -std::cout << vm->py_json(obj); // "123" -``` - -Get the hash value of a python object - -```cpp -PyVar obj = py_var(vm, 1); -i64 h = vm->py_hash(obj); // 1 -``` - -Get the iterator of a python object - -```cpp -PyVar obj = vm->eval("range(3)"); -PyVar iter = vm->py_iter(obj); -``` - -Get the next item of an iterator - -```cpp -PyVar obj = vm->py_next(iter); -if(obj == vm->StopIteration){ - // end of iteration -}else{ - // process obj -} -``` - -Convert a python iterable to a list - -```cpp -PyVar obj = vm->eval("range(3)"); -List list = vm->py_list(obj); -``` - -## Bindings - -Bind a native function - -```cpp -vm->bind(obj, "add(a: int, b: int) -> int", [](VM* vm, ArgsView args){ - int a = py_cast(vm, args[0]); - int b = py_cast(vm, args[1]); - return py_var(vm, a + b); -}); -``` - -Bind a property - -```cpp - // getter and setter of property `x` - vm->bind_property(type, "x: int", - [](VM* vm, ArgsView args){ - Point& self = PK_OBJ_GET(Point, args[0]); - return VAR(self.x); - }, - [](VM* vm, ArgsView args){ - Point& self = PK_OBJ_GET(Point, args[0]); - self.x = py_cast(vm, args[1]); - return vm->None; - }); -``` - -## Modules - -Create a source module - -```cpp -vm->_lazy_modules["test"] = "pi = 3.14"; -// import test -// print(test.pi) # 3.14 -``` - -Create a native module - -```cpp -PyObject* mod = vm->new_module("test"); -vm->setattr(mod, "pi", py_var(vm, 3.14)); -``` diff --git a/docs/coding_style_guide.md b/docs/coding_style_guide.md deleted file mode 100644 index e5974eec..00000000 --- a/docs/coding_style_guide.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -icon: book -order: -5 -label: Coding Style Guide ---- - -# Coding Style Guide - - -## For Python - -Use [PEP-8](https://www.python.org/dev/peps/pep-0008/) as the coding style guide. - -## For C++ - -### Naming rules - -For class names, always use **PascalCase** - -```cpp -// Correct -class FooBar {}; - -// Wrong -class fooBar {}; -class foo_bar {}; -``` - -For function and methods, use **snake_case** - -```cpp -// Correct -int test_func(int x) { return x+1; } - -// Wrong -int TestFunc(int x) { return x+1; } -int testFunc(int x) { return x+1; } -``` - -For special python objects, use the same name as in python. - -```cpp -auto x = vm->None; -vm->SyntaxError(...); -vm->TypeError(...); -vm->call(obj, __repr__); -``` - -For global constants, use **k** prefix with **PascalCase** - -```cpp -const int kMaxCount = 10; -const float kMinValue = 1.0; -``` - -For macros, use **SNAKE_CASE** - -```cpp -#define FOO_BAR 1 -#define TEST(x) x+1 -``` - -### Access control - -Please use python style access control. - -We do not recommend to use C++ keywords such as `private` or `public` to achieve access control. Also do not write any trivial setter/getter. - -Use a single `_` as prefix to indicate a function or variable is for internal use. - -```cpp -class FooBar { -public: - int _count; - int inc() { _count+=1; } - void _clear() { _count=0; } -} -``` - -`_` prefix is just a warning to remind you to use such members carefully. - -It does not forbid users to access internal members. - -### Use compact style - -Try to make the code compact if it does not affect readability. - -```cpp -// Correct -if(x == 1) break; - -// Wrong -if(x == 1){ - break; -} -``` - -### For `std::shared_ptr` - -Use a `_` suffix to indicate a type is a shared pointer. - -```cpp -using CodeObject_ = std::shared_ptr; -CodeObject_ co = std::make_shared(); -``` - diff --git a/docs/features/long.md b/docs/features/long.md deleted file mode 100644 index 90007f1e..00000000 --- a/docs/features/long.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -icon: dot -title: Arbitrary Sized Integers ---- - -Unlike cpython, pkpy's `int` is of limited precision (64-bit). - -For arbitrary sized integers, we provide a builtin `long` type, just like python2's `long`. -`long` is implemented via pure python in [_long.py](https://github.com/pocketpy/pocketpy/blob/main/python/_long.py). - -### Create a long object - -You can use `L` suffix to create a `long` literal from a decimal literal. -Also, you can use `long()` function to create a `long` object from a `int` object or a `str` object. - -```python -a = 1000L -b = long(1000) -c = long('1000') -assert a == b == c -``` - -```python -a = 2L # use `L` suffix to create a `long` object -print(a ** 1000) -# 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376L -``` diff --git a/docs/gsoc/index.yml b/docs/gsoc/index.yml index 0fd22c4b..50a8d7e9 100644 --- a/docs/gsoc/index.yml +++ b/docs/gsoc/index.yml @@ -1,2 +1,2 @@ -order: 19 +order: 0 label: "GSoC" \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index bcd5984c..449c6ce7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,12 +5,11 @@ label: Welcome # Welcome to pocketpy -pkpy is a lightweight(~15K LOC) Python interpreter for game scripting, built on C++17 with STL. +pkpy is a lightweight(~15K LOC) Python 3.x interpreter for game scripting, written in C11. It aims to be an alternative to lua for game scripting, with elegant syntax, powerful features and competitive performance. pkpy is extremely easy to embed via a single header file `pocketpy.h`, without external dependencies. -> **Caution**: pocketpy should not be your first C++ project. Please learn C++ programming, compiling, linking, and debugging before working with pocketpy. There are many resources for this on the net. ## What it looks like diff --git a/docs/quick-start/installation.md b/docs/quick-start/installation.md index d65125ce..7c7400b5 100644 --- a/docs/quick-start/installation.md +++ b/docs/quick-start/installation.md @@ -19,11 +19,6 @@ In your CMakelists.txt, add the following lines: ```cmake add_subdirectory(pocketpy) target_link_libraries( pocketpy) - -if(EMSCRIPTEN) - # exceptions must be enabled for emscripten - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fexceptions") -endif() ``` See [CMakeLists.txt](https://github.com/pocketpy/pocketpy/blob/main/CMakeLists.txt) for details. @@ -34,14 +29,9 @@ It is safe to use `main` branch in production if CI badge is green. To compile it with your project, these flags must be set: -+ `--std=c++17` flag must be set -+ RTTI must be enabled -+ Exception must be enabled ++ `--std=c11` flag must be set + For MSVC, `/utf-8` flag must be set -For emscripten, you must enable exceptions to make pocketpy work properly. -See https://emscripten.org/docs/porting/exceptions.html. - ### Get prebuilt binaries We have prebuilt binaries, @@ -77,41 +67,64 @@ You can download an artifact there which contains the following files. ### Example -```cpp +```c #include "pocketpy.h" +#include -using namespace pkpy; +static bool int_add(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + PY_CHECK_ARG_TYPE(0, tp_int); + PY_CHECK_ARG_TYPE(1, tp_int); + py_i64 a = py_toint(py_arg(0)); + py_i64 b = py_toint(py_arg(1)); + py_newint(py_retval(), a + b); + return true; +} -int main(){ - // Create a virtual machine - VM* vm = new VM(); +int main() { + // Initialize pocketpy + py_initialize(); // Hello world! - vm->exec("print('Hello world!')"); + bool ok = py_exec("print('Hello world!')", "", EXEC_MODE, NULL); + if(!ok) goto __ERROR; - // Create a list - vm->exec("a = [1, 2, 3]"); + // Create a list: [1, 2, 3] + py_Ref r0 = py_getreg(0); + py_newlistn(r0, 3); + py_newint(py_list_getitem(r0, 0), 1); + py_newint(py_list_getitem(r0, 1), 2); + py_newint(py_list_getitem(r0, 2), 3); // Eval the sum of the list - PyVar result = vm->eval("sum(a)"); - std::cout << "Sum of the list: "<< py_cast(vm, result) << std::endl; // 6 + py_Ref f_sum = py_getbuiltin(py_name("sum")); + py_push(f_sum); + py_pushnil(); + py_push(r0); + ok = py_vectorcall(1, 0); + if(!ok) goto __ERROR; - // Bindings - vm->bind(vm->_main, "add(a: int, b: int)", - [](VM* vm, ArgsView args){ - int a = py_cast(vm, args[0]); - int b = py_cast(vm, args[1]); - return py_var(vm, a + b); - }); + printf("Sum of the list: %d\n", (int)py_toint(py_retval())); // 6 - // Call the function - PyVar f_add = vm->_main->attr("add"); - result = vm->call(f_add, py_var(vm, 3), py_var(vm, 7)); - std::cout << "Sum of 2 variables: "<< py_cast(vm, result) << std::endl; // 10 + // Bind native `int_add` as a global variable + py_newnativefunc(r0, int_add); + py_setglobal(py_name("add"), r0); - // Dispose the virtual machine - delete vm; + // Call `add` in python + ok = py_exec("add(3, 7)", "", EVAL_MODE, NULL); + if(!ok) goto __ERROR; + + py_i64 res = py_toint(py_retval()); + printf("Sum of 2 variables: %d\n", (int)res); // 10 + + py_finalize(); return 0; + +__ERROR: + py_printexc(); + py_finalize(); + return 1; +} ``` ### Overview