This commit is contained in:
Bobby Lucero 2024-04-24 20:54:23 -04:00
parent 936d8addf9
commit 16733fdb0e
326 changed files with 91177 additions and 4 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "dependencies/pocketpy"]
path = dependencies/pocketpy
url = https://github.com/pocketpy/pocketpy.git

0
dependencies/__init__.py vendored Normal file
View File

@ -1 +0,0 @@
Subproject commit 8361c7d4a181bf94931b0f1841032d58cdeee481

2
dependencies/pocketpy/.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

View File

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: blueloveTH
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: blueloveTH
---
Thanks for taking the time to fill out a bug report!
Please provide a descriptive title above and fill in the following fields.
### Bug description
A clear and concise description of what the bug is.
You can paste sources here that may cause the error.
### Steps to reproduce
- upload the full stacktrace from your own code line to the error line
- upload a minimum reproducible example
- additional information to help us reproduce the error
### Environment information
You need to provide these strings:
- pocketpy's version
- platform string (win32/linux/darwin/emscripten/android/ios)
- 32-bit or 64-bit
### Additional context
Add any other context about the error.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature]"
labels: enhancement
assignees: blueloveTH
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,157 @@
name: build
on:
push:
paths-ignore:
- 'docs/**'
- 'web/**'
- '**.md'
pull_request:
paths-ignore:
- 'docs/**'
- 'web/**'
- '**.md'
jobs:
build_win32_amalgamated:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: ilammy/msvc-dev-cmd@v1
- name: Compile
shell: powershell
run: |
python amalgamate.py
cd amalgamated
cl.exe /std:c++17 /EHsc /utf-8 /O2 /I. /DPK_ENABLE_OS=1 main.cpp /link /out:pkpy.exe
- uses: actions/upload-artifact@v3
with:
path: amalgamated/pkpy.exe
build_win32:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: ilammy/msvc-dev-cmd@v1
- name: Compile
shell: bash
run: |
mkdir -p output/windows/x86_64
python cmake_build.py
cp main.exe output/windows/x86_64
cp pocketpy.dll output/windows/x86_64
- uses: actions/upload-artifact@v3
with:
path: output
- name: Unit Test
run: python scripts/run_tests.py
- name: Benchmark
run: python scripts/run_tests.py benchmark
build_linux:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Setup Clang
uses: egor-tensin/setup-clang@v1
with:
version: 15
platform: x64
- name: Install libc++
run: sudo apt-get install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15 libclang-rt-15-dev
- name: Unit Test with Coverage
run: bash run_tests.sh
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: .coverage
if: github.ref == 'refs/heads/main'
- name: Compile
run: |
mkdir -p output/linux/x86_64
python cmake_build.py
cp main output/linux/x86_64
cp libpocketpy.so output/linux/x86_64
env:
CXX: clang++
CC: clang
- uses: actions/upload-artifact@v3
with:
path: output
- name: Benchmark
run: python scripts/run_tests.py benchmark
- name: C Binding Test
run: bash run_c_binding_test.sh
build_linux_x86:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Alpine Linux for aarch64
uses: jirutka/setup-alpine@v1
with:
arch: x86
packages: gcc g++ make cmake libc-dev linux-headers python3
- name: Build and Test
run: |
uname -m
python cmake_build.py
python scripts/run_tests.py
python scripts/run_tests.py benchmark
shell: alpine.sh --root {0}
build_darwin:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Compile and Test
run: |
python cmake_build.py
python scripts/run_tests.py
- name: Benchmark
run: python scripts/run_tests.py benchmark
- run: |
python amalgamate.py
cd plugins/macos/pocketpy
mkdir -p output/macos
xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
cp -r build/Release/pocketpy.bundle output/macos
- uses: actions/upload-artifact@v3
with:
path: plugins/macos/pocketpy/output
build_android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r23
local-cache: false
add-to-path: false
- name: Compile Shared Library
run: |
bash build_android.sh arm64-v8a
bash build_android.sh armeabi-v7a
bash build_android.sh x86_64
mkdir -p output/android/arm64-v8a
mkdir -p output/android/armeabi-v7a
mkdir -p output/android/x86_64
cp build/android/arm64-v8a/libpocketpy.so output/android/arm64-v8a
cp build/android/armeabi-v7a/libpocketpy.so output/android/armeabi-v7a
cp build/android/x86_64/libpocketpy.so output/android/x86_64
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
- uses: actions/upload-artifact@v3
with:
path: output
build_ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Compile Frameworks
run: |
git clone https://github.com/leetal/ios-cmake --depth 1 ~/ios-cmake
bash build_ios.sh
mkdir -p output/ios
cp -r build/pocketpy.xcframework output/ios/pocketpy.xcframework
- uses: actions/upload-artifact@v3
with:
path: output

View File

@ -0,0 +1,41 @@
name: website
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
###################################################
- uses: actions/setup-node@v3.1.1
- name: Retype build
run: |
cd docs
npm install retypeapp --global
retype build
###################################################
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v12
with:
version: 3.1.25
actions-cache-folder: 'emsdk-cache'
- name: Compile
run: |
bash build_web.sh
mv web docs/.retype/static
###################################################
- uses: crazy-max/ghaction-github-pages@v3
with:
target_branch: gh-pages
build_dir: docs/.retype
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: github.ref == 'refs/heads/main'

30
dependencies/pocketpy/.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
__pycache__/
.vscode
.ipynb_checkpoints
.DS_Store
.coverage
.idea
gmon.out
gprof.txt
amalgamated
web/lib
*.a
*.so
*.dll
*.dylib
plugins/unity/
plugins/macos/pocketpy/pocketpy.*
main.exe
main.obj
pocketpy.exp
pocketpy.lib
APPS
build
main
pocketpy.dSYM
libpocketpy.dylib.dSYM/
main.dSYM/

View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.10)
project(cjson)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
include_directories(include)
include_directories(../../include)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_library(
cjson
STATIC
src/cJSON.c
src/cJSONw.cpp
)

10
dependencies/pocketpy/3rd/cjson/LICENSE vendored Normal file
View File

@ -0,0 +1,10 @@
MIT License
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Sofware without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,300 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 16
#include <stddef.h>
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* Create a string where valuestring references a string so
* it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
* they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
cJSON_Invalid\
)
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,8 @@
#include "cJSON.h"
#include "pocketpy/pocketpy.h"
namespace pkpy {
void add_module_cjson(VM* vm);
} // namespace pkpy

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
#include "cJSONw.hpp"
namespace pkpy{
static cJSON* convert_python_object_to_cjson(PyObject* obj, VM* vm);
static PyObject* convert_cjson_to_python_object(const cJSON * const item, VM* vm);
template<typename T>
static cJSON* convert_list_to_cjson(const T& list, VM* vm){
cJSON *cjson_list = cJSON_CreateArray();
for(auto& element : list){
cJSON_AddItemToArray(cjson_list, convert_python_object_to_cjson(element, vm));
}
return cjson_list;
}
static cJSON* covert_dict_to_cjson(const Dict& dict, VM* vm){
cJSON *cjson_object = cJSON_CreateObject();
dict.apply([&](PyObject* key, PyObject* val){
cJSON_AddItemToObject(cjson_object, CAST(Str&, key).c_str(), convert_python_object_to_cjson(val, vm));
});
return cjson_object;
}
static cJSON* convert_python_object_to_cjson(PyObject* obj, VM* vm){
if(obj == vm->None) return cJSON_CreateNull();
Type obj_t = vm->_tp(obj);
switch(obj_t){
case VM::tp_int.index: cJSON_CreateNumber(_CAST(i64, obj));
case VM::tp_float.index: cJSON_CreateNumber(_CAST(f64, obj));
case VM::tp_bool.index: cJSON_CreateBool(obj == vm->True);
case VM::tp_str.index: cJSON_CreateString(_CAST(Str&, obj).c_str());
case VM::tp_dict.index: return covert_dict_to_cjson(_CAST(Dict&, obj), vm);
case VM::tp_list.index: return convert_list_to_cjson<List>(_CAST(List&, obj), vm);
case VM::tp_tuple.index: return convert_list_to_cjson<Tuple>(_CAST(Tuple&, obj), vm);
default: break;
}
vm->TypeError(_S("unrecognized type ", _type_name(vm, obj_t).escape()));
PK_UNREACHABLE()
}
static PyObject* convert_cjson_to_list(const cJSON * const item, VM* vm){
List output;
cJSON *element = item->child;
while(element != NULL){
output.push_back(convert_cjson_to_python_object(element, vm));
element = element->next;
}
return VAR(std::move(output));
}
static PyObject* convert_cjson_to_dict(const cJSON* const item, VM* vm){
Dict output(vm);
cJSON *child = item->child;
while(child != NULL){
const char* key = child->string;
const cJSON *child_value = cJSON_GetObjectItemCaseSensitive(item, key);
output.set(VAR(key), convert_cjson_to_python_object(child_value, vm));
child = child->next;
}
return VAR(std::move(output));
}
static PyObject* convert_cjson_to_python_object(const cJSON * const item, VM* vm)
{
if (cJSON_IsString(item))
{
return VAR(Str(item->valuestring));
}
else if (cJSON_IsNumber(item)){
if(item->valuedouble != item->valueint){
return VAR(item->valuedouble);
}
return VAR(item->valueint);
}
else if (cJSON_IsBool(item)){
return item->valueint!=0 ? vm->True : vm->False;
}
else if (cJSON_IsNull(item)){
return vm->None;
}
else if (cJSON_IsArray(item)){
return convert_cjson_to_list(item, vm);
}
else if (cJSON_IsObject(item)){
return convert_cjson_to_dict(item, vm);
}
return vm->None;
}
void add_module_cjson(VM* vm){
PyObject* mod = vm->new_module("cjson");
PK_LOCAL_STATIC cJSON_Hooks hooks;
hooks.malloc_fn = pool64_alloc;
hooks.free_fn = pool64_dealloc;
cJSON_InitHooks(&hooks);
vm->bind_func<1>(mod, "loads", [](VM* vm, ArgsView args){
std::string_view sv;
if(is_type(args[0], vm->tp_bytes)){
sv = PK_OBJ_GET(Bytes, args[0]).sv();
}else{
sv = CAST(Str&, args[0]).sv();
}
cJSON *json = cJSON_ParseWithLength(sv.data(), sv.size());
if(json == NULL){
const char* start = cJSON_GetErrorPtr();
const char* end = start;
while(*end != '\0' && *end != '\n') end++;
vm->IOError(_S("cjson: ", std::string_view(start, end-start)));
}
PyObject* output = convert_cjson_to_python_object(json, vm);
cJSON_Delete(json);
return output;
});
vm->bind_func<1>(mod, "dumps", [](VM* vm, ArgsView args) {
return vm->py_json(args[0]);
// cJSON* cjson = convert_python_object_to_cjson(args[0], vm);
// char* str = cJSON_Print(cjson);
// cJSON_Delete(cjson);
// PyObject* ret = VAR((const char*)str);
// hooks.free_fn(str);
// return ret;
});
}
} // namespace pkpy

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
#pragma once
#include "pocketpy.h"
extern "C"{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
namespace pkpy{
void initialize_lua_bridge(VM* vm, lua_State* newL);
} // namespace pkpy

View File

@ -0,0 +1,353 @@
#include "lua_bridge.hpp"
namespace pkpy{
static lua_State* _L;
static void lua_push_from_python(VM*, PyObject*);
static PyObject* lua_popx_to_python(VM*);
template<typename T>
static void table_apply(VM* vm, T f){
PK_ASSERT(lua_istable(_L, -1));
lua_pushnil(_L); // [key]
while(lua_next(_L, -2) != 0){ // [key, val]
lua_pushvalue(_L, -2); // [key, val, key]
PyObject* key = lua_popx_to_python(vm);
PyObject* val = lua_popx_to_python(vm);
f(key, val); // [key]
}
lua_pop(_L, 1); // []
}
struct LuaExceptionGuard{
int base_size;
LuaExceptionGuard(){ base_size = lua_gettop(_L); }
~LuaExceptionGuard(){
int delta = lua_gettop(_L) - base_size;
if(delta > 0) lua_pop(_L, delta);
}
};
#define LUA_PROTECTED(__B) { LuaExceptionGuard __guard; __B; }
struct PyLuaObject{
PK_ALWAYS_PASS_BY_POINTER(PyLuaObject)
int r;
PyLuaObject(){ r = luaL_ref(_L, LUA_REGISTRYINDEX); }
~PyLuaObject(){ luaL_unref(_L, LUA_REGISTRYINDEX, r); }
};
struct PyLuaTable: PyLuaObject{
PY_CLASS(PyLuaTable, lua, Table)
static void _register(VM* vm, PyObject* mod, PyObject* type){
Type t = PK_OBJ_GET(Type, type);
PyTypeInfo* ti = &vm->_all_types[t];
ti->subclass_enabled = false;
ti->m__getattr__ = [](VM* vm, PyObject* obj, StrName name){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
std::string_view name_sv = name.sv();
lua_pushlstring(_L, name_sv.data(), name_sv.size());
lua_gettable(_L, -2);
PyObject* ret = lua_popx_to_python(vm);
lua_pop(_L, 1);
return ret;
)
};
ti->m__setattr__ = [](VM* vm, PyObject* obj, StrName name, PyObject* val){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
std::string_view name_sv = name.sv();
lua_pushlstring(_L, name_sv.data(), name_sv.size());
lua_push_from_python(vm, val);
lua_settable(_L, -3);
lua_pop(_L, 1);
)
};
ti->m__delattr__ = [](VM* vm, PyObject* obj, StrName name){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
std::string_view name_sv = name.sv();
lua_pushlstring(_L, name_sv.data(), name_sv.size());
lua_pushnil(_L);
lua_settable(_L, -3);
lua_pop(_L, 1);
)
return true;
};
vm->bind_constructor<1>(type, [](VM* vm, ArgsView args){
lua_newtable(_L); // push an empty table onto the stack
PyObject* obj = vm->heap.gcnew<PyLuaTable>(PK_OBJ_GET(Type, args[0]));
return obj;
});
vm->bind__len__(t, [](VM* vm, PyObject* obj){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
i64 len = 0;
lua_pushnil(_L);
while(lua_next(_L, -2) != 0){ len += 1; lua_pop(_L, 1); }
lua_pop(_L, 1);
return len;
});
vm->bind__getitem__(t, [](VM* vm, PyObject* obj, PyObject* key){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
lua_push_from_python(vm, key);
lua_gettable(_L, -2);
PyObject* ret = lua_popx_to_python(vm);
lua_pop(_L, 1);
return ret;
)
});
vm->bind__setitem__(t, [](VM* vm, PyObject* obj, PyObject* key, PyObject* val){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
lua_push_from_python(vm, key);
lua_push_from_python(vm, val);
lua_settable(_L, -3);
lua_pop(_L, 1);
)
});
vm->bind__delitem__(t, [](VM* vm, PyObject* obj, PyObject* key){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
lua_push_from_python(vm, key);
lua_pushnil(_L);
lua_settable(_L, -3);
lua_pop(_L, 1);
)
});
vm->bind__contains__(t, [](VM* vm, PyObject* obj, PyObject* key){
const PyLuaTable& self = _CAST(PyLuaTable&, obj);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
lua_push_from_python(vm, key);
lua_gettable(_L, -2);
bool ret = lua_isnil(_L, -1) == 0;
lua_pop(_L, 2);
return ret ? vm->True : vm->False;
)
});
vm->bind(type, "keys(self) -> list", [](VM* vm, ArgsView args){
const PyLuaTable& self = _CAST(PyLuaTable&, args[0]);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
List ret;
table_apply(vm, [&](PyObject* key, PyObject* val){ ret.push_back(key); });
lua_pop(_L, 1);
return VAR(std::move(ret));
)
});
vm->bind(type, "values(self) -> list", [](VM* vm, ArgsView args){
const PyLuaTable& self = _CAST(PyLuaTable&, args[0]);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
List ret;
table_apply(vm, [&](PyObject* key, PyObject* val){ ret.push_back(val); });
lua_pop(_L, 1);
return VAR(std::move(ret));
)
});
vm->bind(type, "items(self) -> list[tuple]", [](VM* vm, ArgsView args){
const PyLuaTable& self = _CAST(PyLuaTable&, args[0]);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
List ret;
table_apply(vm, [&](PyObject* key, PyObject* val){
PyObject* item = VAR(Tuple(key, val));
ret.push_back(item);
});
lua_pop(_L, 1);
return VAR(std::move(ret));
)
});
}
};
static PyObject* lua_popx_multi_to_python(VM* vm, int count){
if(count == 0){
return vm->None;
}else if(count == 1){
return lua_popx_to_python(vm);
}else if(count > 1){
Tuple ret(count);
for(int i=0; i<count; i++){
ret[i] = lua_popx_to_python(vm);
}
return VAR(std::move(ret));
}
PK_FATAL_ERROR()
}
struct PyLuaFunction: PyLuaObject{
PY_CLASS(PyLuaFunction, lua, Function)
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_notimplemented_constructor<PyLuaFunction>(type);
vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false;
vm->bind_method<-1>(type, "__call__", [](VM* vm, ArgsView args){
if(args.size() < 1) vm->TypeError("__call__ takes at least 1 argument");
const PyLuaFunction& self = _CAST(PyLuaFunction&, args[0]);
int base_size = lua_gettop(_L);
LUA_PROTECTED(
lua_rawgeti(_L, LUA_REGISTRYINDEX, self.r);
for(int i=1; i<args.size(); i++){
lua_push_from_python(vm, args[i]);
}
if(lua_pcall(_L, args.size()-1, LUA_MULTRET, 0)){
const char* error = lua_tostring(_L, -1);
lua_pop(_L, 1);
vm->RuntimeError(error);
}
return lua_popx_multi_to_python(vm, lua_gettop(_L) - base_size);
)
});
}
};
void lua_push_from_python(VM* vm, PyObject* val){
if(val == vm->None){
lua_pushnil(_L);
return;
}
Type t = vm->_tp(val);
switch(t.index){
case VM::tp_bool.index:
lua_pushboolean(_L, val == vm->True);
return;
case VM::tp_int.index:
lua_pushinteger(_L, _CAST(i64, val));
return;
case VM::tp_float.index:
lua_pushnumber(_L, _CAST(f64, val));
return;
case VM::tp_str.index: {
std::string_view sv = _CAST(Str, val).sv();
lua_pushlstring(_L, sv.data(), sv.size());
return;
}
case VM::tp_tuple.index: {
lua_newtable(_L);
int i = 1;
for(PyObject* obj: PK_OBJ_GET(Tuple, val)){
lua_push_from_python(vm, obj);
lua_rawseti(_L, -2, i++);
}
return;
}
case VM::tp_list.index: {
lua_newtable(_L);
int i = 1;
for(PyObject* obj: PK_OBJ_GET(List, val)){
lua_push_from_python(vm, obj);
lua_rawseti(_L, -2, i++);
}
return;
}
case VM::tp_dict.index: {
lua_newtable(_L);
PK_OBJ_GET(Dict, val).apply([&](PyObject* key, PyObject* val){
lua_push_from_python(vm, key);
lua_push_from_python(vm, val);
lua_settable(_L, -3);
});
return;
}
}
if(is_type(val, PyLuaTable::_type(vm))){
const PyLuaTable& table = _CAST(PyLuaTable&, val);
lua_rawgeti(_L, LUA_REGISTRYINDEX, table.r);
return;
}
if(is_type(val, PyLuaFunction::_type(vm))){
const PyLuaFunction& func = _CAST(PyLuaFunction&, val);
lua_rawgeti(_L, LUA_REGISTRYINDEX, func.r);
return;
}
vm->RuntimeError(_S("unsupported python type: ", _type_name(vm, t).escape()));
}
PyObject* lua_popx_to_python(VM* vm) {
int type = lua_type(_L, -1);
switch (type) {
case LUA_TNIL: {
lua_pop(_L, 1);
return vm->None;
}
case LUA_TBOOLEAN: {
bool val = lua_toboolean(_L, -1);
lua_pop(_L, 1);
return val ? vm->True : vm->False;
}
case LUA_TNUMBER: {
double val = lua_tonumber(_L, -1);
lua_pop(_L, 1);
return VAR(val);
}
case LUA_TSTRING: {
const char* val = lua_tostring(_L, -1);
lua_pop(_L, 1);
return VAR(val);
}
case LUA_TTABLE: {
PyObject* obj = vm->heap.gcnew<PyLuaTable>(PyLuaTable::_type(vm));
return obj;
}
case LUA_TFUNCTION: {
PyObject* obj = vm->heap.gcnew<PyLuaFunction>(PyLuaFunction::_type(vm));
return obj;
}
default: {
const char* type_name = lua_typename(_L, type);
lua_pop(_L, 1);
vm->RuntimeError(_S("unsupported lua type: '", type_name, "'"));
}
}
PK_UNREACHABLE()
}
void initialize_lua_bridge(VM* vm, lua_State* newL){
PyObject* mod = vm->new_module("lua");
if(_L != nullptr){
throw std::runtime_error("lua bridge already initialized");
}
_L = newL;
PyLuaTable::register_class(vm, mod);
PyLuaFunction::register_class(vm, mod);
vm->bind(mod, "dostring(__source: str)", [](VM* vm, ArgsView args){
const char* source = CAST(CString, args[0]);
int base_size = lua_gettop(_L);
if (luaL_dostring(_L, source)) {
const char* error = lua_tostring(_L, -1);
lua_pop(_L, 1); // pop error message from the stack
vm->RuntimeError(error);
}
return lua_popx_multi_to_python(vm, lua_gettop(_L) - base_size);
});
}
} // namespace pkpy

