mirror of
https://github.com/pocketpy/pocketpy
synced 2025-11-09 21:20:17 +00:00
Merge branch 'main' of https://github.com/blueloveTH/pocketpy into build
This commit is contained in:
commit
9c6119d2f1
12
.github/workflows/main.yml
vendored
12
.github/workflows/main.yml
vendored
@ -1,5 +1,15 @@
|
|||||||
name: build
|
name: build
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
- 'web/**'
|
||||||
|
- '**.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
- 'web/**'
|
||||||
|
- '**.md'
|
||||||
jobs:
|
jobs:
|
||||||
build_win:
|
build_win:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|||||||
@ -21,19 +21,12 @@ if(NOT ${PREBUILD_RESULT} EQUAL 0)
|
|||||||
message(FATAL_ERROR "prebuild.py: ${PREBUILD_RESULT}")
|
message(FATAL_ERROR "prebuild.py: ${PREBUILD_RESULT}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
add_compile_options("/utf-8")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(EMSCRIPTEN)
|
if(EMSCRIPTEN)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -O3")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
|
||||||
elseif(MSVC)
|
elseif(MSVC)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /utf-8")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /utf-8 /O2")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "/O2")
|
|
||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -O2")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
|
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
|
|||||||
62
README.md
62
README.md
@ -38,7 +38,7 @@ These platforms are officially tested.
|
|||||||
+ Android 64-bit / 32-bit
|
+ Android 64-bit / 32-bit
|
||||||
+ iOS 64-bit
|
+ iOS 64-bit
|
||||||
+ Emscripten 32-bit
|
+ Emscripten 32-bit
|
||||||
+ Raspberry Pi 64-bit
|
+ Raspberry Pi OS 64-bit
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@ -56,11 +56,12 @@ To compile it with your project, these flags must be set:
|
|||||||
|
|
||||||
+ `--std=c++17` flag must be set
|
+ `--std=c++17` flag must be set
|
||||||
+ Exception must be enabled
|
+ Exception must be enabled
|
||||||
|
+ For MSVC, `/utf-8` flag must be set
|
||||||
|
|
||||||
For development build on Linux, use this snippet.
|
For development build on Linux, use this snippet.
|
||||||
```bash
|
```bash
|
||||||
# prerequisites
|
# prerequisites
|
||||||
sudo apt-get install libc++-dev libc++abi-dev clang++
|
sudo apt-get install libc++-dev libc++abi-dev clang
|
||||||
# build the repo
|
# build the repo
|
||||||
bash build.sh
|
bash build.sh
|
||||||
# unittest
|
# unittest
|
||||||
@ -114,26 +115,37 @@ for a quick overview of the supported features.
|
|||||||
|
|
||||||
| Name | Example | Supported |
|
| Name | Example | Supported |
|
||||||
| --------------- | ------------------------------- | --------- |
|
| --------------- | ------------------------------- | --------- |
|
||||||
| If Else | `if..else..elif` | YES |
|
| If Else | `if..else..elif` | ✅ |
|
||||||
| Loop | `for/while/break/continue` | YES |
|
| Loop | `for/while/break/continue` | ✅ |
|
||||||
| Function | `def f(x,*args,y=1):` | YES |
|
| Function | `def f(x,*args,y=1):` | ✅ |
|
||||||
| Subclass | `class A(B):` | YES |
|
| Subclass | `class A(B):` | ✅ |
|
||||||
| List | `[1, 2, 'a']` | YES |
|
| List | `[1, 2, 'a']` | ✅ |
|
||||||
| ListComp | `[i for i in range(5)]` | YES |
|
| ListComp | `[i for i in range(5)]` | ✅ |
|
||||||
| Slice | `a[1:2], a[:2], a[1:]` | YES |
|
| Slice | `a[1:2], a[:2], a[1:]` | ✅ |
|
||||||
| Tuple | `(1, 2, 'a')` | YES |
|
| Tuple | `(1, 2, 'a')` | ✅ |
|
||||||
| Dict | `{'a': 1, 'b': 2}` | YES |
|
| Dict | `{'a': 1, 'b': 2}` | ✅ |
|
||||||
| F-String | `f'value is {x}'` | YES |
|
| F-String | `f'value is {x}'` | ✅ |
|
||||||
| Unpacking | `a, b = 1, 2` | YES |
|
| Unpacking | `a, b = 1, 2` | ✅ |
|
||||||
| Star Unpacking | `a, *b = [1, 2, 3]` | YES |
|
| Star Unpacking | `a, *b = [1, 2, 3]` | ✅ |
|
||||||
| Exception | `raise/try..catch` | YES |
|
| Exception | `raise/try..catch` | ✅ |
|
||||||
| Dynamic Code | `eval()/exec()` | YES |
|
| Dynamic Code | `eval()/exec()` | ✅ |
|
||||||
| Reflection | `hasattr()/getattr()/setattr()` | YES |
|
| Reflection | `hasattr()/getattr()/setattr()` | ✅ |
|
||||||
| Import | `import/from..import` | YES |
|
| Import | `import/from..import` | ✅ |
|
||||||
| Context Block | `with <expr> as <id>:` | YES |
|
| Context Block | `with <expr> as <id>:` | ✅ |
|
||||||
| Type Annotation | `def f(a:int, b:float=1)` | YES |
|
| Type Annotation | `def f(a:int, b:float=1)` | ✅ |
|
||||||
| Generator | `yield i` | YES |
|
| Generator | `yield i` | ✅ |
|
||||||
| Decorator | `@cache` | YES |
|
| Decorator | `@cache` | ✅ |
|
||||||
|
|
||||||
|
## Used By
|
||||||
|
|
||||||
|
| | Description |
|
||||||
|
| ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||||
|
| [TIC-80](https://github.com/nesbox/TIC-80) | TIC-80 is a fantasy computer for making, playing and sharing tiny games. |
|
||||||
|
| [ct-py](https://github.com/blueloveTH/ct-py) | Ct.py🥕 is cross-platform 2D game framework built on raylib and imgui. |
|
||||||
|
| [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 |
|
||||||
|
|
||||||
|
Submit a pull request to add your project here.
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
@ -146,7 +158,11 @@ All kinds of contributions are welcome.
|
|||||||
- any suggestions
|
- any suggestions
|
||||||
- any questions
|
- any questions
|
||||||
|
|
||||||
Check our [Coding Style Guide](https://pocketpy.dev/coding_style_guide/) if you want to contribute C++ code.
|
If you find pocketpy useful, consider star this repository (●'◡'●)
|
||||||
|
|
||||||
|
## Sponsor me
|
||||||
|
|
||||||
|
You can sponsor me via [Github Sponsors](https://github.com/sponsors/blueloveTH). Your sponsorship will help me develop this project continuously.
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
|
|||||||
40
README_zh.md
40
README_zh.md
@ -27,7 +27,7 @@ pkpy 支持任何拥有 C++17 编译器的平台。
|
|||||||
+ Android 64-bit / 32-bit
|
+ Android 64-bit / 32-bit
|
||||||
+ iOS 64-bit
|
+ iOS 64-bit
|
||||||
+ Emscripten 32-bit
|
+ Emscripten 32-bit
|
||||||
+ Raspberry Pi 64-bit
|
+ Raspberry Pi OS 64-bit
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
|
|
||||||
@ -76,25 +76,25 @@ int main(){
|
|||||||
|
|
||||||
| 特性 | 示例 | 支持 |
|
| 特性 | 示例 | 支持 |
|
||||||
| ------------ | ------------------------------- | ---- |
|
| ------------ | ------------------------------- | ---- |
|
||||||
| 分支 | `if..else..elif` | YES |
|
| 分支 | `if..else..elif` | ✅ |
|
||||||
| 循环 | `for/while/break/continue` | YES |
|
| 循环 | `for/while/break/continue` | ✅ |
|
||||||
| 函数 | `def f(x,*args,y=1):` | YES |
|
| 函数 | `def f(x,*args,y=1):` | ✅ |
|
||||||
| 类与继承 | `class A(B):` | YES |
|
| 类与继承 | `class A(B):` | ✅ |
|
||||||
| 列表 | `[1, 2, 'a']` | YES |
|
| 列表 | `[1, 2, 'a']` | ✅ |
|
||||||
| 列表生成式 | `[i for i in range(5)]` | YES |
|
| 列表生成式 | `[i for i in range(5)]` | ✅ |
|
||||||
| 切片 | `a[1:2], a[:2], a[1:]` | YES |
|
| 切片 | `a[1:2], a[:2], a[1:]` | ✅ |
|
||||||
| 元组 | `(1, 2, 'a')` | YES |
|
| 元组 | `(1, 2, 'a')` | ✅ |
|
||||||
| 字典 | `{'a': 1, 'b': 2}` | YES |
|
| 字典 | `{'a': 1, 'b': 2}` | ✅ |
|
||||||
| 格式化字符串 | `f'value is {x}'` | YES |
|
| 格式化字符串 | `f'value is {x}'` | ✅ |
|
||||||
| 序列解包 | `a, b = 1, 2` | YES |
|
| 序列解包 | `a, b = 1, 2` | ✅ |
|
||||||
| 异常 | `raise/try..catch` | YES |
|
| 异常 | `raise/try..catch` | ✅ |
|
||||||
| 动态分发 | `eval()/exec()` | YES |
|
| 动态分发 | `eval()/exec()` | ✅ |
|
||||||
| 反射 | `hasattr()/getattr()/setattr()` | YES |
|
| 反射 | `hasattr()/getattr()/setattr()` | ✅ |
|
||||||
| 导入模块 | `import/from..import` | YES |
|
| 导入模块 | `import/from..import` | ✅ |
|
||||||
| 上下文管理器 | `with <expr> as <id>:` | YES |
|
| 上下文管理器 | `with <expr> as <id>:` | ✅ |
|
||||||
| 类型标注 | `def f(a:int, b:float=1)` | YES |
|
| 类型标注 | `def f(a:int, b:float=1)` | ✅ |
|
||||||
| 生成器 | `yield i` | YES |
|
| 生成器 | `yield i` | ✅ |
|
||||||
| 装饰器 | `@cache` | YES |
|
| 装饰器 | `@cache` | ✅ |
|
||||||
|
|
||||||
## 参考
|
## 参考
|
||||||
|
|
||||||
|
|||||||
11
build.ps1
11
build.ps1
@ -1,9 +1,12 @@
|
|||||||
if (Test-Path build) {
|
if (Test-Path build) {
|
||||||
Remove-Item -Recurse -Force build
|
Remove-Item -Recurse -Force build
|
||||||
}
|
}
|
||||||
mkdir build
|
|
||||||
cd build
|
New-Item -ItemType Directory -Path build
|
||||||
|
Push-Location build
|
||||||
|
|
||||||
cmake ..
|
cmake ..
|
||||||
cmake --build . --config Release
|
cmake --build . --config Release
|
||||||
cp Release/main.exe ../
|
|
||||||
cp Release/pocketpy.dll ../
|
Copy-Item "Release\main.exe" -Destination ".." # Note: NTFS uses backslash (\) instead of slashes (*nix, /)
|
||||||
|
Copy-Item "Release\pocketpy.dll" -Destination ".."
|
||||||
|
|||||||
12
build.sh
12
build.sh
@ -10,24 +10,22 @@ fi
|
|||||||
# Check if clang++ is installed
|
# Check if clang++ is installed
|
||||||
if ! type -P clang++ >/dev/null 2>&1; then
|
if ! type -P clang++ >/dev/null 2>&1; then
|
||||||
echo "clang++ is required and not installed. Kindly install it."
|
echo "clang++ is required and not installed. Kindly install it."
|
||||||
echo "Run: sudo apt-get install libc++-dev libc++abi-dev clang++"
|
echo "Run: sudo apt-get install libc++-dev libc++abi-dev clang"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Requirements satisfied: python3 and clang++ are installed."
|
echo "Requirements satisfied: python3 and clang are installed."
|
||||||
echo "It takes a moment to finish building."
|
echo "It takes a moment to finish building."
|
||||||
echo ""
|
echo ""
|
||||||
echo "> Running prebuild.py... "
|
echo "> Running prebuild.py... "
|
||||||
|
|
||||||
python3 prebuild.py
|
python3 prebuild.py
|
||||||
|
|
||||||
# echo -n "Finding source files... "
|
|
||||||
SRC=$(find src/ -name "*.cpp")
|
SRC=$(find src/ -name "*.cpp")
|
||||||
# echo "Done"
|
|
||||||
|
|
||||||
echo "> Compiling and linking source files... "
|
echo "> Compiling and linking source files... "
|
||||||
|
|
||||||
FLAGS="-std=c++17 -O2 -stdlib=libc++ -Wfatal-errors -Iinclude"
|
FLAGS="-std=c++17 -O1 -stdlib=libc++ -Wfatal-errors -Iinclude"
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
LIB_EXTENSION=".dylib"
|
LIB_EXTENSION=".dylib"
|
||||||
FLAGS="$FLAGS -undefined dynamic_lookup"
|
FLAGS="$FLAGS -undefined dynamic_lookup"
|
||||||
@ -42,10 +40,10 @@ clang++ $FLAGS -o libpocketpy$LIB_EXTENSION $SRC -fPIC -shared -ldl
|
|||||||
# compile main.cpp and link to libpocketpy.so
|
# compile main.cpp and link to libpocketpy.so
|
||||||
echo "> Compiling main.cpp and linking to libpocketpy$LIB_EXTENSION..."
|
echo "> Compiling main.cpp and linking to libpocketpy$LIB_EXTENSION..."
|
||||||
|
|
||||||
clang++ $FLAGS -o main src2/main.cpp -L. -lpocketpy $LINK_FLAGS
|
clang++ $FLAGS -o main -O1 src2/main.cpp -L. -lpocketpy $LINK_FLAGS
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "Build completed successfully. To use pocketpy, run : ./main"
|
echo "Build completed. Type \"./main\" to enter REPL."
|
||||||
else
|
else
|
||||||
echo "Build failed."
|
echo "Build failed."
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
104
docs/bindings.md
104
docs/bindings.md
@ -49,6 +49,65 @@ vm->bind(obj,
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 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
|
### Bind a struct
|
||||||
|
|
||||||
Assume you have a struct `Point` declared as follows.
|
Assume you have a struct `Point` declared as follows.
|
||||||
@ -132,6 +191,47 @@ int main(){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 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
|
### Others
|
||||||
|
|
||||||
@ -144,7 +244,7 @@ They do not take universal function pointer as argument.
|
|||||||
You need to provide the detailed `Type` object and the corresponding function pointer.
|
You need to provide the detailed `Type` object and the corresponding function pointer.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
PyObject* f_add(PyObject* lhs, PyObject* rhs){
|
PyObject* f_add(VM* vm, PyObject* lhs, PyObject* rhs){
|
||||||
int a = py_cast<int>(vm, lhs);
|
int a = py_cast<int>(vm, lhs);
|
||||||
int b = py_cast<int>(vm, rhs);
|
int b = py_cast<int>(vm, rhs);
|
||||||
return py_var(vm, a + b);
|
return py_var(vm, a + b);
|
||||||
@ -160,4 +260,4 @@ For example, `vm->bind__add__` is preferred over `vm->bind_method<1>(type, "__ad
|
|||||||
|
|
||||||
### Further reading
|
### Further reading
|
||||||
|
|
||||||
See [linalg.h](https://github.com/blueloveTH/pocketpy/blob/main/src/linalg.h) for a complete example used by `linalg` module.
|
See [random.cpp](https://github.com/blueloveTH/pocketpy/blob/main/src/random.cpp) for an example used by `random` module.
|
||||||
@ -6,7 +6,14 @@ label: Coding style guide
|
|||||||
|
|
||||||
# Coding Style Guide
|
# Coding Style Guide
|
||||||
|
|
||||||
## Naming rules
|
|
||||||
|
## 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**
|
For class names, always use **PascalCase**
|
||||||
|
|
||||||
@ -53,7 +60,7 @@ For macros, use **SNAKE_CASE**
|
|||||||
#define TEST(x) x+1
|
#define TEST(x) x+1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Access control
|
### Access control
|
||||||
|
|
||||||
Please use python style access control.
|
Please use python style access control.
|
||||||
|
|
||||||
@ -74,7 +81,7 @@ public:
|
|||||||
|
|
||||||
It does not forbid users to access internal members.
|
It does not forbid users to access internal members.
|
||||||
|
|
||||||
## Use compact style
|
### Use compact style
|
||||||
|
|
||||||
Try to make the code compact if it does not affect readability.
|
Try to make the code compact if it does not affect readability.
|
||||||
|
|
||||||
@ -88,7 +95,7 @@ if(x == 1){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## For `std::shared_ptr<T>`
|
### For `std::shared_ptr<T>`
|
||||||
|
|
||||||
Use a `_` suffix to indicate a type is a shared pointer.
|
Use a `_` suffix to indicate a type is a shared pointer.
|
||||||
|
|
||||||
|
|||||||
@ -39,8 +39,12 @@ These platforms are officially tested.
|
|||||||
+ Android 64-bit / 32-bit
|
+ Android 64-bit / 32-bit
|
||||||
+ iOS 64-bit
|
+ iOS 64-bit
|
||||||
+ Emscripten 32-bit
|
+ Emscripten 32-bit
|
||||||
+ Raspberry Pi 64-bit
|
+ Raspberry Pi OS 64-bit
|
||||||
|
|
||||||
|
## Star the repo
|
||||||
|
|
||||||
|
If you find pocketpy useful, consider [star this repository](https://github.com/blueloveth/pocketpy) (●'◡'●)
|
||||||
|
|
||||||
## Sponsor me
|
## Sponsor me
|
||||||
|
|
||||||
You can sponsor me via [Github Sponsors](https://github.com/sponsors/blueloveTH).
|
You can sponsor me via [Github Sponsors](https://github.com/sponsors/blueloveTH). Your sponsorship will help me develop this project continuously.
|
||||||
|
|||||||
@ -43,6 +43,7 @@ To compile it with your project, these flags must be set:
|
|||||||
|
|
||||||
+ `--std=c++17` flag must be set
|
+ `--std=c++17` flag must be set
|
||||||
+ Exception must be enabled
|
+ Exception must be enabled
|
||||||
|
+ For MSVC, `/utf-8` flag must be set
|
||||||
|
|
||||||
For emscripten, you must enable exceptions to make pocketpy work properly.
|
For emscripten, you must enable exceptions to make pocketpy work properly.
|
||||||
See https://emscripten.org/docs/porting/exceptions.html.
|
See https://emscripten.org/docs/porting/exceptions.html.
|
||||||
|
|||||||
@ -81,6 +81,7 @@ OPCODE(CONTAINS_OP)
|
|||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(JUMP_ABSOLUTE)
|
OPCODE(JUMP_ABSOLUTE)
|
||||||
OPCODE(POP_JUMP_IF_FALSE)
|
OPCODE(POP_JUMP_IF_FALSE)
|
||||||
|
OPCODE(POP_JUMP_IF_TRUE)
|
||||||
OPCODE(JUMP_IF_TRUE_OR_POP)
|
OPCODE(JUMP_IF_TRUE_OR_POP)
|
||||||
OPCODE(JUMP_IF_FALSE_OR_POP)
|
OPCODE(JUMP_IF_FALSE_OR_POP)
|
||||||
OPCODE(SHORTCUT_IF_FALSE_OR_POP)
|
OPCODE(SHORTCUT_IF_FALSE_OR_POP)
|
||||||
@ -120,9 +121,9 @@ OPCODE(STORE_CLASS_ATTR)
|
|||||||
OPCODE(WITH_ENTER)
|
OPCODE(WITH_ENTER)
|
||||||
OPCODE(WITH_EXIT)
|
OPCODE(WITH_EXIT)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(ASSERT)
|
|
||||||
OPCODE(EXCEPTION_MATCH)
|
OPCODE(EXCEPTION_MATCH)
|
||||||
OPCODE(RAISE)
|
OPCODE(RAISE)
|
||||||
|
OPCODE(RAISE_ASSERT)
|
||||||
OPCODE(RE_RAISE)
|
OPCODE(RE_RAISE)
|
||||||
OPCODE(POP_EXCEPTION)
|
OPCODE(POP_EXCEPTION)
|
||||||
/**************************/
|
/**************************/
|
||||||
|
|||||||
@ -32,7 +32,10 @@ class Pointer(Generic[T], void_p):
|
|||||||
def __getitem__(self, index: int) -> T: ...
|
def __getitem__(self, index: int) -> T: ...
|
||||||
def __setitem__(self, index: int, value: T) -> None: ...
|
def __setitem__(self, index: int, value: T) -> None: ...
|
||||||
|
|
||||||
class char_p(Pointer[int]): pass
|
class char_p(Pointer[int]):
|
||||||
|
def read_string(self) -> str: ...
|
||||||
|
def write_string(self, value: str) -> None: ...
|
||||||
|
|
||||||
class uchar_p(Pointer[int]): pass
|
class uchar_p(Pointer[int]): pass
|
||||||
class short_p(Pointer[int]): pass
|
class short_p(Pointer[int]): pass
|
||||||
class ushort_p(Pointer[int]): pass
|
class ushort_p(Pointer[int]): pass
|
||||||
|
|||||||
@ -96,14 +96,115 @@ def sorted(iterable, reverse=False, key=None):
|
|||||||
return a
|
return a
|
||||||
|
|
||||||
##### str #####
|
##### str #####
|
||||||
def __f(self, *args):
|
def __f(self: str, *args, **kwargs) -> str:
|
||||||
if '{}' in self:
|
def tokenizeString(s: str):
|
||||||
for i in range(len(args)):
|
tokens = []
|
||||||
self = self.replace('{}', str(args[i]), 1)
|
L, R = 0,0
|
||||||
else:
|
|
||||||
for i in range(len(args)):
|
mode = None
|
||||||
self = self.replace('{'+str(i)+'}', str(args[i]))
|
curArg = 0
|
||||||
return self
|
# lookingForKword = False
|
||||||
|
|
||||||
|
while(R<len(s)):
|
||||||
|
curChar = s[R]
|
||||||
|
nextChar = s[R+1] if R+1<len(s) else ''
|
||||||
|
|
||||||
|
# Invalid case 1: stray '}' encountered, example: "ABCD EFGH {name} IJKL}", "Hello {vv}}", "HELLO {0} WORLD}"
|
||||||
|
if curChar == '}' and nextChar != '}':
|
||||||
|
raise ValueError("Single '}' encountered in format string")
|
||||||
|
|
||||||
|
# Valid Case 1: Escaping case, we escape "{{ or "}}" to be "{" or "}", example: "{{}}", "{{My Name is {0}}}"
|
||||||
|
if (curChar == '{' and nextChar == '{') or (curChar == '}' and nextChar == '}'):
|
||||||
|
|
||||||
|
if (L<R): # Valid Case 1.1: make sure we are not adding empty string
|
||||||
|
tokens.append(s[L:R]) # add the string before the escape
|
||||||
|
|
||||||
|
|
||||||
|
tokens.append(curChar) # Valid Case 1.2: add the escape char
|
||||||
|
L = R+2 # move the left pointer to the next char
|
||||||
|
R = R+2 # move the right pointer to the next char
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Valid Case 2: Regular command line arg case: example: "ABCD EFGH {} IJKL", "{}", "HELLO {} WORLD"
|
||||||
|
elif curChar == '{' and nextChar == '}':
|
||||||
|
if mode is not None and mode != 'auto':
|
||||||
|
# Invalid case 2: mixing automatic and manual field specifications -- example: "ABCD EFGH {name} IJKL {}", "Hello {vv} {}", "HELLO {0} WORLD {}"
|
||||||
|
raise ValueError("Cannot switch from manual field numbering to automatic field specification")
|
||||||
|
|
||||||
|
mode = 'auto'
|
||||||
|
if(L<R): # Valid Case 2.1: make sure we are not adding empty string
|
||||||
|
tokens.append(s[L:R]) # add the string before the special marker for the arg
|
||||||
|
|
||||||
|
tokens.append("{"+str(curArg)+"}") # Valid Case 2.2: add the special marker for the arg
|
||||||
|
curArg+=1 # increment the arg position, this will be used for referencing the arg later
|
||||||
|
|
||||||
|
L = R+2 # move the left pointer to the next char
|
||||||
|
R = R+2 # move the right pointer to the next char
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Valid Case 3: Key-word arg case: example: "ABCD EFGH {name} IJKL", "Hello {vv}", "HELLO {name} WORLD"
|
||||||
|
elif (curChar == '{'):
|
||||||
|
|
||||||
|
if mode is not None and mode != 'manual':
|
||||||
|
# # Invalid case 2: mixing automatic and manual field specifications -- example: "ABCD EFGH {} IJKL {name}", "Hello {} {1}", "HELLO {} WORLD {name}"
|
||||||
|
raise ValueError("Cannot switch from automatic field specification to manual field numbering")
|
||||||
|
|
||||||
|
mode = 'manual'
|
||||||
|
|
||||||
|
if(L<R): # Valid case 3.1: make sure we are not adding empty string
|
||||||
|
tokens.append(s[L:R]) # add the string before the special marker for the arg
|
||||||
|
|
||||||
|
# We look for the end of the keyword
|
||||||
|
kwL = R # Keyword left pointer
|
||||||
|
kwR = R+1 # Keyword right pointer
|
||||||
|
while(kwR<len(s) and s[kwR]!='}'):
|
||||||
|
if s[kwR] == '{': # Invalid case 3: stray '{' encountered, example: "ABCD EFGH {n{ame} IJKL {", "Hello {vv{}}", "HELLO {0} WOR{LD}"
|
||||||
|
raise ValueError("Unexpected '{' in field name")
|
||||||
|
kwR += 1
|
||||||
|
|
||||||
|
# Valid case 3.2: We have successfully found the end of the keyword
|
||||||
|
if kwR<len(s) and s[kwR] == '}':
|
||||||
|
tokens.append(s[kwL:kwR+1]) # add the special marker for the arg
|
||||||
|
L = kwR+1
|
||||||
|
R = kwR+1
|
||||||
|
|
||||||
|
# Invalid case 4: We didn't find the end of the keyword, throw error
|
||||||
|
else:
|
||||||
|
raise ValueError("Expected '}' before end of string")
|
||||||
|
continue
|
||||||
|
|
||||||
|
R = R+1
|
||||||
|
|
||||||
|
|
||||||
|
# Valid case 4: We have reached the end of the string, add the remaining string to the tokens
|
||||||
|
if L<R:
|
||||||
|
tokens.append(s[L:R])
|
||||||
|
|
||||||
|
# print(tokens)
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
tokens = tokenizeString(self)
|
||||||
|
argMap = {}
|
||||||
|
for i, a in enumerate(args):
|
||||||
|
argMap[str(i)] = a
|
||||||
|
final_tokens = []
|
||||||
|
for t in tokens:
|
||||||
|
if t[0] == '{' and t[-1] == '}':
|
||||||
|
key = t[1:-1]
|
||||||
|
argMapVal = argMap.get(key, None)
|
||||||
|
kwargsVal = kwargs.get(key, None)
|
||||||
|
|
||||||
|
if argMapVal is None and kwargsVal is None:
|
||||||
|
raise ValueError("No arg found for token: "+t)
|
||||||
|
elif argMapVal is not None:
|
||||||
|
final_tokens.append(str(argMapVal))
|
||||||
|
else:
|
||||||
|
final_tokens.append(str(kwargsVal))
|
||||||
|
else:
|
||||||
|
final_tokens.append(t)
|
||||||
|
|
||||||
|
return ''.join(final_tokens)
|
||||||
|
|
||||||
str.format = __f
|
str.format = __f
|
||||||
|
|
||||||
def __f(self, chars=None):
|
def __f(self, chars=None):
|
||||||
|
|||||||
@ -473,6 +473,9 @@ __NEXT_STEP:;
|
|||||||
TARGET(POP_JUMP_IF_FALSE)
|
TARGET(POP_JUMP_IF_FALSE)
|
||||||
if(!py_bool(POPX())) frame->jump_abs(byte.arg);
|
if(!py_bool(POPX())) frame->jump_abs(byte.arg);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
TARGET(POP_JUMP_IF_TRUE)
|
||||||
|
if(py_bool(POPX())) frame->jump_abs(byte.arg);
|
||||||
|
DISPATCH();
|
||||||
TARGET(JUMP_IF_TRUE_OR_POP)
|
TARGET(JUMP_IF_TRUE_OR_POP)
|
||||||
if(py_bool(TOP()) == true) frame->jump_abs(byte.arg);
|
if(py_bool(TOP()) == true) frame->jump_abs(byte.arg);
|
||||||
else POP();
|
else POP();
|
||||||
@ -682,19 +685,6 @@ __NEXT_STEP:;
|
|||||||
call_method(POPX(), __exit__);
|
call_method(POPX(), __exit__);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(ASSERT) {
|
|
||||||
_0 = TOP();
|
|
||||||
Str msg;
|
|
||||||
if(is_type(_0, tp_tuple)){
|
|
||||||
auto& t = CAST(Tuple&, _0);
|
|
||||||
if(t.size() != 2) ValueError("assert tuple must have 2 elements");
|
|
||||||
_0 = t[0];
|
|
||||||
msg = CAST(Str&, py_str(t[1]));
|
|
||||||
}
|
|
||||||
bool ok = py_bool(_0);
|
|
||||||
POP();
|
|
||||||
if(!ok) _error("AssertionError", msg);
|
|
||||||
} DISPATCH();
|
|
||||||
TARGET(EXCEPTION_MATCH) {
|
TARGET(EXCEPTION_MATCH) {
|
||||||
const auto& e = CAST(Exception&, TOP());
|
const auto& e = CAST(Exception&, TOP());
|
||||||
_name = StrName(byte.arg);
|
_name = StrName(byte.arg);
|
||||||
@ -705,6 +695,14 @@ __NEXT_STEP:;
|
|||||||
Str msg = _0 == None ? "" : CAST(Str, py_str(_0));
|
Str msg = _0 == None ? "" : CAST(Str, py_str(_0));
|
||||||
_error(StrName(byte.arg), msg);
|
_error(StrName(byte.arg), msg);
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
|
TARGET(RAISE_ASSERT)
|
||||||
|
if(byte.arg){
|
||||||
|
_0 = py_str(POPX());
|
||||||
|
_error("AssertionError", CAST(Str, _0));
|
||||||
|
}else{
|
||||||
|
_error("AssertionError", "");
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
TARGET(RE_RAISE) _raise(true); DISPATCH();
|
TARGET(RE_RAISE) _raise(true); DISPATCH();
|
||||||
TARGET(POP_EXCEPTION) _last_exception = POPX(); DISPATCH();
|
TARGET(POP_EXCEPTION) _last_exception = POPX(); DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
@ -760,12 +758,6 @@ __NEXT_STEP:;
|
|||||||
PK_UNUSED(e);
|
PK_UNUSED(e);
|
||||||
PyObject* obj = POPX();
|
PyObject* obj = POPX();
|
||||||
Exception& _e = CAST(Exception&, obj);
|
Exception& _e = CAST(Exception&, obj);
|
||||||
int actual_ip = frame->_ip;
|
|
||||||
if(_e._ip_on_error >= 0 && _e._code_on_error == (void*)frame->co) actual_ip = _e._ip_on_error;
|
|
||||||
int current_line = frame->co->lines[actual_ip]; // current line
|
|
||||||
auto current_f_name = frame->co->name.sv(); // current function name
|
|
||||||
if(frame->_callable == nullptr) current_f_name = ""; // not in a function
|
|
||||||
_e.st_push(frame->co->src->snapshot(current_line, nullptr, current_f_name));
|
|
||||||
_pop_frame();
|
_pop_frame();
|
||||||
if(callstack.empty()){
|
if(callstack.empty()){
|
||||||
#if PK_DEBUG_FULL_EXCEPTION
|
#if PK_DEBUG_FULL_EXCEPTION
|
||||||
|
|||||||
16
src/cffi.cpp
16
src/cffi.cpp
@ -226,6 +226,22 @@ void add_module_c(VM* vm){
|
|||||||
BIND_PRIMITIVE(bool, "bool")
|
BIND_PRIMITIVE(bool, "bool")
|
||||||
|
|
||||||
#undef BIND_PRIMITIVE
|
#undef BIND_PRIMITIVE
|
||||||
|
|
||||||
|
PyObject* char_p_t = mod->attr("char_p");
|
||||||
|
vm->bind(char_p_t, "read_string(self) -> str", [](VM* vm, ArgsView args){
|
||||||
|
VoidP& voidp = PK_OBJ_GET(VoidP, args[0]);
|
||||||
|
const char* target = (const char*)voidp.ptr;
|
||||||
|
return VAR(target);
|
||||||
|
});
|
||||||
|
|
||||||
|
vm->bind(char_p_t, "write_string(self, value: str)", [](VM* vm, ArgsView args){
|
||||||
|
VoidP& voidp = PK_OBJ_GET(VoidP, args[0]);
|
||||||
|
std::string_view sv = CAST(Str&, args[1]).sv();
|
||||||
|
char* target = (char*)voidp.ptr;
|
||||||
|
memcpy(target, sv.data(), sv.size());
|
||||||
|
target[sv.size()] = '\0';
|
||||||
|
return vm->None;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace pkpy
|
} // namespace pkpy
|
||||||
@ -775,7 +775,10 @@ __EAT_DOTS_END:
|
|||||||
case TK("++"):{
|
case TK("++"):{
|
||||||
consume(TK("@id"));
|
consume(TK("@id"));
|
||||||
StrName name(prev().sv());
|
StrName name(prev().sv());
|
||||||
switch(name_scope()){
|
NameScope scope = name_scope();
|
||||||
|
bool is_global = ctx()->global_names.count(name.sv());
|
||||||
|
if(is_global) scope = NAME_GLOBAL;
|
||||||
|
switch(scope){
|
||||||
case NAME_LOCAL:
|
case NAME_LOCAL:
|
||||||
ctx()->emit(OP_INC_FAST, ctx()->add_varname(name), prev().line);
|
ctx()->emit(OP_INC_FAST, ctx()->add_varname(name), prev().line);
|
||||||
break;
|
break;
|
||||||
@ -802,11 +805,19 @@ __EAT_DOTS_END:
|
|||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TK("assert"):
|
case TK("assert"):{
|
||||||
EXPR_TUPLE(false);
|
EXPR(false); // condition
|
||||||
ctx()->emit(OP_ASSERT, BC_NOARG, kw_line);
|
int index = ctx()->emit(OP_POP_JUMP_IF_TRUE, BC_NOARG, kw_line);
|
||||||
|
int has_msg = 0;
|
||||||
|
if(match(TK(","))){
|
||||||
|
EXPR(false); // message
|
||||||
|
has_msg = 1;
|
||||||
|
}
|
||||||
|
ctx()->emit(OP_RAISE_ASSERT, has_msg, kw_line);
|
||||||
|
ctx()->patch_jump(index);
|
||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TK("global"):
|
case TK("global"):
|
||||||
do {
|
do {
|
||||||
consume(TK("@id"));
|
consume(TK("@id"));
|
||||||
|
|||||||
18
src/vm.cpp
18
src/vm.cpp
@ -1082,13 +1082,21 @@ void VM::_error(Exception e){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VM::_raise(bool re_raise){
|
void VM::_raise(bool re_raise){
|
||||||
Frame* top = top_frame().get();
|
Frame* frame = top_frame().get();
|
||||||
|
Exception& e = PK_OBJ_GET(Exception, s_data.top());
|
||||||
if(!re_raise){
|
if(!re_raise){
|
||||||
Exception& e = PK_OBJ_GET(Exception, s_data.top());
|
e._ip_on_error = frame->_ip;
|
||||||
e._ip_on_error = top->_ip;
|
e._code_on_error = (void*)frame->co;
|
||||||
e._code_on_error = (void*)top->co;
|
|
||||||
}
|
}
|
||||||
bool ok = top->jump_to_exception_handler();
|
bool ok = frame->jump_to_exception_handler();
|
||||||
|
|
||||||
|
int actual_ip = frame->_ip;
|
||||||
|
if(e._ip_on_error >= 0 && e._code_on_error == (void*)frame->co) actual_ip = e._ip_on_error;
|
||||||
|
int current_line = frame->co->lines[actual_ip]; // current line
|
||||||
|
auto current_f_name = frame->co->name.sv(); // current function name
|
||||||
|
if(frame->_callable == nullptr) current_f_name = ""; // not in a function
|
||||||
|
e.st_push(frame->co->src->snapshot(current_line, nullptr, current_f_name));
|
||||||
|
|
||||||
if(ok) throw HandledException();
|
if(ok) throw HandledException();
|
||||||
else throw UnhandledException();
|
else throw UnhandledException();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,6 +97,19 @@ assert "{0} {1} {2}".format("I", "love", "Python") == "I love Python"
|
|||||||
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
|
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
|
||||||
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
|
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
|
||||||
|
|
||||||
|
assert "{k}={v}".format(k="key", v="value") == "key=value"
|
||||||
|
assert "{k}={k}".format(k="key") == "key=key"
|
||||||
|
assert "{0}={1}".format('{0}', '{1}') == "{0}={1}"
|
||||||
|
assert "{{{0}}}".format(1) == "{1}"
|
||||||
|
assert "{0}{1}{1}".format(1, 2, 3) == "122"
|
||||||
|
try:
|
||||||
|
"{0}={1}}".format(1, 2)
|
||||||
|
exit(1)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
assert "{{{}xxx{}x}}".format(1, 2) == "{1xxx2x}"
|
||||||
|
assert "{{abc}}".format() == "{abc}"
|
||||||
|
|
||||||
# 3rd slice
|
# 3rd slice
|
||||||
a = "Hello, World!"
|
a = "Hello, World!"
|
||||||
assert a[::-1] == "!dlroW ,olleH"
|
assert a[::-1] == "!dlroW ,olleH"
|
||||||
|
|||||||
@ -25,4 +25,34 @@ assert floor(-1.2) == -2
|
|||||||
assert ceil(1.2) == 2
|
assert ceil(1.2) == 2
|
||||||
assert ceil(-1.2) == -1
|
assert ceil(-1.2) == -1
|
||||||
|
|
||||||
assert isclose(sqrt(4), 2.0)
|
assert isclose(sqrt(4), 2.0)
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
# test fsum
|
||||||
|
assert math.fsum([0.1] * 10) == 1.0
|
||||||
|
|
||||||
|
# test gcd
|
||||||
|
assert math.gcd(10, 5) == 5
|
||||||
|
assert math.gcd(10, 6) == 2
|
||||||
|
assert math.gcd(10, 7) == 1
|
||||||
|
assert math.gcd(10, 10) == 10
|
||||||
|
assert math.gcd(-10, 10) == 10
|
||||||
|
|
||||||
|
# test modf
|
||||||
|
x, y = math.modf(1.5)
|
||||||
|
assert isclose(x, 0.5)
|
||||||
|
assert isclose(y, 1.0)
|
||||||
|
|
||||||
|
x, y = math.modf(-1.5)
|
||||||
|
assert isclose(x, -0.5)
|
||||||
|
assert isclose(y, -1.0)
|
||||||
|
|
||||||
|
# test factorial
|
||||||
|
assert math.factorial(0) == 1
|
||||||
|
assert math.factorial(1) == 1
|
||||||
|
assert math.factorial(2) == 2
|
||||||
|
assert math.factorial(3) == 6
|
||||||
|
assert math.factorial(4) == 24
|
||||||
|
assert math.factorial(5) == 120
|
||||||
|
|
||||||
|
|||||||
@ -49,4 +49,16 @@ class Vec2(c.struct):
|
|||||||
a = Vec2(1, 2)
|
a = Vec2(1, 2)
|
||||||
assert isinstance(a, c.struct)
|
assert isinstance(a, c.struct)
|
||||||
assert type(a) is Vec2
|
assert type(a) is Vec2
|
||||||
assert repr(a) == "Vec2(1.0, 2.0)"
|
assert repr(a) == "Vec2(1.0, 2.0)"
|
||||||
|
|
||||||
|
a = c.struct(10)
|
||||||
|
p = c.p_cast(a.addr(), c.char_p)
|
||||||
|
p.write_string("Hello!")
|
||||||
|
assert p[0] == ord("H")
|
||||||
|
assert p[1] == ord("e")
|
||||||
|
assert p[2] == ord("l")
|
||||||
|
assert p[3] == ord("l")
|
||||||
|
assert p[4] == ord("o")
|
||||||
|
assert p[5] == ord("!")
|
||||||
|
assert p[6] == 0
|
||||||
|
assert p.read_string() == "Hello!"
|
||||||
|
|||||||
@ -16,6 +16,15 @@ a = {
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
assert json.loads("1") == 1
|
||||||
|
assert json.loads('"1"') == "1"
|
||||||
|
assert json.loads("0.0") == 0.0
|
||||||
|
assert json.loads("[1, 2]") == [1, 2]
|
||||||
|
assert json.loads("null") == None
|
||||||
|
assert json.loads("true") == True
|
||||||
|
assert json.loads("false") == False
|
||||||
|
assert json.loads("{}") == {}
|
||||||
|
|
||||||
_j = json.dumps(a)
|
_j = json.dumps(a)
|
||||||
_a = json.loads(_j)
|
_a = json.loads(_j)
|
||||||
|
|
||||||
|
|||||||
@ -327,23 +327,12 @@ assert result_mat == correct_result_mat
|
|||||||
|
|
||||||
# test determinant
|
# test determinant
|
||||||
test_mat_copy = test_mat.copy()
|
test_mat_copy = test_mat.copy()
|
||||||
list_mat = [[0,0,0], [0,0,0], [0,0,0]]
|
test_mat_copy.determinant()
|
||||||
for i in range(3):
|
|
||||||
for j in range(3):
|
|
||||||
list_mat[i][j] = test_mat[i, j]
|
|
||||||
determinant = list_mat[0][0]*(list_mat[1][1]*list_mat[2][2] - list_mat[1][2]*list_mat[2][1]) - list_mat[0][1]*(list_mat[1][0]*list_mat[2][2] - list_mat[1][2]*list_mat[2][0]) + list_mat[0][2]*(list_mat[1][0]*list_mat[2][1] - list_mat[1][1]*list_mat[2][0])
|
|
||||||
_0 = determinant
|
|
||||||
_1 = test_mat_copy.determinant()
|
|
||||||
_0, _1 = round(_0, 2), round(_1, 2)
|
|
||||||
assert (_0 == _1), f'{_0} != {_1}'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# test __repr__
|
# test __repr__
|
||||||
assert str(static_test_mat_float) == 'mat3x3([[7.2642, -5.4322, 1.8765],\n [-2.4911, 8.9897, -0.7169],\n [9.5580, -3.3363, 4.9514]])'
|
assert str(static_test_mat_float) == 'mat3x3([[7.2642, -5.4322, 1.8765],\n [-2.4911, 8.9897, -0.7169],\n [9.5580, -3.3363, 4.9514]])'
|
||||||
assert str(static_test_mat_int) == 'mat3x3([[1.0000, 2.0000, 3.0000],\n [4.0000, 5.0000, 6.0000],\n [7.0000, 8.0000, 9.0000]])'
|
assert str(static_test_mat_int) == 'mat3x3([[1.0000, 2.0000, 3.0000],\n [4.0000, 5.0000, 6.0000],\n [7.0000, 8.0000, 9.0000]])'
|
||||||
|
|
||||||
|
|
||||||
# test __getnewargs__
|
# test __getnewargs__
|
||||||
test_mat_copy = test_mat.copy()
|
test_mat_copy = test_mat.copy()
|
||||||
element_value_list = [getattr(test_mat, attr) for attr in element_name_list]
|
element_value_list = [getattr(test_mat, attr) for attr in element_name_list]
|
||||||
|
|||||||
@ -6,5 +6,9 @@ try:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
s = traceback.format_exc()
|
s = traceback.format_exc()
|
||||||
|
|
||||||
assert s == r'''Traceback (most recent call last):
|
ok = s == '''Traceback (most recent call last):
|
||||||
KeyError: 6'''
|
File "80_traceback.py", line 5
|
||||||
|
b = a[6]
|
||||||
|
KeyError: 6'''
|
||||||
|
|
||||||
|
assert ok, s
|
||||||
@ -70,3 +70,12 @@ try:
|
|||||||
exit(1)
|
exit(1)
|
||||||
except UnboundLocalError:
|
except UnboundLocalError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
g = 1
|
||||||
|
def f():
|
||||||
|
global g
|
||||||
|
++g
|
||||||
|
|
||||||
|
f(); f()
|
||||||
|
assert g == 3
|
||||||
Loading…
x
Reference in New Issue
Block a user