...
This commit is contained in:
blueloveTH 2023-04-24 18:55:00 +08:00
parent 07a0122f4b
commit 5765760cea
21 changed files with 141 additions and 608 deletions

164
README.md
View File

@ -1,4 +1,4 @@
# PocketPy
# pocketpy: python interpreter in 1 file
<p>
<a title="Build" href="https://github.com/blueloveTH/pocketpy/actions/workflows" ><img src="https://github.com/blueloveTH/pocketpy/actions/workflows/main.yml/badge.svg" /></a>
@ -8,21 +8,57 @@
<img alt="GitHub release" src="https://img.shields.io/github/release/blueloveth/pocketpy.svg"></a>
<a title="Pub" href="https://pub.dev/packages/pocketpy" ><img src="https://img.shields.io/pub/v/pocketpy" /></a>
</p>
PocketPy is a lightweight(~8000 LOC) Python interpreter for game engines/apps.
---
#### The first production-ready version is `v1.0.0`, which will be available soon.
---
**English |** [**简体中文**](README_zh.md)
pkpy is a lightweight(~8000 LOC) Python interpreter for game engine/apps, built on C++17 with STL.
It is extremely easy to embed. Including a compiler, optimizer and bytecode virtual machine. All of them are available in 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/).
![sample_img](docs/sample.png)
## Quick start
Download the `pocketpy.h` on our GitHub release page.
And `#include` it in your project.
https://github.com/blueloveTH/pocketpy/releases/latest
### Compile flags
To compile it with your project, these flags must be set:
+ `--std=c++17` flag must be set
+ Exception must be enabled
+ RTTI is not required
!!!
For maximum performance, we recommend to use `clang++` with `-O2` flag.
!!!
### 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!')", "main.py", EXEC_MODE);
// Create a list
vm->exec("a = [1, 2, 3]", "main.py", EXEC_MODE);
// Eval the sum of the list
PyObject* result = vm->exec("sum(a)", "<eval>", EVAL_MODE);
std::cout << CAST(int, result); // 6
return 0;
}
```
## Features
@ -49,114 +85,6 @@ Please see https://pocketpy.dev for details or try [Live Demo](https://pocketpy.
| Generator | `yield i` | YES |
| Decorator | `@cache` | YES |
## Getting Started
#### C/C++
For C/C++ developers, you can download the `pocketpy.h` on our GitHub release page.
https://github.com/blueloveTH/pocketpy/releases/latest
Check [C-API](https://pocketpy.dev/c-api/vm/) for references. For further customization, you can use [C++ API](https://pocketpy.dev/getting-started/cpp/).
```cpp
#include "pocketpy.h"
int main(){
// Create a virtual machine
auto vm = pkpy_new_vm();
// Hello world!
pkpy_vm_exec(vm, "print('Hello world!')");
// Create a list
pkpy_vm_exec(vm, "a = [1, 2, 3]");
// Eval the sum of the list
char* result = pkpy_vm_eval(vm, "sum(a)");
printf("%s", result); // 6
// Free the resources
pkpy_delete(result);
pkpy_delete(vm);
return 0;
}
```
#### Unity Engine
PocketPy for Unity can be installed via Unity Asset Store.
https://assetstore.unity.com/packages/slug/241120
```csharp
using UnityEngine;
public class Test01 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
// Create a virtual machine
pkpy.VM vm = new pkpy.VM();
// Create a list
vm.exec("a = [1, 2, 3]");
// Eval the sum of the list
string result = vm.eval("sum(a)");
Debug.Log(result); // 6
// Print to the standard output
vm.exec("print(a)");
pkpy.PyOutput o = vm.read_output();
Debug.Log(o.stdout); // [1, 2, 3]
// Create a binding
vm.bind("builtins", "test", (double x) => x+1);
Debug.Log(vm.eval("test(3.14)")); // '4.14'
}
}
```
#### Flutter
Run the following script to install this plugin.
```
flutter pub add pocketpy
```
See https://pocketpy.dev/getting-started/flutter/
#### Pre-compiled Libs
You can download `artifact.zip` from [Github Release](https://github.com/blueloveTH/pocketpy/releases/latest) page. In this archive, there are pre-compiled libraries for many platforms. The file structure is as follows.
```
- android/
- arm64-v8a/
- libpocketpy.so
- armeabi-v7a/
- libpocketpy.so
- x86_64/
- libpocketpy.so
- linux/
- x86_64/
- pocketpy
- pocketpy.so
- macos/
- pocketpy.bundle/
- web/
- lib/
- pocketpy.js
- pocketpy.wasm
- windows/
- x86_64/
- pocketpy.dll
- pocketpy.exe
```
## Contribution
All kinds of contributions are welcome.
@ -183,5 +111,5 @@ Check our [Coding Style Guide](https://pocketpy.dev/coding_style_guide/) if you
## License
PocketPy is licensed under the [MIT License](http://opensource.org/licenses/MIT).
pkpy is licensed under the [MIT License](http://opensource.org/licenses/MIT).

View File

@ -1,4 +1,4 @@
# PocketPy
# pocketpy: python interpreter in 1 file
<p>
<a title="Build" href="https://github.com/blueloveTH/pocketpy/actions/workflows" ><img src="https://github.com/blueloveTH/pocketpy/actions/workflows/main.yml/badge.svg" /></a>
@ -8,46 +8,16 @@
<img alt="GitHub release" src="https://img.shields.io/github/release/blueloveth/pocketpy.svg"></a>
<a title="Pub" href="https://pub.dev/packages/pocketpy" ><img src="https://img.shields.io/pub/v/pocketpy" /></a>
</p>
PocketPy是一个轻量级的Python解释器为嵌入至游戏引擎而设计。
pocketpy是一个轻量级的Python解释器为嵌入至游戏引擎而设计基于C++17和STL。
它包含一个编译器和基于字节码的虚拟机,以及交互式命令窗的实现。所有功能均集成在单个头文件`pocketpy.h`中,不包含外部依赖项,能很方便地嵌入至你的应用。
你可以 [在浏览器中体验](https://pocketpy.dev/static/web/) PocketPy的交互式界面REPL
![sample_img](docs/sample.png)
## 支持的语法特性
| 特性 | 示例 | 支持 |
| ------------ | ------------------------------- | ---- |
| 分支 | `if..else..elif` | YES |
| 循环 | `for/while/break/continue` | YES |
| 函数 | `def f(x,*args,y=1):` | YES |
| 类与继承 | `class A(B):` | YES |
| 列表 | `[1, 2, 'a']` | YES |
| 列表生成式 | `[i for i in range(5)]` | YES |
| 切片 | `a[1:2], a[:2], a[1:]` | YES |
| 元组 | `(1, 2, 'a')` | YES |
| 字典 | `{'a': 1, 'b': 2}` | YES |
| 格式化字符串 | `f'value is {x}'` | YES |
| 序列解包 | `a, b = 1, 2` | YES |
| 异常 | `raise/try..catch` | YES |
| 动态分发 | `eval()/exec()` | YES |
| 反射 | `hasattr()/getattr()/setattr()` | YES |
| 导入模块 | `import/from..import` | YES |
| 上下文管理器 | `with <expr> as <id>:` | YES |
| 类型标注 | `def f(a:int, b:float=1)` | YES |
| 生成器 | `yield i` | YES |
| 装饰器 | `@cache` | YES |
你可以 [在浏览器中体验](https://pocketpy.dev/static/web/) pocketpy的交互式界面REPL
## 快速上手
根据你所使用的语言和平台选择对应的插件。
#### C/C++
你可以在 [Github Release](https://github.com/blueloveTH/pocketpy/releases/latest) 页面下载`pocketpy.h`,并加入到你的工程中。请参考[C-API](https://pocketpy.dev/c-api/vm/)相关的说明对于C++,你也可以使用`VM`类的方法操作虚拟机。
你可以在 [Github Release](https://github.com/blueloveTH/pocketpy/releases/latest) 页面下载`pocketpy.h`
并加入到你的工程中。请参阅 https://pocketpy.dev 以获取更详细的文档。
```cpp
#include "pocketpy.h"
@ -73,79 +43,29 @@ int main(){
}
```
#### Unity Engine
你可以在Unity资源商店下载PocketPy的C#插件支持Windows/MacOS/Android/iOS平台。
https://assetstore.unity.com/packages/slug/241120
```csharp
using UnityEngine;
public class Test01 : MonoBehaviour
{
void Start()
{
// 创建一个虚拟机
pkpy.VM vm = new pkpy.VM();
// 构造一个列表
vm.exec("a = [1, 2, 3]");
// 对列表进行求和
string result = vm.eval("sum(a)");
Debug.Log(result); // 6
// 打印变量`a`,并读取标准输出
vm.exec("print(a)");
pkpy.PyOutput o = vm.read_output();
Debug.Log(o.stdout); // [1, 2, 3]
// 构造一个函数绑定
vm.bind("builtins", "test", (double x) => x+1);
Debug.Log(vm.eval("test(3.14)")); // '4.14'
}
}
```
#### Flutter
使用下列命令安装pocketpy的[Flutter插件](https://pub.dev/packages/pocketpy)支持Windows/Android/iOS/Web平台。
```
flutter pub add pocketpy
```
详细配置请参考 https://pocketpy.dev/getting-started/flutter/
#### 预编译库
你可以在 [Github Release](https://github.com/blueloveTH/pocketpy/releases/latest) 页面下载`artifact.zip`,这个压缩包中包含了一套预编译库。结构如下。
```
- android/
- arm64-v8a/
- libpocketpy.so
- armeabi-v7a/
- libpocketpy.so
- x86_64/
- libpocketpy.so
- linux/
- x86_64/
- pocketpy
- pocketpy.so
- macos/
- pocketpy.bundle/
- web/
- lib/
- pocketpy.js
- pocketpy.wasm
- windows/
- x86_64/
- pocketpy.dll
- pocketpy.exe
```
## 支持的语法特性
| 特性 | 示例 | 支持 |
| ------------ | ------------------------------- | ---- |
| 分支 | `if..else..elif` | YES |
| 循环 | `for/while/break/continue` | YES |
| 函数 | `def f(x,*args,y=1):` | YES |
| 类与继承 | `class A(B):` | YES |
| 列表 | `[1, 2, 'a']` | YES |
| 列表生成式 | `[i for i in range(5)]` | YES |
| 切片 | `a[1:2], a[:2], a[1:]` | YES |
| 元组 | `(1, 2, 'a')` | YES |
| 字典 | `{'a': 1, 'b': 2}` | YES |
| 格式化字符串 | `f'value is {x}'` | YES |
| 序列解包 | `a, b = 1, 2` | YES |
| 异常 | `raise/try..catch` | YES |
| 动态分发 | `eval()/exec()` | YES |
| 反射 | `hasattr()/getattr()/setattr()` | YES |
| 导入模块 | `import/from..import` | YES |
| 上下文管理器 | `with <expr> as <id>:` | YES |
| 类型标注 | `def f(a:int, b:float=1)` | YES |
| 生成器 | `yield i` | YES |
| 装饰器 | `@cache` | YES |
## 参考

View File

@ -3,7 +3,7 @@ icon: home
label: Welcome
---
# Welcome to PocketPy
# Welcome to pocketpy
pkpy is a lightweight(~8000 LOC) Python interpreter for game engine/apps.

BIN
docs/plugins.zip Normal file

Binary file not shown.

View File

@ -1,35 +0,0 @@
---
icon: code
label: 'C'
order: 5
---
For C developers, you can download the `pocketpy.h` on our GitHub release page.
https://github.com/blueloveTH/pocketpy/releases/latest
## Basic Example
```c
#include "pocketpy.h"
int main(){
// Create a virtual machine
auto vm = pkpy_new_vm();
// Hello world!
pkpy_vm_exec(vm, "print('Hello world!')");
// Create a list
pkpy_vm_exec(vm, "a = [1, 2, 3]");
// Eval the sum of the list
char* result = pkpy_vm_eval(vm, "sum(a)");
printf("%s", result); // 6
// Free the resources
pkpy_delete(result);
pkpy_delete(vm);
return 0;
}
```

View File

@ -1,210 +0,0 @@
---
icon: code
label: Flutter
order: 3
---
## Introduction
<p>
<a title="Pub" href="https://pub.dev/packages/pocketpy" ><img src="https://img.shields.io/pub/v/pocketpy" /></a>
</p>
This plugin provides object-oriented interfaces including full functionality of PocketPy [C-API](https://pocketpy.dev/c-api/vm).
Run the following script to install this plugin.
```
flutter pub add pocketpy
```
## Requirements
#### For Android
You may need to set the Android NDK version to "21.4.7075529" or higher in `android/app/build.gradle`.
```
android {
ndkVersion "21.4.7075529"
}
```
#### For iOS
It should work without any setup.
#### For Web
Download an artifact from https://github.com/blueloveTH/pocketpy/releases/latest.
Unzip it and copy `web/lib` into your root folder where `index.html` locates.
```
...
lib/pocketpy.js
lib/pocketpy.wasm
index.html
...
```
Then open `index.html` and add this line before `flutter.js` tag.
```
...
<!-- This script initializes WASM of pocketpy -->
<script src="./lib/pocketpy.js"></script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
...
```
#### For Windows
VS2017 or higher is required to build the windows `.dll`.
Make sure you have C++ component installed.
## Basic Example
```dart
import 'package:pocketpy/pocketpy.dart' as pkpy;
// Create a virtual machine
pkpy.VM vm = pkpy.VM();
// Run a script
String code = 'print("Hello World!")';
vm.exec(code);
// Read the output
var _o = vm.read_output();
print(_o.stdout) // "Hello world!\n"
print(_o.stderr) // ""
// Create a binding
vm.bind<int>("builtins", "add", (int x, int y) => x + y);
vm.eval("add(1, 2)"); // '3'
```
## REPL Widget Example
```dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:pocketpy/pocketpy.dart' as pkpy;
void main() {
runApp(const MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late final pkpy.VM vm;
late final pkpy.REPL repl;
bool needMoreLines = false;
final TextEditingController _controller = TextEditingController();
final StringBuffer buffer = StringBuffer();
@override
void initState() {
super.initState();
// create a pocketpy virtual machine
vm = pkpy.VM();
// create a REPL
repl = pkpy.REPL(vm);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
refresh();
});
}
void addMessage(String text) {
setState(() {
buffer.write(text);
});
}
void submitCode() {
var text = _controller.text;
_controller.clear();
setState(() {
buffer.write(needMoreLines ? '... $text' : '>>> $text\n');
});
needMoreLines = repl.input(text);
refresh();
}
void refresh() {
// ignore: no_leading_underscores_for_local_identifiers
var _o = vm.read_output();
if (_o.stdout.isNotEmpty) buffer.write(_o.stdout);
if (_o.stderr.isNotEmpty) buffer.write(_o.stderr);
setState(() {});
}
@override
Widget build(BuildContext context) {
var style = const TextStyle(fontSize: 16);
return Scaffold(
appBar: AppBar(
title: const Text('Demo'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Expanded(
child: SingleChildScrollView(
reverse: true,
child: Text(
buffer.toString(),
style: style,
textAlign: TextAlign.left,
),
),
),
const SizedBox(
height: 16,
),
SizedBox(
height: 50,
child: TextFormField(
controller: _controller,
style: style,
maxLines: 1,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter Python code',
),
),
),
Container(
height: 60,
alignment: Alignment.centerRight,
child: MaterialButton(
onPressed: submitCode,
color: Colors.blue,
textColor: Colors.white,
child: const Text('Run')),
),
]),
),
);
}
}
```

View File

@ -1,78 +0,0 @@
---
icon: code
label: Unity Engine
order: 4
---
## Introduction
PocketPy for Unity can be installed via Unity Asset Store.
https://assetstore.unity.com/packages/slug/241120
## Example 01
```csharp
using UnityEngine;
public class Test01 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
// Create a virtual machine
pkpy.VM vm = new pkpy.VM();
// Create a list
vm.exec("a = [1, 2, 3]");
// Eval the sum of the list
string result = vm.eval("sum(a)");
Debug.Log(result); // 6
// Print to the standard output
vm.exec("print(a)");
pkpy.PyOutput o = vm.read_output();
Debug.Log(o.stdout); // [1, 2, 3]
// Create a binding
vm.bind("builtins", "test", (double x) => x+1);
Debug.Log(vm.eval("test(3.14)")); // '4.14'
}
}
```
## Example 02
```csharp
using UnityEngine;
using UnityEngine.UI;
public class Test02 : MonoBehaviour
{
Text text;
pkpy.VM vm;
// Start is called before the first frame update
void Start()
{
text = GetComponent<Text>();
Application.targetFrameRate = 60;
vm = new pkpy.VM();
vm.exec("a = 0");
}
// Update is called once per frame
void Update()
{
if(vm == null) return;
vm.exec("a += 1");
text.text = vm.eval("a");
}
}
```

View File

@ -1,5 +1,5 @@
---
icon: code
icon: dot
label: 'Access attributes'
order: 80
---

View File

@ -1,5 +1,5 @@
---
icon: code
icon: dot
label: 'Bind native function'
order: 60
---

View File

@ -1,5 +1,5 @@
---
icon: code
icon: dot
label: 'Call Python function'
order: 70
---

View File

@ -1,5 +1,5 @@
---
icon: code
icon: dot
label: 'Execute Python code'
order: 93
---
@ -17,7 +17,7 @@ Once you have a `VM` instance, you can execute python code by calling `exec` met
If the execution is not successful, e.g. a syntax error or a runtime exception,
the return value will be `nullptr`.
#### Compile mode
### Compile mode
The `mode` parameter controls how the source code is compiled. There are 4 possible values:
+ `EXEC_MODE`, this is the default mode. Just do normal execution.

View File

@ -1,22 +1,15 @@
---
icon: code
icon: dot
label: 'Installation'
order: 100
---
You need to download `pocketpy.h` on our GitHub release page.
Download the `pocketpy.h` on our GitHub release page.
And `#include` it in your project.
https://github.com/blueloveTH/pocketpy/releases/latest
Alternatively, you can install it via vcpkg.io.
(Will be available soon)
```bash
vcpkg install pocketpy
```
## Compile flags
### Compile flags
To compile it with your project, these flags must be set:
@ -28,7 +21,7 @@ To compile it with your project, these flags must be set:
For maximum performance, we recommend to use `clang++` with `-O2` flag.
!!!
## Example
### Example
```cpp
#include "pocketpy.h"
@ -37,7 +30,7 @@ using namespace pkpy;
int main(){
// Create a virtual machine
VM* vm = new VM(true);
VM* vm = new VM();
// Hello world!
vm->exec("print('Hello world!')", "main.py", EXEC_MODE);
@ -51,3 +44,30 @@ int main(){
return 0;
}
```
### Overview
pkpy's C++ interfaces are organized in an object-oriented way.
All classes are located in `pkpy` namespace.
The most important class is the `VM` class. A `VM` instance is a python virtual machine which holds all necessary runtime states, including callstacks, modules, variables, etc.
You need to use the C++ `new` operator to create a `VM` instance.
```cpp
VM* vm = new VM();
```
The constructor can take 2 extra parameters.
#### `VM(bool use_stdio=true, bool enable_os=true)`
+ `use_stdio`, if `true`, the `print()` function outputs string to `stdout`. Error messages will be send to `stderr`; If `false`, they will be sent to an internal buffer. In the latter case, you need to read them via `read_output` manually.
+ `enable_os`, whether to enable OS-related features or not. This setting controls the availability of some priviledged modules such os `io` and `os` as well as builtin function `open`.
When you are done with the `VM` instance, you need to use the C++ `delete` operator to free the memory.
```cpp
delete vm;
```