93
dependencies/pocketpy/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,93 @@
cmake_minimum_required(VERSION 3.10)
project(pocketpy)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /utf-8 /O2")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -frtti -O2")
# disable -Wshorten-64-to-32 for apple
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-shorten-64-to-32")
endif()
endif()
aux_source_directory(src POCKETPY_SRC)
option(PK_USE_CJSON "" OFF)
if(PK_USE_CJSON)
add_subdirectory(3rd/cjson)
add_definitions(-DPK_USE_CJSON)
endif()
option(PK_ENABLE_OS "" OFF)
if(PK_ENABLE_OS)
add_definitions(-DPK_ENABLE_OS=1)
endif()
option(PK_NO_EXPORT_C_API "" OFF)
if(PK_NO_EXPORT_C_API)
add_definitions(-DPK_NO_EXPORT_C_API)
endif()
# PK_IS_MAIN determines whether the project is being used from root
# or if it is added as a dependency (through add_subdirectory for example).
if ("${CMAKE_SOURCE_DIR}" STREQUAL ".")
set(PK_IS_MAIN TRUE)
option(PK_BUILD_SHARED_LIB "Build shared library" OFF)
option(PK_BUILD_STATIC_LIB "Build static library" OFF)
else()
set(PK_IS_MAIN FALSE)
option(PK_BUILD_SHARED_LIB "Build shared library" OFF)
option(PK_BUILD_STATIC_LIB "Build static library" ON)
endif()
if(PK_BUILD_SHARED_LIB)
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC})
elseif(PK_BUILD_STATIC_LIB)
add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})
else()
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC})
set(PROJECT_EXE_NAME main)
add_executable(${PROJECT_EXE_NAME} src2/main.cpp)
target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME})
target_link_libraries(${PROJECT_EXE_NAME} ${CMAKE_DL_LIBS})
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<INSTALL_INTERFACE:include>
)
if(PK_USE_CJSON)
target_link_libraries(${PROJECT_NAME} PRIVATE $<BUILD_INTERFACE:cjson>)
endif()
option(PK_INSTALL "Generate the install target" OFF)
if (PK_INSTALL)
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}_target
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(
DIRECTORY include
DESTINATION include
FILES_MATCHING PATTERN "*.h"
PATTERN "typings" EXCLUDE
)
# generate config.cmake
install(
EXPORT ${PROJECT_NAME}_target
FILE ${PROJECT_NAME}-config.cmake
DESTINATION "share/${PROJECT_NAME}"
)
endif()

21
dependencies/pocketpy/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 blueloveTH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

224
dependencies/pocketpy/README.md vendored Normal file
View File

@ -0,0 +1,224 @@
# pocketpy: python interpreter in 1 file
<p>
<a title="Build" href="https://github.com/pocketpy/pocketpy/actions/workflows" ><img src="https://github.com/pocketpy/pocketpy/actions/workflows/main.yml/badge.svg" /></a>
<a href="https://codecov.io/gh/pocketpy/pocketpy" >
<img src="https://codecov.io/gh/pocketpy/pocketpy/branch/main/graph/badge.svg?token=TI9KAFL0RG"/>
</a>
<a href="https://en.wikipedia.org/wiki/C%2B%2B#Standardization">
<img alt="C++17" src="https://img.shields.io/badge/C%2B%2B-17-blue.svg"></a>
<a href="https://github.com/blueloveth/pocketpy/blob/main/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/blueloveth/pocketpy.svg?color=blue"></a>
<a href="https://github.com/blueloveth/pocketpy/releases">
<img alt="GitHub release" src="https://img.shields.io/github/release/blueloveth/pocketpy.svg"></a>
<!-- docs -->
<a href="https://pocketpy.dev">
<img alt="Website" src="https://img.shields.io/website/https/pocketpy.dev.svg?down_color=red&down_message=offline&up_color=blue&up_message=online"></a>
<a title="Discord" href="https://discord.gg/WWaq72GzXv" >
<img src="https://img.shields.io/discord/1048978026131640390.svg" /></a>
</p>
pkpy is a lightweight(~15K LOC) Python interpreter for game scripting, built on C++17 with STL.
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.
Please see https://pocketpy.dev for details or try [Live Demo](https://pocketpy.dev/static/web/).
## Supported Platforms
pkpy should work on any platform with a C++17 compiler.
These platforms are officially tested.
+ Windows 64-bit
+ Linux 64-bit / 32-bit
+ macOS 64-bit
+ Android 64-bit / 32-bit
+ iOS 64-bit
+ Emscripten 32-bit
+ Raspberry Pi OS 64-bit
## Quick Start
You have two options to integrate pkpy into your project.
#### Use the single header file
Download the `pocketpy.h` on our [GitHub Release](https://github.com/pocketpy/pocketpy/releases) page.
And `#include` it in your project. The header can only be included once.
#### Use CMake
Clone the whole repository as a submodule into your project,
In your CMakelists.txt, add the following lines:
```cmake
add_subdirectory(pocketpy)
target_link_libraries(<your_target> 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.
It is safe to use `main` branch in production if CI badge is green.
### Compile Flags
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
+ For MSVC, `/utf-8` flag must be set
For development build, use this snippet.
```bash
# prerequisites
pip install cmake
# build the repo
python cmake_build.py
# unittest
python scripts/run_tests.py
```
### Example
```cpp
#include "pocketpy.h"
using namespace pkpy;
int main(){
// Create a virtual machine
VM* vm = new VM();
// Hello world!
vm->exec("print('Hello world!')");
// Create a list
vm->exec("a = [1, 2, 3]");
// Eval the sum of the list
PyObject* result = vm->eval("sum(a)");
std::cout << "Sum of the list: "<< py_cast<int>(vm, result) << std::endl; // 6
// Bindings
vm->bind(vm->_main, "add(a: int, b: int)",
[](VM* vm, ArgsView args){
int a = py_cast<int>(vm, args[0]);
int b = py_cast<int>(vm, args[1]);
return py_var(vm, a + b);
});
// Call the function
PyObject* 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<int>(vm, result) << std::endl; // 10
// Dispose the virtual machine
delete vm;
return 0;
}
```
## Features
Check this [Cheatsheet](https://reference.pocketpy.dev/python.html)
for a quick overview of the supported features.
| Name | Example | Supported |
| --------------- | ------------------------------- | --------- |
| If Else | `if..else..elif` | ✅ |
| Loop | `for/while/break/continue` | ✅ |
| Function | `def f(x,*args,y=1):` | ✅ |
| Subclass | `class A(B):` | ✅ |
| List | `[1, 2, 'a']` | ✅ |
| ListComp | `[i for i in range(5)]` | ✅ |
| Slice | `a[1:2], a[:2], a[1:]` | ✅ |
| Tuple | `(1, 2, 'a')` | ✅ |
| Dict | `{'a': 1, 'b': 2}` | ✅ |
| F-String | `f'value is {x}'` | ✅ |
| Unpacking | `a, b = 1, 2` | ✅ |
| Star Unpacking | `a, *b = [1, 2, 3]` | ✅ |
| Exception | `raise/try..catch..finally` | ✅ |
| Dynamic Code | `eval()/exec()` | ✅ |
| Reflection | `hasattr()/getattr()/setattr()` | ✅ |
| Import | `import/from..import` | ✅ |
| Context Block | `with <expr> as <id>:` | ✅ |
| Type Annotation | `def f(a:int, b:float=1)` | ✅ |
| Generator | `yield i` | ✅ |
| Decorator | `@cache` | ✅ |
## Performance
Currently, pkpy is as fast as cpython 3.9.
Performance results for cpython 3.9 are applicable to for pkpy.
See https://pocketpy.dev/performance/ for details.
And these are the results of the primes benchmark on Intel i5-12400F, WSL (Ubuntu 20.04 LTS), which *roughly* reflects the performance among c++, lua, pkpy and cpython.
| name | version | time | file |
| ---- | ---- | ---- | ---- |
| c++ | gnu++11 | `0.104s ■□□□□□□□□□□□□□□□` | [benchmarks/primes.cpp](https://github.com/pocketpy/pocketpy/blob/9481d653b60b81f4590a4d48f2be496f6962261e/benchmarks/primes.cpp) |
| lua | 5.3.3 | `1.576s ■■■■■■■■■□□□□□□□` | [benchmarks/primes.lua](https://github.com/pocketpy/pocketpy/blob/9481d653b60b81f4590a4d48f2be496f6962261e/benchmarks/primes.lua) |
| pkpy | 1.2.7 | `2.385s ■■■■■■■■■■■■■□□□` | [benchmarks/primes.py](https://github.com/pocketpy/pocketpy/blob/9481d653b60b81f4590a4d48f2be496f6962261e/benchmarks/primes.py) |
| cpython | 3.8.10 | `2.871s ■■■■■■■■■■■■■■■■` | [benchmarks/primes.py](https://github.com/pocketpy/pocketpy/blob/9481d653b60b81f4590a4d48f2be496f6962261e/benchmarks/primes.py) |
## Used By
| | Description |
|-----------------------------------------------------------------|--------------------------------------------------------------------------|
| [TIC-80](https://github.com/nesbox/TIC-80) | TIC-80 is a fantasy computer for making, playing and sharing tiny games. |
| [MiniPythonIDE](https://github.com/CU-Production/MiniPythonIDE) | A python ide base on pocketpy |
| [py-js](https://github.com/shakfu/py-js) | Python3 externals for Max / MSP |
| [crescent](https://github.com/chukobyte/crescent) | Crescent is a cross-platform 2D fighting and beat-em-up game engine. |
Submit a pull request to add your project here.
## Contribution
All kinds of contributions are welcome.
- Submit a Pull Request
- fix a bug
- add a new feature
- Open an Issue
- any suggestions
- any questions
If you find pkpy useful, consider star this repository (●'◡'●)
## Sponsor this project
You can sponsor this project via these ways.
+ [Github Sponsors](https://github.com/sponsors/blueloveTH)
+ [Buy me a coffee](https://www.buymeacoffee.com/blueloveth)
Your sponsorship will help us develop pkpy continuously.
## Reference
+ [cpython](https://github.com/python/cpython)
The official implementation of Python programming language.
+ [byterun](https://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html)
An excellent learning material. It illustrates how Python's virtual machine works.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=blueloveth/pocketpy&type=Date)](https://star-history.com/#blueloveth/pocketpy&Date)
## License
[MIT License](http://opensource.org/licenses/MIT)

0
dependencies/pocketpy/__init__.py vendored Normal file
View File

96
dependencies/pocketpy/amalgamate.py vendored Normal file
View File

@ -0,0 +1,96 @@
import os
assert os.system("python prebuild.py") == 0
with open("include/pocketpy/opcodes.h", "rt", encoding='utf-8') as f:
OPCODES_TEXT = '\n' + f.read() + '\n'
pipeline = [
["config.h", "export.h", "_generated.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h"],
["obj.h", "dict.h", "codeobject.h", "frame.h", "profiler.h"],
["gc.h", "vm.h", "ceval.h", "lexer.h", "expr.h", "compiler.h", "repl.h"],
["cffi.h", "bindings.h", "iter.h", "base64.h", "csv.h", "collections.h", "array2d.h", "dataclasses.h", "random.h", "linalg.h", "easing.h", "io.h", "modules.h"],
["pocketpy.h", "pocketpy_c.h"]
]
copied = set()
text = ""
import re
import shutil
import os
import sys
import time
if os.path.exists("amalgamated"):
shutil.rmtree("amalgamated")
time.sleep(0.5)
os.mkdir("amalgamated")
def remove_copied_include(text):
text = text.replace("#pragma once", "")
def _replace(m):
key = m.group(1)
if key.startswith("pocketpy/"):
key = key[9:]
if key in ["user_config.h", "cJSONw.hpp"]:
return m.group(0)
if "opcodes.h" in key:
return OPCODES_TEXT
assert key in copied, f"include {key} not found"
return ""
text = re.sub(
r'#include\s+"(.+)"\s*',
_replace,
text
)
return text
for seq in pipeline:
for j in seq:
print(j)
with open("include/pocketpy/"+j, "rt", encoding='utf-8') as f:
text += remove_copied_include(f.read()) + '\n'
copied.add(j)
j = j.replace(".h", ".cpp")
if os.path.exists("src/"+j):
with open("src/"+j, "rt", encoding='utf-8') as f:
text += remove_copied_include(f.read()) + '\n'
copied.add(j)
# use LF line endings instead of CRLF
with open("amalgamated/pocketpy.h", "wt", encoding='utf-8', newline='\n') as f:
final_text = \
r'''/*
* Copyright (c) 2024 blueloveTH
* Distributed Under The MIT License
* https://github.com/pocketpy/pocketpy
*/
#ifndef POCKETPY_H
#define POCKETPY_H
''' + text + '\n#endif // POCKETPY_H'
f.write(final_text)
shutil.copy("src2/main.cpp", "amalgamated/main.cpp")
with open("amalgamated/main.cpp", "rt", encoding='utf-8') as f:
text = f.read()
text = text.replace('#include "pocketpy/pocketpy.h"', '#include "pocketpy.h"')
with open("amalgamated/main.cpp", "wt", encoding='utf-8', newline='\n') as f:
f.write(text)
if sys.platform in ['linux', 'darwin']:
ok = os.system("clang++ -o main amalgamated/main.cpp -O1 --std=c++17 -frtti -stdlib=libc++")
if ok == 0:
print("Test build success!")
print("amalgamated/pocketpy.h")
def sync(path):
shutil.copy("amalgamated/pocketpy.h", os.path.join(path, "pocketpy.h"))
with open(os.path.join(path, "pocketpy.cpp"), "wt", encoding='utf-8', newline='\n') as f:
f.write("#include \"pocketpy.h\"\n")
sync("plugins/macos/pocketpy")

View File

10
dependencies/pocketpy/benchmarks/fib.py vendored Normal file
View File

@ -0,0 +1,10 @@
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
assert fib(32) == 2178309
# from dis import dis
# dis(fib)
# 7049155 calls

View File

@ -0,0 +1,8 @@
def f(a, b, c):
pass
for i in range(10000000):
f(1, 2, 3)
f(1, 2, 3)
f(1, 2, 3)
f(1, 2, 3)

View File

@ -0,0 +1,11 @@
class A:
def f(self, a, b, c):
pass
a = A()
for i in range(10000000):
a.f(1, 2, 3)
a.f(1, 2, 3)
a.f(1, 2, 3)
a.f(1, 2, 3)

View File

@ -0,0 +1,46 @@
try:
import os
except ImportError:
exit(0)
import sys
is_pkpy = not hasattr(sys, 'getrefcount')
os.chdir('benchmarks')
if is_pkpy:
try:
import cjson as json
except ImportError:
print('[cJSON not Enabled]')
exit(0)
else:
import json
_2489KB = 'WorldMap_GridVania_layout.ldtk'
_1093KB = 'WorldMap_Free_layout.ldtk'
_339KB = 'Typical_2D_platformer_example.ldtk'
with open(f'res/{_2489KB}', 'r') as f:
json_content = f.read()
data: dict = json.loads(json_content)
assert isinstance(data, dict)
# serialize and deserialize
dumped: str = json.dumps(data)
for _ in range(10):
loaded: dict = json.loads(dumped)
assert len(data) == len(loaded)
assert data == loaded
#### very very slow!!
import pickle
with open(f'res/{_339KB}', 'r') as f:
json_content = f.read()
data: dict = json.loads(json_content)
data_pickled: bytes = pickle.dumps(data)
assert isinstance(data_pickled, bytes)
assert pickle.loads(data_pickled) == data

View File

@ -0,0 +1,30 @@
try:
import os
except ImportError:
exit(0)
os.chdir('benchmarks')
import json
_2489KB = 'WorldMap_GridVania_layout.ldtk'
_1093KB = 'WorldMap_Free_layout.ldtk'
_339KB = 'Typical_2D_platformer_example.ldtk'
with open(f'res/{_2489KB}', 'r') as f:
json_content = f.read()
data: dict = json.loads(json_content)
assert isinstance(data, dict)
# serialize and deserialize
dumped: str = json.dumps(data)
loaded: dict = json.loads(dumped)
assert len(data) == len(loaded)
assert data == loaded
#### very very slow!! DO NOT RUN IT
# import pickle
# data_pickled: bytes = pickle.dumps(data)
# assert isinstance(data_pickled, bytes)
# assert pickle.loads(data_pickled) == data

View File

@ -0,0 +1,2 @@
for i in range(10000000):
pass

View File

@ -0,0 +1,2 @@
for i in range(10000000):
x = i

View File

@ -0,0 +1,6 @@
x = 0
for i in range(10000000):
x += 1
assert x == 10000000

View File

@ -0,0 +1,5 @@
for i in range(10000000):
x = i + 1
y = x * 2
z = y - 5
a = x * y // z

View File

@ -0,0 +1,167 @@
#include <vector>
#include <string>
#include <queue>
#include <iostream>
#include <algorithm>
#include <unordered_map>
typedef long long LL;
struct Node{
std::unordered_map<int, Node*> children;
bool terminal;
Node(){
terminal = false;
}
};
struct Sieve{
LL limit;
std::vector<bool> prime;
Sieve(LL limit){
this->limit = limit;
prime = std::vector<bool>(limit + 1, false);
}
std::vector<LL> to_list(){
std::vector<LL> result;
result.push_back(2);
result.push_back(3);
for(LL p = 5; p <= limit; p++){
if(prime[p]){
result.push_back(p);
}
}
return result;
}
void omit_squares(){
LL r = 5;
while(r * r < limit){
if(prime[r]){
LL i = r * r;
while(i < limit){
prime[i] = false;
i = i + r * r;
}
}
r += 1;
}
}
void step1(LL x, LL y){
LL n = (4 * x * x) + (y * y);
if(n <= limit && (n % 12 == 1 || n % 12 == 5)){
prime[n] = !prime[n];
}
}
void step2(LL x, LL y){
LL n = (3 * x * x) + (y * y);
if(n <= limit && n % 12 == 7){
prime[n] = !prime[n];
}
}
void step3(LL x, LL y){
LL n = (3 * x * x) - (y * y);
if(x > y && n <= limit && n % 12 == 11){
prime[n] = !prime[n];
}
}
void loop_y(LL x){
LL y = 1;
while(y * y < limit){
step1(x, y);
step2(x, y);
step3(x, y);
y += 1;
}
}
void loop_x(){
LL x = 1;
while(x * x < limit){
loop_y(x);
x += 1;
}
}
void calc(){
loop_x();
omit_squares();
}
};
Node *generate_trie(std::vector<LL> l){
Node *root = new Node();
for(LL el : l){
Node *head = root;
std::string s = std::to_string(el);
for(char ch : s){
if(head->children.find(ch) == head->children.end()){
head->children[ch] = new Node();
}
head = head->children[ch];
}
head->terminal = true;
}
return root;
}
std::vector<LL> find(LL upper_bound, LL prefix){
Sieve *sieve = new Sieve(upper_bound);
sieve->calc();
std::string str_prefix = std::to_string(prefix);
Node *head = generate_trie(sieve->to_list());
for(char ch : str_prefix){
if(head->children.find(ch) == head->children.end()){
return std::vector<LL>();
}
head = head->children[ch];
}
std::queue<std::pair<Node*, std::string>> q;
std::vector<LL> result;
q.push(std::make_pair(head, str_prefix));
while(!q.empty()){
std::pair<Node*, std::string> top = q.front();
q.pop();
if(top.first->terminal){
result.push_back(std::stoll(top.second));
}
for(std::pair<char, Node*> p : top.first->children){
q.push(std::make_pair(p.second, top.second + p.first));
}
}
std::sort(result.begin(), result.end());
return result;
}
void verify(){
std::vector<LL> left = {2, 23, 29};
std::vector<LL> right = find(100, 2);
if(left != right){
std::cout << "left != right" << std::endl;
exit(1);
}
}
int main(){
const LL UPPER_BOUND = 5000000;
const LL PREFIX = 32338;
verify();
std::vector<LL> results = find(UPPER_BOUND, PREFIX);
std::vector<LL> expected = {323381, 323383, 3233803, 3233809, 3233851, 3233863, 3233873, 3233887, 3233897};
if(results != expected){
std::cout << "results != expected" << std::endl;
exit(1);
}
return 0;
}

View File

@ -0,0 +1,168 @@
local UPPER_BOUND = 5000000
local PREFIX = 32338
local Node = {}
function Node:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.children = {}
obj.terminal = false
return obj
end
local Sieve = {}
function Sieve:new(limit)
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.limit = limit
obj.prime = {}
for i = 0, limit do
obj.prime[i] = false
end
return obj
end
function Sieve:to_list()
local result = {2, 3}
for p = 5, self.limit do
if self.prime[p] then
table.insert(result, p)
end
end
return result
end
function Sieve:omit_squares()
local r = 5
while r * r < self.limit do
if self.prime[r] then
local i = r * r
while i < self.limit do
self.prime[i] = false
i = i + r * r
end
end
r = r + 1
end
return self
end
function Sieve:step1(x, y)
local n = (4 * x * x) + (y * y)
if n <= self.limit and (n % 12 == 1 or n % 12 == 5) then
self.prime[n] = not self.prime[n]
end
end
function Sieve:step2(x, y)
local n = (3 * x * x) + (y * y)
if n <= self.limit and n % 12 == 7 then
self.prime[n] = not self.prime[n]
end
end
function Sieve:step3(x, y)
local n = (3 * x * x) - (y * y)
if x > y and n <= self.limit and n % 12 == 11 then
self.prime[n] = not self.prime[n]
end
end
function Sieve:loop_y(x)
local y = 1
while y * y < self.limit do
self:step1(x, y)
self:step2(x, y)
self:step3(x, y)
y = y + 1
end
end
function Sieve:loop_x()
local x = 1
while x * x < self.limit do
self:loop_y(x)
x = x + 1
end
end
function Sieve:calc()
self:loop_x()
return self:omit_squares()
end
local function generate_trie(l)
local root = Node:new()
for _, el in ipairs(l) do
local head = root
-- attempt to call a nil value (method 'split')
-- how to fix? use string.split
el = tostring(el)
for i=1, #el do
local ch = el:sub(i, i)
if not head.children[ch] then
head.children[ch] = Node:new()
end
head = head.children[ch]
end
head.terminal = true
end
return root
end
local function find(upper_bound, prefix_)
local primes = Sieve:new(upper_bound):calc()
local str_prefix = tostring(prefix_)
local head = generate_trie(primes:to_list())
for i=1, #str_prefix do
local ch = str_prefix:sub(i, i)
head = head.children[ch]
if head == nil then
return nil
end
end
local queue, result = {{head, str_prefix}}, {}
while #queue > 0 do
local tuple = table.remove(queue)
local top, prefix = tuple[1], tuple[2]
if top.terminal then
table.insert(result, tonumber(prefix))
end
for ch, v in pairs(top.children) do
table.insert(queue, 1, {v, prefix .. ch})
end
end
table.sort(result)
return result
end
local function verify()
local left = {2, 23, 29}
local right = find(100, 2)
if #left ~= #right then
print("length not equal")
os.exit(1)
end
for i, v in ipairs(left) do
if v ~= right[i] then
print(string.format("%s != %s", v, right[i]))
os.exit(1)
end
end
end
verify()
local results = find(UPPER_BOUND, PREFIX)
local expected = {323381, 323383, 3233803, 3233809, 3233851, 3233863, 3233873, 3233887, 3233897}
for i, v in ipairs(results) do
if v ~= expected[i] then
print(string.format("%s != %s", v, expected[i]))
os.exit(1)
end
end

View File

@ -0,0 +1,111 @@
UPPER_BOUND = 5000000
PREFIX = 32338
# exit(0)
class Node:
def __init__(self):
self.children = {}
self.terminal = False
class Sieve:
def __init__(self, limit):
self.limit = limit
self.prime = [False] * (limit + 1)
def to_list(self):
result = [2, 3]
for p in range(5, self.limit + 1):
if self.prime[p]:
result.append(p)
return result
def omit_squares(self):
r = 5
while r * r < self.limit:
if self.prime[r]:
i = r * r
while i < self.limit:
self.prime[i] = False
i = i + r * r
r += 1
return self
def step1(self, x, y):
n = (4 * x * x) + (y * y)
if n <= self.limit and (n % 12 == 1 or n % 12 == 5):
self.prime[n] = not self.prime[n]
def step2(self, x, y):
n = (3 * x * x) + (y * y)
if n <= self.limit and n % 12 == 7:
self.prime[n] = not self.prime[n]
def step3(self, x, y):
n = (3 * x * x) - (y * y)
if x > y and n <= self.limit and n % 12 == 11:
self.prime[n] = not self.prime[n]
def loop_y(self, x):
y = 1
while y * y < self.limit:
self.step1(x, y)
self.step2(x, y)
self.step3(x, y)
y += 1
def loop_x(self):
x = 1
while x * x < self.limit:
self.loop_y(x)
x += 1
def calc(self):
self.loop_x()
return self.omit_squares()
def generate_trie(l):
root = Node()
for el in l:
head = root
for ch in str(el):
if ch not in head.children:
head.children[ch] = Node()
head = head.children[ch]
head.terminal = True
return root
def find(upper_bound, prefix):
primes = Sieve(upper_bound).calc()
str_prefix = str(prefix)
head = generate_trie(primes.to_list())
for ch in str_prefix:
head = head.children.get(ch)
if head is None: # either ch does not exist or the value is None
return None
queue, result = [(head, str_prefix)], []
while queue:
top, prefix = queue.pop()
if top.terminal:
result.append(int(prefix))
for ch, v in top.children.items():
queue.insert(0, (v, prefix + ch))
result.sort()
return result
def verify():
left = [2, 23, 29]
right = find(100, 2)
if left != right:
print(f"{left} != {right}")
exit(1)
verify()
results = find(UPPER_BOUND, PREFIX)
assert results == [323381, 323383, 3233803, 3233809, 3233851, 3233863, 3233873, 3233887, 3233897]

View File

@ -0,0 +1,6 @@
def f(n):
if n == 900:
return -1
return f(n + 1)
assert f(0) == -1

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
def is_prime(x):
if x<2:
return False
for i in range(2,x):
if x%i == 0:
return False
return True
def test(n):
k = 0
for i in range(n):
if is_prime(i):
k += 1
return k
# from dis import dis
# dis(test)
# dis(is_prime)
assert test(10000) == 1229

View File

@ -0,0 +1,22 @@
import random
a = [random.randint(-100000, 100000) for i in range(100000)]
def __qsort(a: list, L: int, R: int):
if L >= R: return;
mid = a[(R+L)//2];
i, j = L, R
while i<=j:
while a[i]<mid: i+=1
while a[j]>mid: j-=1
if i<=j:
a[i], a[j] = a[j], a[i]
i+=1
j-=1
__qsort(a, L, j)
__qsort(a, i, R)
from dis import dis
# dis(__qsort)
__qsort(a, 0, len(a)-1)

View File

@ -0,0 +1,6 @@
def f(n):
if n == 0:
return 0
return n + f(n-1)
assert f(900) == 405450

48
dependencies/pocketpy/build.sh vendored Normal file
View File

@ -0,0 +1,48 @@
#!/bin/bash
# Check if clang++ is installed
if ! type -P clang++ >/dev/null 2>&1; then
echo "clang++ is required and not installed. Kindly install it."
echo "Run: sudo apt-get install libc++-dev libc++abi-dev clang"
exit 1
fi
echo "It takes a moment to finish building."
echo ""
echo "> Running prebuild.py... "
python prebuild.py
if [ $? -ne 0 ]; then
echo "prebuild.py failed."
exit 1
fi
SRC=$(find src/ -name "*.cpp")
echo "> Compiling and linking source files... "
FLAGS="-std=c++17 -O1 -stdlib=libc++ -frtti -Wfatal-errors -Iinclude"
if [[ "$OSTYPE" == "darwin"* ]]; then
LIB_EXTENSION=".dylib"
FLAGS="$FLAGS -undefined dynamic_lookup"
LINK_FLAGS=""
else
LIB_EXTENSION=".so"
LINK_FLAGS="-Wl,-rpath=."
fi
clang++ $FLAGS -o libpocketpy$LIB_EXTENSION $SRC -fPIC -shared
# compile main.cpp and link to libpocketpy.so
echo "> Compiling main.cpp and linking to libpocketpy$LIB_EXTENSION..."
clang++ $FLAGS -o main -O1 src2/main.cpp -L. -lpocketpy $LINK_FLAGS
if [ $? -eq 0 ]; then
echo "Build completed. Type \"./main\" to enter REPL."
else
echo "Build failed."
exit 1
fi

18
dependencies/pocketpy/build_android.sh vendored Normal file
View File

@ -0,0 +1,18 @@
# if no $1 default arm64-v8a
if [ -z $1 ]; then
$1=arm64-v8a
fi
mkdir -p build/android/$1
cd build/android/$1
cmake \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$1 \
-DANDROID_PLATFORM=android-22 \
-DANDROID_STL=c++_shared \
../../.. \
-DPK_BUILD_SHARED_LIB=ON -DPK_USE_CJSON=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release

18
dependencies/pocketpy/build_ios.sh vendored Normal file
View File

@ -0,0 +1,18 @@
rm -rf build
mkdir build
cd build
FLAGS="-DCMAKE_TOOLCHAIN_FILE=3rd/ios.toolchain.cmake -DPK_BUILD_STATIC_LIB=ON -DDEPLOYMENT_TARGET=13.0"
cmake -B os64 -G Xcode $FLAGS -DPLATFORM=OS64 ..
cmake --build os64 --config Release
cmake -B simulatorarm64 -G Xcode $FLAGS -DPLATFORM=SIMULATORARM64 ..
cmake --build simulatorarm64 --config Release
xcodebuild -create-xcframework \
-library os64/Release-iphoneos/libpocketpy.a -headers ../include \
-library simulatorarm64/Release-iphonesimulator/libpocketpy.a -headers ../include \
-output pocketpy.xcframework

7
dependencies/pocketpy/build_web.sh vendored Normal file
View File

@ -0,0 +1,7 @@
python prebuild.py
rm -rf web/lib
mkdir web/lib
SRC=$(find src/ -name "*.cpp")
em++ $SRC -Iinclude/ -fexceptions -frtti -s -Os -sEXPORTED_FUNCTIONS=_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js

View File

@ -0,0 +1,5 @@
SRC=$(find src/ -name "*.cpp")
FLAGS="-std=c++17 -O1 -stdlib=libc++ -Iinclude -frtti -W -Wno-unused-parameter -Wno-sign-compare"
clang++ $FLAGS -o main -O1 src2/main.cpp $SRC

View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.17)
project(test_c_bindings)
set(CMAKE_C_STANDARD 11)
option(PK_BUILD_STATIC_LIB "Build static library" ON)
add_subdirectory(
..
${CMAKE_CURRENT_LIST_DIR}/build/pocketpy/
)
include_directories(
../include
)
add_executable(${PROJECT_NAME} test.c)
target_link_libraries(
${PROJECT_NAME}
pocketpy
)

420
dependencies/pocketpy/c_bindings/test.c vendored Normal file
View File

@ -0,0 +1,420 @@
#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! line %i\n", lineno);
free(message);
exit(1);
}
}
}
void error_impl(pkpy_vm* vm, bool result, int lineno) {
if (result) {
printf("ERROR: succeeded where it should have failed line %i\n", lineno);
exit(1);
} else {
char* message;
if (!pkpy_clear_error(vm, &message)){
printf("clear error reported everything was fine\n");
exit(1);
} else {
printf("successfully errored with this message: \n");
printf("%s\n", message);
free(message);
}
}
}
#define check(r) check_impl(vm, (r), __LINE__)
#define fail(r) fail_impl(vm, (r), __LINE__)
#define error(r) error_impl(vm, (r), __LINE__)
int test_binding(pkpy_vm* vm) {
pkpy_push_int(vm, 12);
return 1;
}
int test_multiple_return(pkpy_vm* vm) {
pkpy_push_int(vm, 12);
pkpy_push_int(vm, 13);
return 2;
}
int test_minus(pkpy_vm* vm) {
int a, b;
pkpy_to_int(vm, 0, &a);
pkpy_to_int(vm, 1, &b);
pkpy_push_int(vm, a - b);
return 1;
}
int test_fib(pkpy_vm* vm) {
int n;
pkpy_to_int(vm, 0, &n);
if (n == 1) {
pkpy_push_int(vm, n);
} else {
pkpy_getglobal(vm, pkpy_name("test_fib"));
pkpy_push_null(vm);
pkpy_push_int(vm, n-1);
pkpy_vectorcall(vm, 1);
int r_int;
pkpy_to_int(vm, -1, &r_int);
pkpy_pop_top(vm);
pkpy_push_int(vm, r_int + n);
}
return 1;
}
int test_default_argument(pkpy_vm* vm){
int x;
pkpy_to_int(vm, -1, &x);
bool ok = x == 5;
pkpy_push_bool(vm, ok);
return 1;
}
int test_return_none(pkpy_vm* vm) {
return 0;
}
int test_error_propagate(pkpy_vm* vm) {
pkpy_error(vm, "NameError", pkpy_string("catch me"));
return 1;
}
int test_nested_error(pkpy_vm* vm) {
pkpy_getglobal(vm, pkpy_name("error_from_python"));
pkpy_push_null(vm);
pkpy_vectorcall(vm, 0);
return 0;
}
#define PRINT_TITLE(x) printf("\n====== %s ======\n", x)
int main(int argc, char** argv) {
pkpy_vm* vm = pkpy_new_vm(true);
PRINT_TITLE("test basic exec");
check(pkpy_exec(vm, "print('hello world!')"));
fail(pkpy_getglobal(vm, pkpy_name("nonexistatn")));
// test int methods
PRINT_TITLE("test int methods");
int r_int;
check(pkpy_push_int(vm, 11));
pkpy_CName m_eleven = pkpy_name("eleven");
check(pkpy_setglobal(vm, m_eleven));
check(pkpy_exec(vm, "print(eleven)"));
check(pkpy_getglobal(vm, m_eleven));
check(pkpy_is_int(vm, -1));
check(pkpy_to_int(vm, -1, &r_int));
printf("%i\n", r_int); // 11
printf("%i\n", pkpy_stack_size(vm)); // 1
fail(pkpy_is_float(vm, -1));
fail(pkpy_is_bool(vm, -1));
fail(pkpy_is_string(vm, -1));
fail(pkpy_is_none(vm, -1));
fail(pkpy_is_voidp(vm, -1));
PRINT_TITLE("test float methods");
double r_float;
check(pkpy_push_float(vm, 11.125));
pkpy_CName m_elevenf = pkpy_name("elevenf");
check(pkpy_setglobal(vm, m_elevenf));
check(pkpy_exec(vm, "print(elevenf)"));
check(pkpy_getglobal(vm, m_elevenf));
check(pkpy_is_float(vm, -1));
check(pkpy_to_float(vm, -1, &r_float));
printf("%.3f\n", r_float);
fail(pkpy_is_int(vm, -1));
fail(pkpy_is_bool(vm, -1));
fail(pkpy_is_string(vm, -1));
fail(pkpy_is_none(vm, -1));
fail(pkpy_is_voidp(vm, -1));
PRINT_TITLE("test bool methods");
bool r_bool;
check(pkpy_push_bool(vm, false));
pkpy_CName m_false_test = pkpy_name("false_test");
check(pkpy_setglobal(vm, m_false_test));
check(pkpy_exec(vm, "print(false_test)"));
check(pkpy_getglobal(vm, m_false_test));
check(pkpy_is_bool(vm, -1));
check(pkpy_to_bool(vm, -1, &r_bool));
printf("%i\n", r_bool);
fail(pkpy_is_int(vm, -1));
fail(pkpy_is_float(vm, -1));
fail(pkpy_is_string(vm, -1));
fail(pkpy_is_none(vm, -1));
fail(pkpy_is_voidp(vm, -1));
PRINT_TITLE("test string methods");
pkpy_CString r_string;
check(pkpy_push_string(vm, pkpy_string("hello!")));
check(pkpy_setglobal(vm, pkpy_name("hello1")));
check(pkpy_exec(vm, "print(hello1)"));
check(pkpy_push_string(vm, pkpy_string("hello!")));
check(pkpy_is_string(vm, -1));
check(pkpy_to_string(vm, -1, &r_string));
puts(r_string);
fail(pkpy_is_int(vm, -1));
fail(pkpy_is_float(vm, -1));
fail(pkpy_is_bool(vm, -1));
fail(pkpy_is_none(vm, -1));
fail(pkpy_is_voidp(vm, -1));
PRINT_TITLE("test none methods");
check(pkpy_push_none(vm));
pkpy_CName m_none = pkpy_name("none");
check(pkpy_setglobal(vm, m_none));
check(pkpy_exec(vm, "print(none)"));
check(pkpy_getglobal(vm, m_none));
check(pkpy_is_none(vm, -1));
fail(pkpy_is_int(vm, -1));
fail(pkpy_is_float(vm, -1));
fail(pkpy_is_bool(vm, -1));
fail(pkpy_is_string(vm, -1));
fail(pkpy_is_voidp(vm, -1));
PRINT_TITLE("test voidp methods");
void* vp = (void*) 123;
check(pkpy_push_voidp(vm, vp));
check(pkpy_setglobal(vm, pkpy_name("vp")));
check(pkpy_exec(vm, "print(vp)"));
check(pkpy_getglobal(vm, pkpy_name("vp")));
check(pkpy_is_voidp(vm, -1));
vp = NULL;
check(pkpy_to_voidp(vm, -1, &vp));
printf("%i\n", (int) (intptr_t) vp);
fail(pkpy_is_int(vm, -1));
fail(pkpy_is_float(vm, -1));
fail(pkpy_is_bool(vm, -1));
fail(pkpy_is_string(vm, -1));
fail(pkpy_is_none(vm, -1));
PRINT_TITLE("test sizing and indexing");
int stack_size = pkpy_stack_size(vm);
printf("stack size %i\n", stack_size);
check(pkpy_is_int(vm, 0));
check(pkpy_is_float(vm, 1));
check(pkpy_is_bool(vm, 2));
check(pkpy_is_string(vm, 3));
check(pkpy_is_none(vm, 4));
check(pkpy_is_voidp(vm, 5));
check(pkpy_is_int(vm, -6));
check(pkpy_is_float(vm, -5));
check(pkpy_is_bool(vm, -4));
check(pkpy_is_string(vm, -3));
check(pkpy_is_none(vm, -2));
check(pkpy_is_voidp(vm, -1));
PRINT_TITLE("test error catching");
error(pkpy_exec(vm, "let's make sure syntax errors get caught"));
//stack should be cleared after error is resolved
check(pkpy_stack_size(vm) == 0);
PRINT_TITLE("test simple call");
check(pkpy_exec(vm, "def x(x, y) : return x - y"));
check(pkpy_getglobal(vm, pkpy_name("x")));
check(pkpy_push_null(vm));
check(pkpy_push_int(vm, 2));
check(pkpy_push_int(vm, 3));
check(pkpy_vectorcall(vm, 2));
check(pkpy_to_int(vm, -1, &r_int));
printf("x : %i\n", r_int);
PRINT_TITLE("test vararg call");
check(pkpy_exec(vm, "def vararg_x(*x) : return sum(x)"));
check(pkpy_getglobal(vm, pkpy_name("vararg_x")));
check(pkpy_push_null(vm));
check(pkpy_push_int(vm, 1));
check(pkpy_push_int(vm, 2));
check(pkpy_push_int(vm, 3));
check(pkpy_push_int(vm, 4));
check(pkpy_push_int(vm, 5));
check(pkpy_push_int(vm, 6));
check(pkpy_vectorcall(vm, 6));
check(pkpy_to_int(vm, -1, &r_int));
printf("vararg_x : %i\n", r_int);
PRINT_TITLE("test keyword call");
check(pkpy_exec(vm, "def keyword_x(x=1, y=1) : return x+y"));
check(pkpy_getglobal(vm, pkpy_name("keyword_x")));
check(pkpy_push_null(vm));
check(pkpy_push_int(vm, 3));
check(pkpy_vectorcall(vm, 1));
check(pkpy_to_int(vm, -1, &r_int));
printf("keyword_x : %i\n", r_int); // 3+1
check(pkpy_getglobal(vm, pkpy_name("keyword_x")));
check(pkpy_push_null(vm));
check(pkpy_vectorcall(vm, 0));
check(pkpy_to_int(vm, -1, &r_int));
printf("keyword_x : %i\n", r_int); // 1+1
check(pkpy_stack_size(vm) == 4);
check(pkpy_pop(vm, 4)); // clear stack
PRINT_TITLE("test return many");
check(pkpy_exec(vm, "def retmany_x() : return 1, 2, 3"));
check(pkpy_getglobal(vm, pkpy_name("retmany_x")));
check(pkpy_push_null(vm));
check(pkpy_vectorcall(vm, 0));
check(pkpy_stack_size(vm) == 1);
check(pkpy_unpack_sequence(vm, 3));
check(pkpy_stack_size(vm) == 3);
check(pkpy_to_int(vm, -3, &r_int));
printf("retmany_x : %i\n", r_int);
check(pkpy_to_int(vm, -2, &r_int));
printf("retmany_x : %i\n", r_int);
check(pkpy_to_int(vm, -1, &r_int));
printf("retmany_x : %i\n", r_int);
// test argument error
check(pkpy_getglobal(vm, pkpy_name("x")));
check(pkpy_push_null(vm));
error(pkpy_vectorcall(vm, 0));
check(pkpy_exec(vm, "l = []"));
check(pkpy_getglobal(vm, pkpy_name("l")));
check(pkpy_get_unbound_method(vm, pkpy_name("append")));
check(pkpy_push_string(vm, pkpy_string("hello")));
check(pkpy_vectorcall(vm, 1));
check(pkpy_pop_top(vm)); // pop None returned by append()
check(pkpy_exec(vm, "print(l)"));
PRINT_TITLE("test bindings");
check(pkpy_push_function(vm, "test_binding()", test_binding));
check(pkpy_setglobal(vm, pkpy_name("test_binding")));
check(pkpy_exec(vm, "print(test_binding())"));
check(pkpy_stack_size(vm) == 0);
check(pkpy_push_function(vm, "test_multiple_return()", test_multiple_return));
check(pkpy_setglobal(vm, pkpy_name("test_multiple_return")));
check(pkpy_stack_size(vm) == 0);
check(pkpy_push_function(vm, "test_default_argument(x=5)", test_default_argument));
check(pkpy_push_null(vm));
check(pkpy_vectorcall(vm, 0));
check(pkpy_stack_size(vm) == 1);
check(pkpy_is_bool(vm, -1) == true);
check(pkpy_to_bool(vm, -1, &r_bool));
check(r_bool == true);
check(pkpy_pop_top(vm));
check(pkpy_stack_size(vm) == 0);
PRINT_TITLE("test bindings 2");
check(pkpy_push_function(vm, "test_minus(a, b)", test_minus));
check(pkpy_setglobal(vm, pkpy_name("test_minus")));
check(pkpy_exec(vm, "print(test_minus(5, 3))"));
check(pkpy_exec(vm, "for i in range(5): print(test_minus(5, i))"));
check(pkpy_stack_size(vm) == 0);
PRINT_TITLE("test bindings fib");
check(pkpy_push_function(vm, "test_fib(n: int) -> int", test_fib));
check(pkpy_setglobal(vm, pkpy_name("test_fib")));
check(pkpy_exec(vm, "print(test_fib(10))"));
check(pkpy_stack_size(vm) == 0);
PRINT_TITLE("test error propagate");
check(pkpy_push_function(vm, "test_error_propagate()", test_error_propagate));
check(pkpy_setglobal(vm, pkpy_name("test_error_propagate")));
error(pkpy_exec(vm, "test_error_propagate()"));
check(pkpy_getglobal(vm, pkpy_name("test_multiple_return")));
check(pkpy_push_null(vm));
check(pkpy_vectorcall(vm, 0));
check(pkpy_stack_size(vm) == 1);
check(pkpy_unpack_sequence(vm, 2));
check(pkpy_stack_size(vm) == 2);
check(pkpy_pop(vm, 2));
check(pkpy_stack_size(vm) == 0);
PRINT_TITLE("test other errors");
check(pkpy_getglobal(vm, pkpy_name("test_error_propagate")));
check(pkpy_pop_top(vm));
fail(pkpy_getglobal(vm, pkpy_name("nonexistant")));
error(pkpy_exec(vm, "raise NameError('testing error throwing from python')"));
PRINT_TITLE("test TypeError");
check(pkpy_push_float(vm, 2.0));
error(pkpy_to_int(vm, -1, &r_int));
PRINT_TITLE("test complicated errors");
pkpy_exec(vm, "test_error_propagate()");
check(pkpy_check_error(vm));
pkpy_clear_error(vm, NULL);
//this should be catchable
check(pkpy_exec(vm, "try : test_error_propagate(); except NameError : pass"));
error(pkpy_error(vm, "Exception", pkpy_string("test direct error mechanism")));
//more complicated error handling
check(pkpy_exec(vm, "def error_from_python() : raise NotImplementedError()"));
check(pkpy_push_function(vm, "test_nested_error()", test_nested_error));
check(pkpy_setglobal(vm, pkpy_name("test_nested_error")));
error(pkpy_exec(vm, "test_nested_error()"));
PRINT_TITLE("test getattr/setattr");
check(pkpy_stack_size(vm) == 0);
check(pkpy_exec(vm, "import math"));
check(pkpy_getglobal(vm, pkpy_name("math")));
check(pkpy_getattr(vm, pkpy_name("pi")));
check(pkpy_to_float(vm, -1, &r_float));
printf("pi: %.2f\n", (float)r_float);
check(pkpy_pop(vm, 1));
// math.pi = 2
check(pkpy_push_int(vm, 2));
check(pkpy_eval(vm, "math"));
check(pkpy_setattr(vm, pkpy_name("pi")));
check(pkpy_exec(vm, "print(math.pi)"));
PRINT_TITLE("test eval");
check(pkpy_eval(vm, "math.pi"));
check(pkpy_to_float(vm, -1, &r_float));
printf("pi: %.2f\n", (float)r_float);
check(pkpy_pop(vm, 1));
check(pkpy_stack_size(vm) == 0);
PRINT_TITLE("test py_repr");
check(pkpy_eval(vm, "['1', 2, (3, '4')]"));
check(pkpy_py_repr(vm));
check(pkpy_to_string(vm, -1, &r_string));
puts(r_string);
check(pkpy_pop_top(vm));
check(pkpy_stack_size(vm) == 0);
return 0;
}

View File

@ -0,0 +1,112 @@
====== test basic exec ======
hello world!
====== test int methods ======
11
11
1
====== test float methods ======
11.125
11.125
====== test bool methods ======
False
0
====== test string methods ======
hello!
hello!
====== test none methods ======
None
====== test voidp methods ======
<void* at 0x7b>
123
====== test sizing and indexing ======
stack size 6
====== test error catching ======
successfully errored with this message:
File "main.py", line 1
let's make sure syntax errors get caught
SyntaxError: EOL while scanning string literal
====== test simple call ======
x : -1
====== test vararg call ======
vararg_x : 21
====== test keyword call ======
keyword_x : 4
keyword_x : 2
====== test return many ======
retmany_x : 1
retmany_x : 2
retmany_x : 3
successfully errored with this message:
TypeError: x() takes 2 positional arguments but 0 were given
['hello']
====== test bindings ======
12
====== test bindings 2 ======
2
5
4
3
2
1
====== test bindings fib ======
55
====== test error propagate ======
successfully errored with this message:
Traceback (most recent call last):
File "main.py", line 1
test_error_propagate()
NameError: catch me
====== test other errors ======
successfully errored with this message:
Traceback (most recent call last):
File "main.py", line 1
raise NameError('testing error throwing from python')
NameError: testing error throwing from python
====== test TypeError ======
successfully errored with this message:
TypeError: expected 'int', got 'float'
====== test complicated errors ======
Traceback (most recent call last):
File "main.py", line 1
test_error_propagate()
NameError: catch me
successfully errored with this message:
Traceback (most recent call last):
Exception: test direct error mechanism
successfully errored with this message:
Traceback (most recent call last):
File "main.py", line 1
test_nested_error()
File "main.py", line 1, in error_from_python
def error_from_python() : raise NotImplementedError()
NotImplementedError
====== test getattr/setattr ======
pi: 3.14
2
====== test eval ======
pi: 2.00
====== test py_repr ======
['1', 2, (3, '4')]

25
dependencies/pocketpy/cmake_build.py vendored Normal file
View File

@ -0,0 +1,25 @@
import os
import sys
import shutil
assert os.system("python prebuild.py") == 0
if not os.path.exists("build"):
os.mkdir("build")
os.chdir("build")
code = os.system("cmake .. -DPK_USE_CJSON=ON -DPK_ENABLE_OS=ON -DCMAKE_BUILD_TYPE=Release")
assert code == 0
code = os.system("cmake --build . --config Release")
assert code == 0
if sys.platform == "win32":
shutil.copy("Release/main.exe", "../main.exe")
shutil.copy("Release/pocketpy.dll", "../pocketpy.dll")
elif sys.platform == "darwin":
shutil.copy("main", "../main")
shutil.copy("libpocketpy.dylib", "../libpocketpy.dylib")
else:
shutil.copy("main", "../main")
shutil.copy("libpocketpy.so", "../libpocketpy.so")

11
dependencies/pocketpy/compile_flags.txt vendored Normal file
View File

@ -0,0 +1,11 @@
-xc++
-Wall
-W*
-std=c++17
-stdlib=libc++
-Iinclude/
-I3rd/cjson/include/
-I3rd/lua_bridge/include/

View File

@ -0,0 +1,19 @@
---
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.
!!!

View File

@ -0,0 +1,23 @@
---
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.

View File

@ -0,0 +1,3 @@
label: C-API
icon: code
order: 1

View File

@ -0,0 +1,42 @@
---
title: Introduction
icon: dot
order: 10
---
### What C-API is for
The C-APIs are designed for these purposes:
1. Your target platform does not support C++17. You compile pkpy into a static library and use its exported C-APIs.
2. You want to write a native module that can be imported via `__import__` at runtime. By using C-APIs, the module is portable across different compilers without C++ ABI compatibility issues.
Our C-APIs take a lot of inspiration from the lua C-APIs.
Methods return a `bool` indicating if the operation succeeded or not.
Special thanks for [@koltenpearson](https://github.com/koltenpearson)'s contribution.
!!!
C-APIs are always stable and backward compatible.
!!!
### Basic functions
+ `pkpy_vm* pkpy_new_vm(bool enable_os)`
Wraps `new VM(enable_os)` in C++.
+ `void pkpy_delete_vm(pkpy_vm*)`
Wraps `delete vm` in C++.
+ `bool pkpy_exec(pkpy_vm*, const char* source)`
Wraps `vm->exec`. Execute a string of source code.
+ `bool pkpy_exec_2(pkpy_vm*, const char* source, const char* filename, int mode, const char* module)`
Wraps `vm->exec_2`. Execute a string of source code with more options.
+ `void pkpy_set_main_argv(pkpy_vm*, int argc, char** argv)`
Wraps `vm->set_main_argv`. Set the `sys.argv` before executing scripts.

View File

@ -0,0 +1,21 @@
---
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`.

View File

@ -0,0 +1,153 @@
---
title: Stack Manipulation
icon: dot
order: 8
---
### Basic manipulation
+ `bool pkpy_dup(pkpy_vm*, int)`
Duplicate the value at the given index.
+ `bool pkpy_pop(pkpy_vm*, int)`
Pop `n` values from the stack.
+ `bool pkpy_pop_top(pkpy_vm*)`
Pop the top value from the stack.
+ `bool pkpy_dup_top(pkpy_vm*)`
Duplicate the top value on the stack.
+ `bool pkpy_rot_two(pkpy_vm*)`
Swap the top two values on the stack.
+ `int pkpy_stack_size(pkpy_vm*)`
Get the element count of the stack.
### Basic push, check and convert
+ `pkpy_push_xxx` pushes a value onto the stack.
+ `pkpy_is_xxx` checks if the value at the given index is of the given type.
+ `pkpy_to_xxx` converts the value at the given index to the given type.
Stack index is 0-based instead of 1-based. And it can be negative, which means the index is counted from the top of the stack.
```c
// int
PK_EXPORT bool pkpy_push_int(pkpy_vm*, int);
PK_EXPORT bool pkpy_is_int(pkpy_vm*, int i);
PK_EXPORT bool pkpy_to_int(pkpy_vm*, int i, int* out);
// float
PK_EXPORT bool pkpy_push_float(pkpy_vm*, double);
PK_EXPORT bool pkpy_is_float(pkpy_vm*, int i);
PK_EXPORT bool pkpy_to_float(pkpy_vm*, int i, double* out);
// bool
PK_EXPORT bool pkpy_push_bool(pkpy_vm*, bool);
PK_EXPORT bool pkpy_is_bool(pkpy_vm*, int i);
PK_EXPORT bool pkpy_to_bool(pkpy_vm*, int i, bool* out);
// string
PK_EXPORT bool pkpy_push_string(pkpy_vm*, pkpy_CString);
PK_EXPORT bool pkpy_is_string(pkpy_vm*, int i);
PK_EXPORT bool pkpy_to_string(pkpy_vm*, int i, pkpy_CString* out);
// void_p
PK_EXPORT bool pkpy_push_voidp(pkpy_vm*, void*);
PK_EXPORT bool pkpy_is_voidp(pkpy_vm*, int i);
PK_EXPORT bool pkpy_to_voidp(pkpy_vm*, int i, void** out);
// none
PK_EXPORT bool pkpy_push_none(pkpy_vm*);
PK_EXPORT bool pkpy_is_none(pkpy_vm*, int i);
```
### Special push
+ `pkpy_push_null(pkpy_vm*)`
Push a `PY_NULL` onto the stack. It is used for `pkpy_vectorcall`.
+ `pkpy_push_function(pkpy_vm*, const char* sig, pkpy_CFunction f)`
Push a function onto the stack. `sig` is the function signature, e.g. `add(a: int, b: int) -> int`. `f` is the function pointer.
+ `pkpy_push_module(pkpy_vm*, const char* name)`
Push a new module onto the stack. `name` is the module name. This is not `import`. It creates a new module object.
### Variable access
+ `bool pkpy_getattr(pkpy_vm*, pkpy_CName name)`
Push `b.<name>` onto the stack. Return false if the attribute is not found.
```
[b] -> [b.<name>]
```
+ `bool pkpy_setattr(pkpy_vm*, pkpy_CName name)`
Set `b.<name>` to the value at the top of the stack.
First push the value, then push `b`.
```
[value, b] -> []
```
+ `bool pkpy_getglobal(pkpy_vm*, pkpy_CName name)`
Push a global/builtin variable onto the stack. Return false if the variable is not found.
```
[] -> [value]
```
+ `bool pkpy_setglobal(pkpy_vm*, pkpy_CName name)`
Set a global variable to the value at the top of the stack.
```
[value] -> []
```
+ `bool pkpy_eval(pkpy_vm*, const char* source)`
Evaluate a string and push the result onto the stack.
```
[] -> [result]
```
+ `bool pkpy_unpack_sequence(pkpy_vm*, int size)`
Unpack a sequence at the top of the stack. `size` is the element count of the sequence.
```
[a] -> [a[0], a[1], ..., a[size - 1]]
```
+ `bool pkpy_get_unbound_method(pkpy_vm*, pkpy_CName name)`
It is used for method call.
Get an unbound method from the object at the top of the stack. `name` is the method name.
Also push the object as self.
```
[obj] -> [obj.<name> self]
```
+ `bool pkpy_py_repr(pkpy_vm*)`
Get the repr of the value at the top of the stack.
```
[value] -> [repr(value)]
```

267
dependencies/pocketpy/docs/bindings.md vendored Normal file
View File

@ -0,0 +1,267 @@
---
icon: cpu
title: Write Bindings
order: 18
---
In order to use a C/C++ library in python, you need to write bindings for it.
## Manual bindings
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`.
```cpp
typedef PyObject* (*NativeFuncC)(VM*, ArgsView);
```
+ 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 `PyObject*`, which should not be `nullptr`. If there is no return value, return `vm->None`.
### Bind a function or method
Use `vm->bind` to bind a function or method.
+ `PyObject* bind(PyObject*, const char* sig, NativeFuncC)`
+ `PyObject* bind(PyObject*, 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<int>(vm, args[0]);
int b = py_cast<int>(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<int>(vm, args[0]);
int b = py_cast<int>(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 `PyObject*` 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<YourVM*>(_vm);
return py_var(vm, vm->x);
});
return 0;
}
```
The 2nd way is to use `vm->bind`'s last parameter `userdata`, you can store a POD type smaller than 8 bytes.
And use `lambda_get_userdata<T>(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<int>(args.begin());
return py_var(vm, x);
}, x); // capture x
```
The 3rd way is to change the macro `PK_ENABLE_STD_FUNCTION` in `config.h`:
```cpp
#define PK_ENABLE_STD_FUNCTION 0 // => 1
```
Then you can use standard capture list in lambda.
### Bind a struct
Assume you have a struct `Point` declared as follows.
```cpp
struct Point{
int x;
int y;
}
```
You can write a wrapper class `wrapped__Point`. Add `PY_CLASS` macro into your wrapper class and implement a static function `_register`.
Inside the `_register` function, do bind methods and properties.
```cpp
PY_CLASS(T, mod, name)
// T is the struct type in cpp
// mod is the module name in python
// name is the class name in python
```
### Example
```cpp
struct wrapped__Point{
// special macro for wrapper class
PY_CLASS(wrapped__Point, builtins, Point)
// ^T ^module ^name
// wrapped value
Point value;
// special method _ returns a pointer of the wrapped value
Point* _() { return &value; }
// define default constructors
wrapped__Point() = default;
wrapped__Point(const wrapped__Point&) = default;
// define wrapped constructor
wrapped__Point(Point value){
this->value = value;
}
static void _register(VM* vm, PyObject* mod, PyObject* type){
// enable default constructor and struct-like methods
// if you don't use this, you must bind a `__new__` method as constructor
PY_STRUCT_LIKE(wrapped__Point)
// wrap field x
PY_FIELD(wrapped__Point, "x", _, x)
// wrap field y
PY_FIELD(wrapped__Point, "y", _, y)
// __init__ method
vm->bind(type, "__init__(self, x, y)", [](VM* vm, ArgsView args){
wrapped__Point& self = _py_cast<wrapped__Point&>(vm, args[0]);
self.value.x = py_cast<int>(vm, args[1]);
self.value.y = py_cast<int>(vm, args[2]);
return vm->None;
});
// other custom methods
// ...
}
}
int main(){
VM* vm = new VM();
// register the wrapper class somewhere
wrapped__Point::register_class(vm, vm->builtins);
// use the Point class
vm->exec("a = 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 `PyObject*` in its fields, you need to handle gc for them.
```cpp
struct Container{
PY_CLASS(Container, builtins, Container)
PyObject* a;
std::vector<PyObject*> b;
// ...
}
```
Add a magic method `_gc_mark() const` to your custom type.
```cpp
struct Container{
PY_CLASS(Container, builtins, Container)
PyObject* a;
std::vector<PyObject*> b;
// ...
void _gc_mark() const{
// mark a
if(a) PK_OBJ_MARK(a);
// mark elements in b
for(PyObject* obj : b){
if(obj) PK_OBJ_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
You may see somewhere in the code that `vm->bind_method<>` or `vm->bind_func<>` is used.
They are old style binding functions and are deprecated.
It is recommended to use `vm->bind`.
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
PyObject* f_add(VM* vm, PyObject* lhs, PyObject* rhs){
int a = py_cast<int>(vm, lhs);
int b = py_cast<int>(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_method<1>(type, "__add__", ...)`.
## Automatic bindings
pkpy supports automatic binding generation **only for C libraries**.
See [pkpy-bindings](https://github.com/blueloveTH/pkpy-bindings) for details.
It takes a C header file and generates a python module stub (`*.pyi`) and a C++ binding file (`*.cpp`).
## 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`.

View File

@ -0,0 +1,104 @@
---
icon: cpu
title: Reuse Lua Bindings
order: 17
---
!!!
This feature is available in `v1.4.0` or higher.
!!!
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;
}
```

342
dependencies/pocketpy/docs/cheatsheet.md vendored Normal file
View File

@ -0,0 +1,342 @@
---
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
PyObject* obj = vm->eval("123");
std::cout << py_cast<int>(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(Exception& e){
std::cerr << e.summary() << std::endl;
}
```
## Interop with native types
Create primitive objects
```cpp
PyObject* 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");
PyObject* 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"));
PyObject* 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"));
PyObject* obj = py_var(vm, std::move(d));
```
Get native types from python objects
```cpp
PyObject* obj;
i64 a = py_cast<i64>(vm, obj);
f64 b = py_cast<f64>(vm, obj);
Str& c = py_cast<Str&>(vm, obj); // reference cast
bool d = py_cast<bool>(vm, obj);
Tuple& e = py_cast<Tuple&>(vm, obj); // reference cast
List& f = py_cast<List&>(vm, obj); // reference cast
Dict& g = py_cast<Dict&>(vm, obj); // reference cast
```
Get native types without type checking
```cpp
// unsafe version 1 (for int and float you must use `_py_cast`)
i64 a = _py_cast<i64>(vm, obj);
f64 b = _py_cast<f64>(vm, obj);
Tuple& c = _py_cast<Tuple&>(vm, obj);
// unsafe version 2 (for others, you can use both versions)
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
PyObject* int_t = vm->_t(vm->tp_int);
PyObject* float_t = vm->_t(vm->tp_float);
PyObject* object_t = vm->_t(vm->tp_object);
PyObject* tuple_t = vm->_t(vm->tp_tuple);
PyObject* list_t = vm->_t(vm->tp_list);
```
Access extended python types
```cpp
// VoidP was defined by `PY_CLASS` macro
PyObject* voidp_t = VoidP::_type(vm);
```
Check if an object is a python type
```cpp
PyObject* obj;
bool ok = is_type(obj, vm->tp_int); // check if obj is an int
```
Get the type of a python object
```cpp
PyObject* obj = py_var(vm, 1);
PyObject* t = vm->_t(obj); // <class 'int'>
```
Convert a type object into a type index
```cpp
PyObject* 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
PyObject* 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
PyObject* obj = vm->exec("MyClass(1, 2)");
PyObject* 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
PyObject* f_add = vm->eval("add");
PyObject* ret = vm->call(f_add, py_var(vm, 1), py_var(vm, 2));
std::cout << py_cast<int>(vm, ret); // 3
```
Call a method
```cpp
PyObject* obj = vm->exec("MyClass(1, 2)");
PyObject* 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");
PyObject* ret = vm->call_method(obj, m_sum);
```
## Special operations
Compare two python objects
```cpp
PyObject* obj1 = py_var(vm, 1);
PyObject* obj2 = py_var(vm, 2);
bool ok = vm->py_eq(obj1, obj2);
```
Convert a python object to string
```cpp
PyObject* obj = py_var(vm, 123);
PyObject* s = vm->py_str(obj); // 123
```
Get the string representation of a python object
```cpp
PyObject* obj = py_var(vm, "123");
std::cout << vm->py_repr(obj); // '123'
```
Get the JSON representation of a python object
```cpp
PyObject* obj = py_var(vm, 123);
std::cout << vm->py_json(obj); // "123"
```
Get the hash value of a python object
```cpp
PyObject* obj = py_var(vm, 1);
i64 h = vm->py_hash(obj); // 1
```
Get the iterator of a python object
```cpp
PyObject* obj = vm->eval("range(3)");
PyObject* iter = vm->py_iter(obj);
```
Get the next item of an iterator
```cpp
PyObject* obj = vm->py_next(iter);
if(obj == vm->StopIteration){
// end of iteration
}
```
Convert a python iterable to a list
```cpp
PyObject* obj = vm->eval("range(3)");
PyObject* 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<int>(vm, args[0]);
int b = py_cast<int>(vm, args[1]);
return py_var(vm, a + b);
});
// Bind a native function with docstring
vm->bind(obj,
"add(a: int, b: int) -> int",
"add two integers", [](VM* vm, ArgsView args){
int a = py_cast<int>(vm, args[0]);
int b = py_cast<int>(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<int>(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));
```

View File

@ -0,0 +1,106 @@
---
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<T>`
Use a `_` suffix to indicate a type is a shared pointer.
```cpp
using CodeObject_ = std::shared_ptr<CodeObject>;
CodeObject_ co = std::make_shared<CodeObject>();
```

View File

@ -0,0 +1,92 @@
---
icon: dot
title: Basic Features
order: 100
---
Check this [Cheatsheet](https://reference.pocketpy.dev/python.html)
for a quick overview of the supported features.
The following table shows the basic features of pkpy with respect to [cpython](https://github.com/python/cpython).
The features marked with `YES` are supported, and the features marked with `NO` are not supported.
| Name | Example | Supported |
| --------------- | ------------------------------- | --------- |
| If Else | `if..else..elif` | YES |
| Loop | `for/while/break/continue` | YES |
| Function | `def f(x,*args,y=1):` | YES |
| Subclass | `class A(B):` | YES |
| List | `[1, 2, 'a']` | YES |
| ListComp | `[i for i in range(5)]` | YES |
| Slice | `a[1:2], a[:2], a[1:]` | YES |
| Tuple | `(1, 2, 'a')` | YES |
| Dict | `{'a': 1, 'b': 2}` | YES |
| F-String | `f'value is {x}'` | YES |
| Unpacking | `a, b = 1, 2` | YES |
| Star Unpacking | `a, *b = [1, 2, 3]` | YES |
| Exception | `raise/try..catch..finally` | YES |
| Dynamic Code | `eval()/exec()` | YES |
| Reflection | `hasattr()/getattr()/setattr()` | YES |
| Import | `import/from..import` | YES |
| Context Block | `with <expr> as <id>:` | YES |
| Type Annotation | `def f(a:int, b:float=1)` | YES |
| Generator | `yield i` | YES |
| Decorator | `@cache` | YES |
## Supported magic methods
#### Unary operators
+ `__repr__`
+ `__str__`
+ `__hash__`
+ `__len__`
+ `__iter__`
+ `__next__`
+ `__neg__`
#### Logical operators
+ `__eq__`
+ `__lt__`
+ `__le__`
+ `__gt__`
+ `__ge__`
+ `__contains__`
#### Binary operators
+ `__add__`
+ `__radd__`
+ `__sub__`
+ `__rsub__`
+ `__mul__`
+ `__rmul__`
+ `__truediv__`
+ `__floordiv__`
+ `__mod__`
+ `__pow__`
+ `__matmul__`
+ `__lshift__`
+ `__rshift__`
+ `__and__`
+ `__or__`
+ `__xor__`
+ `__invert__`
#### Indexer
+ `__getitem__`
+ `__setitem__`
+ `__delitem__`
#### Specials
+ `__new__`
+ `__init__`
+ `__call__`
+ `__divmod__`
+ `__enter__`
+ `__exit__`
+ `__name__`
+ `__all__`

View File

@ -0,0 +1,24 @@
---
icon: dot
title: Debugging
---
!!!
This feature is available in `v1.4.5` or higher.
!!!
You can invoke `breakpoint()` in your python code to start a PDB-like session.
The following commands are supported:
+ `h, help`: show this help message
+ `q, quit`: exit the debugger
+ `n, next`: execute next line
+ `s, step`: step into
+ `w, where`: show current stack frame
+ `c, continue`: continue execution
+ `a, args`: show local variables
+ `l, list`: show lines around current line
+ `ll, longlist`: show all lines
+ `p, print <expr>`: evaluate expression
+ `!, execute statement`: execute statement

View File

@ -0,0 +1,42 @@
---
icon: dot
title: Comparison with CPython
order: 99
---
[cpython](https://github.com/python/cpython) is the reference implementation of the Python programming language. It is written in C and is the most widely used implementation of Python.
## The design goal
**pkpy aims to be an alternative to lua for
game scripting, not cpython for general purpose programming.**
+ For syntax and semantics, pkpy is designed to be as close to cpython as possible.
+ For ecosystem and others, pkpy is not compatible with cpython.
pkpy supports most of the syntax and semantics of python.
For performance and simplicity, some features are not implemented, or behave differently.
The easiest way to test a feature is to [try it on your browser](https://pocketpy.dev/static/web/).
## Unimplemented features
1. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
2. `__slots__` in class definition.
3. `else` clause in try..except.
4. Inplace methods like `__iadd__` and `__imul__`.
5. `__del__` in class definition.
6. Multiple inheritance.
## Different behaviors
1. positional and keyword arguments are strictly evaluated.
2. `++i` and `--j` is an increment/decrement statement, not an expression.
3. `int` does not derive from `bool`.
4. `int` is 64-bit. You can use `long` type explicitly for arbitrary sized integers.
5. `__ne__` is not required. Define `__eq__` is enough.
6. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/pocketpy/pocketpy/issues/55).
7. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
8. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
9. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
10. `str.split` and `str.splitlines` will remove all empty entries.
11. `__getattr__`, `__setattr__` and `__delattr__` can only be set in cpp.

View File

@ -0,0 +1,30 @@
---
icon: dot
title: Goto Statement
---
pkpy supports goto/label just like C. You are allowed to **change the control flow unconditionally**.
## Define a label
```
== <identifier> ==
```
## Goto a label
```
-> <identifier>
```
## Example
```python
for i in range(10):
for j in range(10):
for k in range(10):
-> exit
== exit ==
print('exit')
```

View File

@ -0,0 +1,23 @@
---
icon: dot
title: Increment Statement
---
pkpy provides `++i` and `--j` statements to operate a simple named `int` variable.
+ `++i` is equivalent to `i+=1`, but much faster
+ `--j` is equivalent to `j-=1`, but much faster
## Example
```python
a = 1
++a
assert a == 2
def f(a):
--a
return a
assert f(3) == 2
```

View File

@ -0,0 +1,3 @@
icon: star
order: 16
label: "Features"

View File

@ -0,0 +1,27 @@
---
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
```

View File

@ -0,0 +1,145 @@
---
icon: dot
title: Precompiling
---
pkpy allows you to precompile python code into two special forms, which can be executed later.
### In-memory precompilation
You can use `vm->compile` to compile your source code into a `CodeObject_` object.
This object can be executed later by `vm->_exec`.
```cpp
CodeObject_ code = vm->compile("print('Hello, world!')", "<string>", EXEC_MODE);
vm->_exec(code); // Hello, world!
```
This `CodeObject_` object is a very non-generic form of the compiled code,
which is an in-memory form. Very efficient, but not portable.
You are not able to save it to a file or load it from a file.
### String precompilation
In order to save the compiled code to a file, you need to use `vm->precompile`.
It does some basic preprocessing and outputs the result as a human-readable string.
```cpp
// precompile the source code into a string
Str source = vm->precompile("print('Hello, world!')", "<string>", EXEC_MODE);
CodeObject code = vm->compile(source, "<string>", EXEC_MODE);
vm->_exec(code); // Hello, world!
```
You can also use python's `compile` function to achieve the same effect.
```python
code = compile("print('Hello, world!')", "<string>", "exec")
exec(code) # Hello, world!
```
Let's take a look at the precompiled string.
```python
print(code)
```
```txt
pkpy:1.4.5
0
=1
print
=6
5,1,0,
6,0,,,
42,,1,
8,,,S48656c6c6f2c20776f726c6421
43,,0,
3,,,
```
Comparing with **In-memory precompilation**,
**String precompilation** drops most of the information of the original source code.
It has an encryption effect, which can protect your source code from being stolen.
This also means there is no source line information when an error occurs.
```python
src = """
def f(a, b):
return g(a, b)
def g(a, b):
c = f(a, b)
d = g(a, b)
return c + d
"""
code = compile(src, "<exec>", "exec")
exec(code)
f(1, 2)
```
You will get this (without source line information):
```txt
Traceback (most recent call last):
File "<exec>", line 3, in f
File "<exec>", line 6, in g
File "<exec>", line 3, in f
File "<exec>", line 6, in g
File "<exec>", line 3, in f
File "<exec>", line 6, in g
File "<exec>", line 3, in f
StackOverflowError
```
instead of this (with source line information):
```txt
Traceback (most recent call last):
File "<stdin>", line 2, in f
return g(a, b)
File "<stdin>", line 2, in g
c = f(a, b)
File "<stdin>", line 2, in f
return g(a, b)
File "<stdin>", line 2, in g
c = f(a, b)
File "<stdin>", line 2, in f
return g(a, b)
File "<stdin>", line 2, in g
c = f(a, b)
File "<stdin>", line 2, in f
return g(a, b)
StackOverflowError
```
!!!
String compilation has no guarantee of compatibility between different versions of pkpy.
!!!
You can use this snnipet to convert every python file in a directory into precompiled strings.
```python
# precompile.py
import sys, os
def precompile(filepath: str):
"""Precompile a python file inplace"""
print(filepath)
with open(filepath, 'r') as f:
source = f.read()
source = compile(source, filepath, 'exec')
with open(filepath, 'w') as f:
f.write(source)
def traverse(root: str):
"""Traverse a directory and precompile every python file"""
for entry in os.listdir(root):
entrypath = os.path.join(root, entry)
if os.path.isdir(entrypath):
traverse(entrypath)
elif entrypath.endswith(".py"):
precompile(entrypath)
```

View File

@ -0,0 +1,12 @@
---
icon: dot
title: Undefined Behaviour
---
These are the undefined behaviours of pkpy. The behaviour of pkpy is undefined if you do the following things.
1. Delete a builtin object. For example, `del int.__add__`.
2. Call an unbound method with the wrong type of `self`. For example, `int.__add__('1', 2)`.
3. Type `T`'s `__new__` returns an object that is not an instance of `T`.
4. Call `__new__` with a type that is not a subclass of `type`.
5. `__eq__`, `__lt__` or `__contains__`, etc.. returns a value that is not a boolean.

View File

@ -0,0 +1,58 @@
---
icon: rocket
order: 10
label: "Application Guide"
---
Before starting, please read the [Ideas](ideas.md) page and choose a project you are interested in.
Set up a C++ compiler, clone pocketpy sources from github and try to build.
This helps you confirm that your skills and experience match the requirements of the project.
### Build guide for beginners
First, you need to install these tools:
1. Python(>= 3.8), I am sure you already have it.
2. A C++ compiler, such as GCC, Clang or MSVC. If you are on Linux, `gcc` and `g++` are already installed. If you are on Windows, you can install Visual Studio with C++ development tools.
3. CMake(>= 3.10), a cross-platform build tool. You can use `pip install cmake` to install it.
Then, clone pocketpy sources from github and try to build:
```bash
git clone https://github.com/pocketpy/pocketpy
cd pocketpy
python cmake_build.py
```
If everything goes well, you will get a `main` executable (`main.exe` on Windows) in the root directory of pocketpy.
Simply run it and you will enter pocketpy's REPL.
```txt
pocketpy 1.4.0 (Jan 24 2024, 12:39:13) [32 bit] on emscripten
https://github.com/pocketpy/pocketpy
Type "exit()" to exit.
>>>
>>> "Hello, world"
'Hello, world'
```
### Application guide
**Your need to send an email to `blueloveth@foxmail.com` with the following information:**
1. A brief introduction about yourself, including the most related open sourced project you have worked on before. It is highly recommended to attach your Github profile link.
2. A technical proposal for the project you are interested in working on, including:
+ Your understanding of the project.
+ The technical approach/architecture you will adopt.
+ The challenges you might face and how you will overcome them.
3. A timeline for the project, including the milestones and deliverables.
4. Other information required by the Google Summer of Code program.
### Coding style guide
See [Coding Style Guide](../coding_style_guide.md).
### Contact us
If you have any questions, you can join our [Discord](https://discord.gg/WWaq72GzXv)
or contact me via email.
We are glad to help you with your application.

View File

@ -0,0 +1,40 @@
---
icon: light-bulb
order: 0
label: "Project Ideas"
---
### Implement pybind11 for bindings
+ Difficulty Level: 5/5 (Hard)
+ Skill: Advanced C++ with metaprogramming; Python
+ Project Length: Medium (175 hours)
pocketpy has provided a low-level API for creating bindings. It is fast, lightweight and easy to debug.
However, it still requires a lot of boilerplate code to create bindings for complex C++ classes.
The community has long expected a high-level API for creating bindings.
[pybind11](https://github.com/pybind/pybind11)
is the most popular C++ library for creating Python bindings for CPython. A bunch of Python libraries are using it. pybind11 adopts a template metaprogramming approach to automatically generate bindings for C++ classes.
Our goal is to introduce a pybind11 compatible solution to pocketpy as an alternative way to create bindings
for functions and classes.
You can use C\+\+17 features to implement it, instead of C++11 used in pybind11.
See https://github.com/pocketpy/pocketpy/issues/216 for more details.
### Add `numpy` module
+ Difficulty Level: 4/5 (Intermediate)
+ Skill: Intermediate C++; Python; Linear Algebra
+ Project Length: Medium (175 hours)
Though pocketpy is designed for game scripting,
some people are using it for scientific computing.
It would be nice to have a `numpy` module in pocketpy.
`numpy` is a huge project.
Our goal is to implement a most commonly used subset of `numpy` in pocketpy.
You can mix C++ and Python code to simplify the overall workloads.
See https://github.com/pocketpy/pocketpy/issues/202 for more details.

View File

@ -0,0 +1,2 @@
order: 19
label: "GSoC"

55
dependencies/pocketpy/docs/index.md vendored Normal file
View File

@ -0,0 +1,55 @@
---
icon: home
label: Welcome
---
# Welcome to pocketpy
pkpy is a lightweight(~15K LOC) Python interpreter for game scripting, built on C++17 with STL.
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
```python
def is_prime(x):
if x < 2:
return False
for i in range(2, x):
if x % i == 0:
return False
return True
primes = [i for i in range(2, 20) if is_prime(i)]
print(primes)
# [2, 3, 5, 7, 11, 13, 17, 19]
```
## Supported platforms
pkpy should work on any platform with a C++17 compiler.
These platforms are officially tested.
+ Windows 64-bit
+ Linux 64-bit / 32-bit
+ macOS 64-bit
+ Android 64-bit / 32-bit
+ iOS 64-bit
+ Emscripten 32-bit
+ Raspberry Pi OS 64-bit
## Star the repo
If you find pkpy useful, consider [star this repository](https://github.com/blueloveth/pocketpy) (●'◡'●)
## Sponsor this project
You can sponsor this project via these ways.
+ [Github Sponsors](https://github.com/sponsors/blueloveTH)
+ [Buy me a coffee](https://www.buymeacoffee.com/blueloveth)
Your sponsorship will help us develop pkpy continuously.

33
dependencies/pocketpy/docs/license.md vendored Normal file
View File

@ -0,0 +1,33 @@
---
icon: verified
order: -15
label: License
---
# License
pkpy is licensed under the [MIT License](http://opensource.org/licenses/MIT).
```
MIT License
Copyright (c) 2024 blueloveTH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

View File

@ -0,0 +1,19 @@
---
icon: package
label: array2d
---
Efficient general-purpose 2D array.
https://github.com/pocketpy/pocketpy/blob/main/include/typings/array2d.pyi
## Example
```python
from array2d import array2d
a = array2d(3, 4, default=0)
a[1, 2] = 5
print(a[1, 2]) # 5
```

View File

@ -0,0 +1,13 @@
---
icon: package
label: base64
---
### `base64.b64encode(b: bytes) -> bytes`
Encode bytes-like object `b` using the standard Base64 alphabet.
### `base64.b64decode(b: bytes) -> bytes`
Decode Base64 encoded bytes-like object `b`.

View File

@ -0,0 +1,24 @@
---
icon: package
label: bisect
---
### `bisect.bisect_left(a, x)`
Return the index where to insert item `x` in list `a`, assuming `a` is sorted.
### `bisect.bisect_right(a, x)`
Return the index where to insert item `x` in list `a`, assuming `a` is sorted.
### `bisect.insort_left(a, x)`
Insert item `x` in list `a`, and keep it sorted assuming `a` is sorted.
If x is already in a, insert it to the left of the leftmost x.
### `bisect.insort_right(a, x)`
Insert item `x` in list `a`, and keep it sorted assuming `a` is sorted.
If x is already in a, insert it to the right of the rightmost x.

View File

@ -0,0 +1,8 @@
---
icon: package
label: c
---
Interop with pointers and C structs.
https://github.com/pocketpy/pocketpy/blob/main/include/typings/c.pyi

View File

@ -0,0 +1,12 @@
---
icon: package
label: cmath
---
!!!
This module is experimental and may have bugs or other issues.
!!!
Mathematical functions for complex numbers.
https://docs.python.org/3/library/cmath.html

View File

@ -0,0 +1,17 @@
---
icon: package
label: collections
---
### `collections.Counter(iterable)`
Return a `dict` containing the counts of each element in `iterable`.
### `collections.deque`
A double-ended queue.
### `collections.defaultdict`
A dictionary that returns a default value when a key is not found.

View File

@ -0,0 +1,8 @@
---
icon: package
label: colorsys
---
The same as the standard library module `colorsys` in python 3.11.
https://github.com/python/cpython/blob/3.11/Lib/colorsys.py

View File

@ -0,0 +1,33 @@
---
icon: package
label: csv
---
### `csv.reader(csvfile: list[str]) -> list[list]`
Parse a CSV file into a list of lists.
### `csv.DictReader(csvfile: list[str]) -> list[dict]`
Parse a CSV file into a list of dictionaries.
## Example
```python
import csv
data = """a,b,c
1,2,3
"""
print(csv.reader(data.splitlines()))
# [
# ['a', 'b', 'c'],
# ['1', '2', '3']
# ]
print(csv.DictReader(data.splitlines()))
# [
# {'a': '1', 'b': '2', 'c': '3'}
# ]
```

View File

@ -0,0 +1,14 @@
---
icon: package
label: dataclasses
---
### `dataclasses.dataclass`
A decorator that is used to add generated special method to classes, including `__init__`, `__repr__` and `__eq__`.
### `dataclasses.asdict(obj) -> dict`
Convert a dataclass instance to a dictionary.

View File

@ -0,0 +1,12 @@
---
icon: package
label: datetime
---
### `datetime.now()`
Returns the current date and time as a `datetime` object.
### `date.today()`
Returns the current local date as a `date` object.

View File

@ -0,0 +1,38 @@
---
icon: package
label: easing
---
Python wrapper for [easing functions](https://easings.net/).
+ `easing.Linear(t: float) -> float`
+ `easing.InSine(t: float) -> float`
+ `easing.OutSine(t: float) -> float`
+ `easing.InOutSine(t: float) -> float`
+ `easing.InQuad(t: float) -> float`
+ `easing.OutQuad(t: float) -> float`
+ `easing.InOutQuad(t: float) -> float`
+ `easing.InCubic(t: float) -> float`
+ `easing.OutCubic(t: float) -> float`
+ `easing.InOutCubic(t: float) -> float`
+ `easing.InQuart(t: float) -> float`
+ `easing.OutQuart(t: float) -> float`
+ `easing.InOutQuart(t: float) -> float`
+ `easing.InQuint(t: float) -> float`
+ `easing.OutQuint(t: float) -> float`
+ `easing.InOutQuint(t: float) -> float`
+ `easing.InExpo(t: float) -> float`
+ `easing.OutExpo(t: float) -> float`
+ `easing.InOutExpo(t: float) -> float`
+ `easing.InCirc(t: float) -> float`
+ `easing.OutCirc(t: float) -> float`
+ `easing.InOutCirc(t: float) -> float`
+ `easing.InBack(t: float) -> float`
+ `easing.OutBack(t: float) -> float`
+ `easing.InOutBack(t: float) -> float`
+ `easing.InElastic(t: float) -> float`
+ `easing.OutElastic(t: float) -> float`
+ `easing.InOutElastic(t: float) -> float`
+ `easing.InBounce(t: float) -> float`
+ `easing.OutBounce(t: float) -> float`
+ `easing.InOutBounce(t: float) -> float`

View File

@ -0,0 +1,24 @@
---
icon: package
label: enum
---
### `enum.Enum`
Base class for creating enumerated constants.
Example:
```python
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color.RED) # Color.RED
print(Color.RED.name) # 'RED'
print(Color.RED.value) # 1
```

View File

@ -0,0 +1,16 @@
---
icon: package
label: functools
---
### `functools.cache`
A decorator that caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned, and not re-evaluated.
### `functools.reduce(function, sequence, initial=...)`
Apply a function of two arguments cumulatively to the items of a sequence, from left to right, so as to reduce the sequence to a single value. For example, `functools.reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])` calculates `((((1+2)+3)+4)+5)`. The left argument, `x`, is the accumulated value and the right argument, `y`, is the update value from the sequence. If the optional `initial` is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty.
### `functools.partial(f, *args, **kwargs)`
Return a new partial object which when called will behave like `f` called with the positional arguments `args` and keyword arguments `kwargs`. If more arguments are supplied to the call, they are appended to `args`. If additional keyword arguments are supplied, they extend and override `kwargs`.

View File

@ -0,0 +1,9 @@
---
icon: package
label: gc
---
### `gc.collect()`
Invoke the garbage collector.

View File

@ -0,0 +1,24 @@
---
icon: package
label: heapq
---
### `heapq.heappush(heap, item)`
Push the value `item` onto the heap, maintaining the heap invariant.
### `heapq.heappop(heap)`
Pop and return the smallest item from the heap, maintaining the heap invariant. If the heap is empty, IndexError is raised. To access the smallest item without popping it, use `heap[0]`.
### `heapq.heapify(x)`
Transform list `x` into a heap, in-place, in linear time.
### `heapq.heappushpop(heap, item)`
Push `item` on the heap, then pop and return the smallest item from the heap. The combined action runs more efficiently than `heappush()` followed by a separate `heappop()`.
### `heapq.heapreplace(heap, item)`
Pop and return the smallest item from the heap, and also push the new item. The heap size doesnt change. If the heap is empty, IndexError is raised.

View File

@ -0,0 +1,2 @@
icon: package
order: 10

View File

@ -0,0 +1,42 @@
---
icon: package-dependencies
label: io
---
!!!
This module is optional. Set `PK_ENABLE_OS` to `1` to enable it.
!!!
### `io.FileIO.read(size=-1) -> bytes | str`
Read up to `size` bytes from the file. If `size` is negative or omitted, read until EOF.
### `io.FileIO.write(data: bytes | str)`
Write the given data to the file.
### `io.FileIO.seek(offset, whence=0) -> int`
Change the file position to the given offset. The `whence` argument is optional and defaults to `0` (absolute file positioning); other values are `1` (seek relative to the current position) and `2` (seek relative to the file's end).
### `io.FileIO.tell() -> int`
Return the current file position.
### `io.FileIO.close()`
Close the file.
### `io.SEEK_SET`
Seek from the beginning of the file.
### `io.SEEK_CUR`
Seek from the current position.
### `io.SEEK_END`
Seek from the end of the file.

View File

@ -0,0 +1,8 @@
---
icon: package
label: itertools
---
### `itertools.zip_longest(a, b)`
Returns an iterator that aggregates elements from the input iterables. If the input iterables are of different lengths, missing values are filled-in with `None`.

View File

@ -0,0 +1,19 @@
---
icon: package
label: json
---
pkpy has two JSON modules.
1. The built-in JSON module is always available and can be imported via `import json`.
2. After `v1.2.7`, you can set `PK_USE_CJSON` to `ON` in CMakeLists.txt to enable an alternative JSON module `cjson`.
**Their interfaces are the same.** `cjson` is faster while the built-in `json` is more stable since it was developed earlier.
### `json.loads(data: str | bytes)`
Decode a JSON string into a python object.
### `json.dumps(obj) -> str`
Encode a python object into a JSON string.

View File

@ -0,0 +1,178 @@
---
icon: package
label: linalg
---
Provide `mat3x3`, `vec2`, `vec3` and `vec4` types.
This classes adopt `torch`'s naming convention. Methods with `_` suffix will modify the instance itself.
https://github.com/pocketpy/pocketpy/blob/main/include/typings/linalg.pyi
```python
from typing import overload
from c import _StructLike, float_p
class vec2(_StructLike['vec2']):
x: float
y: float
def __init__(self, x: float, y: float) -> None: ...
def __add__(self, other: vec2) -> vec2: ...
def __sub__(self, other: vec2) -> vec2: ...
@overload
def __mul__(self, other: float) -> vec2: ...
@overload
def __mul__(self, other: vec2) -> vec2: ...
def __rmul__(self, other: float) -> vec2: ...
def __truediv__(self, other: float) -> vec2: ...
def dot(self, other: vec2) -> float: ...
def cross(self, other: vec2) -> float: ...
def length(self) -> float: ...
def length_squared(self) -> float: ...
def normalize(self) -> vec2: ...
def rotate(self, radians: float) -> vec2: ...
def copy_(self, other: vec2) -> None: ...
def normalize_(self) -> None: ...
def rotate_(self, radians: float) -> None: ...
@staticmethod
def angle(__from: vec2, __to: vec2) -> float:
"""Returns the angle in radians between vectors `from` and `to`.
The result range is `[-pi, pi]`.
+ if y axis is top to bottom, positive value means clockwise
+ if y axis is bottom to top, positive value means counter-clockwise
"""
@staticmethod
def smooth_damp(current: vec2, target: vec2, current_velocity_: vec2, smooth_time: float, max_speed: float, delta_time: float) -> vec2:
...
class vec3(_StructLike['vec3']):
x: float
y: float
z: float
def __init__(self, x: float, y: float, z: float) -> None: ...
def __add__(self, other: vec3) -> vec3: ...
def __sub__(self, other: vec3) -> vec3: ...
@overload
def __mul__(self, other: float) -> vec3: ...
@overload
def __mul__(self, other: vec3) -> vec3: ...
def __rmul__(self, other: float) -> vec3: ...
def __truediv__(self, other: float) -> vec3: ...
def dot(self, other: vec3) -> float: ...
def cross(self, other: vec3) -> float: ...
def length(self) -> float: ...
def length_squared(self) -> float: ...
def normalize(self) -> vec3: ...
def copy_(self, other: vec3) -> None: ...
def normalize_(self) -> None: ...
class vec4(_StructLike['vec4']):
x: float
y: float
z: float
w: float
def __init__(self, x: float, y: float, z: float, w: float) -> None: ...
def __add__(self, other: vec4) -> vec4: ...
def __sub__(self, other: vec4) -> vec4: ...
@overload
def __mul__(self, other: float) -> vec4: ...
@overload
def __mul__(self, other: vec4) -> vec4: ...
def __rmul__(self, other: float) -> vec4: ...
def __truediv__(self, other: float) -> vec4: ...
def dot(self, other: vec4) -> float: ...
def length(self) -> float: ...
def length_squared(self) -> float: ...
def normalize(self) -> vec4: ...
def copy_(self, other: vec4) -> None: ...
def normalize_(self) -> None: ...
class mat3x3(_StructLike['mat3x3']):
_11: float
_12: float
_13: float
_21: float
_22: float
_23: float
_31: float
_32: float
_33: float
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, _11, _12, _13, _21, _22, _23, _31, _32, _33) -> None: ...
@overload
def __init__(self, a: list[float]): ...
def determinant(self) -> float: ...
def invert(self) -> mat3x3: ...
def transpose(self) -> mat3x3: ...
def __getitem__(self, index: tuple[int, int]) -> float: ...
def __setitem__(self, index: tuple[int, int], value: float) -> None: ...
def __add__(self, other: mat3x3) -> mat3x3: ...
def __sub__(self, other: mat3x3) -> mat3x3: ...
def __mul__(self, other: float) -> mat3x3: ...
def __rmul__(self, other: float) -> mat3x3: ...
def __truediv__(self, other: float) -> mat3x3: ...
def __invert__(self) -> mat3x3: ...
@overload
def __matmul__(self, other: mat3x3) -> mat3x3: ...
@overload
def __matmul__(self, other: vec3) -> vec3: ...
def matmul(self, other: mat3x3, out: mat3x3 = None) -> mat3x3 | None: ...
def copy_(self, other: mat3x3) -> None: ...
def invert_(self) -> None: ...
def transpose_(self) -> None: ...
@staticmethod
def zeros() -> mat3x3: ...
@staticmethod
def ones() -> mat3x3: ...
@staticmethod
def identity() -> mat3x3: ...
# affine transformations
@staticmethod
def trs(t: vec2, r: float, s: vec2) -> mat3x3: ...
def copy_trs_(self, t: vec2, r: float, s: vec2) -> None: ...
def copy_t_(self, t: vec2) -> None: ...
def copy_r_(self, r: float) -> None: ...
def copy_s_(self, s: vec2) -> None: ...
def _t(self) -> vec2: ...
def _r(self) -> float: ...
def _s(self) -> vec2: ...
def is_affine(self) -> bool: ...
def transform_point(self, p: vec2) -> vec2: ...
def transform_vector(self, v: vec2) -> vec2: ...
vec2_p = float_p
vec3_p = float_p
vec4_p = float_p
mat3x3_p = float_p
```

View File

@ -0,0 +1,39 @@
---
icon: package
label: line_profiler
---
Line-by-line profiler for Python.
## Example
```python
from line_profiler import LineProfiler
def my_func():
a = 0
for i in range(1000000):
a += i
return a
lp = LineProfiler()
lp.add_function(my_func)
lp.runcall(my_func)
lp.print_stats()
```
```txt
Total time: 0.243s
File: 84_line_profiler.py
Function: my_func at line 3
Line # Hits Time Per Hit % Time Line Contents
==============================================================
3 def my_func():
4 1 0 0 0.0 a = 0
5 1000001 69 0 28.4 for i in range(1000000):
6 1000001 174 0 71.6 a += i
7 1 0 0 0.0 return a
```

View File

@ -0,0 +1,133 @@
---
icon: package
label: math
---
### `math.pi`
3.141592653589793
### `math.e`
2.718281828459045
### `math.inf`
The `inf`.
### `math.nan`
The `nan`.
### `math.ceil(x)`
Return the ceiling of `x` as a float, the smallest integer value greater than or equal to `x`.
### `math.fabs(x)`
Return the absolute value of `x`.
### `math.floor(x)`
Return the floor of `x` as a float, the largest integer value less than or equal to `x`.
### `math.fsum(iterable)`
Return an accurate floating point sum of values in the iterable. Avoids loss of precision by tracking multiple intermediate partial sums:
```
>>> sum([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
0.9999999999999999
>>> fsum([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
1.0
```
### `math.gcd(a, b)`
Return the greatest common divisor of the integers `a` and `b`.
### `math.isfinite(x)`
Return `True` if `x` is neither an infinity nor a NaN, and `False` otherwise.
### `math.isinf(x)`
Return `True` if `x` is a positive or negative infinity, and `False` otherwise.
### `math.isnan(x)`
Return `True` if `x` is a NaN (not a number), and `False` otherwise.
### `math.isclose(a, b)`
Return `True` if the values `a` and `b` are close to each other and `False` otherwise.
### `math.exp(x)`
Return `e` raised to the power of `x`.
### `math.log(x)`
Return the natural logarithm of `x` (to base `e`).
### `math.log2(x)`
Return the base-2 logarithm of `x`. This is usually more accurate than `log(x, 2)`.
### `math.log10(x)`
Return the base-10 logarithm of `x`. This is usually more accurate than `log(x, 10)`.
### `math.pow(x, y)`
Return `x` raised to the power `y`.
### `math.sqrt(x)`
Return the square root of `x`.
### `math.acos(x)`
Return the arc cosine of `x`, in radians.
### `math.asin(x)`
Return the arc sine of `x`, in radians.
### `math.atan(x)`
Return the arc tangent of `x`, in radians.
### `math.atan2(y, x)`
Return `atan(y / x)`, in radians. The result is between `-pi` and `pi`. The vector in the plane from the origin to point `(x, y)` makes this angle with the positive X axis. The point of `atan2()` is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle. For example, `atan(1)` and `atan2(1, 1)` are both `pi/4`, but `atan2(-1, -1)` is `-3*pi/4`.
### `math.cos(x)`
Return the cosine of `x` radians.
### `math.sin(x)`
Return the sine of `x` radians.
### `math.tan(x)`
Return the tangent of `x` radians.
### `math.degrees(x)`
Convert angle `x` from radians to degrees.
### `math.radians(x)`
Convert angle `x` from degrees to radians.
### `math.modf(x)`
Return the fractional and integer parts of `x`. Both results carry the sign of `x` and are floats.
### `math.factorial(x)`
Return `x` factorial as an integer.

Some files were not shown because too many files have changed in this diff Show More