View File

@ -1,5 +1,5 @@
---
icon: code
icon: dot
label: 'Interop with PyObject'
order: 90
---
@ -22,7 +22,7 @@ PyObject* i = VAR("abc");
std::cout << CAST(Str, i); // abc
```
#### Types
### Types
| python type | C type | note |
| ------------ | ---------------- | ---------------------- |

View File

@ -1,30 +0,0 @@
---
icon: code
label: 'Overview'
order: 95
---
pkpy's C++ interfaces are organized in an object-oriented way.
All classes are located in `pkpy` namespace.
The most important class is the `VM` class. A `VM` instance is a python virtual machine which holds all necessary runtime states, including callstacks, modules, variables, etc.
You need to use the C++ `new` operator to create a `VM` instance.
```cpp
VM* vm = new VM();
```
The constructor can take 2 extra parameters.
#### `VM(bool use_stdio=true, bool enable_os=true)`
+ `use_stdio`, if `true`, the `print()` function outputs string to `stdout`. Error messages will be send to `stderr`; If `false`, they will be sent to an internal buffer. In the latter case, you need to read them via `read_output` manually.
+ `enable_os`, whether to enable OS-related features or not. This setting controls the availability of some priviledged modules such os `io` and `os` as well as builtin function `open`.
When you are done with the `VM` instance, you need to use the C++ `delete` operator to free the memory.
```cpp
delete vm;
```

View File

@ -1,5 +1,5 @@
---
icon: code
icon: dot
label: 'Wrap native struct'
order: 50
---
@ -77,7 +77,7 @@ struct Vector2 {
};
int main(){
VM* vm = new VM(true);
VM* vm = new VM();
// create a new module `test`
PyObject* mod = vm->new_module("test");
// register `Vector2` to `test` module

View File

@ -2,12 +2,12 @@ input: .
output: .retype
url: https://pocketpy.dev
branding:
title: PocketPy
title: pocketpy
label: v1.0
logo: "./static/logo.png"
favicon: "./static/logo.png"
# logo: "./static/logo.png"
# favicon: "./static/logo.png"
meta:
title: " | A lightweight Python interpreter for game engines"
title: " | Open Source Python interpreter in 1 file"
links:
- text: "Home"
icon: home

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

BIN
docs/static/logo.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View File

@ -890,9 +890,7 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
// if this function is simple, a.k.a, no kwargs and no *args and not a generator
// we can use a fast path to avoid using buffer copy
if(fn.is_simple){
#if DEBUG_EXTRA_CHECK
for(PyObject** p=p0; p<args.begin(); p++) *p = nullptr;
#endif
if(args.size() > fn.decl->args.size()) TypeError("too many positional arguments");
int spaces = co->varnames.size() - fn.decl->args.size();
for(int j=0; j<spaces; j++) PUSH(nullptr);
callstack.emplace(&s_data, p0, co, _module, callable, FastLocals(co, args.begin()));

View File

@ -33,3 +33,23 @@ assert b == [2, 3, 4]
a, *b = [1]
assert a == 1
assert b == []
# test perfect forwarding
def f0(a, b, *args):
return a + b + sum(args)
x = f0(1, 2, 3, 4)
assert x == 10
a = [1, 2, 3, 4]
x = f0(*a)
assert x == 10
def f1(a):
return a
try:
x = f1(*[1, 2, 3, 4])
exit(1)
except TypeError:
pass