remove *.cpp

This commit is contained in:
blueloveTH 2024-06-24 00:06:31 +08:00
parent 23d7b56c86
commit 19ed0fdaf5
78 changed files with 808 additions and 9157 deletions

BIN
.github/workflows.zip vendored Normal file

Binary file not shown.

View File

@ -1,204 +0,0 @@
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 /Ox /I. /DPK_ENABLE_OS=1 main.cpp /link /out: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/x86_64
python cmake_build.py
cp main.exe output/x86_64
cp pocketpy.dll output/x86_64
- uses: actions/upload-artifact@v4
with:
name: windows
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 and Test
run: |
mkdir -p output/x86_64
python cmake_build.py
python scripts/run_tests.py
cp main output/x86_64
cp libpocketpy.so output/x86_64
env:
CXX: clang++
CC: clang
- uses: actions/upload-artifact@v4
with:
name: linux
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 output
# xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
# cp -r build/Release/pocketpy.bundle output
# - uses: actions/upload-artifact@v4
# with:
# name: macos
# 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/arm64-v8a
mkdir -p output/armeabi-v7a
mkdir -p output/x86_64
cp build/android/arm64-v8a/libpocketpy.so output/arm64-v8a
cp build/android/armeabi-v7a/libpocketpy.so output/armeabi-v7a
cp build/android/x86_64/libpocketpy.so output/x86_64
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
- uses: actions/upload-artifact@v4
with:
name: android
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
cp -r build/pocketpy.xcframework output/pocketpy.xcframework
- uses: actions/upload-artifact@v4
with:
name: ios
path: output
merge:
runs-on: ubuntu-latest
needs: [ build_win32, build_linux, build_darwin, build_android, build_ios ]
steps:
- name: "Create output directory"
run: "mkdir $GITHUB_WORKSPACE/output"
- name: "Merge win32"
uses: actions/download-artifact@v4.1.7
with:
name: windows
path: $GITHUB_WORKSPACE/output/windows
- name: "Merge linux"
uses: actions/download-artifact@v4.1.7
with:
name: linux
path: $GITHUB_WORKSPACE/output/linux
# - name: "Merge darwin"
# uses: actions/download-artifact@v4.1.7
# with:
# name: macos
# path: $GITHUB_WORKSPACE/output/macos
- name: "Merge android"
uses: actions/download-artifact@v4.1.7
with:
name: android
path: $GITHUB_WORKSPACE/output/android
- name: "Merge ios"
uses: actions/download-artifact@v4.1.7
with:
name: ios
path: $GITHUB_WORKSPACE/output/ios
- name: "Upload merged artifact"
uses: actions/upload-artifact@v4.3.3
with:
name: all-in-one
path: $GITHUB_WORKSPACE/output

View File

@ -1,42 +0,0 @@
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: |
python scripts/build_references.py
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'

View File

@ -1,9 +1,9 @@
-Wall -Wall
-W* -W*
-xc++ -xc
-std=c++17 -std=c11
-Iinclude/ -Iinclude/
-I3rd/cjson/include/ -I3rd/cjson/include/
-I3rd/lua_bridge/include/

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
#include "pocketpy/pocketpy.hpp" #include "pocketpy/pocketpy.h"

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
#include "pocketpy/pocketpy.hpp" #include "pocketpy/pocketpy.h"

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "pocketpy/common/vector.h" #include "pocketpy/common/vector.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"

View File

@ -1,404 +0,0 @@
#pragma once
#include "pocketpy/common/sstream.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/common/memorypool.h"
#include "pocketpy/common/vector.h"
#include "pocketpy/common/vector.hpp"
#include "pocketpy/common/str.h"
#include "pocketpy/common/strname.h"
#include <cassert>
#include <string_view>
#include <string>
#include <ostream>
namespace pkpy {
struct Str: pkpy_Str {
bool is_inlined() const { return is_sso; }
Str(){
pkpy_Str__ctor2(this, "", 0);
}
Str(pkpy_Str&& s){
std::memcpy(this, &s, sizeof(pkpy_Str));
}
Str(const std::string& s){
pkpy_Str__ctor2(this, s.data(), s.size());
}
Str(std::string_view s){
pkpy_Str__ctor2(this, s.data(), s.size());
}
Str(const char* s){
pkpy_Str__ctor2(this, s, strlen(s));
}
Str(const char* s, int len){
pkpy_Str__ctor2(this, s, len);
}
Str(pair<char*, int> detached) {
this->size = detached.second;
this->is_ascii = c11__isascii(detached.first, detached.second);
this->is_sso = false;
this->_ptr = detached.first;
assert(_ptr[size] == '\0');
}
Str(const Str& other){
pkpy_Str__ctor2(this, pkpy_Str__data(&other), other.size);
}
Str(Str&& other){
std::memcpy(this, &other, sizeof(pkpy_Str));
other.size = 0;
other.is_sso = true;
}
operator std::string_view () const { return sv(); }
const char* begin() const { return pkpy_Str__data(this); }
const char* end() const { return pkpy_Str__data(this) + size; }
int length() const { return size; }
char operator[] (int idx) const { return pkpy_Str__data(this)[idx]; }
bool empty() const { return size == 0; }
size_t hash() const { return std::hash<std::string_view>()(sv()); }
Str& operator= (const Str& other){
pkpy_Str__dtor(this);
pkpy_Str__ctor2(this, pkpy_Str__data(&other), other.size);
return *this;
}
Str operator+ (const Str& other) const{
return pkpy_Str__concat(this, &other);
}
Str operator+ (const char* other) const{
return pkpy_Str__concat2(this, other, strlen(other));
}
friend Str operator+ (const char* self, const Str& other){
pkpy_Str tmp;
pkpy_Str__ctor2(&tmp, self, strlen(self));
pkpy_Str retval = pkpy_Str__concat(&tmp, &other);
pkpy_Str__dtor(&tmp);
return retval;
}
bool operator== (const std::string_view other) const{
int res = pkpy_Str__cmp2(this, other.data(), other.size());
return res == 0;
}
bool operator!= (const std::string_view other) const{
int res = pkpy_Str__cmp2(this, other.data(), other.size());
return res != 0;
}
bool operator< (const std::string_view other) const{
int res = pkpy_Str__cmp2(this, other.data(), other.size());
return res < 0;
}
friend bool operator< (const std::string_view other, const Str& str){
int res = pkpy_Str__cmp2(&str, other.data(), other.size());
return res > 0;
}
bool operator== (const char* p) const{
int res = pkpy_Str__cmp2(this, p, strlen(p));
return res == 0;
}
bool operator!= (const char* p) const{
int res = pkpy_Str__cmp2(this, p, strlen(p));
return res != 0;
}
bool operator== (const Str& other) const{
return pkpy_Str__cmp(this, &other) == 0;
}
bool operator!= (const Str& other) const{
return pkpy_Str__cmp(this, &other) != 0;
}
bool operator< (const Str& other) const{
return pkpy_Str__cmp(this, &other) < 0;
}
bool operator> (const Str& other) const{
return pkpy_Str__cmp(this, &other) > 0;
}
bool operator<= (const Str& other) const{
return pkpy_Str__cmp(this, &other) <= 0;
}
bool operator>= (const Str& other) const{
return pkpy_Str__cmp(this, &other) >= 0;
}
~Str(){
pkpy_Str__dtor(this);
}
friend std::ostream& operator<< (std::ostream& os, const Str& self){
os.write(pkpy_Str__data(&self), self.size);
return os;
}
const char* c_str() const { return pkpy_Str__data(this); }
std::string_view sv() const {
return std::string_view(pkpy_Str__data(this), size);
}
std::string str() const {
return std::string(pkpy_Str__data(this), size);
}
Str slice(int start, int stop) const{
return pkpy_Str__slice2(this, start, stop);
}
Str slice(int start) const{
return pkpy_Str__slice(this, start);
}
Str substr(int start) const{
return pkpy_Str__slice(this, start);
}
Str strip(bool left, bool right, const Str& chars) const{
return pkpy_Str__strip2(this, left, right, &chars);
}
Str strip(bool left = true, bool right = true) const{
return pkpy_Str__strip(this, left, right);
}
Str lstrip() const { return strip(true, false); }
Str rstrip() const { return strip(false, true); }
Str lower() const{
return pkpy_Str__lower(this);
}
Str upper() const{
return pkpy_Str__upper(this);
}
Str replace(char old, char new_) const{
return pkpy_Str__replace(this, old, new_);
}
Str replace(const Str& old, const Str& new_) const{
return pkpy_Str__replace2(this, &old, &new_);
}
Str escape(char quote='\'') const{
return pkpy_Str__escape(this, quote);
}
vector<std::string_view> split(const Str& sep) const{
c11_vector/* T=c11_string */ res = pkpy_Str__split2(this, &sep);
vector<std::string_view> retval(res.count);
for(int i = 0; i < res.count; i++){
c11_string tmp = c11__getitem(c11_string, &res, i);
retval[i] = std::string_view(tmp.data, tmp.size);
}
c11_vector__dtor(&res);
return retval;
}
vector<std::string_view> split(char sep) const{
c11_vector/* T=c11_string */ res = pkpy_Str__split(this, sep);
vector<std::string_view> retval(res.count);
for(int i = 0; i < res.count; i++){
c11_string tmp = c11__getitem(c11_string, &res, i);
retval[i] = std::string_view(tmp.data, tmp.size);
}
c11_vector__dtor(&res);
return retval;
}
int index(const Str& sub, int start = 0) const{
return pkpy_Str__index(this, &sub, start);
}
int count(const Str& sub) const{
return pkpy_Str__count(this, &sub);
}
/*************unicode*************/
int _unicode_index_to_byte(int i) const{
return pkpy_Str__unicode_index_to_byte(this, i);
}
int _byte_index_to_unicode(int n) const{
return pkpy_Str__byte_index_to_unicode(this, n);
}
Str u8_getitem(int i) const{
return pkpy_Str__u8_getitem(this, i);
}
Str u8_slice(int start, int stop, int step) const{
return pkpy_Str__u8_slice(this, start, stop, step);
}
int u8_length() const{
return pkpy_Str__u8_length(this);
}
};
struct StrName {
uint16_t index;
StrName() : index(0) {}
StrName(uint16_t index) : index(index) {}
StrName(const char* s) : index(get(s).index) {}
StrName(const Str& s) : index(get(s.sv()).index) {}
bool empty() const { return index == 0; }
Str escape() const { return Str(sv()).escape(); }
bool operator== (const StrName& other) const noexcept { return this->index == other.index; }
bool operator!= (const StrName& other) const noexcept { return this->index != other.index; }
bool operator< (const StrName& other) const noexcept { return sv() < other.sv(); }
bool operator> (const StrName& other) const noexcept { return sv() > other.sv(); }
inline static StrName get(std::string_view s){
uint16_t index = pkpy_StrName__map2({s.data(), (int)s.size()});
return StrName(index);
}
std::string_view sv() const{
return pkpy_StrName__rmap(index);
}
const char* c_str() const{
return pkpy_StrName__rmap(index);
}
};
struct SStream: pk_SStream {
PK_ALWAYS_PASS_BY_POINTER(SStream)
int _precision = -1;
bool _submited = false;
bool empty() const { return data.count == 0; }
void setprecision(int precision) { _precision = precision; }
SStream() {
pk_SStream__ctor(this);
}
SStream(int guess_size) { c11_vector__reserve(&data, guess_size); }
~SStream() {
// in case of error
if(!_submited) pk_SStream__dtor(this);
}
Str str(){
assert(!_submited);
_submited = true;
return pk_SStream__submit(this);
}
SStream& operator<< (const Str& val){
pk_SStream__write_Str(this, &val);
return *this;
}
SStream& operator<< (const char* val){
pk_SStream__write_cstr(this, val);
return *this;
}
SStream& operator<< (int val){
pk_SStream__write_int(this, val);
return *this;
}
SStream& operator<< (size_t val){
// size_t could overflow int64, but nevermind...
pk_SStream__write_i64(this, val);
return *this;
}
SStream& operator<< (i64 val){
pk_SStream__write_i64(this, val);
return *this;
}
SStream& operator<< (f64 val){
pk_SStream__write_double(this, val, _precision);
return *this;
}
SStream& operator<< (const std::string& val){
pk_SStream__write_cstrn(this, val.data(), val.size());
return *this;
}
SStream& operator<< (std::string_view val){
pk_SStream__write_cstrn(this, val.data(), val.size());
return *this;
}
SStream& operator<< (c11_string val){
pk_SStream__write_cstrn(this, val.data, val.size);
return *this;
}
SStream& operator<< (char val){
pk_SStream__write_char(this, val);
return *this;
}
SStream& operator<< (StrName name){
std::string_view sv = name.sv();
pk_SStream__write_cstrn(this, sv.data(), sv.size());
return *this;
}
void write_hex(unsigned char val, bool non_zero = false){
pk_SStream__write_hex(this, val, non_zero);
}
void write_ptr(void* p){
pk_SStream__write_ptr(this, p);
}
};
#ifdef _S
#undef _S
#endif
template <typename... Args>
Str _S(Args&&... args) {
SStream ss;
(ss << ... << args);
return ss.str();
}
struct CString {
const char* ptr;
CString(const char* ptr) : ptr(ptr) {}
operator const char* () const { return ptr; }
};
#define DEF_SNAME(name) const static StrName name(#name)
} // namespace pkpy

View File

@ -7,14 +7,14 @@
extern "C" { extern "C" {
#endif #endif
typedef uint16_t pkpy_StrName; typedef uint16_t StrName;
uint16_t pkpy_StrName__map(const char*); uint16_t pk_StrName__map(const char*);
uint16_t pkpy_StrName__map2(c11_string); uint16_t pk_StrName__map2(c11_string);
const char* pkpy_StrName__rmap(uint16_t index); const char* pk_StrName__rmap(uint16_t index);
void pkpy_StrName__initialize(); void pk_StrName__initialize();
void pkpy_StrName__finalize(); void pk_StrName__finalize();
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,46 +0,0 @@
#pragma once
#include <type_traits>
namespace pkpy {
// is_pod_v<> for c++17 and c++20
template <typename T>
constexpr inline bool is_pod_v = std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
// https://en.cppreference.com/w/cpp/types/is_integral
template <typename T>
constexpr inline bool is_integral_v = !std::is_same_v<T, bool> && std::is_integral_v<T>;
template <typename T>
constexpr inline bool is_floating_point_v = std::is_same_v<T, float> || std::is_same_v<T, double>;
// by default, only `int` and `float` enable SSO
// users can specialize this template to enable SSO for other types
// SSO types cannot have instance dict
template <typename T>
constexpr inline bool is_sso_v = is_integral_v<T> || is_floating_point_v<T>;
template <typename T>
using obj_get_t = T&;
template <typename T>
constexpr inline bool is_trivially_relocatable_v =
std::is_trivially_copyable_v<T> && std::is_trivially_destructible_v<T>;
template <typename, typename = void>
struct has_gc_marker : std::false_type {};
template <typename T>
struct has_gc_marker<T, std::void_t<decltype(&T::_gc_mark)>> : std::true_type {};
template <typename T>
constexpr inline int py_sizeof = 16 + sizeof(T);
#define PK_ALWAYS_PASS_BY_POINTER(T) \
T(const T&) = delete; \
T& operator= (const T&) = delete; \
T(T&&) = delete; \
T& operator= (T&&) = delete;
} // namespace pkpy

View File

@ -1,37 +0,0 @@
#pragma once
#include <cstdint>
namespace pkpy {
using i64 = int64_t; // always 64-bit
using f64 = double; // always 64-bit
static_assert(sizeof(i64) == 8);
static_assert(sizeof(f64) == 8);
// Explicitly allow copying if copy constructor is deleted
struct explicit_copy_t {
explicit explicit_copy_t() = default;
};
template <typename K, typename V>
struct pair {
K first;
V second;
pair(K first, V second) : first(first), second(second) {}
};
// Dummy types
struct DummyInstance {};
struct DummyModule {};
struct NoReturn {};
// Forward declarations
struct PyObject;
struct Frame;
class VM;
}; // namespace pkpy

View File

@ -33,6 +33,8 @@ extern const char* kPlatformStrings[];
#define PK_MIN(a, b) ((a) < (b) ? (a) : (b)) #define PK_MIN(a, b) ((a) < (b) ? (a) : (b))
#define PK_MAX(a, b) ((a) > (b) ? (a) : (b)) #define PK_MAX(a, b) ((a) > (b) ? (a) : (b))
#define PK_ARRAY_COUNT(a) (sizeof(a) / sizeof(a[0]))
// NARGS // NARGS
#define PK_NARGS_SEQ(_1, _2, _3, _4, N, ...) N #define PK_NARGS_SEQ(_1, _2, _3, _4, N, ...) N
#define PK_NARGS(...) PK_NARGS_SEQ(__VA_ARGS__, 4, 3, 2, 1, 0) #define PK_NARGS(...) PK_NARGS_SEQ(__VA_ARGS__, 4, 3, 2, 1, 0)

View File

@ -1,459 +0,0 @@
#pragma once
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <algorithm>
#include "pocketpy/common/traits.hpp"
#include "pocketpy/common/types.hpp"
#include "pocketpy/common/algorithm.h"
namespace pkpy {
template <typename T>
struct array {
static_assert(is_pod_v<T>);
T* _data;
int _size;
using size_type = int;
array() : _data(nullptr), _size(0) {}
array(int size) : _data((T*)std::malloc(sizeof(T) * size)), _size(size) {}
array(array&& other) noexcept : _data(other._data), _size(other._size) {
other._data = nullptr;
other._size = 0;
}
array(const array& other) = delete;
array(explicit_copy_t, const array& other) {
_data = (T*)std::malloc(sizeof(T) * other._size);
_size = other._size;
for(int i = 0; i < _size; i++)
_data[i] = other._data[i];
}
array(T* data, int size) : _data(data), _size(size) {}
array& operator= (array&& other) noexcept {
if(_data) std::free(_data);
_data = other._data;
_size = other._size;
other._data = nullptr;
other._size = 0;
return *this;
}
array& operator= (const array& other) = delete;
T& operator[] (int i) {
assert(i >= 0 && i < _size);
return _data[i];
}
const T& operator[] (int i) const {
assert(i >= 0 && i < _size);
return _data[i];
}
int size() const { return _size; }
T* begin() const { return _data; }
T* end() const { return _data + _size; }
T* data() const { return _data; }
pair<T*, int> detach() noexcept {
pair<T*, int> retval(_data, _size);
_data = nullptr;
_size = 0;
return retval;
}
~array() {
if(_data) std::free(_data);
}
};
template <bool may_alias = false, typename T>
void uninitialized_copy_n(const T* src, int n, T* dest) {
if(n == 0) return;
if constexpr(std::is_trivially_copyable_v<T>) {
if constexpr(may_alias) {
std::memmove(dest, src, sizeof(T) * n);
} else {
std::memcpy(dest, src, sizeof(T) * n);
}
} else {
for(int i = 0; i < n; i++) {
new (dest + i) T(*(src + i));
}
}
}
template <bool may_alias = false, bool backward = false, typename T>
void uninitialized_relocate_n(T* src, int n, T* dest) {
if(n == 0) return;
if constexpr(is_trivially_relocatable_v<T>) {
if constexpr(may_alias) {
std::memmove(dest, src, sizeof(T) * n);
} else {
std::memcpy(dest, src, sizeof(T) * n);
}
} else {
if constexpr(backward) {
for(int i = n - 1; i >= 0; i--) {
new (dest + i) T(std::move(*(src + i)));
(src + i)->~T();
}
} else {
for(int i = 0; i < n; i++) {
new (dest + i) T(std::move(*(src + i)));
(src + i)->~T();
}
}
}
}
template <typename T>
struct vector {
T* _data;
int _capacity;
int _size;
using size_type = int;
vector() : _data(nullptr), _capacity(0), _size(0) {}
vector(int size) : _data((T*)std::malloc(sizeof(T) * size)), _capacity(size), _size(size) {}
vector(vector&& other) noexcept : _data(other._data), _capacity(other._capacity), _size(other._size) {
other._data = nullptr;
other._capacity = 0;
other._size = 0;
}
vector(const vector& other) = delete;
vector(explicit_copy_t, const vector& other) : _capacity(other._size), _size(other._size) {
_data = (T*)std::malloc(sizeof(T) * _capacity);
uninitialized_copy_n(other._data, _size, _data);
}
// allow move
vector& operator= (vector&& other) noexcept {
if(_data) {
std::destroy(begin(), end());
std::free(_data);
}
new (this) vector(std::move(other));
return *this;
}
// disallow copy
vector& operator= (const vector& other) = delete;
bool empty() const { return _size == 0; }
int size() const { return _size; }
int capacity() const { return _capacity; }
T& back() {
assert(_size > 0);
return _data[_size - 1];
}
T* begin() const { return _data; }
T* end() const { return _data + _size; }
T* data() const { return _data; }
T& operator[] (int i) {
assert(i >= 0 && i < _size);
return _data[i];
}
const T& operator[] (int i) const {
assert(i >= 0 && i < _size);
return _data[i];
}
void clear() {
std::destroy(begin(), end());
_size = 0;
}
void reserve(int cap) {
if(cap < 4) cap = 4; // minimum capacity
if(cap <= capacity()) return;
T* new_data = (T*)std::malloc(sizeof(T) * cap);
uninitialized_relocate_n(_data, _size, new_data);
if(_data) std::free(_data);
_data = new_data;
_capacity = cap;
}
template <typename... Args>
void emplace_back(Args&&... args) {
if(_size == _capacity) reserve(_capacity * 2);
new (_data + _size) T(std::forward<Args>(args)...);
_size++;
}
void push_back(const T& t) { emplace_back(t); }
void push_back(T&& t) { emplace_back(std::move(t)); }
bool contains(const T& t) const {
for(int i = 0; i < _size; i++) {
if(_data[i] == t) return true;
}
return false;
}
void extend(const T* begin, const T* end) {
int n = end - begin;
reserve(_size + n);
uninitialized_copy_n(begin, n, _data + _size);
_size += n;
}
void insert(const T* it, const T& t) {
assert(it >= begin() && it <= end());
int pos = it - begin();
if(_size == _capacity) {
int new_capacity = (_capacity == 0) ? 4 : _capacity * 2;
T* new_data = (T*)std::malloc(sizeof(T) * new_capacity);
uninitialized_relocate_n(_data, pos, new_data);
new (new_data + pos) T(t);
uninitialized_relocate_n(_data + pos, _size - pos, new_data + pos + 1);
if(_data) std::free(_data);
_data = new_data;
_capacity = new_capacity;
} else {
uninitialized_relocate_n<true, true>(_data + pos, _size - pos, _data + pos + 1);
new (_data + pos) T(t);
}
_size++;
}
void erase(T* it) {
assert(it >= begin() && it < end());
int pos = it - begin();
_data[pos].~T();
uninitialized_relocate_n<true>(_data + pos + 1, _size - pos, _data + pos);
_size--;
}
void pop_back() {
assert(_size > 0);
_size--;
_data[_size].~T();
}
[[nodiscard]] T popx_back() {
T retval = std::move(back());
pop_back();
return retval;
}
pair<T*, int> detach() noexcept {
pair<T*, int> retval(_data, _size);
_data = nullptr;
_capacity = 0;
_size = 0;
return retval;
}
void swap(vector& other) {
std::swap(_data, other._data);
std::swap(_capacity, other._capacity);
std::swap(_size, other._size);
}
~vector() {
if(_data) {
std::destroy(begin(), end());
std::free(_data);
}
}
};
template <typename T, std::size_t N>
struct small_vector {
static_assert(is_pod_v<T>);
alignas(T) char _buffer[sizeof(T) * N];
T* _begin;
T* _end;
T* _capacity;
[[nodiscard]] bool is_small() const { return _begin == reinterpret_cast<const T*>(_buffer); }
[[nodiscard]] int size() const { return _end - _begin; }
[[nodiscard]] int capacity() const { return _capacity - _begin; }
[[nodiscard]] bool empty() const { return _begin == _end; }
[[nodiscard]] T* data() const { return _begin; }
[[nodiscard]] T* begin() const { return _begin; }
[[nodiscard]] T* end() const { return _end; }
[[nodiscard]] T& back() const { return *(end() - 1); }
[[nodiscard]] T& operator[] (int index) { return _begin[index]; }
[[nodiscard]] const T& operator[] (int index) const { return _begin[index]; }
small_vector() : _begin(reinterpret_cast<T*>(_buffer)), _end(_begin), _capacity(_begin + N) {}
small_vector(const small_vector& other) noexcept {
const auto size = other.size();
const auto capacity = other.capacity();
_begin = reinterpret_cast<T*>(other.is_small() ? _buffer : std::malloc(sizeof(T) * capacity));
uninitialized_copy_n(other._begin, size, this->_begin);
_end = _begin + size;
_capacity = _begin + capacity;
}
small_vector(small_vector&& other) noexcept {
if(other.is_small()) {
_begin = reinterpret_cast<T*>(_buffer);
uninitialized_relocate_n((T*)other._buffer, other.size(), (T*)_buffer);
_end = _begin + other.size();
_capacity = _begin + N;
} else {
_begin = other._begin;
_end = other._end;
_capacity = other._capacity;
}
other._begin = reinterpret_cast<T*>(other._buffer);
other._end = other._begin;
other._capacity = other._begin + N;
}
small_vector& operator= (const small_vector& other) = delete;
small_vector& operator= (small_vector&& other) noexcept {
if(this != &other) {
~small_vector();
::new (this) small_vector(std::move(other));
}
return *this;
}
~small_vector() {
std::destroy(_begin, _end);
if(!is_small()) std::free(_begin);
}
template <typename... Args>
void emplace_back(Args&&... args) noexcept {
if(_end == _capacity) {
const auto new_capacity = capacity() * 2;
const auto size = this->size();
auto new_data = (T*)std::malloc(sizeof(T) * new_capacity);
uninitialized_relocate_n(_begin, size, new_data);
if(!is_small()) std::free(_begin);
_begin = new_data;
_end = _begin + size;
_capacity = _begin + new_capacity;
}
::new (_end) T(std::forward<Args>(args)...);
_end++;
}
void push_back(const T& value) { emplace_back(value); }
void push_back(T&& value) { emplace_back(std::move(value)); }
void pop_back() {
_end--;
_end->~T();
}
void clear() {
std::destroy(_begin, _end);
_end = _begin;
}
};
template <typename T, std::size_t N>
class small_vector_2 : public small_vector<T, N> {
public:
small_vector_2() = default;
small_vector_2(const small_vector_2& other) = delete;
small_vector_2& operator= (const small_vector_2& other) = delete;
small_vector_2(small_vector_2&& other) = delete;
small_vector_2& operator= (small_vector_2&& other) = delete;
};
template <typename K, typename V>
struct small_map {
struct Item {
K first;
V second;
bool operator< (const K& other) const { return first < other; }
bool operator< (const Item& other) const { return first < other.first; }
};
vector<Item> _data;
small_map() = default;
using size_type = int;
int size() const { return _data.size(); }
bool empty() const { return _data.empty(); }
Item* begin() const { return _data.begin(); }
Item* end() const { return _data.end(); }
Item* data() const { return _data.data(); }
void insert(const K& key, const V& value) {
Item* it = std::lower_bound(_data.begin(), _data.end(), key);
assert(it == _data.end() || it->first != key);
_data.insert(it, {key, value});
}
V* try_get(const K& key) const {
auto it = std::lower_bound(_data.begin(), _data.end(), key);
if(it == _data.end() || it->first != key) return nullptr;
return &it->second;
}
V get(const K& key, V default_value) const {
static_assert(is_pod_v<V>);
auto it = try_get(key);
return it ? *it : default_value;
}
bool contains(const K& key) const { return try_get(key) != nullptr; }
void clear() { _data.clear(); }
const V& operator[] (const K& key) const {
auto it = try_get(key);
assert(it != nullptr);
return *it;
}
};
} // namespace pkpy

View File

@ -1,229 +0,0 @@
#pragma once
#include "pocketpy/interpreter/cffi.hpp"
namespace pkpy {
struct NativeProxyFuncCBase {
virtual PyVar operator() (VM* vm, ArgsView args) = 0;
};
template <typename Ret, typename... Params>
struct NativeProxyFuncC final : NativeProxyFuncCBase {
constexpr static int N = sizeof...(Params);
using _Fp = Ret (*)(Params...);
_Fp func;
NativeProxyFuncC(_Fp func) : func(func) {}
PyVar operator() (VM* vm, ArgsView args) override {
assert(args.size() == N);
return call<Ret>(vm, args, std::make_index_sequence<N>());
}
template <typename __Ret, size_t... Is>
PyVar call(VM* vm, ArgsView args, std::index_sequence<Is...>) {
if constexpr(std::is_void_v<__Ret>) {
func(py_cast<Params>(vm, args[Is])...);
return vm->None;
} else {
__Ret ret = func(py_cast<Params>(vm, args[Is])...);
return VAR(std::move(ret));
}
}
};
template <typename Ret, typename T, typename... Params>
struct NativeProxyMethodC final : NativeProxyFuncCBase {
constexpr static int N = sizeof...(Params);
using _Fp = Ret (T::*)(Params...);
_Fp func;
NativeProxyMethodC(_Fp func) : func(func) {}
PyVar operator() (VM* vm, ArgsView args) override {
assert(args.size() == N + 1);
return call<Ret>(vm, args, std::make_index_sequence<N>());
}
template <typename __Ret, size_t... Is>
PyVar call(VM* vm, ArgsView args, std::index_sequence<Is...>) {
obj_get_t<T> self = PK_OBJ_GET(T, args[0]); // use unsafe cast for derived classes
if constexpr(std::is_void_v<__Ret>) {
(self.*func)(py_cast<Params>(vm, args[Is + 1])...);
return vm->None;
} else {
__Ret ret = (self.*func)(py_cast<Params>(vm, args[Is + 1])...);
return VAR(std::move(ret));
}
}
};
/*****************************************************************/
inline PyVar __proxy_wrapper(VM* vm, ArgsView args) {
NativeProxyFuncCBase* pf = lambda_get_userdata<NativeProxyFuncCBase*>(args.begin());
return (*pf)(vm, args);
}
template <typename Ret, typename... Params>
PyObject* VM::bind(PyObject* obj, const char* sig, Ret (*func)(Params...), BindType bt) {
NativeProxyFuncCBase* proxy = new NativeProxyFuncC<Ret, Params...>(func);
return vm->bind(obj, sig, __proxy_wrapper, proxy, bt);
}
template <typename Ret, typename T, typename... Params>
PyObject* VM::bind(PyObject* obj, const char* sig, Ret (T::*func)(Params...), BindType bt) {
NativeProxyFuncCBase* proxy = new NativeProxyMethodC<Ret, T, Params...>(func);
return vm->bind(obj, sig, __proxy_wrapper, proxy, bt);
}
template <typename Ret, typename... Params>
PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Ret (*func)(Params...), BindType bt) {
NativeProxyFuncCBase* proxy = new NativeProxyFuncC<Ret, Params...>(func);
return vm->bind(obj, sig, docstring, __proxy_wrapper, proxy, bt);
}
template <typename Ret, typename T, typename... Params>
PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Ret (T::*func)(Params...), BindType bt) {
NativeProxyFuncCBase* proxy = new NativeProxyMethodC<Ret, T, Params...>(func);
return vm->bind(obj, sig, docstring, __proxy_wrapper, proxy, bt);
}
template <typename T, typename F, bool ReadOnly>
PyObject* VM::bind_field(PyObject* obj, const char* name, F T::*field) {
static_assert(!std::is_reference_v<F>);
assert(is_type(obj, tp_type));
std::string_view name_sv(name);
int pos = name_sv.find(':');
if(pos > 0) name_sv = name_sv.substr(0, pos);
auto fget = [](VM* vm, ArgsView args) -> PyVar {
obj_get_t<T> self = PK_OBJ_GET(T, args[0]);
F T::*field = lambda_get_userdata<F T::*>(args.begin());
return VAR(self.*field);
};
PyVar _0 = new_object<NativeFunc>(tp_native_func, fget, 1, field);
PyVar _1 = vm->None;
if constexpr(!ReadOnly) {
auto fset = [](VM* vm, ArgsView args) {
obj_get_t<T> self = PK_OBJ_GET(T, args[0]);
F T::*field = lambda_get_userdata<F T::*>(args.begin());
self.*field = py_cast<F>(vm, args[1]);
return vm->None;
};
_1 = new_object<NativeFunc>(tp_native_func, fset, 2, field);
}
PyObject* prop = new_object<Property>(tp_property, _0, _1).get();
obj->attr().set(StrName(name_sv), prop);
return prop;
}
/*****************************************************************/
#define PY_FIELD(T, NAME, EXPR) \
vm->bind_property( \
type, \
NAME, \
[](VM* vm, ArgsView args) { \
obj_get_t<T> self = PK_OBJ_GET(T, args[0]); \
return VAR(self.EXPR); \
}, \
[](VM* vm, ArgsView args) { \
obj_get_t<T> self = PK_OBJ_GET(T, args[0]); \
self.EXPR = CAST(decltype(self.EXPR), args[1]); \
return vm->None; \
});
#define PY_READONLY_FIELD(T, NAME, EXPR) \
vm->bind_property(type, NAME, [](VM* vm, ArgsView args) { \
obj_get_t<T> self = PK_OBJ_GET(T, args[0]); \
return VAR(self.EXPR); \
});
#define PY_PROPERTY(T, NAME, FGET, FSET) \
vm->bind_property( \
type, \
NAME, \
[](VM* vm, ArgsView args) { \
obj_get_t<T> self = PK_OBJ_GET(T, args[0]); \
return VAR(self.FGET()); \
}, \
[](VM* vm, ArgsView args) { \
obj_get_t<T> self = PK_OBJ_GET(T, args[0]); \
using __NT = decltype(self.FGET()); \
self.FSET(CAST(__NT, args[1])); \
return vm->None; \
});
#define PY_READONLY_PROPERTY(T, NAME, FGET) \
vm->bind_property(type, NAME, [](VM* vm, ArgsView args) { \
obj_get_t<T> self = PK_OBJ_GET(T, args[0]); \
return VAR(self.FGET()); \
});
/*****************************************************************/
#define PY_STRUCT_LIKE(wT) \
static_assert(std::is_trivially_copyable<wT>::value); \
static_assert(!is_sso_v<wT>); \
type->attr().set("__struct__", vm->True); \
vm->bind_func( \
type, \
"fromstruct", \
1, \
[](VM* vm, ArgsView args) { \
Struct& s = CAST(Struct&, args[0]); \
if(s.size != sizeof(wT)) vm->ValueError("size mismatch"); \
PyVar obj = vm->new_user_object<wT>(); \
std::memcpy(&_CAST(wT&, obj), s.p, sizeof(wT)); \
return obj; \
}, \
{}, \
BindType_STATICMETHOD); \
vm->bind_func(type, "tostruct", 1, [](VM* vm, ArgsView args) { \
wT& self = _CAST(wT&, args[0]); \
return vm->new_user_object<Struct>(&self, sizeof(wT)); \
}); \
vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args) { \
wT& self = _CAST(wT&, args[0]); \
return vm->new_user_object<VoidP>(&self); \
}); \
vm->bind_func(type, "copy", 1, [](VM* vm, ArgsView args) { \
wT& self = _CAST(wT&, args[0]); \
return vm->new_user_object<wT>(self); \
}); \
vm->bind_func(type, "sizeof", 1, [](VM* vm, ArgsView args) { \
return VAR(sizeof(wT)); \
}); \
vm->bind__eq__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) { \
wT& self = _CAST(wT&, _0); \
if(!vm->isinstance(_1, vm->_tp_user<wT>())) return vm->NotImplemented; \
wT& other = _CAST(wT&, _1); \
return VAR(self == other); \
});
#define PY_POINTER_SETGETITEM(T) \
vm->bind__getitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) { \
obj_get_t<VoidP> self = PK_OBJ_GET(VoidP, _0); \
i64 i = CAST(i64, _1); \
T* tgt = reinterpret_cast<T*>(self.ptr); \
return VAR(tgt[i]); \
}); \
vm->bind__setitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1, PyVar _2) { \
obj_get_t<VoidP> self = PK_OBJ_GET(VoidP, _0); \
i64 i = CAST(i64, _1); \
T* tgt = reinterpret_cast<T*>(self.ptr); \
tgt[i] = CAST(T, _2); \
});
#define PK_LAMBDA(x) \
([](VM* vm, ArgsView args) -> PyVar { \
return x; \
})
#define PK_VAR_LAMBDA(x) \
([](VM* vm, ArgsView args) -> PyVar { \
return VAR(x); \
})
#define PK_ACTION(x) \
([](VM* vm, ArgsView args) -> PyVar { \
x; \
return vm->None; \
})
} // namespace pkpy

View File

@ -1,105 +0,0 @@
#pragma once
#include "pocketpy/interpreter/vm.hpp"
namespace pkpy {
#define PY_CLASS(T, mod, name) \
[[deprecated]] static Type _type(VM* vm) { return vm->_cxx_typeid_map[typeid(T)]; } \
[[deprecated]] static PyVar register_class(VM* vm, PyVar mod, Type base = VM::tp_object) { \
return vm->register_user_class<T>(mod, #name, base); \
}
struct VoidP {
void* ptr;
VoidP(const void* ptr) : ptr(const_cast<void*>(ptr)) {}
bool operator== (const VoidP& other) const { return ptr == other.ptr; }
bool operator!= (const VoidP& other) const { return ptr != other.ptr; }
bool operator< (const VoidP& other) const { return ptr < other.ptr; }
bool operator<= (const VoidP& other) const { return ptr <= other.ptr; }
bool operator> (const VoidP& other) const { return ptr > other.ptr; }
bool operator>= (const VoidP& other) const { return ptr >= other.ptr; }
Str hex() const {
SStream ss;
ss.write_ptr(ptr);
return ss.str();
}
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
#define POINTER_VAR(Tp, NAME) \
inline PyVar py_var(VM* vm, Tp val) { \
const static pair<StrName, StrName> P("c", NAME); \
PyVar type = vm->_modules[P.first]->attr()[P.second]; \
return vm->new_object<VoidP>(type->as<Type>(), val); \
}
POINTER_VAR(char*, "char_p")
// const char* is special, need to rethink about it
POINTER_VAR(const unsigned char*, "uchar_p")
POINTER_VAR(const short*, "short_p")
POINTER_VAR(const unsigned short*, "ushort_p")
POINTER_VAR(const int*, "int_p")
POINTER_VAR(const unsigned int*, "uint_p")
POINTER_VAR(const long*, "long_p")
POINTER_VAR(const unsigned long*, "ulong_p")
POINTER_VAR(const long long*, "longlong_p")
POINTER_VAR(const unsigned long long*, "ulonglong_p")
POINTER_VAR(const float*, "float_p")
POINTER_VAR(const double*, "double_p")
POINTER_VAR(const bool*, "bool_p")
#undef POINTER_VAR
struct Struct {
constexpr static int INLINE_SIZE = 24;
char _inlined[INLINE_SIZE];
char* p;
int size;
Struct(int new_size, bool zero_init = true) {
this->size = new_size;
if(size <= INLINE_SIZE) {
p = _inlined;
} else {
p = (char*)std::malloc(size);
}
if(zero_init) std::memset(p, 0, size);
}
Struct(void* p, int size) : Struct(size, false) {
if(p != nullptr) std::memcpy(this->p, p, size);
}
Struct(const Struct& other) : Struct(other.p, other.size) {}
~Struct() {
if(p != _inlined) std::free(p);
}
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
/***********************************************/
template <typename Tp>
Tp to_void_p(VM* vm, PyVar var) {
static_assert(std::is_pointer_v<Tp>);
if(is_none(var)) return nullptr; // None can be casted to any pointer implicitly
VoidP& p = CAST(VoidP&, var);
return reinterpret_cast<Tp>(p.ptr);
}
/*****************************************************************/
void add_module_c(VM* vm);
} // namespace pkpy

View File

@ -0,0 +1,102 @@
#pragma once
#include "pocketpy/common/memorypool.h"
#include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/namedict.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/common/config.h"
#include "pocketpy/common/strname.h"
#ifdef __cplusplus
extern "C" {
#endif
PyVar* FastLocals__try_get_by_name(PyVar* locals, const CodeObject* co, StrName name);
pk_NameDict* FastLocals__to_namedict(PyVar* locals, const CodeObject* co);
typedef struct ValueStack {
// We allocate extra PK_VM_STACK_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`.
PyVar* sp;
PyVar* end;
PyVar begin[PK_VM_STACK_SIZE + PK_VM_STACK_SIZE / 128];
} ValueStack;
void ValueStack__ctor(ValueStack* self);
void ValueStack__clear(ValueStack* self);
typedef struct UnwindTarget {
struct UnwindTarget* next;
int iblock;
int offset;
} UnwindTarget;
UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset);
void UnwindTarget__delete(UnwindTarget* self);
typedef struct Frame {
struct Frame* f_back; // TODO: set this
const Bytecode* ip;
const CodeObject* co;
PyObject* module_;
PyObject* function; // a function object or NULL (global scope)
PyVar* p0; // unwinding base
PyVar* locals; // locals base
const CodeObject* locals_co;
UnwindTarget* uw_list;
} Frame;
Frame* Frame__new(Frame* f_back, const CodeObject* co, PyObject* module_, PyObject* function, PyVar* p0, PyVar* locals, const CodeObject* locals_co);
void Frame__delete(Frame* self);
PK_INLINE int Frame__ip(const Frame* self){
return self->ip - (Bytecode*)self->co->codes.data;
}
PK_INLINE int Frame__lineno(const Frame* self){
int ip = Frame__ip(self);
return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).lineno;
}
PK_INLINE int Frame__iblock(const Frame* self){
int ip = Frame__ip(self);
return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).iblock;
}
PK_INLINE pk_NameDict* Frame__f_globals(Frame* self){
return self->module_->dict;
}
PK_INLINE PyVar* Frame__f_globals_try_get(Frame* self, StrName name){
return pk_NameDict__try_get(self->module_->dict, name);
}
PyVar* Frame__f_closure_try_get(Frame* self, StrName name);
int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*);
void Frame__prepare_jump_break(Frame* self, ValueStack*, int);
int Frame__prepare_loop_break(Frame* self, ValueStack*);
int Frame__exit_block(Frame* self, ValueStack*, int);
void Frame__gc_mark(Frame* self);
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock);
void Frame__set_unwind_target(Frame* self, PyVar* sp);
#ifdef __cplusplus
}
#endif
// some patch here
#ifdef __cplusplus
#include "pocketpy/objects/codeobject.hpp"
extern "C"{
inline PyVar* Frame__f_closure_try_get(Frame* self, StrName name){
if(self->function == NULL) return NULL;
pkpy::Function* fn = PyObject__as(pkpy::Function, self->function);
if(fn->_closure == nullptr) return nullptr;
return pk_NameDict__try_get(fn->_closure, name);
}
}
#endif

View File

@ -1,214 +0,0 @@
#pragma once
#include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/codeobject.hpp"
namespace pkpy {
// weak reference fast locals
struct FastLocals {
// this is a weak reference
const CodeObject* co;
PyVar* a;
int size() const { return co->nlocals; }
PyVar& operator[] (int i) { return a[i]; }
PyVar operator[] (int i) const { return a[i]; }
FastLocals(const CodeObject* co, PyVar* a) : co(co), a(a) {}
PyVar* try_get_name(StrName name);
NameDict* to_namedict();
PyVar* begin() const { return a; }
PyVar* end() const { return a + size(); }
};
struct ValueStack {
PK_ALWAYS_PASS_BY_POINTER(ValueStack)
// We allocate extra PK_VM_STACK_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`.
PyVar _begin[PK_VM_STACK_SIZE + PK_VM_STACK_SIZE / 128];
PyVar* _sp;
PyVar* _max_end;
constexpr static size_t max_size() { return PK_VM_STACK_SIZE; }
ValueStack() : _sp(_begin), _max_end(_begin + PK_VM_STACK_SIZE) {}
PyVar& top() { return _sp[-1]; }
PyVar top() const { return _sp[-1]; }
PyVar& second() { return _sp[-2]; }
PyVar second() const { return _sp[-2]; }
PyVar& third() { return _sp[-3]; }
PyVar third() const { return _sp[-3]; }
PyVar& peek(int n) { return _sp[-n]; }
PyVar peek(int n) const { return _sp[-n]; }
void push(PyVar v) { *_sp++ = v; }
void push(std::nullptr_t) { std::memset(_sp++, 0, sizeof(PyVar)); }
void pop() { --_sp; }
PyVar popx() {
--_sp;
return *_sp;
}
ArgsView view(int n) { return ArgsView(_sp - n, _sp); }
void shrink(int n) { _sp -= n; }
int size() const { return _sp - _begin; }
bool empty() const { return _sp == _begin; }
PyVar* begin() { return _begin; }
PyVar* end() { return _sp; }
void reset(PyVar* sp) { _sp = sp; }
void clear() { _sp = _begin; }
bool is_overflow() const { return _sp >= _max_end; }
template <typename... Args>
void emplace(Args&&... args) {
new (_sp) PyVar(std::forward<Args>(args)...);
++_sp;
}
};
struct UnwindTarget {
UnwindTarget* next;
int iblock;
int offset;
UnwindTarget(int iblock, int offset) : next(nullptr), iblock(iblock), offset(offset) {}
};
struct Frame {
PK_ALWAYS_PASS_BY_POINTER(Frame)
const Bytecode* _ip;
// This is for unwinding only, use `actual_sp_base()` for value stack access
PyVar* _sp_base;
const CodeObject* co;
PyObject* _module;
PyObject* _callable; // a function object or nullptr (global scope)
FastLocals _locals;
// This list will be freed in __pop_frame
UnwindTarget* _uw_list;
NameDict& f_globals() { return _module->attr(); }
PyVar* f_closure_try_get(StrName name);
int ip() const { return _ip - (Bytecode*)co->codes.data; }
// function scope
Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, PyVar* _locals_base) :
_ip((Bytecode*)co->codes.data - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable),
_locals(co, _locals_base), _uw_list(nullptr) {}
// exec/eval
Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, FastLocals _locals) :
_ip((Bytecode*)co->codes.data - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals),
_uw_list(nullptr) {}
// global scope
Frame(PyVar* p0, const CodeObject* co, PyObject* _module) :
_ip((Bytecode*)co->codes.data - 1), _sp_base(p0), co(co), _module(_module), _callable(nullptr),
_locals(co, p0), _uw_list(nullptr) {}
PyVar* actual_sp_base() const { return _locals.a; }
ArgsView stack_view(ValueStack* _s) const { return ArgsView(actual_sp_base(), _s->_sp); }
[[nodiscard]] int prepare_jump_exception_handler(ValueStack*);
void prepare_jump_break(ValueStack*, int);
int _exit_block(ValueStack*, int);
[[nodiscard]] int prepare_loop_break(ValueStack* s_data) {
int iblock = c11__getitem(BytecodeEx, &co->codes_ex, ip()).iblock;
int target = c11__getitem(CodeBlock, &co->blocks, iblock).end;
prepare_jump_break(s_data, target);
return target;
}
int curr_lineno() const {
return c11__getitem(BytecodeEx, &co->codes_ex, ip()).lineno;
}
void set_unwind_target(PyVar* _sp);
UnwindTarget* find_unwind_target(int iblock);
void _gc_mark(VM* vm) const;
~Frame();
};
struct LinkedFrame {
LinkedFrame* f_back;
Frame frame;
template <typename... Args>
LinkedFrame(LinkedFrame* f_back, Args&&... args) : f_back(f_back), frame(std::forward<Args>(args)...) {}
};
struct CallStack {
static_assert(sizeof(LinkedFrame) <= 128);
LinkedFrame* _tail;
int _size;
CallStack() : _tail(nullptr), _size(0) {}
int size() const { return _size; }
bool empty() const { return _size == 0; }
void clear() {
while(!empty())
pop();
}
template <typename... Args>
void emplace(Args&&... args) {
static_assert(sizeof(LinkedFrame) <= kPoolFrameBlockSize);
_tail = new (PoolFrame_alloc()) LinkedFrame(_tail, std::forward<Args>(args)...);
++_size;
}
void pop();
LinkedFrame* popx();
void pushx(LinkedFrame* p);
Frame& top() const {
assert(!empty());
return _tail->frame;
}
template <typename Func>
void apply(Func&& f) {
for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back)
f(p->frame);
}
~CallStack() { clear(); }
};
}; // namespace pkpy

View File

@ -13,13 +13,13 @@ typedef struct pk_ManagedHeap{
int gc_threshold; int gc_threshold;
int gc_counter; int gc_counter;
int gc_lock_counter; int gc_lock_counter;
pkpy_VM* vm; pk_VM* vm;
void (*_gc_on_delete)(pkpy_VM*, PyObject*); void (*_gc_on_delete)(pk_VM*, PyObject*);
void (*_gc_marker_ex)(pkpy_VM*); void (*_gc_marker_ex)(pk_VM*);
} pk_ManagedHeap; } pk_ManagedHeap;
void pk_ManagedHeap__ctor(pk_ManagedHeap* self, pkpy_VM* vm); void pk_ManagedHeap__ctor(pk_ManagedHeap* self, pk_VM* vm);
void pk_ManagedHeap__dtor(pk_ManagedHeap* self); void pk_ManagedHeap__dtor(pk_ManagedHeap* self);
void pk_ManagedHeap__push_lock(pk_ManagedHeap* self); void pk_ManagedHeap__push_lock(pk_ManagedHeap* self);
@ -29,7 +29,8 @@ void pk_ManagedHeap__collect_if_needed(pk_ManagedHeap* self);
int pk_ManagedHeap__collect(pk_ManagedHeap* self); int pk_ManagedHeap__collect(pk_ManagedHeap* self);
int pk_ManagedHeap__sweep(pk_ManagedHeap* self); int pk_ManagedHeap__sweep(pk_ManagedHeap* self);
PyObject* pk_ManagedHeap__new(pk_ManagedHeap* self, pkpy_Type type, int size, bool gc); PyObject* pk_ManagedHeap__new(pk_ManagedHeap* self, Type type, int size);
PyObject* pk_ManagedHeap__gcnew(pk_ManagedHeap* self, Type type, int size);
// external implementation // external implementation
void pk_ManagedHeap__mark(pk_ManagedHeap* self); void pk_ManagedHeap__mark(pk_ManagedHeap* self);

View File

@ -1,86 +0,0 @@
#pragma once
#include "pocketpy/interpreter/bindings.hpp"
namespace pkpy {
struct RangeIter { // step > 0
Range r;
i64 current;
RangeIter(Range r) : r(r), current(r.start) {}
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
struct RangeIterR { // step < 0
Range r;
i64 current;
RangeIterR(Range r) : r(r), current(r.start) {}
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
struct ArrayIter {
PyObject* ref;
PyVar* end;
PyVar* current;
ArrayIter(PyObject* ref, PyVar* begin, PyVar* end) : ref(ref), end(end), current(begin) {}
void _gc_mark(VM* vm) const { vm->__obj_gc_mark(ref); }
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
struct StringIter {
PyVar ref;
int i; // byte index
StringIter(PyVar ref) : ref(ref), i(0) {}
void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
struct Generator {
LinkedFrame* lf;
int state; // 0,1,2
List s_backup;
Generator(LinkedFrame* lf, ArgsView buffer) : lf(lf), state(0) {
for(PyVar obj: buffer)
s_backup.push_back(obj);
}
void _gc_mark(VM* vm) {
if(lf == nullptr) return;
lf->frame._gc_mark(vm);
vm->__stack_gc_mark(s_backup.begin(), s_backup.end());
}
PyVar next(VM* vm);
static void _register(VM* vm, PyObject* mod, PyObject* type);
~Generator() {
if(lf) {
lf->~LinkedFrame();
PoolFrame_dealloc(lf);
}
}
};
struct DictItemsIter {
PyVar ref;
pkpy_DictIter it;
DictItemsIter(PyVar ref) : ref(ref) { it = PK_OBJ_GET(Dict, ref).iter(); }
void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
} // namespace pkpy

View File

@ -1,44 +0,0 @@
#pragma once
#include "pocketpy/common/config.h"
#include "pocketpy/interpreter/frame.hpp"
#if PK_ENABLE_PROFILER
#include <ctime>
namespace pkpy {
struct _LineRecord {
int line;
i64 hits;
clock_t time;
_LineRecord() : line(-1), hits(0), time(0) {}
bool is_valid() const { return line != -1; }
};
struct _FrameRecord {
int callstack_size;
Frame* frame;
clock_t prev_time;
_LineRecord* prev_record;
};
struct LineProfiler {
// filename -> records
small_map<std::string_view, _LineRecord*> records;
vector<_FrameRecord> frames;
vector<FuncDecl*> functions;
void begin();
void _step(int, Frame*);
void _step_end(int, Frame*, int);
void end();
Str stats();
~LineProfiler();
};
} // namespace pkpy
#endif

View File

@ -1,12 +1,82 @@
#include "pocketpy/objects/object.h" #pragma once
typedef struct pkpy_VM{ #include "pocketpy/pocketpy.h"
PyVar True; #include "pocketpy/interpreter/gc.h"
PyVar False; #include "pocketpy/interpreter/frame.h"
PyVar None;
PyVar NotImplemented;
PyVar Ellipsis;
} pkpy_VM;
void pkpy_VM__ctor(pkpy_VM* self); #ifdef __cplusplus
void pkpy_VM__dtor(pkpy_VM* self); extern "C" {
#endif
typedef struct pk_TypeInfo{
StrName name;
Type base;
PyObject* obj; // the type object itself
PyObject* module; // the module where the type is defined
bool subclass_enabled;
void (*dtor)(void*);
void (*gc_mark)(void*);
c11_vector/*T=StrName*/ annotated_fields;
/* Magic Caches */
// unary operators
py_CFunction m__repr__, m__str__, m__hash__, m__len__;
py_CFunction m__iter__, m__next__, m__neg__, m__invert__;
// binary operators
py_CFunction m__eq__, m__lt__, m__le__, m__gt__, m__ge__, m__contains__;
py_CFunction m__add__, m__sub__, m__mul__, m__truediv__, m__floordiv__;
py_CFunction m__mod__, m__pow__, m__matmul__;
py_CFunction m__lshift__, m__rshift__, m__and__, m__xor__, m__or__;
// indexer
py_CFunction m__getitem__, m__setitem__, m__delitem__;
// attribute access (internal use only)
py_CFunction m__getattr__, m__setattr__, m__delattr__;
// backdoors
py_CFunction on_end_subclass; // for enum module
} pk_TypeInfo;
void pk_TypeInfo__ctor(pk_TypeInfo *self, StrName name, Type base, PyObject* obj, PyObject* module, bool subclass_enabled);
void pk_TypeInfo__dtor(pk_TypeInfo* self);
typedef struct pk_VM {
Frame* top_frame;
pk_NameDict modules;
c11_vector/*T=pk_TypeInfo*/ types;
PyObject* StopIteration; // a special Exception class
PyObject* builtins; // builtins module
PyObject* main; // __main__ module
void (*_ceval_on_step)(struct pk_VM*, Frame*, Bytecode);
unsigned char* (*_import_file)(struct pk_VM*, const char*);
void (*_stdout)(struct pk_VM*, const char*);
void (*_stderr)(struct pk_VM*, const char*);
// singleton objects
PyVar True, False, None, NotImplemented, Ellipsis;
PyObject* __last_exception;
PyObject* __curr_class;
PyObject* __cached_object_new;
FuncDecl_ __dynamic_func_decl;
PyVar __vectorcall_buffer[PK_MAX_CO_VARNAMES];
void* userdata; // for user-defined data (unused by pkpy itself)
pk_ManagedHeap heap;
ValueStack stack; // put `stack` at the end for better cache locality
} pk_VM;
void pk_VM__ctor(pk_VM* self);
void pk_VM__dtor(pk_VM* self);
Type pk_VM__new_type(pk_VM* self, const char* name, Type base, PyObject* module, bool subclass_enabled);
PyObject* pk_VM__new_module(pk_VM* self, const char* name, const char* package);
#ifdef __cplusplus
}
#endif

View File

@ -1,11 +1,12 @@
#pragma once #pragma once
#include "pocketpy/objects/object.hpp" #include "pocketpy/objects/object.h"
#include "pocketpy/objects/base.h"
#include "pocketpy/objects/dict.hpp" #include "pocketpy/objects/dict.hpp"
#include "pocketpy/objects/error.hpp" #include "pocketpy/objects/error.hpp"
#include "pocketpy/objects/builtins.hpp" #include "pocketpy/objects/builtins.hpp"
#include "pocketpy/interpreter/gc.h" #include "pocketpy/interpreter/gc.h"
#include "pocketpy/interpreter/frame.hpp" #include "pocketpy/interpreter/frame.h"
#include "pocketpy/interpreter/profiler.hpp" #include "pocketpy/interpreter/profiler.hpp"
#include <typeindex> #include <typeindex>
@ -14,14 +15,15 @@ namespace pkpy {
/* Stack manipulation macros */ /* Stack manipulation macros */
// https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123 // https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
#define TOP() (s_data.top()) #define TOP() (s_data.sp[-1])
#define SECOND() (s_data.second()) #define SECOND() (s_data.sp[-2])
#define THIRD() (s_data.third()) #define THIRD() (s_data.sp[-3])
#define STACK_SHRINK(n) (s_data.shrink(n)) #define STACK_SHRINK(n) (s_data.sp -= (n))
#define PUSH(v) (s_data.push(v)) #define PUSH(v) (*s_data.sp++ = (v))
#define POP() (s_data.pop()) #define PUSH_NULL() memset(s_data.sp++, 0, sizeof(PyVar))
#define POPX() (s_data.popx()) #define POP() (--s_data.sp)
#define STACK_VIEW(n) (s_data.view(n)) #define POPX() (*--s_data.sp)
#define STACK_VIEW(n) (ArgsView(s_data.sp - (n), s_data.sp))
typedef PyVar (*BinaryFuncC)(VM*, PyVar, PyVar); typedef PyVar (*BinaryFuncC)(VM*, PyVar, PyVar);
typedef void (*RegisterFunc)(VM*, PyObject*, PyObject*); typedef void (*RegisterFunc)(VM*, PyObject*, PyObject*);
@ -163,12 +165,11 @@ class VM {
public: public:
pk_ManagedHeap heap; pk_ManagedHeap heap;
ValueStack s_data; Frame* top_frame;
CallStack callstack;
vector<PyTypeInfo> _all_types; vector<PyTypeInfo> _all_types;
NameDict _modules; // loaded modules pk_NameDict _modules; // loaded modules
small_map<StrName, Str> _lazy_modules; // lazy loaded modules small_map<StrName, Str> _lazy_modules; // lazy loaded modules
struct { struct {
PyObject* error; PyObject* error;
@ -200,13 +201,10 @@ public:
void (*_stdout)(const char*, int); void (*_stdout)(const char*, int);
void (*_stderr)(const char*, int); void (*_stderr)(const char*, int);
unsigned char* (*_import_handler)(const char*, int*); unsigned char* (*_import_handler)(const char*, int*);
// function<void(const char*, int)> _stdout;
// function<void(const char*, int)> _stderr;
// function<unsigned char*(const char*, int*)> _import_handler;
// for quick access // for quick access
constexpr static Type tp_object = Type(1), tp_type = Type(2); constexpr static Type tp_object = Type(1), tp_type = Type(2);
constexpr static Type tp_int = Type(kTpIntIndex), tp_float = Type(kTpFloatIndex), tp_bool = Type(5), constexpr static Type tp_int = Type(3), tp_float = Type(4), tp_bool = Type(5),
tp_str = Type(6); tp_str = Type(6);
constexpr static Type tp_list = Type(7), tp_tuple = Type(8); constexpr static Type tp_list = Type(7), tp_tuple = Type(8);
constexpr static Type tp_slice = Type(9), tp_range = Type(10), tp_module = Type(11); constexpr static Type tp_slice = Type(9), tp_range = Type(10), tp_module = Type(11);
@ -214,7 +212,7 @@ public:
constexpr static Type tp_super = Type(15), tp_exception = Type(16), tp_bytes = Type(17), tp_mappingproxy = Type(18); constexpr static Type tp_super = Type(15), tp_exception = Type(16), tp_bytes = Type(17), tp_mappingproxy = Type(18);
constexpr static Type tp_dict = Type(19), tp_property = Type(20), tp_star_wrapper = Type(21); constexpr static Type tp_dict = Type(19), tp_property = Type(20), tp_star_wrapper = Type(21);
constexpr static Type tp_staticmethod = Type(22), tp_classmethod = Type(23); constexpr static Type tp_staticmethod = Type(22), tp_classmethod = Type(23);
constexpr static Type tp_none_type = Type(kTpNoneTypeIndex), tp_not_implemented_type = Type(kTpNotImplementedTypeIndex); constexpr static Type tp_none_type = Type(24), tp_not_implemented_type = Type(25);
constexpr static Type tp_ellipsis = Type(26); constexpr static Type tp_ellipsis = Type(26);
PyVar True; PyVar True;
@ -224,6 +222,10 @@ public:
PyVar Ellipsis; PyVar Ellipsis;
const bool enable_os; const bool enable_os;
// put s_data at the end of VM struct to improve cache locality
ValueStack s_data;
VM(bool enable_os = true); VM(bool enable_os = true);
// clang-format off // clang-format off
@ -255,10 +257,10 @@ public:
return !py_eq(lhs, rhs); return !py_eq(lhs, rhs);
} }
PyVar py_op(std::string_view name); // (name) -> operator.name PyVar py_op(const char* name); // (name) -> operator.name
void py_exec(std::string_view, PyVar, PyVar); // exec(source, globals, locals) void py_exec(const char*, PyVar, PyVar); // exec(source, globals, locals)
PyVar py_eval(std::string_view, PyVar, PyVar); // eval(source, globals, locals) PyVar py_eval(const char*, PyVar, PyVar); // eval(source, globals, locals)
#endif #endif
#if PK_REGION("Utility Methods") #if PK_REGION("Utility Methods")
@ -267,7 +269,7 @@ public:
i64 normalized_index(i64 index, int size); i64 normalized_index(i64 index, int size);
Str disassemble(CodeObject* co); Str disassemble(CodeObject* co);
void parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step); void parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step);
void obj_gc_mark(PyVar obj) { if(obj.is_ptr) __obj_gc_mark(obj.get()); } void obj_gc_mark(PyVar obj) { if(obj.is_ptr) __obj_gc_mark(obj._obj); }
void obj_gc_mark(PyObject* p) { if(p) __obj_gc_mark(p); } void obj_gc_mark(PyObject* p) { if(p) __obj_gc_mark(p); }
#endif #endif
@ -288,7 +290,7 @@ public:
template<typename ...Args> template<typename ...Args>
PyVar _exec(Args&&... args){ PyVar _exec(Args&&... args){
callstack.emplace(s_data._sp, std::forward<Args>(args)...); callstack.emplace(s_data.sp, std::forward<Args>(args)...);
return __run_top_frame(); return __run_top_frame();
} }
#endif #endif
@ -298,7 +300,7 @@ public:
template<typename... Args> template<typename... Args>
PyVar call(PyVar callable, Args&&... args){ PyVar call(PyVar callable, Args&&... args){
PUSH(callable); PUSH(PY_NULL); PUSH(callable); PUSH_NULL();
__push_varargs(args...); __push_varargs(args...);
return vectorcall(sizeof...(args)); return vectorcall(sizeof...(args));
} }
@ -433,44 +435,44 @@ public:
template<typename T> template<typename T>
bool is_user_type(PyVar obj){ return _tp(obj) == _tp_user<T>(); } bool is_user_type(PyVar obj){ return _tp(obj) == _tp_user<T>(); }
template<typename T> // template<typename T>
PyObject* register_user_class(PyObject*, StrName, RegisterFunc, Type base=tp_object, bool subclass_enabled=false); // PyObject* register_user_class(PyObject*, StrName, RegisterFunc, Type base=tp_object, bool subclass_enabled=false);
template<typename T> // template<typename T>
PyObject* register_user_class(PyObject*, StrName, Type base=tp_object, bool subclass_enabled=false); // PyObject* register_user_class(PyObject*, StrName, Type base=tp_object, bool subclass_enabled=false);
template<typename T, typename ...Args> // template<typename T, typename ...Args>
PyVar new_user_object(Args&&... args){ // PyVar new_user_object(Args&&... args){
return new_object<T>(_tp_user<T>(), std::forward<Args>(args)...); // return new_object<T>(_tp_user<T>(), std::forward<Args>(args)...);
} // }
template<typename T, typename ...Args> template<typename T, typename ...Args>
PyVar new_object(Type type, Args&&... args){ PyVar new_object(Type type, Args&&... args){
static_assert(!is_sso_v<T>); static_assert(!is_sso_v<T>);
static_assert(std::is_same_v<T, std::decay_t<T>>); static_assert(std::is_same_v<T, std::decay_t<T>>);
PyObject* p = (PyObject*)pk_ManagedHeap__new(&heap, type, py_sizeof<T>, true); PyObject* p = pk_ManagedHeap__new(&heap, type, py_sizeof<T>, true);
new (p->_value_ptr()) T(std::forward<Args>(args)...); new (PyObject__value_ptr(p)) T(std::forward<Args>(args)...);
// backdoor for important builtin types // backdoor for important builtin types
if constexpr(std::is_same_v<T, DummyInstance> if constexpr(std::is_same_v<T, DummyInstance>
|| std::is_same_v<T, Type> || std::is_same_v<T, Type>
|| std::is_same_v<T, DummyModule>) { || std::is_same_v<T, DummyModule>) {
p->_attr = new NameDict(); p->_attr = pk_NameDict__new();
} }
return p; return PyVar__fromobj(p);
} }
template<typename T, typename ...Args> template<typename T, typename ...Args>
PyVar new_object_no_gc(Type type, Args&&... args){ PyVar new_object_no_gc(Type type, Args&&... args){
static_assert(!is_sso_v<T>); static_assert(!is_sso_v<T>);
static_assert(std::is_same_v<T, std::decay_t<T>>); static_assert(std::is_same_v<T, std::decay_t<T>>);
PyObject* p = (PyObject*)pk_ManagedHeap__new(&heap, type, py_sizeof<T>, false); PyObject* p = pk_ManagedHeap__new(&heap, type, py_sizeof<T>, true);
new (p->_value_ptr()) T(std::forward<Args>(args)...); new (PyObject__value_ptr(p)) T(std::forward<Args>(args)...);
// backdoor for important builtin types // backdoor for important builtin types
if constexpr(std::is_same_v<T, DummyInstance> if constexpr(std::is_same_v<T, DummyInstance>
|| std::is_same_v<T, Type> || std::is_same_v<T, Type>
|| std::is_same_v<T, DummyModule>) { || std::is_same_v<T, DummyModule>) {
p->_attr = new NameDict(); p->_attr = pk_NameDict__new();
} }
return p; return PyVar__fromobj(p);
} }
#endif #endif
@ -499,7 +501,7 @@ public:
PyVar __format_object(PyVar, Str); PyVar __format_object(PyVar, Str);
PyVar __run_top_frame(); PyVar __run_top_frame();
void __pop_frame(); void __pop_frame();
PyVar __py_generator(LinkedFrame* frame, ArgsView buffer); PyVar __py_generator(Frame* frame, ArgsView buffer);
void __op_unpack_sequence(uint16_t arg); void __op_unpack_sequence(uint16_t arg);
void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl*); void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl*);
void __unpack_as_list(ArgsView args, List& list); void __unpack_as_list(ArgsView args, List& list);
@ -686,32 +688,32 @@ __T _py_cast(VM* vm, PyVar obj) {
return _py_cast__internal<__T, false>(vm, obj); return _py_cast__internal<__T, false>(vm, obj);
} }
template <typename T> // template <typename T>
PyObject* // PyObject*
VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _register, Type base, bool subclass_enabled) { // VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _register, Type base, bool subclass_enabled) {
PyObject* type = new_type_object(mod, name, base, subclass_enabled, PyTypeInfo::Vt::get<T>()); // PyObject* type = new_type_object(mod, name, base, subclass_enabled, PyTypeInfo::Vt::get<T>());
mod->attr().set(name, type); // mod->attr().set(name, type);
_cxx_typeid_map.insert(typeid(T), type->as<Type>()); // _cxx_typeid_map.insert(typeid(T), type->as<Type>());
_register(this, mod, type); // _register(this, mod, type);
if(!type->attr().contains(__new__)) { // if(!type->attr().contains(__new__)) {
if constexpr(std::is_default_constructible_v<T>) { // if constexpr(std::is_default_constructible_v<T>) {
bind_func(type, __new__, -1, [](VM* vm, ArgsView args) { // bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
Type cls_t = args[0]->as<Type>(); // Type cls_t = args[0]->as<Type>();
return vm->new_object<T>(cls_t); // return vm->new_object<T>(cls_t);
}); // });
} else { // } else {
bind_func(type, __new__, -1, [](VM* vm, ArgsView args) { // bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
vm->NotImplementedError(); // vm->NotImplementedError();
return vm->None; // return vm->None;
}); // });
} // }
} // }
return type; // return type;
} // }
template <typename T> // template <typename T>
PyObject* VM::register_user_class(PyObject* mod, StrName name, Type base, bool subclass_enabled) { // PyObject* VM::register_user_class(PyObject* mod, StrName name, Type base, bool subclass_enabled) {
return register_user_class<T>(mod, name, &T::_register, base, subclass_enabled); // return register_user_class<T>(mod, name, &T::_register, base, subclass_enabled);
} // }
} // namespace pkpy } // namespace pkpy

View File

@ -1,9 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
void add_module_array2d(VM* vm);
} // namespace pkpy

View File

@ -1,9 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
void add_module_base64(VM* vm);
} // namespace pkpy

View File

@ -1,9 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
void add_module_csv(VM* vm);
} // namespace pkpy

View File

@ -1,9 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
void add_module_dataclasses(VM* vm);
} // namespace pkpy

View File

@ -1,9 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
void add_module_easing(VM* vm);
} // namespace pkpy

View File

@ -1,9 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
unsigned char* _default_import_handler(const char*, int*);
void add_module_os(VM* vm);
void add_module_io(VM* vm);
} // namespace pkpy

View File

@ -1,215 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
#include "pocketpy/common/traits.hpp"
#include <cmath>
namespace pkpy {
inline bool isclose(float a, float b) { return std::fabs(a - b) < 1e-4; }
struct Vec2 {
static void _register(VM* vm, PyObject* mod, PyObject* type);
float x, y;
Vec2() : x(0.0f), y(0.0f) {}
Vec2(float x, float y) : x(x), y(y) {}
Vec2 operator+ (const Vec2& v) const { return Vec2(x + v.x, y + v.y); }
Vec2 operator- (const Vec2& v) const { return Vec2(x - v.x, y - v.y); }
Vec2 operator* (float s) const { return Vec2(x * s, y * s); }
Vec2 operator* (const Vec2& v) const { return Vec2(x * v.x, y * v.y); }
Vec2 operator/ (float s) const { return Vec2(x / s, y / s); }
Vec2 operator- () const { return Vec2(-x, -y); }
bool operator== (const Vec2& v) const { return isclose(x, v.x) && isclose(y, v.y); }
bool operator!= (const Vec2& v) const { return !isclose(x, v.x) || !isclose(y, v.y); }
float operator[] (int i) const { return (&x)[i]; }
float dot(const Vec2& v) const { return x * v.x + y * v.y; }
float cross(const Vec2& v) const { return x * v.y - y * v.x; }
float length() const { return sqrtf(x * x + y * y); }
float length_squared() const { return x * x + y * y; }
Vec2 normalize() const {
float l = length();
return Vec2(x / l, y / l);
}
Vec2 rotate(float radian) const {
float cr = cosf(radian), sr = sinf(radian);
return Vec2(x * cr - y * sr, x * sr + y * cr);
}
};
struct Vec3 {
static void _register(VM* vm, PyObject* mod, PyObject* type);
float x, y, z;
Vec3() : x(0.0f), y(0.0f), z(0.0f) {}
Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
Vec3 operator+ (const Vec3& v) const { return Vec3(x + v.x, y + v.y, z + v.z); }
Vec3 operator- (const Vec3& v) const { return Vec3(x - v.x, y - v.y, z - v.z); }
Vec3 operator* (float s) const { return Vec3(x * s, y * s, z * s); }
Vec3 operator* (const Vec3& v) const { return Vec3(x * v.x, y * v.y, z * v.z); }
Vec3 operator/ (float s) const { return Vec3(x / s, y / s, z / s); }
Vec3 operator- () const { return Vec3(-x, -y, -z); }
bool operator== (const Vec3& v) const { return isclose(x, v.x) && isclose(y, v.y) && isclose(z, v.z); }
bool operator!= (const Vec3& v) const { return !isclose(x, v.x) || !isclose(y, v.y) || !isclose(z, v.z); }
float operator[] (int i) const { return (&x)[i]; }
float dot(const Vec3& v) const { return x * v.x + y * v.y + z * v.z; }
Vec3 cross(const Vec3& v) const { return Vec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); }
float length() const { return sqrtf(x * x + y * y + z * z); }
float length_squared() const { return x * x + y * y + z * z; }
Vec3 normalize() const {
float l = length();
return Vec3(x / l, y / l, z / l);
}
};
struct Vec4 {
static void _register(VM* vm, PyObject* mod, PyObject* type);
float x, y, z, w;
Vec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {}
Vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
Vec4 operator+ (const Vec4& v) const { return Vec4(x + v.x, y + v.y, z + v.z, w + v.w); }
Vec4 operator- (const Vec4& v) const { return Vec4(x - v.x, y - v.y, z - v.z, w - v.w); }
Vec4 operator* (float s) const { return Vec4(x * s, y * s, z * s, w * s); }
Vec4 operator* (const Vec4& v) const { return Vec4(x * v.x, y * v.y, z * v.z, w * v.w); }
Vec4 operator/ (float s) const { return Vec4(x / s, y / s, z / s, w / s); }
Vec4 operator- () const { return Vec4(-x, -y, -z, -w); }
bool operator== (const Vec4& v) const {
return isclose(x, v.x) && isclose(y, v.y) && isclose(z, v.z) && isclose(w, v.w);
}
bool operator!= (const Vec4& v) const {
return !isclose(x, v.x) || !isclose(y, v.y) || !isclose(z, v.z) || !isclose(w, v.w);
}
float operator[] (int i) const { return (&x)[i]; }
float dot(const Vec4& v) const { return x * v.x + y * v.y + z * v.z + w * v.w; }
float length() const { return sqrtf(x * x + y * y + z * z + w * w); }
float length_squared() const { return x * x + y * y + z * z + w * w; }
Vec4 normalize() const {
float l = length();
return Vec4(x / l, y / l, z / l, w / l);
}
NoReturn normalize_() {
float l = length();
x /= l;
y /= l;
z /= l;
w /= l;
return {};
}
NoReturn copy_(const Vec4& v) {
x = v.x;
y = v.y;
z = v.z;
w = v.w;
return {};
}
};
struct Mat3x3 {
static void _register(VM* vm, PyObject* mod, PyObject* type);
union {
struct {
float _11, _12, _13;
float _21, _22, _23;
float _31, _32, _33;
};
float m[3][3];
float v[9];
};
Mat3x3();
Mat3x3(float, float, float, float, float, float, float, float, float);
static Mat3x3 zeros();
static Mat3x3 ones();
static Mat3x3 identity();
Mat3x3 operator+ (const Mat3x3& other) const;
Mat3x3 operator- (const Mat3x3& other) const;
Mat3x3 operator* (float scalar) const;
Mat3x3 operator/ (float scalar) const;
bool operator== (const Mat3x3& other) const;
bool operator!= (const Mat3x3& other) const;
Mat3x3 matmul(const Mat3x3& other) const;
Vec3 matmul(const Vec3& other) const;
float determinant() const;
Mat3x3 transpose() const;
bool inverse(Mat3x3& out) const;
/*************** affine transformations ***************/
static Mat3x3 trs(Vec2 t, float radian, Vec2 s);
bool is_affine() const;
Vec2 _t() const;
float _r() const;
Vec2 _s() const;
};
void add_module_linalg(VM* vm);
static_assert(is_pod_v<Vec2>);
static_assert(is_pod_v<Vec3>);
static_assert(is_pod_v<Vec4>);
static_assert(is_pod_v<Mat3x3>);
// template <>
// constexpr inline bool is_sso_v<Vec2> = true;
// template <>
// constexpr inline bool is_sso_v<Vec3> = true;
} // namespace pkpy

View File

@ -1,18 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
void add_module_time(VM* vm);
void add_module_sys(VM* vm);
void add_module_json(VM* vm);
void add_module_math(VM* vm);
void add_module_traceback(VM* vm);
void add_module_dis(VM* vm);
void add_module_gc(VM* vm);
void add_module_line_profiler(VM* vm);
void add_module_enum(VM* vm);
void add_module___builtins(VM* vm);
} // namespace pkpy

View File

@ -1,9 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
namespace pkpy {
void add_module_random(VM* vm);
} // namespace pkpy

View File

@ -7,14 +7,16 @@
#include "string.h" #include "string.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/objects/public.h" #include "pocketpy/pocketpy.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef int16_t Type;
typedef struct PyVar{ typedef struct PyVar{
pkpy_Type type; Type type;
bool is_ptr; bool is_ptr;
int extra; int extra;
union { union {
@ -37,22 +39,23 @@ typedef struct PyVar{
static_assert(sizeof(PyVar) == 16, "sizeof(PyVar) != 16"); static_assert(sizeof(PyVar) == 16, "sizeof(PyVar) != 16");
/* predefined vars */ /* predefined vars */
static const pkpy_Type tp_object = 1, tp_type = 2; static const Type tp_object = {1}, tp_type = {2};
static const pkpy_Type tp_int = 3, tp_float = 4, tp_bool = 5, tp_str = 6; static const Type tp_int = {3}, tp_float = {4}, tp_bool = {5}, tp_str = {6};
static const pkpy_Type tp_list = 7, tp_tuple = 8; static const Type tp_list = {7}, tp_tuple = {8};
static const pkpy_Type tp_slice = 9, tp_range = 10, tp_module = 11; static const Type tp_slice = {9}, tp_range = {10}, tp_module = {11};
static const pkpy_Type tp_function = 12, tp_native_func = 13, tp_bound_method = 14; static const Type tp_function = {12}, tp_native_func = {13}, tp_bound_method = {14};
static const pkpy_Type tp_super = 15, tp_exception = 16, tp_bytes = 17, tp_mappingproxy = 18; static const Type tp_super = {15}, tp_exception = {16}, tp_bytes = {17}, tp_mappingproxy = {18};
static const pkpy_Type tp_dict = 19, tp_property = 20, tp_star_wrapper = 21; static const Type tp_dict = {19}, tp_property = {20}, tp_star_wrapper = {21};
static const pkpy_Type tp_staticmethod = 22, tp_classmethod = 23; static const Type tp_staticmethod = {22}, tp_classmethod = {23};
static const pkpy_Type tp_none_type = 24, tp_not_implemented_type = 25; static const Type tp_none_type = {24}, tp_not_implemented_type = {25};
static const pkpy_Type tp_ellipsis = 26; static const Type tp_ellipsis = {26};
static const pkpy_Type tp_op_call = 27, tp_op_yield = 28; static const Type tp_op_call = {27}, tp_op_yield = {28};
static const Type tp_syntax_error = {29}, tp_stop_iteration = {30};
PK_INLINE bool PyVar__is_null(const PyVar* self) { return self->type == 0; } PK_INLINE bool PyVar__is_null(const PyVar* self) { return self->type == 0; }
PK_INLINE int64_t PyVar__hash(const PyVar* self) { return self->extra + self->_i64; } PK_INLINE int64_t PyVar__hash(const PyVar* self) { return self->extra + self->_i64; }
PK_INLINE void PyVar__ctor(PyVar* self, pkpy_Type type, PyObject* obj){ PK_INLINE void PyVar__ctor(PyVar* self, Type type, PyObject* obj){
self->type = type; self->type = type;
self->is_ptr = true; self->is_ptr = true;
self->_obj = obj; self->_obj = obj;
@ -67,7 +70,7 @@ PK_INLINE bool PyVar__IS_OP(const PyVar* a, const PyVar* b){
#define pkpy_Var__is_null(self) ((self)->type == 0) #define pkpy_Var__is_null(self) ((self)->type == 0)
#define pkpy_Var__set_null(self) do { (self)->type = 0; } while(0) #define pkpy_Var__set_null(self) do { (self)->type = 0; } while(0)
extern PyVar pkpy_NULL, pkpy_OP_CALL, pkpy_OP_YIELD; extern PyVar PY_NULL, PY_OP_CALL, PY_OP_YIELD;
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,85 +0,0 @@
#pragma once
#include "pocketpy/common/types.hpp"
#include "pocketpy/common/traits.hpp"
#include "pocketpy/objects/base.h"
namespace pkpy {
struct Type {
pkpy_Type index;
constexpr Type() : index(0) {}
constexpr Type(pkpy_Type index) : index(index) {}
bool operator== (Type other) const { return this->index == other.index; }
bool operator!= (Type other) const { return this->index != other.index; }
constexpr operator pkpy_Type () const { return index; }
};
struct PyVar final: ::PyVar {
// uninitialized
PyVar() = default;
// implict conversion
PyVar(PyObject* existing){
PyVar__ctor3(this, (::PyObject*)existing);
}
/* We must initialize all members to allow == operator to work correctly */
// zero initialized
PyVar(std::nullptr_t){
set_null();
}
// PyObject* initialized (is_sso = false)
PyVar(Type type, PyObject* p){
PyVar__ctor(this, type, (::PyObject*)p);
}
PyVar(Type type, i64 value){
this->type = type;
this->is_ptr = false;
this->_i64 = value;
}
explicit operator bool () const { return (bool)type; }
void set_null() {
memset(this, 0, sizeof(PyVar));
}
bool operator==(PyObject* other){
return is_ptr && (PyObject*)_obj == other;
}
bool operator!=(PyObject* other){
return !is_ptr || (PyObject*)_obj != other;
}
bool operator==(std::nullptr_t){
return type == 0;
}
bool operator!=(std::nullptr_t){
return type != 0;
}
PyObject* get() const {
assert(is_ptr);
return (PyObject*)_obj;
}
PyObject* operator->() const {
assert(is_ptr);
return (PyObject*)_obj;
}
i64 hash() const { return PyVar__hash(this); }
template <typename T>
obj_get_t<T> obj_get();
// implicit convert from ::PyVar
PyVar(const ::PyVar& var) {
memcpy(this, &var, sizeof(var));
}
};
static_assert(sizeof(PyVar) == 16 && is_pod_v<PyVar>);
} // namespace pkpy

View File

@ -1,141 +0,0 @@
#pragma once
#include "pocketpy/common/vector.hpp"
#include "pocketpy/objects/object.hpp"
namespace pkpy {
struct BoundMethod {
PyVar self;
PyVar func;
BoundMethod(PyVar self, PyVar func) : self(self), func(func) {}
void _gc_mark(VM*) const;
};
struct StaticMethod {
PyVar func;
StaticMethod(PyVar func) : func(func) {}
void _gc_mark(VM*) const;
};
struct ClassMethod {
PyVar func;
ClassMethod(PyVar func) : func(func) {}
void _gc_mark(VM*) const;
};
struct Property {
PyVar getter;
PyVar setter;
Property(PyVar getter, PyVar setter) : getter(getter), setter(setter) {}
void _gc_mark(VM*) const;
};
struct Range {
i64 start = 0;
i64 stop = -1;
i64 step = 1;
};
struct StarWrapper {
int level; // either 1 or 2
PyVar obj;
StarWrapper(int level, PyVar obj) : level(level), obj(obj) {}
void _gc_mark(VM*) const;
};
using Bytes = array<unsigned char>;
struct Super {
PyVar first;
Type second;
Super(PyVar first, Type second) : first(first), second(second) {}
void _gc_mark(VM*) const;
};
struct Slice {
PyVar start;
PyVar stop;
PyVar step;
Slice(PyVar start, PyVar stop, PyVar step) : start(start), stop(stop), step(step) {}
void _gc_mark(VM*) const;
};
const inline int16_t kTpIntIndex = 3;
const inline int16_t kTpFloatIndex = 4;
const inline int16_t kTpNoneTypeIndex = 24;
const inline int16_t kTpNotImplementedTypeIndex = 25;
inline bool is_tagged(PyVar p) noexcept { return !p.is_ptr; }
inline bool is_float(PyVar p) noexcept { return p.type == kTpFloatIndex; }
inline bool is_int(PyVar p) noexcept { return p.type == kTpIntIndex; }
inline bool is_none(PyVar p) noexcept { return p.type == kTpNoneTypeIndex; }
inline bool is_not_implemented(PyVar p) noexcept { return p.type == kTpNotImplementedTypeIndex; }
inline bool is_type(PyVar obj, Type type) {
assert(obj != nullptr);
return obj.type == type;
}
inline bool is_type(PyObject* p, Type type) {
assert(p != nullptr);
return p->type == type;
}
struct MappingProxy {
PyObject* obj;
MappingProxy(PyObject* obj) : obj(obj) {}
NameDict& attr() { return obj->attr(); }
void _gc_mark(VM*) const;
};
StrName _type_name(VM* vm, Type type);
template <typename T>
T to_void_p(VM*, PyVar);
PyVar from_void_p(VM*, void*);
template <typename T>
T& PyVar::obj_get() {
static_assert(!is_sso_v<T>, "unsupported");
assert(is_ptr);
void* v = PyObject__value_ptr(_obj);
return *reinterpret_cast<T*>(v);
}
#define PK_OBJ_GET(T, obj) ((obj).obj_get<T>())
// deprecated
#define PK_OBJ_MARK(obj) vm->obj_gc_mark(obj)
#define VAR(x) py_var(vm, x)
#define CAST(T, x) py_cast<T>(vm, x)
#define _CAST(T, x) _py_cast<T>(vm, x)
#define CAST_F(x) py_cast<f64>(vm, x)
#define CAST_DEFAULT(T, x, default_value) (x != vm->None) ? py_cast<T>(vm, x) : (default_value)
/*****************************************************************/
#define PY_NULL nullptr
} // namespace pkpy

View File

@ -1,66 +0,0 @@
#pragma once
#include "pocketpy/common/any.h"
#include "pocketpy/common/traits.hpp"
#include "pocketpy/objects/tuplelist.hpp"
#include "pocketpy/objects/namedict.hpp"
#include "pocketpy/objects/sourcedata.h"
#include "pocketpy/common/smallmap.h"
#include "pocketpy/objects/codeobject.h"
namespace pkpy {
typedef PyVar (*NativeFuncC)(VM*, ArgsView);
struct NativeFunc {
NativeFuncC f;
int argc; // old style argc-based call
FuncDecl_ decl; // new style decl-based call
any _userdata;
NativeFunc(NativeFuncC f, int argc, any userdata = {}) :
f(f), argc(argc), decl(nullptr), _userdata(std::move(userdata)) {}
NativeFunc(NativeFuncC f, FuncDecl_ decl, any userdata = {}) :
f(f), argc(-1), decl(decl), _userdata(std::move(userdata)) {}
PyVar call(VM* vm, ArgsView args) const { return f(vm, args); }
void _gc_mark(VM*) const;
~NativeFunc() {
if(decl) PK_DECREF(decl);
}
};
struct Function {
PK_ALWAYS_PASS_BY_POINTER(Function)
FuncDecl_ decl;
PyObject* _module; // weak ref
PyObject* _class; // weak ref
NameDict* _closure; // strong ref
Function(FuncDecl_ decl, PyObject* _module, PyObject* _class, NameDict* _closure) :
decl(decl), _module(_module), _class(_class), _closure(_closure) {
PK_INCREF(decl);
}
void _gc_mark(VM*) const;
~Function() {
PK_DECREF(decl);
delete _closure;
}
};
template <typename T>
T lambda_get_userdata(PyVar* p) {
static_assert(std::is_same_v<T, std::decay_t<T>>);
static_assert(is_pod_v<T>);
int offset = p[-1] != nullptr ? -1 : -2;
NativeFunc& nf = p[offset].obj_get<NativeFunc>();
return nf._userdata.as<T>();
}
} // namespace pkpy

View File

@ -1,98 +0,0 @@
#pragma once
#include "pocketpy/objects/base.hpp"
#include "pocketpy/objects/tuplelist.hpp"
#include "pocketpy/objects/dict.h"
namespace pkpy {
struct Dict : private pkpy_Dict {
Dict() {
pkpy_Dict__ctor(this);
}
Dict(Dict&& other) {
std::memcpy(this, &other, sizeof(Dict));
pkpy_Dict__ctor(&other);
}
Dict(const Dict& other) {
// OPTIMIZEME: reduce copy
auto clone = pkpy_Dict__copy(&other);
std::memcpy(this, &clone, sizeof(Dict));
}
Dict& operator= (const Dict&) = delete;
Dict& operator= (Dict&&) = delete;
int size() const { return count; }
void set(VM* vm, PyVar key, PyVar val) {
pkpy_Dict__set(this, *(::PyVar*)(&key), *(::PyVar*)(&val));
}
PyVar try_get(VM* vm, PyVar key) const {
auto res = pkpy_Dict__try_get(this, *(::PyVar*)(&key));
if (!res) return nullptr;
return *(const PyVar*)(res);
}
bool contains(VM* vm, PyVar key) const {
return pkpy_Dict__contains(this, *(::PyVar*)(&key));
}
bool del(VM* vm, PyVar key) {
return pkpy_Dict__del(this, *(::PyVar*)(&key));
}
void update(VM* vm, const Dict& other) {
pkpy_Dict__update(this, &other);
}
template <typename __Func>
void apply(__Func f) const {
pkpy_DictIter it = iter();
PyVar key, val;
while(pkpy_DictIter__next(&it, (::PyVar*)(&key), (::PyVar*)(&val))) {
f(key, val);
}
}
Tuple keys() const {
Tuple res(count);
pkpy_DictIter it = iter();
PyVar key, val;
int i = 0;
while(pkpy_DictIter__next(&it, (::PyVar*)(&key), (::PyVar*)(&val))) {
res[i++] = key;
}
return res;
}
Tuple values() const {
Tuple res(count);
pkpy_DictIter it = iter();
PyVar key, val;
int i = 0;
while(pkpy_DictIter__next(&it, (::PyVar*)(&key), (::PyVar*)(&val))) {
res[i++] = val;
}
return res;
}
pkpy_DictIter iter() const {
return pkpy_Dict__iter(this);
}
void clear() {
pkpy_Dict__clear(this);
}
~Dict() {
pkpy_Dict__dtor(this);
}
void _gc_mark(VM*) const;
};
} // namespace pkpy

View File

@ -17,7 +17,7 @@ typedef struct pkpy_ExceptionFrame {
} pkpy_ExceptionFrame; } pkpy_ExceptionFrame;
typedef struct pkpy_Exception { typedef struct pkpy_Exception {
pkpy_StrName type; pk_StrName type;
pkpy_Str msg; pkpy_Str msg;
bool is_re; bool is_re;
@ -29,7 +29,7 @@ typedef struct pkpy_Exception {
c11_vector/*T=pkpy_ExceptionFrame*/ stacktrace; c11_vector/*T=pkpy_ExceptionFrame*/ stacktrace;
} pkpy_Exception; } pkpy_Exception;
void pkpy_Exception__ctor(pkpy_Exception* self, pkpy_StrName type); void pkpy_Exception__ctor(pkpy_Exception* self, pk_StrName type);
void pkpy_Exception__dtor(pkpy_Exception* self); void pkpy_Exception__dtor(pkpy_Exception* self);
void pkpy_Exception__stpush(pkpy_Exception* self, pkpy_SourceData_ src, int lineno, const char* cursor, const char* name); void pkpy_Exception__stpush(pkpy_Exception* self, pkpy_SourceData_ src, int lineno, const char* cursor, const char* name);
pkpy_Str pkpy_Exception__summary(pkpy_Exception* self); pkpy_Str pkpy_Exception__summary(pkpy_Exception* self);

View File

@ -1,64 +0,0 @@
#pragma once
#include "pocketpy/common/str.hpp"
#include "pocketpy/common/traits.hpp"
#include "pocketpy/objects/sourcedata.h"
#include "pocketpy/objects/error.h"
namespace pkpy {
struct NeedMoreLines {
NeedMoreLines(bool is_compiling_class) : is_compiling_class(is_compiling_class) {}
bool is_compiling_class;
};
enum class InternalExceptionType : int { Null, Handled, Unhandled, ToBeRaised };
struct InternalException final {
InternalExceptionType type;
int arg;
InternalException() : type(InternalExceptionType::Null), arg(-1) {}
InternalException(InternalExceptionType type, int arg = -1) : type(type), arg(arg) {}
};
struct Exception: pkpy_Exception{
PK_ALWAYS_PASS_BY_POINTER(Exception)
Exception(uint16_t type){
pkpy_Exception__ctor(this, type);
}
~Exception(){
pkpy_Exception__dtor(this);
}
void stpush(pkpy_SourceData_ src, int lineno, const char* cursor, const char* name){
pkpy_Exception__stpush(this, src, lineno, cursor, name);
}
Str summary(){
return pkpy_Exception__summary(this);
}
};
struct TopLevelException : std::exception {
VM* vm;
Exception* ptr;
TopLevelException(VM* vm, Exception* ptr) : vm(vm), ptr(ptr) {}
Str summary() const { return ptr->summary(); }
const char* what() const noexcept override {
static Str cached_summary;
cached_summary = summary();
return cached_summary.c_str();
}
};
} // namespace pkpy

View File

@ -12,7 +12,7 @@ extern "C" {
#define SMALLMAP_T__HEADER #define SMALLMAP_T__HEADER
#define K uint16_t #define K uint16_t
#define V PyVar #define V PyVar
#define NAME pkpy_NameDict #define NAME pk_NameDict
#include "pocketpy/xmacros/smallmap.h" #include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__HEADER #undef SMALLMAP_T__HEADER

View File

@ -1,104 +0,0 @@
#pragma once
#include "pocketpy/common/config.h"
#include "pocketpy/common/str.hpp"
#include "pocketpy/common/utils.h"
#include "pocketpy/objects/object.hpp"
#include "pocketpy/objects/namedict.h"
#include <string_view>
namespace pkpy {
struct NameDict: pkpy_NameDict {
PK_ALWAYS_PASS_BY_POINTER(NameDict)
using Item = pair<StrName, PyVar>;
NameDict() {
pkpy_NameDict__ctor(this);
}
~NameDict() {
pkpy_NameDict__dtor(this);
}
uint16_t size() const {
return count;
}
void set(StrName key, PyVar val){
PyVar* p = (PyVar*)&val;
pkpy_NameDict__set(this, key.index, *p);
}
PyVar try_get(StrName key) const{
PyVar* p = try_get_2(key);
if(p) return *p;
return nullptr;
}
PyVar* try_get_2(StrName key) const{
PyVar* p = (PyVar*)pkpy_NameDict__try_get(this, key.index);
return p;
}
PyVar try_get_likely_found(StrName key) const{
return try_get(key);
}
PyVar* try_get_2_likely_found(StrName key) const{
return try_get_2(key);
}
bool del(StrName key){
return pkpy_NameDict__del(this, key.index);
}
bool contains(StrName key) const{
return pkpy_NameDict__contains(this, key.index);
}
PyVar operator[] (StrName key) const{
PyVar* val = try_get_2_likely_found(key);
if(val == nullptr){
PK_FATAL_ERROR("NameDict key not found: %d (%s)\n", (int)key.index, key.escape().c_str())
}
return *val;
}
void clear(){
pkpy_NameDict__clear(this);
}
array<StrName> keys() const{
array<StrName> retval((int)size());
for(int i=0; i<size(); i++){
auto it = c11__at(pkpy_NameDict_KV, this, i);
retval[i] = StrName(it->key);
}
return retval;
}
array<Item> items() const{
array<Item> retval((int)size());
for(int i=0; i<size(); i++){
auto it = c11__at(pkpy_NameDict_KV, this, i);
PyVar* p = (PyVar*)&it->value;
retval[i] = Item(StrName(it->key), *p);
}
return retval;
}
void apply(void (*f)(StrName, PyVar, void*), void* data){
for(int i=0; i<size(); i++){
auto it = c11__at(pkpy_NameDict_KV, this, i);
PyVar* p = (PyVar*)&it->value;
f(StrName(it->key), *p, data);
}
}
};
static_assert(sizeof(NameDict) <= 128);
} // namespace pkpy

View File

@ -8,22 +8,33 @@ extern "C" {
#endif #endif
typedef struct PyObject{ typedef struct PyObject{
pkpy_Type type; // we have a duplicated type here for convenience Type type; // we have a duplicated type here for convenience
bool gc_is_large; bool gc_is_large;
bool gc_marked; bool gc_marked;
pkpy_NameDict* _attr; // gc will delete this on destruction pk_NameDict* dict; // gc will delete this on destruction
} PyObject; } PyObject;
static_assert(sizeof(PyObject) <= 16, "!(sizeof(PyObject) <= 16)"); static_assert(sizeof(PyObject) <= 16, "!(sizeof(PyObject) <= 16)");
#define PyObject__value_ptr(self) ((char*)self + 16) #define PyObject__value_ptr(self) ((char*)self + 16)
#define PyObject__as(T, self) (T*)(PyObject__value_ptr(self)) #define PyObject__as(T, self) (T*)(PyObject__value_ptr(self))
#define PK_OBJ_GET(T, val) (*(T*)(PyObject__value_ptr((val)._obj)))
#define PK_OBJ_SIZEOF(T) (sizeof(T) + 16)
PK_INLINE void PyObject__ctor(PyObject* self, pkpy_Type type, bool gc_is_large){ PK_INLINE void PyObject__ctor(PyObject* self, Type type, bool gc_is_large){
self->type = type; self->type = type;
self->gc_is_large = gc_is_large; self->gc_is_large = gc_is_large;
self->gc_marked = false; self->gc_marked = false;
self->_attr = NULL; self->dict = NULL;
}
PK_INLINE PyVar PyVar__fromobj(PyObject* obj){
PyVar retval = {
.type = obj->type,
.is_ptr = true,
._obj = obj;
};
return retval;
} }
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1,37 +0,0 @@
#pragma once
#include "pocketpy/common/str.hpp"
#include "pocketpy/common/config.h"
#include "pocketpy/objects/base.hpp"
#include "pocketpy/objects/object.h"
namespace pkpy {
struct NameDict;
struct PyObject final: ::PyObject {
bool is_attr_valid() const noexcept { return _attr != nullptr; }
void* _value_ptr() noexcept { return PyObject__value_ptr(this); }
NameDict& attr() const{
assert(is_attr_valid());
return *(NameDict*)_attr;
}
PyVar attr(StrName name) const;
template <typename T>
T& as() noexcept {
static_assert(std::is_same_v<T, std::decay_t<T>>);
return *reinterpret_cast<T*>(_value_ptr());
}
PyObject(Type type, bool gc_is_large){
PyObject__ctor(this, type, gc_is_large);
}
};
static_assert(sizeof(PyObject) <= 16);
} // namespace pkpy

View File

@ -1,77 +0,0 @@
#pragma once
#include "pocketpy/common/vector.hpp"
#include "pocketpy/objects/base.hpp"
namespace pkpy {
struct Tuple {
const static int INLINED_SIZE = 3;
PyVar* _args;
PyVar _inlined[INLINED_SIZE];
int _size;
Tuple(int n);
Tuple(Tuple&& other) noexcept;
Tuple(const Tuple& other) = delete;
Tuple& operator= (const Tuple& other) = delete;
Tuple& operator= (Tuple&& other) = delete;
~Tuple();
Tuple(PyVar, PyVar);
Tuple(PyVar, PyVar, PyVar);
bool is_inlined() const { return _args == _inlined; }
PyVar& operator[] (int i) { return _args[i]; }
PyVar operator[] (int i) const { return _args[i]; }
int size() const { return _size; }
PyVar* begin() const { return _args; }
PyVar* end() const { return _args + _size; }
PyVar* data() const { return _args; }
void _gc_mark(VM*) const;
};
struct List : public vector<PyVar> {
using vector<PyVar>::vector;
void _gc_mark(VM*) const;
Tuple to_tuple() const {
Tuple ret(size());
for(int i = 0; i < size(); i++)
ret[i] = (*this)[i];
return ret;
}
};
// a lightweight view for function args, it does not own the memory
struct ArgsView {
PyVar* _begin;
PyVar* _end;
ArgsView(PyVar* begin, PyVar* end) : _begin(begin), _end(end) {}
ArgsView(const Tuple& t) : _begin(t.begin()), _end(t.end()) {}
PyVar* begin() const { return _begin; }
PyVar* end() const { return _end; }
int size() const { return _end - _begin; }
bool empty() const { return _begin == _end; }
PyVar operator[] (int i) const { return _begin[i]; }
List to_list() const;
Tuple to_tuple() const;
};
} // namespace pkpy

View File

@ -1,5 +1,3 @@
#pragma once
#include "stdint.h" #include "stdint.h"
#include "stdbool.h" #include "stdbool.h"
@ -7,19 +5,27 @@
extern "C" { extern "C" {
#endif #endif
typedef int16_t pkpy_Type;
typedef struct PyObject PyObject; typedef struct PyObject PyObject;
typedef struct PyVar PyVar; typedef struct PyVar PyVar;
typedef struct pkpy_VM pkpy_VM; typedef struct pk_VM pk_VM;
typedef struct py_Error py_Error;
struct pkpy_G { typedef unsigned (*py_CFunction)(const PyVar*, int);
pkpy_VM* vm;
} extern pkpy_g; extern pk_VM* pk_vm;
void py_initialize(); void py_initialize();
void py_switch_vm(const char* name); // void py_switch_vm(const char* name);
void py_finalize(); void py_finalize();
py_Error* py_exec_simple(const char*);
py_Error* py_eval_simple(const char*, PyVar*);
/* py_error */
void py_Error__print(const py_Error*);
void py_Error__delete(py_Error*);
bool py_eq(const PyVar*, const PyVar*); bool py_eq(const PyVar*, const PyVar*);
bool py_le(const PyVar*, const PyVar*); bool py_le(const PyVar*, const PyVar*);
int64_t py_hash(const PyVar*); int64_t py_hash(const PyVar*);
@ -34,6 +40,8 @@ void py_newstr2(PyVar*, const char*, int);
void py_newbytes(PyVar*, const uint8_t*, int); void py_newbytes(PyVar*, const uint8_t*, int);
void py_newnone(PyVar*); void py_newnone(PyVar*);
#define py_isnull(self) ((self)->type == 0)
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,25 +0,0 @@
#pragma once
#include "pocketpy/common/traits.hpp"
#include "pocketpy/objects/builtins.hpp"
#include "pocketpy/interpreter/vm.hpp"
#include "pocketpy/interpreter/iter.hpp"
#include "pocketpy/interpreter/bindings.hpp"
#include "pocketpy/compiler/compiler.hpp"
#include "pocketpy/modules/linalg.hpp"
#include "pocketpy/tools/repl.hpp"
namespace pkpy {
static_assert(py_sizeof<Str> <= 64);
static_assert(py_sizeof<Mat3x3> <= 64);
static_assert(py_sizeof<Struct> <= 64);
static_assert(py_sizeof<Tuple> <= 80);
static_assert(py_sizeof<List> <= 64);
static_assert(py_sizeof<Dict> <= 64);
static_assert(py_sizeof<RangeIter> <= 64);
static_assert(py_sizeof<RangeIterR> <= 64);
static_assert(py_sizeof<ArrayIter> <= 64);
static_assert(py_sizeof<StringIter> <= 64);
static_assert(py_sizeof<Generator> <= 64);
static_assert(py_sizeof<DictItemsIter> <= 64);
} // namespace pkpy

View File

@ -1,107 +0,0 @@
#ifndef POCKETPY_C_H
#define POCKETPY_C_H
#include <stdbool.h>
#include <stdint.h>
#include "pocketpy/common/export.h"
#ifdef __cplusplus
extern "C" {
namespace pkpy{
#endif
typedef struct pkpy_vm_handle pkpy_vm;
typedef int (*pkpy_CFunction)(pkpy_vm*);
typedef void (*pkpy_COutputHandler)(const char*, int);
typedef unsigned char* (*pkpy_CImportHandler)(const char*, int*);
typedef int pkpy_CName;
typedef int pkpy_CType;
typedef const char* pkpy_CString;
/* Basic Functions */
PK_EXPORT pkpy_vm* pkpy_new_vm(bool enable_os);
PK_EXPORT void pkpy_delete_vm(pkpy_vm*);
PK_EXPORT bool pkpy_exec(pkpy_vm*, const char* source);
PK_EXPORT bool pkpy_exec_2(pkpy_vm*, const char* source, const char* filename, int mode, const char* module);
PK_EXPORT void pkpy_set_main_argv(pkpy_vm*, int argc, char** argv);
/* Stack Manipulation */
PK_EXPORT bool pkpy_dup(pkpy_vm*, int i);
PK_EXPORT bool pkpy_pop(pkpy_vm*, int n);
PK_EXPORT bool pkpy_pop_top(pkpy_vm*);
PK_EXPORT bool pkpy_dup_top(pkpy_vm*);
PK_EXPORT bool pkpy_rot_two(pkpy_vm*);
PK_EXPORT int pkpy_stack_size(pkpy_vm*);
// int
PK_EXPORT bool pkpy_push_int(pkpy_vm*, int val);
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 val);
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 val);
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 val);
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* val);
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
PK_EXPORT bool pkpy_push_null(pkpy_vm*);
PK_EXPORT bool pkpy_push_function(pkpy_vm*, const char* sig, pkpy_CFunction val);
PK_EXPORT bool pkpy_push_module(pkpy_vm*, const char* name);
// some opt
PK_EXPORT bool pkpy_getattr(pkpy_vm*, pkpy_CName name);
PK_EXPORT bool pkpy_setattr(pkpy_vm*, pkpy_CName name);
PK_EXPORT bool pkpy_getglobal(pkpy_vm*, pkpy_CName name);
PK_EXPORT bool pkpy_setglobal(pkpy_vm*, pkpy_CName name);
PK_EXPORT bool pkpy_eval(pkpy_vm*, const char* source);
PK_EXPORT bool pkpy_unpack_sequence(pkpy_vm*, int size);
PK_EXPORT bool pkpy_get_unbound_method(pkpy_vm*, pkpy_CName name);
PK_EXPORT bool pkpy_py_repr(pkpy_vm*);
PK_EXPORT bool pkpy_py_str(pkpy_vm*);
PK_EXPORT bool pkpy_py_import(pkpy_vm*, pkpy_CString name);
/* Error Handling */
PK_EXPORT bool pkpy_error(pkpy_vm*, const char* name, pkpy_CString msg);
PK_EXPORT bool pkpy_check_error(pkpy_vm*);
PK_EXPORT bool pkpy_clear_error(pkpy_vm*, char** message);
/* Callables */
PK_EXPORT bool pkpy_vectorcall(pkpy_vm*, int argc);
/* Special APIs */
PK_EXPORT void pkpy_free(void* p);
#define pkpy_string(__s) (__s)
PK_EXPORT pkpy_CName pkpy_name(const char* s);
PK_EXPORT pkpy_CString pkpy_name_to_string(pkpy_CName name);
PK_EXPORT void pkpy_set_output_handler(pkpy_vm*, pkpy_COutputHandler handler);
PK_EXPORT void pkpy_set_import_handler(pkpy_vm*, pkpy_CImportHandler handler);
/* REPL */
PK_EXPORT void* pkpy_new_repl(pkpy_vm*);
PK_EXPORT bool pkpy_repl_input(void* r, const char* line);
PK_EXPORT void pkpy_delete_repl(void* repl);
#ifdef __cplusplus
}
}
#endif
#endif

View File

@ -37,6 +37,8 @@ typedef c11_vector NAME;
void METHOD(ctor)(NAME* self); void METHOD(ctor)(NAME* self);
void METHOD(dtor)(NAME* self); void METHOD(dtor)(NAME* self);
NAME* METHOD(new)();
void METHOD(delete)(NAME* self);
void METHOD(set)(NAME* self, K key, V value); void METHOD(set)(NAME* self, K key, V value);
V* METHOD(try_get)(const NAME* self, K key); V* METHOD(try_get)(const NAME* self, K key);
V METHOD(get)(const NAME* self, K key, V default_value); V METHOD(get)(const NAME* self, K key, V default_value);
@ -58,6 +60,17 @@ void METHOD(dtor)(NAME* self) {
c11_vector__dtor(self); c11_vector__dtor(self);
} }
NAME* METHOD(new)() {
NAME* self = malloc(sizeof(NAME));
METHOD(ctor)(self);
return self;
}
void METHOD(delete)(NAME* self) {
METHOD(dtor)(self);
free(self);
}
void METHOD(set)(NAME* self, K key, V value) { void METHOD(set)(NAME* self, K key, V value) {
int index; int index;
c11__lower_bound(KV, self->data, self->count, key, partial_less, &index); c11__lower_bound(KV, self->data, self->count, key, partial_less, &index);

View File

@ -1,3 +0,0 @@
#pragma once
#include "pocketpy/pocketpy_c.h"

View File

@ -10,7 +10,7 @@ static c11_smallmap_s2n _interned;
static c11_vector/*T=char* */ _r_interned; static c11_vector/*T=char* */ _r_interned;
static bool _initialized = false; static bool _initialized = false;
void pkpy_StrName__initialize(){ void pk_StrName__initialize(){
if(_initialized) return; if(_initialized) return;
c11_smallmap_s2n__ctor(&_interned); c11_smallmap_s2n__ctor(&_interned);
for(int i=0; i<_r_interned.count; i++){ for(int i=0; i<_r_interned.count; i++){
@ -20,77 +20,77 @@ void pkpy_StrName__initialize(){
_initialized = true; _initialized = true;
// unary operators // unary operators
__repr__ = pkpy_StrName__map("__repr__"); __repr__ = pk_StrName__map("__repr__");
__str__ = pkpy_StrName__map("__str__"); __str__ = pk_StrName__map("__str__");
__hash__ = pkpy_StrName__map("__hash__"); __hash__ = pk_StrName__map("__hash__");
__len__ = pkpy_StrName__map("__len__"); __len__ = pk_StrName__map("__len__");
__iter__ = pkpy_StrName__map("__iter__"); __iter__ = pk_StrName__map("__iter__");
__next__ = pkpy_StrName__map("__next__"); __next__ = pk_StrName__map("__next__");
__neg__ = pkpy_StrName__map("__neg__"); __neg__ = pk_StrName__map("__neg__");
// logical operators // logical operators
__eq__ = pkpy_StrName__map("__eq__"); __eq__ = pk_StrName__map("__eq__");
__lt__ = pkpy_StrName__map("__lt__"); __lt__ = pk_StrName__map("__lt__");
__le__ = pkpy_StrName__map("__le__"); __le__ = pk_StrName__map("__le__");
__gt__ = pkpy_StrName__map("__gt__"); __gt__ = pk_StrName__map("__gt__");
__ge__ = pkpy_StrName__map("__ge__"); __ge__ = pk_StrName__map("__ge__");
__contains__ = pkpy_StrName__map("__contains__"); __contains__ = pk_StrName__map("__contains__");
// binary operators // binary operators
__add__ = pkpy_StrName__map("__add__"); __add__ = pk_StrName__map("__add__");
__radd__ = pkpy_StrName__map("__radd__"); __radd__ = pk_StrName__map("__radd__");
__sub__ = pkpy_StrName__map("__sub__"); __sub__ = pk_StrName__map("__sub__");
__rsub__ = pkpy_StrName__map("__rsub__"); __rsub__ = pk_StrName__map("__rsub__");
__mul__ = pkpy_StrName__map("__mul__"); __mul__ = pk_StrName__map("__mul__");
__rmul__ = pkpy_StrName__map("__rmul__"); __rmul__ = pk_StrName__map("__rmul__");
__truediv__ = pkpy_StrName__map("__truediv__"); __truediv__ = pk_StrName__map("__truediv__");
__floordiv__ = pkpy_StrName__map("__floordiv__"); __floordiv__ = pk_StrName__map("__floordiv__");
__mod__ = pkpy_StrName__map("__mod__"); __mod__ = pk_StrName__map("__mod__");
__pow__ = pkpy_StrName__map("__pow__"); __pow__ = pk_StrName__map("__pow__");
__matmul__ = pkpy_StrName__map("__matmul__"); __matmul__ = pk_StrName__map("__matmul__");
__lshift__ = pkpy_StrName__map("__lshift__"); __lshift__ = pk_StrName__map("__lshift__");
__rshift__ = pkpy_StrName__map("__rshift__"); __rshift__ = pk_StrName__map("__rshift__");
__and__ = pkpy_StrName__map("__and__"); __and__ = pk_StrName__map("__and__");
__or__ = pkpy_StrName__map("__or__"); __or__ = pk_StrName__map("__or__");
__xor__ = pkpy_StrName__map("__xor__"); __xor__ = pk_StrName__map("__xor__");
__invert__ = pkpy_StrName__map("__invert__"); __invert__ = pk_StrName__map("__invert__");
// indexer // indexer
__getitem__ = pkpy_StrName__map("__getitem__"); __getitem__ = pk_StrName__map("__getitem__");
__setitem__ = pkpy_StrName__map("__setitem__"); __setitem__ = pk_StrName__map("__setitem__");
__delitem__ = pkpy_StrName__map("__delitem__"); __delitem__ = pk_StrName__map("__delitem__");
// specials // specials
__new__ = pkpy_StrName__map("__new__"); __new__ = pk_StrName__map("__new__");
__init__ = pkpy_StrName__map("__init__"); __init__ = pk_StrName__map("__init__");
__call__ = pkpy_StrName__map("__call__"); __call__ = pk_StrName__map("__call__");
__divmod__ = pkpy_StrName__map("__divmod__"); __divmod__ = pk_StrName__map("__divmod__");
__enter__ = pkpy_StrName__map("__enter__"); __enter__ = pk_StrName__map("__enter__");
__exit__ = pkpy_StrName__map("__exit__"); __exit__ = pk_StrName__map("__exit__");
__name__ = pkpy_StrName__map("__name__"); __name__ = pk_StrName__map("__name__");
__all__ = pkpy_StrName__map("__all__"); __all__ = pk_StrName__map("__all__");
__package__ = pkpy_StrName__map("__package__"); __package__ = pk_StrName__map("__package__");
__path__ = pkpy_StrName__map("__path__"); __path__ = pk_StrName__map("__path__");
__class__ = pkpy_StrName__map("__class__"); __class__ = pk_StrName__map("__class__");
__missing__ = pkpy_StrName__map("__missing__"); __missing__ = pk_StrName__map("__missing__");
pk_id_add = pkpy_StrName__map("add"); pk_id_add = pk_StrName__map("add");
pk_id_set = pkpy_StrName__map("set"); pk_id_set = pk_StrName__map("set");
pk_id_long = pkpy_StrName__map("long"); pk_id_long = pk_StrName__map("long");
pk_id_complex = pkpy_StrName__map("complex"); pk_id_complex = pk_StrName__map("complex");
} }
void pkpy_StrName__finalize(){ void pk_StrName__finalize(){
if(!_initialized) return; if(!_initialized) return;
c11_smallmap_s2n__dtor(&_interned); c11_smallmap_s2n__dtor(&_interned);
c11_vector__dtor(&_r_interned); c11_vector__dtor(&_r_interned);
} }
uint16_t pkpy_StrName__map(const char* name){ uint16_t pk_StrName__map(const char* name){
return pkpy_StrName__map2((c11_string){name, strlen(name)}); return pk_StrName__map2((c11_string){name, strlen(name)});
} }
uint16_t pkpy_StrName__map2(c11_string name){ uint16_t pk_StrName__map2(c11_string name){
// TODO: PK_GLOBAL_SCOPE_LOCK() // TODO: PK_GLOBAL_SCOPE_LOCK()
if(!_initialized){ if(!_initialized){
pkpy_StrName__initialize(); // lazy init pk_StrName__initialize(); // lazy init
} }
uint16_t index = c11_smallmap_s2n__get(&_interned, name, 0); uint16_t index = c11_smallmap_s2n__get(&_interned, name, 0);
if(index != 0) return index; if(index != 0) return index;
@ -110,7 +110,7 @@ uint16_t pkpy_StrName__map2(c11_string name){
return index; return index;
} }
const char* pkpy_StrName__rmap(uint16_t index){ const char* pk_StrName__rmap(uint16_t index){
assert(_initialized); assert(_initialized);
assert(index > 0 && index <= _interned.count); assert(index > 0 && index <= _interned.count);
return c11__getitem(char*, &_r_interned, index - 1); return c11__getitem(char*, &_r_interned, index - 1);

View File

@ -2,7 +2,7 @@
#include "pocketpy/common/strname.h" #include "pocketpy/common/strname.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
void pkpy_Exception__ctor(pkpy_Exception* self, pkpy_StrName type){ void pkpy_Exception__ctor(pkpy_Exception* self, pk_StrName type){
self->type = type; self->type = type;
self->is_re = true; self->is_re = true;
self->_ip_on_error = -1; self->_ip_on_error = -1;
@ -48,7 +48,7 @@ pkpy_Str pkpy_Exception__summary(pkpy_Exception* self){
pk_SStream__write_cstr(&ss, "\n"); pk_SStream__write_cstr(&ss, "\n");
} }
const char* name = pkpy_StrName__rmap(self->type); const char* name = pk_StrName__rmap(self->type);
pk_SStream__write_cstr(&ss, name); pk_SStream__write_cstr(&ss, name);
if(self->msg.size > 0){ if(self->msg.size > 0){

View File

@ -25,7 +25,7 @@ namespace pkpy {
} else { \ } else { \
PyVar self; \ PyVar self; \
PyVar _2 = get_unbound_method(_0, func, &self, false); \ PyVar _2 = get_unbound_method(_0, func, &self, false); \
if(_2) \ if(_2.type) \
ret = call_method(self, _2, _1); \ ret = call_method(self, _2, _1); \
else \ else \
ret = NotImplemented; \ ret = NotImplemented; \
@ -33,7 +33,7 @@ namespace pkpy {
if(is_not_implemented(ret)) { \ if(is_not_implemented(ret)) { \
PyVar self; \ PyVar self; \
PyVar _2 = get_unbound_method(_1, rfunc, &self, false); \ PyVar _2 = get_unbound_method(_1, rfunc, &self, false); \
if(_2) \ if(_2.type) \
ret = call_method(self, _2, _0); \ ret = call_method(self, _2, _0); \
else \ else \
BinaryOptError(op, _0, _1); \ BinaryOptError(op, _0, _1); \
@ -192,7 +192,7 @@ PyVar VM::__run_top_frame() {
if(decl->nested) { if(decl->nested) {
NameDict* captured = frame->_locals.to_namedict(); NameDict* captured = frame->_locals.to_namedict();
obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, captured); obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, captured);
uint16_t name = pkpy_StrName__map2(pkpy_Str__sv(&decl->code->name)); uint16_t name = pk_StrName__map2(pkpy_Str__sv(&decl->code->name));
captured->set(name, obj); captured->set(name, obj);
} else { } else {
obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, nullptr); obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, nullptr);
@ -863,7 +863,7 @@ PyVar VM::__run_top_frame() {
} }
} }
DISPATCH() DISPATCH()
case OP_YIELD_VALUE: return pkpy_OP_YIELD; case OP_YIELD_VALUE: return PY_OP_YIELD;
/*****************************************/ /*****************************************/
case OP_LIST_APPEND: { case OP_LIST_APPEND: {
PyVar _0 = POPX(); PyVar _0 = POPX();
@ -935,7 +935,7 @@ PyVar VM::__run_top_frame() {
DISPATCH_JUMP_ABSOLUTE(target) DISPATCH_JUMP_ABSOLUTE(target)
} else { } else {
PUSH(_0); PUSH(_0);
return pkpy_OP_YIELD; return PY_OP_YIELD;
} }
} }
case OP_FOR_ITER_UNPACK: { case OP_FOR_ITER_UNPACK: {
@ -1075,7 +1075,7 @@ PyVar VM::__run_top_frame() {
DISPATCH() DISPATCH()
/*****************************************/ /*****************************************/
case OP_TRY_ENTER: { case OP_TRY_ENTER: {
frame->set_unwind_target(s_data._sp); frame->set_unwind_target(s_data.sp);
DISPATCH() DISPATCH()
} }
case OP_EXCEPTION_MATCH: { case OP_EXCEPTION_MATCH: {

View File

@ -1,289 +0,0 @@
#include "pocketpy/interpreter/cffi.hpp"
namespace pkpy {
void VoidP::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind_func(type, __new__, 2, [](VM* vm, ArgsView args) {
Type cls = PK_OBJ_GET(Type, args[0]);
i64 addr = CAST(i64, args[1]);
return vm->new_object<VoidP>(cls, reinterpret_cast<void*>(addr));
});
vm->bind__hash__(type->as<Type>(), [](VM* vm, PyVar obj) {
obj_get_t<VoidP> self = PK_OBJ_GET(VoidP, obj);
return reinterpret_cast<i64>(self.ptr);
});
vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar obj) -> Str {
obj_get_t<VoidP> self = PK_OBJ_GET(VoidP, obj);
return _S("<void* at ", self.hex(), ">");
});
#define BIND_CMP(name, op) \
vm->bind##name(type->as<Type>(), [](VM* vm, PyVar lhs, PyVar rhs) { \
if(!vm->isinstance(rhs, vm->_tp_user<VoidP>())) return vm->NotImplemented; \
void* _0 = PK_OBJ_GET(VoidP, lhs).ptr; \
void* _1 = PK_OBJ_GET(VoidP, rhs).ptr; \
return VAR(_0 op _1); \
});
BIND_CMP(__eq__, ==)
BIND_CMP(__lt__, <)
BIND_CMP(__le__, <=)
BIND_CMP(__gt__, >)
BIND_CMP(__ge__, >=)
#undef BIND_CMP
}
void Struct::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind_func(type, __new__, 2, [](VM* vm, ArgsView args) {
Type cls = PK_OBJ_GET(Type, args[0]);
int size = CAST(int, args[1]);
return vm->new_object<Struct>(cls, size);
});
vm->bind_func(type, "hex", 1, [](VM* vm, ArgsView args) {
const Struct& self = _CAST(Struct&, args[0]);
SStream ss;
for(int i = 0; i < self.size; i++)
ss.write_hex((unsigned char)self.p[i]);
return VAR(ss.str());
});
// @staticmethod
vm->bind_func(
type,
"fromhex",
1,
[](VM* vm, ArgsView args) {
const Str& s = CAST(Str&, args[0]);
if(s.size < 2 || s.size % 2 != 0) vm->ValueError("invalid hex string");
Struct buffer(s.size / 2, false);
for(int i = 0; i < s.size; i += 2) {
char c = 0;
if(s[i] >= '0' && s[i] <= '9')
c += s[i] - '0';
else if(s[i] >= 'A' && s[i] <= 'F')
c += s[i] - 'A' + 10;
else if(s[i] >= 'a' && s[i] <= 'f')
c += s[i] - 'a' + 10;
else
vm->ValueError(_S("invalid hex char: '", s[i], "'"));
c <<= 4;
if(s[i + 1] >= '0' && s[i + 1] <= '9')
c += s[i + 1] - '0';
else if(s[i + 1] >= 'A' && s[i + 1] <= 'F')
c += s[i + 1] - 'A' + 10;
else if(s[i + 1] >= 'a' && s[i + 1] <= 'f')
c += s[i + 1] - 'a' + 10;
else
vm->ValueError(_S("invalid hex char: '", s[i + 1], "'"));
buffer.p[i / 2] = c;
}
return vm->new_user_object<Struct>(std::move(buffer));
},
{},
BindType_STATICMETHOD);
vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar obj) {
Struct& self = _CAST(Struct&, obj);
SStream ss;
ss << "<struct object of " << self.size << " bytes>";
return ss.str();
});
vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args) {
Struct& self = _CAST(Struct&, args[0]);
return vm->new_user_object<VoidP>(self.p);
});
vm->bind_func(type, "sizeof", 1, [](VM* vm, ArgsView args) {
Struct& self = _CAST(Struct&, args[0]);
return VAR(self.size);
});
vm->bind_func(type, "copy", 1, [](VM* vm, ArgsView args) {
const Struct& self = _CAST(Struct&, args[0]);
return vm->new_object<Struct>(vm->_tp(args[0]), self);
});
vm->bind__eq__(type->as<Type>(), [](VM* vm, PyVar lhs, PyVar rhs) {
Struct& self = _CAST(Struct&, lhs);
if(!vm->is_user_type<Struct>(rhs)) return vm->NotImplemented;
Struct& other = _CAST(Struct&, rhs);
bool ok = self.size == other.size && memcmp(self.p, other.p, self.size) == 0;
return VAR(ok);
});
#define BIND_SETGET(T, name) \
vm->bind(type, "read_" name "(self, offset=0)", [](VM* vm, ArgsView args) { \
Struct& self = _CAST(Struct&, args[0]); \
i64 offset = CAST(i64, args[1]); \
void* ptr = self.p + offset; \
return VAR(*(T*)ptr); \
}); \
vm->bind(type, "write_" name "(self, value, offset=0)", [](VM* vm, ArgsView args) { \
Struct& self = _CAST(Struct&, args[0]); \
i64 offset = CAST(i64, args[2]); \
void* ptr = self.p + offset; \
*(T*)ptr = CAST(T, args[1]); \
return vm->None; \
});
BIND_SETGET(char, "char")
BIND_SETGET(unsigned char, "uchar")
BIND_SETGET(short, "short")
BIND_SETGET(unsigned short, "ushort")
BIND_SETGET(int, "int")
BIND_SETGET(unsigned int, "uint")
BIND_SETGET(long, "long")
BIND_SETGET(unsigned long, "ulong")
BIND_SETGET(long long, "longlong")
BIND_SETGET(unsigned long long, "ulonglong")
BIND_SETGET(float, "float")
BIND_SETGET(double, "double")
BIND_SETGET(bool, "bool")
BIND_SETGET(void*, "void_p")
#undef BIND_SETGET
}
void add_module_c(VM* vm) {
PyObject* mod = vm->new_module("c");
vm->bind_func(mod, "malloc", 1, [](VM* vm, ArgsView args) {
i64 size = CAST(i64, args[0]);
return VAR(std::malloc(size));
});
vm->bind_func(mod, "free", 1, [](VM* vm, ArgsView args) {
void* p = CAST(void*, args[0]);
std::free(p);
return vm->None;
});
vm->bind_func(mod, "memset", 3, [](VM* vm, ArgsView args) {
void* p = CAST(void*, args[0]);
std::memset(p, CAST(int, args[1]), CAST(size_t, args[2]));
return vm->None;
});
vm->bind_func(mod, "memcpy", 3, [](VM* vm, ArgsView args) {
void* dst = CAST(void*, args[0]);
void* src = CAST(void*, args[1]);
i64 size = CAST(i64, args[2]);
std::memcpy(dst, src, size);
return vm->None;
});
vm->register_user_class<VoidP>(mod, "void_p", VM::tp_object, true);
vm->register_user_class<Struct>(mod, "struct", VM::tp_object, true);
mod->attr().set("NULL", vm->new_user_object<VoidP>(nullptr));
vm->bind(mod, "p_cast(ptr: 'void_p', cls: type[T]) -> T", [](VM* vm, ArgsView args) {
VoidP& ptr = CAST(VoidP&, args[0]);
vm->check_type(args[1], vm->tp_type);
Type cls = PK_OBJ_GET(Type, args[1]);
if(!vm->issubclass(cls, vm->_tp_user<VoidP>())) { vm->ValueError("expected a subclass of void_p"); }
return vm->new_object<VoidP>(cls, ptr.ptr);
});
vm->bind(mod, "p_value(ptr: 'void_p') -> int", [](VM* vm, ArgsView args) {
VoidP& ptr = CAST(VoidP&, args[0]);
return VAR(reinterpret_cast<i64>(ptr.ptr));
});
vm->bind(mod, "pp_deref(ptr: Tp) -> Tp", [](VM* vm, ArgsView args) {
VoidP& ptr = CAST(VoidP&, args[0]);
void* value = *reinterpret_cast<void**>(ptr.ptr);
return vm->new_object<VoidP>(args[0].type, value);
});
PyObject* type;
Type type_t;
#define BIND_PRIMITIVE(T, CNAME) \
vm->bind_func(mod, CNAME "_", 1, [](VM* vm, ArgsView args) { \
T val = CAST(T, args[0]); \
return vm->new_user_object<Struct>(&val, sizeof(T)); \
}); \
type = vm->new_type_object(mod, CNAME "_p", vm->_tp_user<VoidP>(), true); \
mod->attr().set(CNAME "_p", type); \
type_t = type->as<Type>(); \
vm->bind_func(type, "read", 1, [](VM* vm, ArgsView args) { \
obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]); \
T* target = (T*)voidp.ptr; \
return VAR(*target); \
}); \
vm->bind_func(type, "write", 2, [](VM* vm, ArgsView args) { \
obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]); \
T val = CAST(T, args[1]); \
T* target = (T*)voidp.ptr; \
*target = val; \
return vm->None; \
}); \
vm->bind__getitem__(type_t, [](VM* vm, PyVar obj, PyVar index) { \
obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, obj); \
i64 offset = CAST(i64, index); \
T* target = (T*)voidp.ptr; \
return VAR(target[offset]); \
}); \
vm->bind__setitem__(type_t, [](VM* vm, PyVar obj, PyVar index, PyVar value) { \
obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, obj); \
i64 offset = CAST(i64, index); \
T* target = (T*)voidp.ptr; \
target[offset] = CAST(T, value); \
}); \
vm->bind__add__(type_t, [](VM* vm, PyVar lhs, PyVar rhs) { \
obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, lhs); \
i64 offset = CAST(i64, rhs); \
T* target = (T*)voidp.ptr; \
return vm->new_object<VoidP>(lhs.type, target + offset); \
}); \
vm->bind__sub__(type_t, [](VM* vm, PyVar lhs, PyVar rhs) { \
obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, lhs); \
i64 offset = CAST(i64, rhs); \
T* target = (T*)voidp.ptr; \
return vm->new_object<VoidP>(lhs.type, target - offset); \
}); \
vm->bind__repr__(type_t, [](VM* vm, PyVar obj) -> Str { \
VoidP& self = _CAST(VoidP&, obj); \
return _S("<", CNAME, "* at ", self.hex(), ">"); \
});
BIND_PRIMITIVE(char, "char")
BIND_PRIMITIVE(unsigned char, "uchar")
BIND_PRIMITIVE(short, "short")
BIND_PRIMITIVE(unsigned short, "ushort")
BIND_PRIMITIVE(int, "int")
BIND_PRIMITIVE(unsigned int, "uint")
BIND_PRIMITIVE(long, "long")
BIND_PRIMITIVE(unsigned long, "ulong")
BIND_PRIMITIVE(long long, "longlong")
BIND_PRIMITIVE(unsigned long long, "ulonglong")
BIND_PRIMITIVE(float, "float")
BIND_PRIMITIVE(double, "double")
BIND_PRIMITIVE(bool, "bool")
#undef BIND_PRIMITIVE
PyObject* char_p_t = mod->attr()["char_p"].get();
vm->bind(char_p_t, "read_string(self) -> str", [](VM* vm, ArgsView args) {
obj_get_t<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) {
obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]);
std::string_view sv = CAST(Str&, args[1]).sv();
char* target = (char*)voidp.ptr;
std::memcpy(target, sv.data(), sv.size());
target[sv.size()] = '\0';
return vm->None;
});
}
PyVar from_void_p(VM* vm, void* p) { return vm->new_user_object<VoidP>(p); }
} // namespace pkpy

133
src/interpreter/frame.c Normal file
View File

@ -0,0 +1,133 @@
#include "pocketpy/interpreter/frame.h"
#include "pocketpy/objects/object.h"
void ValueStack__ctor(ValueStack* self) {
self->sp = self->begin;
self->end = self->begin + PK_VM_STACK_SIZE;
}
void ValueStack__clear(ValueStack* self) {
self->sp = self->begin;
}
PyVar* FastLocals__try_get_by_name(PyVar* locals, const CodeObject* co, StrName name){
int index = c11_smallmap_n2i__get(&co->varnames_inv, name, -1);
if(index == -1) return NULL;
return &locals[index];
}
pk_NameDict* FastLocals__to_namedict(PyVar* locals, const CodeObject* co) {
pk_NameDict* dict = pk_NameDict__new();
c11_vector__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) {
PyVar value = locals[entry->value];
if(!py_isnull(&value)){
pk_NameDict__set(dict, entry->key, value);
}
}
return dict;
}
UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset){
UnwindTarget* self = malloc(sizeof(UnwindTarget));
self->next = next;
self->iblock = iblock;
self->offset = offset;
return self;
}
void UnwindTarget__delete(UnwindTarget* self){
free(self);
}
Frame* Frame__new(Frame* f_back, const CodeObject* co, PyObject* module_, PyObject* function, PyVar* p0, PyVar* locals, const CodeObject* locals_co){
static_assert(sizeof(Frame) <= kPoolFrameBlockSize);
Frame* self = PoolFrame_alloc();
self->f_back = f_back;
self->ip = (Bytecode*)co->codes.data - 1;
self->co = co;
self->module_ = module_;
self->function = function;
self->p0 = p0;
self->locals = locals;
self->locals_co = locals_co;
self->uw_list = NULL;
return self;
}
void Frame__delete(Frame* self){
while(self->uw_list) {
UnwindTarget* p = self->uw_list;
self->uw_list = p->next;
UnwindTarget__delete(p);
}
PoolFrame_dealloc(self);
}
int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s){
// try to find a parent try block
int iblock = Frame__iblock(self);
while(iblock >= 0) {
CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, iblock);
if(block->type == CodeBlockType_TRY_EXCEPT) break;
iblock = block->parent;
}
if(iblock < 0) return -1;
PyVar obj = *--_s->sp; // pop exception object
UnwindTarget* uw = Frame__find_unwind_target(self, iblock);
_s->sp = (self->locals + uw->offset); // unwind the stack
*(_s->sp++) = obj; // push it back
return c11__at(CodeBlock, &self->co->blocks, iblock)->end;
}
void Frame__prepare_jump_break(Frame* self, ValueStack* _s, int target){
int iblock = Frame__iblock(self);
if(target >= self->co->codes.count) {
while(iblock >= 0) iblock = Frame__exit_block(self, _s, iblock);
} else {
// BUG (solved)
// for i in range(4):
// _ = 0
// # if there is no op here, the block check will fail
// while i: --i
int next_block = c11__at(BytecodeEx, &self->co->codes_ex, target)->iblock;
while(iblock >= 0 && iblock != next_block)
iblock = Frame__exit_block(self, _s, iblock);
assert(iblock == next_block);
}
}
int Frame__prepare_loop_break(Frame* self, ValueStack* _s){
int iblock = Frame__iblock(self);
int target = c11__getitem(CodeBlock, &self->co->blocks, iblock).end;
Frame__prepare_jump_break(self, _s, target);
return target;
}
int Frame__exit_block(Frame* self, ValueStack* _s, int iblock){
CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, iblock);
if(block->type == CodeBlockType_FOR_LOOP) {
_s->sp--; // pop iterator
} else if(block->type == CodeBlockType_CONTEXT_MANAGER) {
_s->sp--; // pop context variable
}
return block->parent;
}
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock){
UnwindTarget* uw;
for(uw = self->uw_list; uw; uw = uw->next) {
if(uw->iblock == iblock) return uw;
}
return NULL;
}
void Frame__set_unwind_target(Frame* self, PyVar* sp) {
int iblock = Frame__iblock(self);
UnwindTarget* existing = Frame__find_unwind_target(self, iblock);
if(existing) {
existing->offset = sp - self->locals;
} else {
UnwindTarget* prev = self->uw_list;
self->uw_list = UnwindTarget__new(prev, iblock, sp - self->locals);
}
}

View File

@ -1,122 +0,0 @@
#include "pocketpy/interpreter/frame.hpp"
#include "pocketpy/common/smallmap.h"
namespace pkpy {
PyVar* FastLocals::try_get_name(StrName name) {
int index = c11_smallmap_n2i__get(&co->varnames_inv, name.index, -1);
if(index == -1) return nullptr;
return &a[index];
}
NameDict* FastLocals::to_namedict() {
NameDict* dict = new NameDict();
for(int i=0; i<co->varnames_inv.count; i++){
auto entry = c11__getitem(c11_smallmap_n2i_KV, &co->varnames_inv, i);
PyVar value = a[entry.value];
if(value) dict->set(StrName(entry.key), value);
}
return dict;
}
PyVar* Frame::f_closure_try_get(StrName name) {
if(_callable == nullptr) return nullptr;
Function& fn = _callable->as<Function>();
if(fn._closure == nullptr) return nullptr;
return fn._closure->try_get_2(name);
}
int Frame::prepare_jump_exception_handler(ValueStack* _s) {
// try to find a parent try block
int i = c11__at(BytecodeEx, &co->codes_ex, ip())->iblock;
while(i >= 0) {
CodeBlock* block = c11__at(CodeBlock, &co->blocks, i);
if(block->type == CodeBlockType_TRY_EXCEPT) break;
i = block->parent;
}
if(i < 0) return -1;
PyVar obj = _s->popx(); // pop exception object
UnwindTarget* uw = find_unwind_target(i);
_s->reset(actual_sp_base() + uw->offset); // unwind the stack
_s->push(obj); // push it back
return c11__at(CodeBlock, &co->blocks, i)->end;
}
int Frame::_exit_block(ValueStack* _s, int i) {
CodeBlock* block = c11__at(CodeBlock, &co->blocks, i);
if(block->type == CodeBlockType_FOR_LOOP) {
_s->pop(); // pop the iterator
} else if(block->type == CodeBlockType_CONTEXT_MANAGER) {
_s->pop();
}
return block->parent;
}
void Frame::prepare_jump_break(ValueStack* _s, int target) {
int i = c11__at(BytecodeEx, &co->codes_ex, ip())->iblock;
if(target >= co->codes.count) {
while(i >= 0) i = _exit_block(_s, i);
} else {
// BUG (solved)
// for i in range(4):
// _ = 0
// # if there is no op here, the block check will fail
// while i: --i
int next_block = c11__at(BytecodeEx, &co->codes_ex, target)->iblock;
while(i >= 0 && i != next_block)
i = _exit_block(_s, i);
assert(i == next_block);
}
}
void Frame::set_unwind_target(PyVar* _sp) {
int iblock = c11__at(BytecodeEx, &co->codes_ex, ip())->iblock;
UnwindTarget* existing = find_unwind_target(iblock);
if(existing) {
existing->offset = _sp - actual_sp_base();
} else {
UnwindTarget* prev = _uw_list;
_uw_list = new UnwindTarget(iblock, _sp - actual_sp_base());
_uw_list->next = prev;
}
}
UnwindTarget* Frame::find_unwind_target(int iblock) {
UnwindTarget* p;
for(p = _uw_list; p != nullptr; p = p->next) {
if(p->iblock == iblock) return p;
}
return nullptr;
}
Frame::~Frame() {
while(_uw_list != nullptr) {
UnwindTarget* p = _uw_list;
_uw_list = p->next;
delete p;
}
}
void CallStack::pop() {
assert(!empty());
LinkedFrame* p = _tail;
_tail = p->f_back;
p->~LinkedFrame();
PoolFrame_dealloc(p);
--_size;
}
LinkedFrame* CallStack::popx() {
assert(!empty());
LinkedFrame* p = _tail;
_tail = p->f_back;
--_size;
p->f_back = nullptr; // unlink
return p;
}
void CallStack::pushx(LinkedFrame* p) {
p->f_back = _tail;
_tail = p;
++_size;
}
} // namespace pkpy

View File

@ -1,7 +1,7 @@
#include "pocketpy/interpreter/gc.h" #include "pocketpy/interpreter/gc.h"
#include "pocketpy/common/memorypool.h" #include "pocketpy/common/memorypool.h"
void pk_ManagedHeap__ctor(pk_ManagedHeap *self, pkpy_VM *vm){ void pk_ManagedHeap__ctor(pk_ManagedHeap *self, pk_VM *vm){
c11_vector__ctor(&self->no_gc, sizeof(PyObject*)); c11_vector__ctor(&self->no_gc, sizeof(PyObject*));
c11_vector__ctor(&self->gen, sizeof(PyObject*)); c11_vector__ctor(&self->gen, sizeof(PyObject*));
@ -88,9 +88,8 @@ int pk_ManagedHeap__sweep(pk_ManagedHeap *self){
return freed; return freed;
} }
PyObject* pk_ManagedHeap__new(pk_ManagedHeap *self, pkpy_Type type, int size, bool gc){ PyObject* pk_ManagedHeap__new(pk_ManagedHeap *self, Type type, int size){
PyObject* obj; PyObject* obj;
// TODO: can we use compile time check?
if(size <= kPoolObjectBlockSize){ if(size <= kPoolObjectBlockSize){
obj = PoolObject_alloc(); obj = PoolObject_alloc();
PyObject__ctor(obj, type, false); PyObject__ctor(obj, type, false);
@ -98,12 +97,21 @@ PyObject* pk_ManagedHeap__new(pk_ManagedHeap *self, pkpy_Type type, int size, bo
obj = malloc(size); obj = malloc(size);
PyObject__ctor(obj, type, true); PyObject__ctor(obj, type, true);
} }
// TODO: can we use compile time check? c11_vector__push(PyObject*, &self->no_gc, obj);
if(gc){
c11_vector__push(PyObject*, &self->gen, obj);
self->gc_counter++;
}else{
c11_vector__push(PyObject*, &self->no_gc, obj);
}
return obj; return obj;
} }
PyObject* pk_ManagedHeap__gcnew(pk_ManagedHeap *self, Type type, int size){
PyObject* obj;
if(size <= kPoolObjectBlockSize){
obj = PoolObject_alloc();
PyObject__ctor(obj, type, false);
}else{
obj = malloc(size);
PyObject__ctor(obj, type, true);
}
c11_vector__push(PyObject*, &self->gen, obj);
self->gc_counter++;
return obj;
}

View File

@ -1,135 +0,0 @@
#include "pocketpy/interpreter/iter.hpp"
#include "pocketpy/objects/base.h"
namespace pkpy {
void RangeIter::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
return _0;
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
RangeIter& self = PK_OBJ_GET(RangeIter, _0);
if(self.current >= self.r.stop) return 0;
vm->s_data.emplace(VM::tp_int, self.current);
self.current += self.r.step;
return 1;
});
}
void RangeIterR::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
return _0;
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
RangeIterR& self = PK_OBJ_GET(RangeIterR, _0);
if(self.current <= self.r.stop) return 0;
vm->s_data.emplace(VM::tp_int, self.current);
self.current += self.r.step;
return 1;
});
}
void ArrayIter::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
return _0;
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
ArrayIter& self = _CAST(ArrayIter&, _0);
if(self.current == self.end) return 0;
vm->s_data.push(*self.current++);
return 1;
});
}
void StringIter::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
return _0;
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
StringIter& self = _CAST(StringIter&, _0);
Str& s = PK_OBJ_GET(Str, self.ref);
if(self.i == s.size) return 0;
int start = self.i;
int len = c11__u8_header(s[self.i], false);
self.i += len;
vm->s_data.push(VAR(s.slice(start, self.i)));
return 1;
});
}
PyVar Generator::next(VM* vm) {
if(state == 2) return vm->StopIteration;
// reset frame._sp_base
lf->frame._sp_base = vm->s_data._sp;
lf->frame._locals.a = vm->s_data._sp;
// restore the context
for(PyVar obj: s_backup)
vm->s_data.push(obj);
s_backup.clear();
vm->callstack.pushx(lf);
lf = nullptr;
PyVar ret;
try {
ret = vm->__run_top_frame();
} catch(...) {
state = 2; // end this generator immediately when an exception is thrown
throw;
}
if(ret.type == tp_op_yield) {
// backup the context
lf = vm->callstack.popx();
ret = vm->s_data.popx();
for(PyVar obj: lf->frame.stack_view(&vm->s_data))
s_backup.push_back(obj);
vm->s_data.reset(lf->frame._sp_base);
// TODO: should we add this snippet here?
// #if PK_ENABLE_PROFILER
// if(!_next_breakpoint.empty() && callstack.size()<_next_breakpoint.callstack_size){
// _next_breakpoint = NextBreakpoint();
// }
// #endif
state = 1;
if(ret == vm->StopIteration) state = 2;
return ret;
} else {
state = 2;
return vm->StopIteration;
}
}
void Generator::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
return _0;
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
Generator& self = _CAST(Generator&, _0);
PyVar retval = self.next(vm);
if(retval == vm->StopIteration) return 0;
vm->s_data.push(retval);
return 1;
});
}
void DictItemsIter::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
return _0;
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
DictItemsIter& self = _CAST(DictItemsIter&, _0);
PyVar key, val;
if (pkpy_DictIter__next(&self.it, (PyVar*)(&key), (PyVar*)(&val))) {
vm->s_data.push(key);
vm->s_data.push(val);
return 2;
}
return 0;
});
}
PyVar VM::__py_generator(LinkedFrame* frame, ArgsView buffer) {
return vm->new_user_object<Generator>(std::move(frame), buffer);
}
} // namespace pkpy

View File

@ -1,131 +0,0 @@
#include "pocketpy/interpreter/profiler.hpp"
#if PK_ENABLE_PROFILER
namespace pkpy {
static std::string left_pad(std::string s, int width) {
int n = width - s.size();
if(n <= 0) return s;
return std::string(n, ' ') + s;
}
static std::string to_string_1f(f64 x) {
char buf[32];
snprintf(buf, 32, "%.1f", x);
return buf;
}
void LineProfiler::begin() { frames.clear(); }
void LineProfiler::_step(int callstack_size, Frame* frame) {
auto line_info = frame->co->lines[frame->ip()];
if(line_info.is_virtual) return;
std::string_view filename = frame->co->src.filename().sv();
int line = line_info.lineno;
if(frames.empty()) {
frames.push_back({callstack_size, frame, clock(), nullptr});
} else {
_step_end(callstack_size, frame, line);
}
_LineRecord* file_records;
auto p = records.try_get(filename);
if(p == nullptr) {
int total_lines = frame->co->src->line_starts.size();
file_records = new _LineRecord[total_lines + 1];
for(int i = 1; i <= total_lines; i++) file_records[i].line = i;
records.insert(filename, file_records);
}else{
file_records = *p;
}
frames.back().prev_record = &file_records[line];
}
void LineProfiler::_step_end(int callstack_size, Frame* frame, int line) {
clock_t now = clock();
_FrameRecord& top_frame_record = frames.back();
_LineRecord* prev_record = top_frame_record.prev_record;
int id_delta = callstack_size - top_frame_record.callstack_size;
assert(abs(id_delta) <= 1);
// current line is about to change
if(prev_record->line != line) {
clock_t delta = now - top_frame_record.prev_time;
top_frame_record.prev_time = now;
if(id_delta != 1) prev_record->hits++;
prev_record->time += delta;
}
if(id_delta == 1) {
frames.push_back({callstack_size, frame, now, nullptr});
} else {
if(id_delta == -1) frames.pop_back();
}
}
void LineProfiler::end() {
clock_t now = clock();
_FrameRecord& top_frame_record = frames.back();
_LineRecord* prev_record = top_frame_record.prev_record;
clock_t delta = now - top_frame_record.prev_time;
top_frame_record.prev_time = now;
prev_record->hits++;
prev_record->time += delta;
frames.pop_back();
assert(frames.empty());
}
Str LineProfiler::stats() {
SStream ss;
for(FuncDecl* decl: functions) {
int start_line = decl->code->start_line;
int end_line = decl->code->end_line;
if(start_line == -1 || end_line == -1) continue;
std::string_view filename = decl->code->src.filename().sv();
const _LineRecord* file_records = records[filename];
clock_t total_time = 0;
for(int line = start_line; line <= end_line; line++) {
total_time += file_records[line].time;
}
ss << "Total time: " << (f64)total_time / CLOCKS_PER_SEC << "s\n";
ss << "File: " << filename << "\n";
ss << "Function: " << decl->code->name << " at line " << start_line << "\n";
ss << "Line # Hits Time Per Hit % Time Line Contents\n";
ss << "==============================================================\n";
for(int line = start_line; line <= end_line; line++) {
const _LineRecord& record = file_records[line];
if(!record.is_valid()) continue;
ss << left_pad(std::to_string(line), 6);
if(record.hits == 0) {
ss << std::string(10 + 13 + 9 + 9, ' ');
} else {
ss << left_pad(std::to_string(record.hits), 10);
ss << left_pad(std::to_string(record.time), 13);
ss << left_pad(std::to_string(record.time / record.hits), 9);
if(total_time == 0) {
ss << left_pad("0.0", 9);
} else {
ss << left_pad(to_string_1f(record.time * (f64)100 / total_time), 9);
}
}
// line_content
ss << " " << decl->code->src->get_line(line) << "\n";
}
ss << "\n";
}
return ss.str();
}
LineProfiler::~LineProfiler() {
for(auto& p: records) delete p.second;
}
} // namespace pkpy
#endif

View File

@ -1,40 +1,161 @@
// #include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
// #include "pocketpy/objects/base.h"
// void pkpy_VM__ctor(pkpy_VM* self){ static unsigned char* pk_default_import_file(pk_VM* vm, const char* path){
// self->True = (PyVar){ return NULL;
// .type=tp_bool, }
// .is_ptr=true,
// .extra=1,
// ._obj=pkpy_VM__gcnew(self, tp_bool)
// };
// self->False = (PyVar){ static void pk_default_stdout(pk_VM* vm, const char* s){
// .type=tp_bool, fprintf(stdout, "%s", s);
// .is_ptr=true, }
// .extra=0,
// ._obj=pkpy_VM__gcnew(self, tp_bool)
// };
// self->None = (PyVar){ static void pk_default_stderr(pk_VM* vm, const char* s){
// .type=tp_none_type, fprintf(stderr, "%s", s);
// .is_ptr=true, }
// ._obj=pkpy_VM__gcnew(self, tp_none_type)
// };
// self->NotImplemented = (PyVar){ void pk_TypeInfo__ctor(pk_TypeInfo *self, StrName name, Type base, PyObject* obj, PyObject* module, bool subclass_enabled){
// .type=tp_not_implemented_type, memset(self, 0, sizeof(pk_TypeInfo));
// .is_ptr=true,
// ._obj=pkpy_VM__gcnew(self, tp_not_implemented_type) self->name = name;
// }; self->base = base;
// self->Ellipsis = (PyVar){ self->obj = obj;
// .type=tp_ellipsis, self->module = module;
// .is_ptr=true, self->subclass_enabled = subclass_enabled;
// ._obj=pkpy_VM__gcnew(self, tp_ellipsis)
// };
// }
// void pkpy_VM__dtor(pkpy_VM* self){ c11_vector__ctor(&self->annotated_fields, sizeof(StrName));
}
// } void pk_TypeInfo__dtor(pk_TypeInfo *self){
c11_vector__dtor(&self->annotated_fields);
}
void pk_VM__ctor(pk_VM* self){
self->top_frame = NULL;
pk_NameDict__ctor(&self->modules);
c11_vector__ctor(&self->types, sizeof(pk_TypeInfo));
self->StopIteration = NULL;
self->builtins = NULL;
self->main = NULL;
self->_ceval_on_step = NULL;
self->_import_file = pk_default_import_file;
self->_stdout = pk_default_stdout;
self->_stderr = pk_default_stderr;
self->__last_exception = NULL;
self->__curr_class = NULL;
self->__cached_object_new = NULL;
self->__dynamic_func_decl = NULL;
pk_ManagedHeap__ctor(&self->heap, self);
ValueStack__ctor(&self->stack);
self->True = (PyVar){.type=tp_bool, .is_ptr=true, .extra=1,
._obj=pk_ManagedHeap__gcnew(&self->heap, tp_bool, 1),
};
self->False = (PyVar){.type=tp_bool, .is_ptr=true, .extra=0,
._obj=pk_ManagedHeap__gcnew(&self->heap, tp_bool, 1),
};
self->None = (PyVar){.type=tp_none_type, .is_ptr=true,
._obj=pk_ManagedHeap__gcnew(&self->heap, tp_none_type, 1),
};
self->NotImplemented = (PyVar){.type=tp_not_implemented_type, .is_ptr=true,
._obj=pk_ManagedHeap__gcnew(&self->heap, tp_not_implemented_type, 1),
};
self->Ellipsis = (PyVar){.type=tp_ellipsis, .is_ptr=true,
._obj=pk_ManagedHeap__gcnew(&self->heap, tp_ellipsis, 1),
};
/* Init Builtin Types */
// 0: unused
pk_TypeInfo__ctor(c11_vector__emplace(&self->types), 0, 0, NULL, NULL);
#define validate(t, expr) if(t != (expr)) abort()
validate(tp_object, pk_VM__new_type(self, "object", 0, NULL, true));
validate(tp_type, pk_VM__new_type(self, "type", 1, NULL, false));
validate(tp_int, pk_VM__new_type(self, "int", tp_object, NULL, false));
validate(tp_float, pk_VM__new_type(self, "float", tp_object, NULL, false));
validate(tp_bool, pk_VM__new_type(self, "bool", tp_object, NULL, false));
validate(tp_str, pk_VM__new_type(self, "str", tp_object, NULL, false));
validate(tp_list, pk_VM__new_type(self, "list", tp_object, NULL, false));
validate(tp_tuple, pk_VM__new_type(self, "tuple", tp_object, NULL, false));
validate(tp_slice, pk_VM__new_type(self, "slice", tp_object, NULL, false));
validate(tp_range, pk_VM__new_type(self, "range", tp_object, NULL, false));
validate(tp_module, pk_VM__new_type(self, "module", tp_object, NULL, false));
validate(tp_function, pk_VM__new_type(self, "function", tp_object, NULL, false));
validate(tp_native_func, pk_VM__new_type(self, "native_func", tp_object, NULL, false));
validate(tp_bound_method, pk_VM__new_type(self, "bound_method", tp_object, NULL, false));
validate(tp_super, pk_VM__new_type(self, "super", tp_object, NULL, false));
validate(tp_exception, pk_VM__new_type(self, "Exception", tp_object, NULL, true));
validate(tp_bytes, pk_VM__new_type(self, "bytes", tp_object, NULL, false));
validate(tp_mappingproxy, pk_VM__new_type(self, "mappingproxy", tp_object, NULL, false));
validate(tp_dict, pk_VM__new_type(self, "dict", tp_object, NULL, true));
validate(tp_property, pk_VM__new_type(self, "property", tp_object, NULL, false));
validate(tp_star_wrapper, pk_VM__new_type(self, "star_wrapper", tp_object, NULL, false));
validate(tp_staticmethod, pk_VM__new_type(self, "staticmethod", tp_object, NULL, false));
validate(tp_classmethod, pk_VM__new_type(self, "classmethod", tp_object, NULL, false));
validate(tp_none_type, pk_VM__new_type(self, "NoneType", tp_object, NULL, false));
validate(tp_not_implemented_type, pk_VM__new_type(self, "NotImplementedType", tp_object, NULL, false));
validate(tp_ellipsis, pk_VM__new_type(self, "ellipsis", tp_object, NULL, false));
validate(tp_op_call, pk_VM__new_type(self, "__op_call", tp_object, NULL, false));
validate(tp_op_yield, pk_VM__new_type(self, "__op_yield", tp_object, NULL, false));
validate(tp_syntax_error, pk_VM__new_type(self, "SyntaxError", tp_exception, NULL, false));
validate(tp_stop_iteration, pk_VM__new_type(self, "StopIteration", tp_exception, NULL, false));
#undef validate
self->StopIteration = c11__at(pk_TypeInfo, &self->types, tp_stop_iteration)->obj;
self->builtins = pk_VM__new_module(self, "builtins", NULL);
/* Setup Public Builtin Types */
Type public_types[] = {
tp_object, tp_type,
tp_int, tp_float, tp_bool, tp_str,
tp_list, tp_tuple,
tp_slice, tp_range,
tp_bytes, tp_dict, tp_property,
tp_exception, tp_stop_iteration, tp_syntax_error
};
for(int i=0; i<PK_ARRAY_COUNT(public_types); i++){
Type t = public_types[i];
pk_TypeInfo* ti = c11__at(pk_TypeInfo, &self->types, t);
pk_NameDict__set(self->builtins->dict, ti->name, PyVar__fromobj(ti->obj));
}
pk_NameDict__set(self->builtins->dict, pk_StrName__map("NotImplemented"), self->NotImplemented);
/* Do Buildin Bindings*/
// TODO: ...
self->main = pk_VM__new_module(self, "__main__", NULL);
}
void pk_VM__dtor(pk_VM* self){
PK_DECREF(self->__dynamic_func_decl);
// destroy all objects
pk_ManagedHeap__dtor(&self->heap);
// clear frames
// ...
pk_NameDict__dtor(&self->modules);
c11_vector__dtor(&self->types);
ValueStack__clear(&self->stack);
}
Type pk_VM__new_type(pk_VM* self, const char* name, Type base, PyObject* module, bool subclass_enabled){
Type type = self->types.count;
pk_TypeInfo* ti = c11_vector__emplace(&self->types);
PyObject* typeobj = pk_ManagedHeap__gcnew(&self->heap, tp_type, PK_OBJ_SIZEOF(Type));
*PyObject__as(Type, typeobj) = type;
pk_TypeInfo__ctor(ti, pk_StrName__map(name), base, typeobj, module, subclass_enabled);
return type;
}

View File

@ -1,5 +1,6 @@
#include "pocketpy/interpreter/vm.hpp" #include "pocketpy/interpreter/vm.hpp"
#include "pocketpy/common/memorypool.h" #include "pocketpy/common/memorypool.h"
#include "pocketpy/interpreter/frame.h"
#include "pocketpy/objects/base.h" #include "pocketpy/objects/base.h"
#include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/public.h" #include "pocketpy/objects/public.h"
@ -86,10 +87,11 @@ struct JsonSerializer {
}; };
VM::VM(bool enable_os) : enable_os(enable_os) { VM::VM(bool enable_os) : enable_os(enable_os) {
pkpy_g.vm = (pkpy_VM*)this; // setup the current VM pkpy_g.vm = (pk_VM*)this; // setup the current VM
Pools_initialize(); Pools_initialize();
pkpy_StrName__initialize(); pk_StrName__initialize();
pk_ManagedHeap__ctor(&heap, (pkpy_VM*)this);
pk_ManagedHeap__ctor(&heap, (pk_VM*)this);
static ::PyObject __true_obj = {tp_bool, false, false, NULL}; static ::PyObject __true_obj = {tp_bool, false, false, NULL};
static ::PyObject __false_obj = {tp_bool, false, false, NULL}; static ::PyObject __false_obj = {tp_bool, false, false, NULL};
@ -127,7 +129,7 @@ Str VM::py_str(PyVar obj) {
if(ti->m__str__) return ti->m__str__(this, obj); if(ti->m__str__) return ti->m__str__(this, obj);
PyVar self; PyVar self;
PyVar f = get_unbound_method(obj, __str__, &self, false); PyVar f = get_unbound_method(obj, __str__, &self, false);
if(self) { if(self.type) {
PyVar retval = call_method(self, f); PyVar retval = call_method(self, f);
if(!is_type(retval, tp_str)) { throw std::runtime_error("object.__str__ must return str"); } if(!is_type(retval, tp_str)) { throw std::runtime_error("object.__str__ must return str"); }
return PK_OBJ_GET(Str, retval); return PK_OBJ_GET(Str, retval);
@ -153,7 +155,7 @@ PyVar VM::py_iter(PyVar obj) {
if(ti->m__iter__) return ti->m__iter__(this, obj); if(ti->m__iter__) return ti->m__iter__(this, obj);
PyVar self; PyVar self;
PyVar iter_f = get_unbound_method(obj, __iter__, &self, false); PyVar iter_f = get_unbound_method(obj, __iter__, &self, false);
if(self) return call_method(self, iter_f); if(self.type) return call_method(self, iter_f);
TypeError(_type_name(vm, _tp(obj)).escape() + " object is not iterable"); TypeError(_type_name(vm, _tp(obj)).escape() + " object is not iterable");
return nullptr; return nullptr;
} }
@ -223,8 +225,8 @@ PyVar VM::exec(std::string_view source, Str filename, CompileMode mode, PyObject
stderr_write(msg); stderr_write(msg);
} }
CodeObject__delete(code); CodeObject__delete(code);
callstack.clear(); while(top_frame) __pop_frame(); // this changes s_data.sp, it must put before ValueStack__clear();
s_data.clear(); ValueStack__clear(&s_data);
return nullptr; return nullptr;
} }
@ -320,7 +322,7 @@ PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key) {
if(args_tuple.size() == 0) TypeError("expected at least 1 argument, got 0"); if(args_tuple.size() == 0) TypeError("expected at least 1 argument, got 0");
ArgsView view(nullptr, nullptr); ArgsView view;
if(args_tuple.size() == 1) { if(args_tuple.size() == 1) {
view = cast_array_view(args_tuple[0]); view = cast_array_view(args_tuple[0]);
} else { } else {
@ -428,11 +430,11 @@ VM::~VM() {
PK_DECREF(__dynamic_func_decl); PK_DECREF(__dynamic_func_decl);
// destroy all objects // destroy all objects
pk_ManagedHeap__dtor(&heap); pk_ManagedHeap__dtor(&heap);
pk_NameDict__dtor(&_modules);
// clear everything // clear everything
callstack.clear(); while(top_frame) __pop_frame(); // this changes s_data.sp, it must put before ValueStack__clear();
s_data.clear(); ValueStack__clear(&s_data);
_all_types.clear(); _all_types.clear();
_modules.clear();
_lazy_modules.clear(); _lazy_modules.clear();
} }
@ -608,7 +610,7 @@ PyVar VM::__py_exec_internal(const CodeObject* code, PyVar globals, PyVar locals
}); });
PyObject* _callable = PyObject* _callable =
new_object<Function>(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure).get(); new_object<Function>(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure).get();
retval = vm->_exec(code, globals_obj, _callable, vm->s_data._sp); retval = vm->_exec(code, globals_obj, _callable, vm->s_data.sp);
} }
if(globals_dict) { if(globals_dict) {
@ -1063,7 +1065,7 @@ void VM::__prepare_py_call(PyVar* buffer, ArgsView args, ArgsView kwargs, const
} }
PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) { PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
PyVar* p1 = s_data._sp - KWARGC * 2; PyVar* p1 = (PyVar*)(s_data.sp - KWARGC * 2);
PyVar* p0 = p1 - ARGC - 2; PyVar* p0 = p1 - ARGC - 2;
// [callable, <self>, args..., kwargs...] // [callable, <self>, args..., kwargs...]
// ^p0 ^p1 ^_sp // ^p0 ^p1 ^_sp
@ -1082,14 +1084,14 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
} }
ArgsView args((!p0[1]) ? (p0 + 2) : (p0 + 1), p1); ArgsView args((!p0[1]) ? (p0 + 2) : (p0 + 1), p1);
ArgsView kwargs(p1, s_data._sp); ArgsView kwargs(p1, s_data.sp);
PyVar* _base = args.begin(); PyVar* _base = args.begin();
if(callable_t == tp_function) { if(callable_t == tp_function) {
/*****************_py_call*****************/ /*****************_py_call*****************/
// check stack overflow // check stack overflow
if(s_data.is_overflow()) StackOverflowError(); if(s_data.sp > s_data.end) StackOverflowError();
const Function& fn = PK_OBJ_GET(Function, callable); const Function& fn = PK_OBJ_GET(Function, callable);
const CodeObject* co = fn.decl->code; const CodeObject* co = fn.decl->code;
@ -1098,7 +1100,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
case FuncType_NORMAL: case FuncType_NORMAL:
__prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl); __prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl);
// copy buffer back to stack // copy buffer back to stack
s_data.reset(_base + co->nlocals); s_data.sp = (_base + co->nlocals);
for(int j = 0; j < co->nlocals; j++) for(int j = 0; j < co->nlocals; j++)
_base[j] = __vectorcall_buffer[j]; _base[j] = __vectorcall_buffer[j];
break; break;
@ -1114,9 +1116,9 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
} }
// [callable, <self>, args..., local_vars...] // [callable, <self>, args..., local_vars...]
// ^p0 ^p1 ^_sp // ^p0 ^p1 ^_sp
s_data.reset(_base + co->nlocals); s_data.sp = (_base + co->nlocals);
// initialize local variables to PY_NULL // initialize local variables to PY_NULL
std::memset(p1, 0, (char*)s_data._sp - (char*)p1); std::memset(p1, 0, (char*)s_data.sp - (char*)p1);
break; break;
case FuncType_EMPTY: case FuncType_EMPTY:
if(args.size() != fn.decl->args.count){ if(args.size() != fn.decl->args.count){
@ -1128,11 +1130,11 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
if(!kwargs.empty()){ if(!kwargs.empty()){
TypeError(pk_format("{} takes no keyword arguments", &co->name)); TypeError(pk_format("{} takes no keyword arguments", &co->name));
} }
s_data.reset(p0); s_data.sp = p0;
return None; return None;
case FuncType_GENERATOR: case FuncType_GENERATOR:
__prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl); __prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl);
s_data.reset(p0); s_data.sp = p0;
callstack.emplace(nullptr, co, fn._module, callable.get(), nullptr); callstack.emplace(nullptr, co, fn._module, callable.get(), nullptr);
return __py_generator(callstack.popx(), return __py_generator(callstack.popx(),
ArgsView(__vectorcall_buffer, __vectorcall_buffer + co->nlocals)); ArgsView(__vectorcall_buffer, __vectorcall_buffer + co->nlocals));
@ -1141,7 +1143,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
// simple or normal // simple or normal
callstack.emplace(p0, co, fn._module, callable.get(), args.begin()); callstack.emplace(p0, co, fn._module, callable.get(), args.begin());
if(op_call) return pkpy_OP_CALL; if(op_call) return PY_OP_CALL;
return __run_top_frame(); return __run_top_frame();
/*****************_py_call*****************/ /*****************_py_call*****************/
} }
@ -1153,10 +1155,10 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
int co_nlocals = f.decl->code->nlocals; int co_nlocals = f.decl->code->nlocals;
__prepare_py_call(__vectorcall_buffer, args, kwargs, f.decl); __prepare_py_call(__vectorcall_buffer, args, kwargs, f.decl);
// copy buffer back to stack // copy buffer back to stack
s_data.reset(_base + co_nlocals); s_data.sp = (_base + co_nlocals);
for(int j = 0; j < co_nlocals; j++) for(int j = 0; j < co_nlocals; j++)
_base[j] = __vectorcall_buffer[j]; _base[j] = __vectorcall_buffer[j];
ret = f.call(vm, ArgsView(s_data._sp - co_nlocals, s_data._sp)); ret = f.call(vm, ArgsView(s_data.sp - co_nlocals, s_data.sp));
} else { } else {
if(f.argc != -1) { if(f.argc != -1) {
if(KWARGC != 0) if(KWARGC != 0)
@ -1166,7 +1168,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
} }
ret = f.call(this, args); ret = f.call(this, args);
} }
s_data.reset(p0); s_data.sp = (p0);
return ret; return ret;
} }
@ -1180,7 +1182,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
obj = vm->new_object<DummyInstance>(PK_OBJ_GET(Type, callable)); obj = vm->new_object<DummyInstance>(PK_OBJ_GET(Type, callable));
} else { } else {
PUSH(new_f); PUSH(new_f);
PUSH(PY_NULL); PUSH_NULL();
PUSH(callable); // cls PUSH(callable); // cls
for(PyVar o: args) for(PyVar o: args)
PUSH(o); PUSH(o);
@ -1204,7 +1206,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
// in cpython it raises a TypeError if the return value is not None // in cpython it raises a TypeError if the return value is not None
} else { } else {
// manually reset the stack // manually reset the stack
s_data.reset(p0); s_data.sp = (p0);
} }
return obj; return obj;
} }
@ -1474,7 +1476,7 @@ void VM::_error(PyVar e_obj) {
void VM::__raise_exc(bool re_raise) { void VM::__raise_exc(bool re_raise) {
Frame* frame = &callstack.top(); Frame* frame = &callstack.top();
Exception& e = PK_OBJ_GET(Exception, s_data.top()); Exception& e = PK_OBJ_GET(Exception, (PyVar&)s_data.sp[-1]);
if(!re_raise) { if(!re_raise) {
e._ip_on_error = frame->ip(); e._ip_on_error = frame->ip();
e._code_on_error = (void*)frame->co; e._code_on_error = (void*)frame->co;
@ -1537,9 +1539,10 @@ void VM::bind__delitem__(Type type, void (*f)(VM*, PyVar, PyVar)) {
PyVar VM::__pack_next_retval(unsigned n) { PyVar VM::__pack_next_retval(unsigned n) {
if(n == 0) return StopIteration; if(n == 0) return StopIteration;
if(n == 1) return s_data.popx(); if(n == 1) return *--s_data.sp;
PyVar retval = VAR(s_data.view(n).to_tuple()); ArgsView view(s_data.sp - n, s_data.sp);
s_data._sp -= n; PyVar retval = VAR(view.to_tuple());
s_data.sp -= n;
return retval; return retval;
} }
@ -1692,8 +1695,12 @@ void NextBreakpoint::_step(VM* vm) {
#endif #endif
void VM::__pop_frame() { void VM::__pop_frame() {
s_data.reset(callstack.top()._sp_base); assert(top_frame);
callstack.pop(); s_data.sp = top_frame->p0;
// callstack.pop()
Frame* p = top_frame;
top_frame = p->f_back;
Frame__delete(p);
#if PK_ENABLE_PROFILER #if PK_ENABLE_PROFILER
if(!_next_breakpoint.empty() && callstack.size() < _next_breakpoint.callstack_size) { if(!_next_breakpoint.empty() && callstack.size() < _next_breakpoint.callstack_size) {
@ -1937,8 +1944,8 @@ extern "C"{
vm->obj_gc_mark(vm->__last_exception); vm->obj_gc_mark(vm->__last_exception);
vm->obj_gc_mark(vm->__curr_class); vm->obj_gc_mark(vm->__curr_class);
vm->obj_gc_mark(vm->__c.error); vm->obj_gc_mark(vm->__c.error);
vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end()); vm->__stack_gc_mark((PyVar*)vm->s_data.begin, (PyVar*)vm->s_data.end);
if(self->_gc_marker_ex) self->_gc_marker_ex((pkpy_VM*)vm); if(self->_gc_marker_ex) self->_gc_marker_ex((pk_VM*)vm);
} }
void pk_ManagedHeap__delete_obj(pk_ManagedHeap* self, ::PyObject* __obj){ void pk_ManagedHeap__delete_obj(pk_ManagedHeap* self, ::PyObject* __obj){

View File

@ -1,406 +0,0 @@
#include "pocketpy/modules/array2d.hpp"
#include "pocketpy/interpreter/bindings.hpp"
namespace pkpy {
struct Array2d {
PK_ALWAYS_PASS_BY_POINTER(Array2d)
PyVar* data;
int n_cols;
int n_rows;
int numel;
Array2d() {
data = nullptr;
n_cols = 0;
n_rows = 0;
numel = 0;
}
void init(int n_cols, int n_rows) {
this->n_cols = n_cols;
this->n_rows = n_rows;
this->numel = n_cols * n_rows;
this->data = new PyVar[numel];
}
bool is_valid(int col, int row) const { return 0 <= col && col < n_cols && 0 <= row && row < n_rows; }
void check_valid(VM* vm, int col, int row) const {
if(is_valid(col, row)) return;
vm->IndexError(_S('(', col, ", ", row, ')', " is not a valid index for array2d(", n_cols, ", ", n_rows, ')'));
}
PyVar _get(int col, int row) { return data[row * n_cols + col]; }
void _set(int col, int row, PyVar value) { data[row * n_cols + col] = value; }
static void _register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind(type, "__new__(cls, *args, **kwargs)", [](VM* vm, ArgsView args) {
Type cls = PK_OBJ_GET(Type, args[0]);
return vm->new_object<Array2d>(cls);
});
vm->bind(type, "__init__(self, n_cols: int, n_rows: int, default=None)", [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
int n_cols = CAST(int, args[1]);
int n_rows = CAST(int, args[2]);
if(n_cols <= 0 || n_rows <= 0) { vm->ValueError("n_cols and n_rows must be positive integers"); }
self.init(n_cols, n_rows);
if(vm->py_callable(args[3])) {
for(int i = 0; i < self.numel; i++)
self.data[i] = vm->call(args[3]);
} else {
for(int i = 0; i < self.numel; i++)
self.data[i] = args[3];
}
return vm->None;
});
PY_READONLY_FIELD(Array2d, "n_cols", n_cols);
PY_READONLY_FIELD(Array2d, "n_rows", n_rows);
PY_READONLY_FIELD(Array2d, "width", n_cols);
PY_READONLY_FIELD(Array2d, "height", n_rows);
PY_READONLY_FIELD(Array2d, "numel", numel);
// _get
vm->bind_func(type, "_get", 3, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
int col = CAST(int, args[1]);
int row = CAST(int, args[2]);
self.check_valid(vm, col, row);
return self._get(col, row);
});
// _set
vm->bind_func(type, "_set", 4, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
int col = CAST(int, args[1]);
int row = CAST(int, args[2]);
self.check_valid(vm, col, row);
self._set(col, row, args[3]);
return vm->None;
});
vm->bind_func(type, "is_valid", 3, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
int col = CAST(int, args[1]);
int row = CAST(int, args[2]);
return VAR(self.is_valid(col, row));
});
vm->bind(type, "get(self, col: int, row: int, default=None)", [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
int col = CAST(int, args[1]);
int row = CAST(int, args[2]);
if(!self.is_valid(col, row)) return args[3];
return self._get(col, row);
});
#define HANDLE_SLICE() \
int start_col, stop_col, step_col; \
int start_row, stop_row, step_row; \
vm->parse_int_slice(PK_OBJ_GET(Slice, xy[0]), self.n_cols, start_col, stop_col, step_col); \
vm->parse_int_slice(PK_OBJ_GET(Slice, xy[1]), self.n_rows, start_row, stop_row, step_row); \
if(step_col != 1 || step_row != 1) vm->ValueError("slice step must be 1"); \
int slice_width = stop_col - start_col; \
int slice_height = stop_row - start_row; \
if(slice_width <= 0 || slice_height <= 0) vm->ValueError("slice width and height must be positive");
vm->bind__getitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
Array2d& self = PK_OBJ_GET(Array2d, _0);
const Tuple& xy = CAST(Tuple&, _1);
if(is_int(xy[0]) && is_int(xy[1])) {
i64 col = xy[0]._i64;
i64 row = xy[1]._i64;
self.check_valid(vm, col, row);
return self._get(col, row);
}
if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)) {
HANDLE_SLICE();
PyVar new_array_obj = vm->new_user_object<Array2d>();
Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
new_array.init(stop_col - start_col, stop_row - start_row);
for(int j = start_row; j < stop_row; j++) {
for(int i = start_col; i < stop_col; i++) {
new_array._set(i - start_col, j - start_row, self._get(i, j));
}
}
return new_array_obj;
}
vm->TypeError("expected `tuple[int, int]` or `tuple[slice, slice]` as index");
});
vm->bind__setitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1, PyVar _2) {
Array2d& self = PK_OBJ_GET(Array2d, _0);
const Tuple& xy = CAST(Tuple&, _1);
if(is_int(xy[0]) && is_int(xy[1])) {
i64 col = xy[0]._i64;
i64 row = xy[1]._i64;
self.check_valid(vm, col, row);
self._set(col, row, _2);
return;
}
if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)) {
HANDLE_SLICE();
bool is_basic_type = false;
switch(vm->_tp(_2)) {
case VM::tp_int: is_basic_type = true; break;
case VM::tp_float: is_basic_type = true; break;
case VM::tp_str: is_basic_type = true; break;
case VM::tp_bool: is_basic_type = true; break;
default: is_basic_type = is_none(_2);
}
if(is_basic_type) {
for(int j = 0; j < slice_height; j++)
for(int i = 0; i < slice_width; i++)
self._set(i + start_col, j + start_row, _2);
return;
}
if(!vm->is_user_type<Array2d>(_2)) {
vm->TypeError(_S("expected int/float/str/bool/None or an array2d instance"));
}
Array2d& other = PK_OBJ_GET(Array2d, _2);
if(slice_width != other.n_cols || slice_height != other.n_rows) {
vm->ValueError("array2d size does not match the slice size");
}
for(int j = 0; j < slice_height; j++)
for(int i = 0; i < slice_width; i++)
self._set(i + start_col, j + start_row, other._get(i, j));
return;
}
vm->TypeError("expected `tuple[int, int]` or `tuple[slice, slice]` as index");
});
#undef HANDLE_SLICE
vm->bind_func(type, "tolist", 1, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
List t(self.n_rows);
for(int j = 0; j < self.n_rows; j++) {
List row(self.n_cols);
for(int i = 0; i < self.n_cols; i++)
row[i] = self._get(i, j);
t[j] = VAR(std::move(row));
}
return VAR(std::move(t));
});
vm->bind__len__(type->as<Type>(), [](VM* vm, PyVar _0) {
Array2d& self = PK_OBJ_GET(Array2d, _0);
return (i64)self.numel;
});
vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar _0) -> Str {
Array2d& self = PK_OBJ_GET(Array2d, _0);
return _S("array2d(", self.n_cols, ", ", self.n_rows, ')');
});
vm->bind_func(type, "map", 2, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
PyVar f = args[1];
PyVar new_array_obj = vm->new_user_object<Array2d>();
Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
new_array.init(self.n_cols, self.n_rows);
for(int i = 0; i < new_array.numel; i++) {
new_array.data[i] = vm->call(f, self.data[i]);
}
return new_array_obj;
});
vm->bind_func(type, "copy", 1, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
PyVar new_array_obj = vm->new_user_object<Array2d>();
Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
new_array.init(self.n_cols, self.n_rows);
for(int i = 0; i < new_array.numel; i++) {
new_array.data[i] = self.data[i];
}
return new_array_obj;
});
vm->bind_func(type, "fill_", 2, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
for(int i = 0; i < self.numel; i++) {
self.data[i] = args[1];
}
return vm->None;
});
vm->bind_func(type, "apply_", 2, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
PyVar f = args[1];
for(int i = 0; i < self.numel; i++) {
self.data[i] = vm->call(f, self.data[i]);
}
return vm->None;
});
vm->bind_func(type, "copy_", 2, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
if(is_type(args[1], VM::tp_list)) {
const List& list = PK_OBJ_GET(List, args[1]);
if(list.size() != self.numel) {
vm->ValueError("list size must be equal to the number of elements in the array2d");
}
for(int i = 0; i < self.numel; i++) {
self.data[i] = list[i];
}
return vm->None;
}
Array2d& other = CAST(Array2d&, args[1]);
// if self and other have different sizes, re-initialize self
if(self.n_cols != other.n_cols || self.n_rows != other.n_rows) {
delete self.data;
self.init(other.n_cols, other.n_rows);
}
for(int i = 0; i < self.numel; i++) {
self.data[i] = other.data[i];
}
return vm->None;
});
vm->bind__eq__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
Array2d& self = PK_OBJ_GET(Array2d, _0);
if(!vm->is_user_type<Array2d>(_1)) return vm->NotImplemented;
Array2d& other = PK_OBJ_GET(Array2d, _1);
if(self.n_cols != other.n_cols || self.n_rows != other.n_rows) return vm->False;
for(int i = 0; i < self.numel; i++) {
if(vm->py_ne(self.data[i], other.data[i])) return vm->False;
}
return vm->True;
});
vm->bind(type, "count_neighbors(self, value, neighborhood='Moore') -> array2d[int]", [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
PyVar new_array_obj = vm->new_user_object<Array2d>();
Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
new_array.init(self.n_cols, self.n_rows);
PyVar value = args[1];
const Str& neighborhood = CAST(Str&, args[2]);
if(neighborhood == "Moore") {
for(int j = 0; j < new_array.n_rows; j++) {
for(int i = 0; i < new_array.n_cols; i++) {
int count = 0;
count += self.is_valid(i - 1, j - 1) && vm->py_eq(self._get(i - 1, j - 1), value);
count += self.is_valid(i, j - 1) && vm->py_eq(self._get(i, j - 1), value);
count += self.is_valid(i + 1, j - 1) && vm->py_eq(self._get(i + 1, j - 1), value);
count += self.is_valid(i - 1, j) && vm->py_eq(self._get(i - 1, j), value);
count += self.is_valid(i + 1, j) && vm->py_eq(self._get(i + 1, j), value);
count += self.is_valid(i - 1, j + 1) && vm->py_eq(self._get(i - 1, j + 1), value);
count += self.is_valid(i, j + 1) && vm->py_eq(self._get(i, j + 1), value);
count += self.is_valid(i + 1, j + 1) && vm->py_eq(self._get(i + 1, j + 1), value);
new_array._set(i, j, VAR(count));
}
}
} else if(neighborhood == "von Neumann") {
for(int j = 0; j < new_array.n_rows; j++) {
for(int i = 0; i < new_array.n_cols; i++) {
int count = 0;
count += self.is_valid(i, j - 1) && vm->py_eq(self._get(i, j - 1), value);
count += self.is_valid(i - 1, j) && vm->py_eq(self._get(i - 1, j), value);
count += self.is_valid(i + 1, j) && vm->py_eq(self._get(i + 1, j), value);
count += self.is_valid(i, j + 1) && vm->py_eq(self._get(i, j + 1), value);
new_array._set(i, j, VAR(count));
}
}
} else {
vm->ValueError("neighborhood must be 'Moore' or 'von Neumann'");
}
return new_array_obj;
});
vm->bind_func(type, "count", 2, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
PyVar value = args[1];
int count = 0;
for(int i = 0; i < self.numel; i++)
count += vm->py_eq(self.data[i], value);
return VAR(count);
});
vm->bind_func(type, "find_bounding_rect", 2, [](VM* vm, ArgsView args) {
Array2d& self = PK_OBJ_GET(Array2d, args[0]);
PyVar value = args[1];
int left = self.n_cols;
int top = self.n_rows;
int right = 0;
int bottom = 0;
for(int j = 0; j < self.n_rows; j++) {
for(int i = 0; i < self.n_cols; i++) {
if(vm->py_eq(self._get(i, j), value)) {
left = (std::min)(left, i);
top = (std::min)(top, j);
right = (std::max)(right, i);
bottom = (std::max)(bottom, j);
}
}
}
int width = right - left + 1;
int height = bottom - top + 1;
if(width <= 0 || height <= 0) return vm->None;
Tuple t(4);
t[0] = VAR(left);
t[1] = VAR(top);
t[2] = VAR(width);
t[3] = VAR(height);
return VAR(std::move(t));
});
}
void _gc_mark(VM* vm) const {
for(int i = 0; i < numel; i++)
vm->obj_gc_mark(data[i]);
}
~Array2d() { delete[] data; }
};
struct Array2dIter {
PK_ALWAYS_PASS_BY_POINTER(Array2dIter)
PyVar ref;
Array2d* a;
int i;
Array2dIter(PyVar ref, Array2d* a) : ref(ref), a(a), i(0) {}
void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }
static void _register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
return _0;
});
vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
Array2dIter& self = PK_OBJ_GET(Array2dIter, _0);
if(self.i == self.a->numel) return 0;
std::div_t res = std::div(self.i, self.a->n_cols);
vm->s_data.emplace(VM::tp_int, res.rem);
vm->s_data.emplace(VM::tp_int, res.quot);
vm->s_data.push(self.a->data[self.i++]);
return 3;
});
}
};
void add_module_array2d(VM* vm) {
PyObject* mod = vm->new_module("array2d");
vm->register_user_class<Array2d>(mod, "array2d", VM::tp_object, true);
vm->register_user_class<Array2dIter>(mod, "_array2d_iter");
Type array2d_iter_t = vm->_tp_user<Array2d>();
vm->bind__iter__(array2d_iter_t, [](VM* vm, PyVar _0) {
return vm->new_user_object<Array2dIter>(_0, &_0.obj_get<Array2d>());
});
}
} // namespace pkpy

View File

@ -1,175 +0,0 @@
#include "pocketpy/modules/base64.hpp"
#include "pocketpy/interpreter/bindings.hpp"
namespace pkpy {
// https://github.com/zhicheng/base64/blob/master/base64.c
const char BASE64_PAD = '=';
const char BASE64DE_FIRST = '+';
const char BASE64DE_LAST = 'z';
// clang-format off
/* BASE 64 encode table */
const char base64en[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
};
/* ASCII order for BASE 64 decode, 255 in unused character */
const unsigned char base64de[] = {
/* nul, soh, stx, etx, eot, enq, ack, bel, */
255, 255, 255, 255, 255, 255, 255, 255,
/* bs, ht, nl, vt, np, cr, so, si, */
255, 255, 255, 255, 255, 255, 255, 255,
/* dle, dc1, dc2, dc3, dc4, nak, syn, etb, */
255, 255, 255, 255, 255, 255, 255, 255,
/* can, em, sub, esc, fs, gs, rs, us, */
255, 255, 255, 255, 255, 255, 255, 255,
/* sp, '!', '"', '#', '$', '%', '&', ''', */
255, 255, 255, 255, 255, 255, 255, 255,
/* '(', ')', '*', '+', ',', '-', '.', '/', */
255, 255, 255, 62, 255, 255, 255, 63,
/* '0', '1', '2', '3', '4', '5', '6', '7', */
52, 53, 54, 55, 56, 57, 58, 59,
/* '8', '9', ':', ';', '<', '=', '>', '?', */
60, 61, 255, 255, 255, 255, 255, 255,
/* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', */
255, 0, 1, 2, 3, 4, 5, 6,
/* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', */
7, 8, 9, 10, 11, 12, 13, 14,
/* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', */
15, 16, 17, 18, 19, 20, 21, 22,
/* 'X', 'Y', 'Z', '[', '\', ']', '^', '_', */
23, 24, 25, 255, 255, 255, 255, 255,
/* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', */
255, 26, 27, 28, 29, 30, 31, 32,
/* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', */
33, 34, 35, 36, 37, 38, 39, 40,
/* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', */
41, 42, 43, 44, 45, 46, 47, 48,
/* 'x', 'y', 'z', '{', '|', '}', '~', del, */
49, 50, 51, 255, 255, 255, 255, 255
};
// clang-format on
static unsigned int base64_encode(const unsigned char* in, unsigned int inlen, char* out) {
int s;
unsigned int i;
unsigned int j;
unsigned char c;
unsigned char l;
s = 0;
l = 0;
for(i = j = 0; i < inlen; i++) {
c = in[i];
switch(s) {
case 0:
s = 1;
out[j++] = base64en[(c >> 2) & 0x3F];
break;
case 1:
s = 2;
out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xF)];
break;
case 2:
s = 0;
out[j++] = base64en[((l & 0xF) << 2) | ((c >> 6) & 0x3)];
out[j++] = base64en[c & 0x3F];
break;
}
l = c;
}
switch(s) {
case 1:
out[j++] = base64en[(l & 0x3) << 4];
out[j++] = BASE64_PAD;
out[j++] = BASE64_PAD;
break;
case 2:
out[j++] = base64en[(l & 0xF) << 2];
out[j++] = BASE64_PAD;
break;
}
out[j] = 0;
return j;
}
static unsigned int base64_decode(const char* in, unsigned int inlen, unsigned char* out) {
unsigned int i;
unsigned int j;
unsigned char c;
if(inlen & 0x3) { return 0; }
for(i = j = 0; i < inlen; i++) {
if(in[i] == BASE64_PAD) { break; }
if(in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST) { return 0; }
c = base64de[(unsigned char)in[i]];
if(c == 255) { return 0; }
switch(i & 0x3) {
case 0: out[j] = (c << 2) & 0xFF; break;
case 1:
out[j++] |= (c >> 4) & 0x3;
out[j] = (c & 0xF) << 4;
break;
case 2:
out[j++] |= (c >> 2) & 0xF;
out[j] = (c & 0x3) << 6;
break;
case 3: out[j++] |= c; break;
}
}
return j;
}
void add_module_base64(VM* vm) {
PyObject* mod = vm->new_module("base64");
// b64encode
vm->bind_func(mod, "b64encode", 1, [](VM* vm, ArgsView args) {
Bytes& b = CAST(Bytes&, args[0]);
unsigned char* p = (unsigned char*)std::malloc(b.size() * 2);
int size = base64_encode((const unsigned char*)b.data(), b.size(), (char*)p);
return VAR(Bytes(p, size));
});
// b64decode
vm->bind_func(mod, "b64decode", 1, [](VM* vm, ArgsView args) {
Bytes& b = CAST(Bytes&, args[0]);
unsigned char* p = (unsigned char*)std::malloc(b.size());
int size = base64_decode((const char*)b.data(), b.size(), p);
return VAR(Bytes(p, size));
});
}
} // namespace pkpy

View File

@ -1,87 +0,0 @@
#include "pocketpy/modules/csv.hpp"
#include "pocketpy/interpreter/bindings.hpp"
namespace pkpy {
void add_module_csv(VM* vm) {
PyObject* mod = vm->new_module("csv");
vm->bind(mod, "reader(csvfile: list[str]) -> list[list]", [](VM* vm, ArgsView args) {
const List& csvfile = CAST(List&, args[0]);
List ret;
for(int i = 0; i < csvfile.size(); i++) {
std::string_view line = CAST(Str&, csvfile[i]).sv();
if(i == 0) {
// Skip utf8 BOM if there is any.
if(strncmp(line.data(), "\xEF\xBB\xBF", 3) == 0) line = line.substr(3);
}
List row;
int j;
bool in_quote = false;
std::string buffer;
__NEXT_LINE:
j = 0;
while(j < line.size()) {
switch(line[j]) {
case '"':
if(in_quote) {
if(j + 1 < line.size() && line[j + 1] == '"') {
buffer += '"';
j++;
} else {
in_quote = false;
}
} else {
in_quote = true;
}
break;
case ',':
if(in_quote) {
buffer += line[j];
} else {
row.push_back(VAR(buffer));
buffer.clear();
}
break;
case '\r': break; // ignore
default: buffer += line[j]; break;
}
j++;
}
if(in_quote) {
if(i == csvfile.size() - 1) {
vm->ValueError("unterminated quote");
} else {
buffer += '\n';
i++;
line = CAST(Str&, csvfile[i]).sv();
goto __NEXT_LINE;
}
}
row.push_back(VAR(buffer));
ret.push_back(VAR(std::move(row)));
}
return VAR(std::move(ret));
});
vm->bind(mod, "DictReader(csvfile: list[str]) -> list[dict]", [](VM* vm, ArgsView args) {
PyVar csv_reader = vm->_modules["csv"]->attr()["reader"];
PyVar ret_obj = vm->call(csv_reader, args[0]);
const List& ret = CAST(List&, ret_obj);
if(ret.size() == 0) { vm->ValueError("empty csvfile"); }
const List& header = CAST(List&, ret[0]);
List new_ret;
for(int i = 1; i < ret.size(); i++) {
const List& row = CAST(List&, ret[i]);
if(row.size() != header.size()) { vm->ValueError("row.size() != header.size()"); }
Dict row_dict;
for(int j = 0; j < header.size(); j++) {
row_dict.set(vm, header[j], row[j]);
}
new_ret.push_back(VAR(std::move(row_dict)));
}
return VAR(std::move(new_ret));
});
}
} // namespace pkpy

View File

@ -1,120 +0,0 @@
#include "pocketpy/modules/dataclasses.hpp"
#include "pocketpy/interpreter/bindings.hpp"
namespace pkpy {
static void patch__init__(VM* vm, Type cls) {
vm->bind(vm->_t(cls), "__init__(self, *args, **kwargs)", [](VM* vm, ArgsView _view) {
PyVar self = _view[0];
const Tuple& args = CAST(Tuple&, _view[1]);
const Dict& kwargs_ = CAST(Dict&, _view[2]);
NameDict kwargs;
kwargs_.apply([&](PyVar k, PyVar v) {
kwargs.set(CAST(Str&, k), v);
});
Type cls = vm->_tp(self);
const PyTypeInfo* cls_info = &vm->_all_types[cls];
NameDict& cls_d = cls_info->obj->attr();
const auto& fields = cls_info->annotated_fields;
int i = 0; // index into args
for(StrName field: fields) {
if(kwargs.contains(field)) {
self->attr().set(field, kwargs[field]);
kwargs.del(field);
} else {
if(i < args.size()) {
self->attr().set(field, args[i]);
++i;
} else if(cls_d.contains(field)) { // has default value
self->attr().set(field, cls_d[field]);
} else {
vm->TypeError(_S(cls_info->name, " missing required argument ", field.escape()));
}
}
}
if(args.size() > i) {
vm->TypeError(
_S(cls_info->name, " takes ", fields.size(), " positional arguments but ", args.size(), " were given"));
}
if(kwargs.size() > 0) {
StrName unexpected_key = kwargs.items()[0].first;
vm->TypeError(_S(cls_info->name, " got an unexpected keyword argument ", unexpected_key.escape()));
}
return vm->None;
});
}
static void patch__repr__(VM* vm, Type cls) {
vm->bind__repr__(cls, [](VM* vm, PyVar _0) -> Str {
const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
const auto& fields = cls_info->annotated_fields;
const NameDict& obj_d = _0->attr();
SStream ss;
ss << cls_info->name << "(";
bool first = true;
for(StrName field: fields) {
if(first)
first = false;
else
ss << ", ";
ss << field << "=" << vm->py_repr(obj_d[field]);
}
ss << ")";
return ss.str();
});
}
static void patch__eq__(VM* vm, Type cls) {
vm->bind__eq__(cls, [](VM* vm, PyVar _0, PyVar _1) {
if(vm->_tp(_0) != vm->_tp(_1)) return vm->NotImplemented;
const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
const auto& fields = cls_info->annotated_fields;
for(StrName field: fields) {
PyVar lhs = _0->attr()[field];
PyVar rhs = _1->attr()[field];
if(vm->py_ne(lhs, rhs)) return vm->False;
}
return vm->True;
});
}
void add_module_dataclasses(VM* vm) {
PyObject* mod = vm->new_module("dataclasses");
vm->bind_func(mod, "dataclass", 1, [](VM* vm, ArgsView args) {
vm->check_type(args[0], VM::tp_type);
Type cls = PK_OBJ_GET(Type, args[0]);
NameDict& cls_d = args[0]->attr();
if(!cls_d.contains(__init__)) patch__init__(vm, cls);
if(!cls_d.contains(__repr__)) patch__repr__(vm, cls);
if(!cls_d.contains(__eq__)) patch__eq__(vm, cls);
const auto& fields = vm->_all_types[cls].annotated_fields;
bool has_default = false;
for(StrName field: fields) {
if(cls_d.contains(field)) {
has_default = true;
} else {
if(has_default) {
vm->TypeError(_S("non-default argument ", field.escape(), " follows default argument"));
}
}
}
return args[0];
});
vm->bind_func(mod, "asdict", 1, [](VM* vm, ArgsView args) {
const auto& fields = vm->_tp_info(args[0])->annotated_fields;
const NameDict& obj_d = args[0]->attr();
Dict d;
for(StrName field: fields) {
d.set(vm, VAR(field.sv()), obj_d[field]);
}
return VAR(std::move(d));
});
}
} // namespace pkpy

View File

@ -1,218 +0,0 @@
#include "pocketpy/modules/easing.hpp"
#include "pocketpy/interpreter/bindings.hpp"
#include <cmath>
namespace pkpy {
// https://easings.net/
const double kPi = 3.1415926545;
static double easeLinear(double x) { return x; }
static double easeInSine(double x) { return 1.0 - std::cos(x * kPi / 2); }
static double easeOutSine(double x) { return std::sin(x * kPi / 2); }
static double easeInOutSine(double x) { return -(std::cos(kPi * x) - 1) / 2; }
static double easeInQuad(double x) { return x * x; }
static double easeOutQuad(double x) { return 1 - std::pow(1 - x, 2); }
static double easeInOutQuad(double x) {
if(x < 0.5) {
return 2 * x * x;
} else {
return 1 - std::pow(-2 * x + 2, 2) / 2;
}
}
static double easeInCubic(double x) { return x * x * x; }
static double easeOutCubic(double x) { return 1 - std::pow(1 - x, 3); }
static double easeInOutCubic(double x) {
if(x < 0.5) {
return 4 * x * x * x;
} else {
return 1 - std::pow(-2 * x + 2, 3) / 2;
}
}
static double easeInQuart(double x) { return std::pow(x, 4); }
static double easeOutQuart(double x) { return 1 - std::pow(1 - x, 4); }
static double easeInOutQuart(double x) {
if(x < 0.5) {
return 8 * std::pow(x, 4);
} else {
return 1 - std::pow(-2 * x + 2, 4) / 2;
}
}
static double easeInQuint(double x) { return std::pow(x, 5); }
static double easeOutQuint(double x) { return 1 - std::pow(1 - x, 5); }
static double easeInOutQuint(double x) {
if(x < 0.5) {
return 16 * std::pow(x, 5);
} else {
return 1 - std::pow(-2 * x + 2, 5) / 2;
}
}
static double easeInExpo(double x) { return x == 0 ? 0 : std::pow(2, 10 * x - 10); }
static double easeOutExpo(double x) { return x == 1 ? 1 : 1 - std::pow(2, -10 * x); }
static double easeInOutExpo(double x) {
if(x == 0) {
return 0;
} else if(x == 1) {
return 1;
} else if(x < 0.5) {
return std::pow(2, 20 * x - 10) / 2;
} else {
return (2 - std::pow(2, -20 * x + 10)) / 2;
}
}
static double easeInCirc(double x) { return 1 - std::sqrt(1 - std::pow(x, 2)); }
static double easeOutCirc(double x) { return std::sqrt(1 - std::pow(x - 1, 2)); }
static double easeInOutCirc(double x) {
if(x < 0.5) {
return (1 - std::sqrt(1 - std::pow(2 * x, 2))) / 2;
} else {
return (std::sqrt(1 - std::pow(-2 * x + 2, 2)) + 1) / 2;
}
}
static double easeInBack(double x) {
const double c1 = 1.70158;
const double c3 = c1 + 1;
return c3 * x * x * x - c1 * x * x;
}
static double easeOutBack(double x) {
const double c1 = 1.70158;
const double c3 = c1 + 1;
return 1 + c3 * std::pow(x - 1, 3) + c1 * std::pow(x - 1, 2);
}
static double easeInOutBack(double x) {
const double c1 = 1.70158;
const double c2 = c1 * 1.525;
if(x < 0.5) {
return (std::pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2;
} else {
return (std::pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
}
}
static double easeInElastic(double x) {
const double c4 = (2 * kPi) / 3;
if(x == 0) {
return 0;
} else if(x == 1) {
return 1;
} else {
return -std::pow(2, 10 * x - 10) * std::sin((x * 10 - 10.75) * c4);
}
}
static double easeOutElastic(double x) {
const double c4 = (2 * kPi) / 3;
if(x == 0) {
return 0;
} else if(x == 1) {
return 1;
} else {
return std::pow(2, -10 * x) * std::sin((x * 10 - 0.75) * c4) + 1;
}
}
static double easeInOutElastic(double x) {
const double c5 = (2 * kPi) / 4.5;
if(x == 0) {
return 0;
} else if(x == 1) {
return 1;
} else if(x < 0.5) {
return -(std::pow(2, 20 * x - 10) * std::sin((20 * x - 11.125) * c5)) / 2;
} else {
return (std::pow(2, -20 * x + 10) * std::sin((20 * x - 11.125) * c5)) / 2 + 1;
}
}
static double easeOutBounce(double x) {
const double n1 = 7.5625;
const double d1 = 2.75;
if(x < 1 / d1) {
return n1 * x * x;
} else if(x < 2 / d1) {
x -= 1.5 / d1;
return n1 * x * x + 0.75;
} else if(x < 2.5 / d1) {
x -= 2.25 / d1;
return n1 * x * x + 0.9375;
} else {
x -= 2.625 / d1;
return n1 * x * x + 0.984375;
}
}
static double easeInBounce(double x) { return 1 - easeOutBounce(1 - x); }
static double easeInOutBounce(double x) {
return x < 0.5 ? (1 - easeOutBounce(1 - 2 * x)) / 2 : (1 + easeOutBounce(2 * x - 1)) / 2;
}
void add_module_easing(VM* vm) {
PyObject* mod = vm->new_module("easing");
#define EASE(name) \
vm->bind_func(mod, #name, 1, [](VM* vm, ArgsView args) { \
f64 t = CAST(f64, args[0]); \
return VAR(ease##name(t)); \
});
EASE(Linear)
EASE(InSine)
EASE(OutSine)
EASE(InOutSine)
EASE(InQuad)
EASE(OutQuad)
EASE(InOutQuad)
EASE(InCubic)
EASE(OutCubic)
EASE(InOutCubic)
EASE(InQuart)
EASE(OutQuart)
EASE(InOutQuart)
EASE(InQuint)
EASE(OutQuint)
EASE(InOutQuint)
EASE(InExpo)
EASE(OutExpo)
EASE(InOutExpo)
EASE(InCirc)
EASE(OutCirc)
EASE(InOutCirc)
EASE(InBack)
EASE(OutBack)
EASE(InOutBack)
EASE(InElastic)
EASE(OutElastic)
EASE(InOutElastic)
EASE(InBounce)
EASE(OutBounce)
EASE(InOutBounce)
#undef EASE
}
} // namespace pkpy

View File

@ -1,247 +0,0 @@
#include "pocketpy/modules/io.hpp"
#include "pocketpy/interpreter/bindings.hpp"
#if PK_ENABLE_OS
#include <filesystem>
#include <cstdio>
#endif
namespace pkpy {
#if PK_ENABLE_OS
struct FileIO {
FILE* fp;
bool is_text;
FileIO(VM* vm, const Str& file, const Str& mode);
void close();
static void _register(VM* vm, PyObject* mod, PyObject* type);
};
static FILE* io_fopen(const char* name, const char* mode) {
#if _MSC_VER
FILE* fp;
errno_t err = fopen_s(&fp, name, mode);
if(err != 0) return nullptr;
return fp;
#else
return fopen(name, mode);
#endif
}
static size_t io_fread(void* buffer, size_t size, size_t count, FILE* fp) {
#if _MSC_VER
return fread_s(buffer, std::numeric_limits<size_t>::max(), size, count, fp);
#else
return fread(buffer, size, count, fp);
#endif
}
unsigned char* _default_import_handler(const char* name, int* out_size) {
bool exists = std::filesystem::exists(std::filesystem::path(name));
if(!exists) return nullptr;
FILE* fp = io_fopen(name, "rb");
if(!fp) return nullptr;
fseek(fp, 0, SEEK_END);
int buffer_size = ftell(fp);
unsigned char* buffer = new unsigned char[buffer_size];
fseek(fp, 0, SEEK_SET);
size_t sz = io_fread(buffer, 1, buffer_size, fp);
(void)sz; // suppress warning
fclose(fp);
*out_size = buffer_size;
return buffer;
};
void FileIO::_register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind_func(type, __new__, 3, [](VM* vm, ArgsView args) {
Type cls = PK_OBJ_GET(Type, args[0]);
return vm->new_object<FileIO>(cls, vm, py_cast<Str&>(vm, args[1]), py_cast<Str&>(vm, args[2]));
});
vm->bind(type, "read(self, size=-1)", [](VM* vm, ArgsView args) {
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
i64 size = CAST(i64, args[1]);
i64 buffer_size;
if(size < 0) {
long current = ftell(io.fp);
fseek(io.fp, 0, SEEK_END);
buffer_size = ftell(io.fp);
fseek(io.fp, current, SEEK_SET);
} else {
buffer_size = size;
}
unsigned char* buffer = (unsigned char*)std::malloc(buffer_size);
i64 actual_size = io_fread(buffer, 1, buffer_size, io.fp);
assert(actual_size <= buffer_size);
// in text mode, CR may be dropped, which may cause `actual_size < buffer_size`
Bytes b(buffer, actual_size);
if(io.is_text) { return VAR(std::string_view((char*)b.data(), b.size())); }
return VAR(std::move(b));
});
vm->bind_func(type, "write", 2, [](VM* vm, ArgsView args) {
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
if(io.is_text) {
Str& s = CAST(Str&, args[1]);
fwrite(s.c_str(), 1, s.length(), io.fp);
} else {
Bytes& buffer = CAST(Bytes&, args[1]);
fwrite(buffer.data(), 1, buffer.size(), io.fp);
}
return vm->None;
});
vm->bind_func(type, "tell", 1, [](VM* vm, ArgsView args) {
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
long pos = ftell(io.fp);
if(pos == -1) vm->IOError(strerror(errno));
return VAR(pos);
});
vm->bind_func(type, "seek", 3, [](VM* vm, ArgsView args) {
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
long offset = CAST(long, args[1]);
int whence = CAST(int, args[2]);
int ret = fseek(io.fp, offset, whence);
if(ret != 0) vm->IOError(strerror(errno));
return vm->None;
});
vm->bind_func(type, "close", 1, [](VM* vm, ArgsView args) {
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
io.close();
return vm->None;
});
vm->bind_func(type, __exit__, 1, [](VM* vm, ArgsView args) {
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
io.close();
return vm->None;
});
vm->bind_func(type, __enter__, 1, PK_LAMBDA(args[0]));
}
FileIO::FileIO(VM* vm, const Str& file, const Str& mode) {
this->is_text = mode.sv().find("b") == std::string::npos;
fp = io_fopen(file.c_str(), mode.c_str());
if(!fp) vm->IOError(strerror(errno));
}
void FileIO::close() {
if(fp == nullptr) return;
fclose(fp);
fp = nullptr;
}
void add_module_io(VM* vm) {
PyObject* mod = vm->new_module("io");
vm->register_user_class<FileIO>(mod, "FileIO");
mod->attr().set("SEEK_SET", VAR(SEEK_SET));
mod->attr().set("SEEK_CUR", VAR(SEEK_CUR));
mod->attr().set("SEEK_END", VAR(SEEK_END));
vm->bind(vm->builtins, "open(path, mode='r')", [](VM* vm, ArgsView args) {
return vm->call(vm->_modules["io"]->attr("FileIO"), args[0], args[1]);
});
}
void add_module_os(VM* vm) {
PyObject* mod = vm->new_module("os");
PyObject* path_obj = vm->new_object<DummyInstance>(VM::tp_object).get();
mod->attr().set("path", path_obj);
// Working directory is shared by all VMs!!
vm->bind_func(mod, "getcwd", 0, [](VM* vm, ArgsView args) {
return VAR(std::filesystem::current_path().string());
});
vm->bind_func(mod, "chdir", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
std::filesystem::current_path(path);
return vm->None;
});
vm->bind_func(mod, "listdir", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
std::filesystem::directory_iterator di;
try {
di = std::filesystem::directory_iterator(path);
} catch(std::filesystem::filesystem_error&) { vm->IOError(path.string()); }
List ret;
for(auto& p: di)
ret.push_back(VAR(p.path().filename().string()));
return VAR(std::move(ret));
});
vm->bind_func(mod, "remove", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func(mod, "mkdir", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::create_directory(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func(mod, "rmdir", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func(path_obj, "join", -1, [](VM* vm, ArgsView args) {
std::filesystem::path path;
for(int i = 0; i < args.size(); i++) {
path /= CAST(Str&, args[i]).sv();
}
return VAR(path.string());
});
vm->bind_func(path_obj, "exists", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool exists = std::filesystem::exists(path);
return VAR(exists);
});
vm->bind_func(path_obj, "basename", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
return VAR(path.filename().string());
});
vm->bind_func(path_obj, "isdir", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool isdir = std::filesystem::is_directory(path);
return VAR(isdir);
});
vm->bind_func(path_obj, "isfile", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
bool isfile = std::filesystem::is_regular_file(path);
return VAR(isfile);
});
vm->bind_func(path_obj, "abspath", 1, [](VM* vm, ArgsView args) {
std::filesystem::path path(CAST(Str&, args[0]).sv());
return VAR(std::filesystem::absolute(path).string());
});
}
#else
void add_module_io(VM* vm) {}
void add_module_os(VM* vm) {}
unsigned char* _default_import_handler(const char* name, int* out_size) { return nullptr; }
#endif
} // namespace pkpy

View File

@ -1,717 +0,0 @@
#include "pocketpy/modules/linalg.hpp"
#include "pocketpy/interpreter/bindings.hpp"
namespace pkpy {
#define BIND_VEC_VEC_OP(D, name, op) \
vm->bind##name(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) { \
Vec##D self = _CAST(Vec##D, _0); \
Vec##D other = CAST(Vec##D, _1); \
return VAR(self op other); \
});
#define BIND_VEC_FLOAT_OP(D, name, op) \
vm->bind##name(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) { \
Vec##D self = _CAST(Vec##D, _0); \
f64 other = CAST(f64, _1); \
return VAR(self op other); \
});
#define BIND_VEC_FUNCTION_0(T, name) \
vm->bind_func(type, #name, 1, [](VM* vm, ArgsView args) { \
T self = _CAST(T, args[0]); \
return VAR(self.name()); \
});
#define BIND_VEC_FUNCTION_1(T, name) \
vm->bind_func(type, #name, 2, [](VM* vm, ArgsView args) { \
T self = _CAST(T, args[0]); \
T other = CAST(T, args[1]); \
return VAR(self.name(other)); \
});
#define BIND_VEC_MUL_OP(D) \
vm->bind__mul__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) { \
Vec##D self = _CAST(Vec##D, _0); \
if(vm->is_user_type<Vec##D>(_1)) { \
Vec##D other = _CAST(Vec##D, _1); \
return VAR(self * other); \
} \
f64 other = CAST(f64, _1); \
return VAR(self * other); \
}); \
vm->bind_func(type, "__rmul__", 2, [](VM* vm, ArgsView args) { \
Vec##D self = _CAST(Vec##D, args[0]); \
f64 other = CAST(f64, args[1]); \
return VAR(self * other); \
}); \
vm->bind__truediv__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) { \
Vec##D self = _CAST(Vec##D, _0); \
f64 other = CAST(f64, _1); \
return VAR(self / other); \
});
#define BIND_VEC_GETITEM(D) \
vm->bind__getitem__(type->as<Type>(), [](VM* vm, PyVar obj, PyVar index) { \
Vec##D self = _CAST(Vec##D, obj); \
i64 i = CAST(i64, index); \
if(i < 0 || i >= D) vm->IndexError("index out of range"); \
return VAR(self[i]); \
});
#define BIND_SSO_VEC_COMMON(D) \
vm->bind__eq__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) { \
Vec##D self = _CAST(Vec##D, _0); \
if(!vm->is_user_type<Vec##D>(_1)) return vm->NotImplemented; \
Vec##D other = _CAST(Vec##D, _1); \
return VAR(self == other); \
}); \
vm->bind_func(type, "__getnewargs__", 1, [](VM* vm, ArgsView args) { \
Vec##D self = _CAST(Vec##D, args[0]); \
Tuple t(D); \
for(int i = 0; i < D; i++) \
t[i] = VAR(self[i]); \
return VAR(std::move(t)); \
});
// https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Math/Vector2.cs#L289
static Vec2
SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float smoothTime, float maxSpeed, float deltaTime) {
// Based on Game Programming Gems 4 Chapter 1.10
smoothTime = (std::max)(0.0001F, smoothTime);
float omega = 2.0F / smoothTime;
float x = omega * deltaTime;
float exp = 1.0F / (1.0F + x + 0.48F * x * x + 0.235F * x * x * x);
float change_x = current.x - target.x;
float change_y = current.y - target.y;
Vec2 originalTo = target;
// Clamp maximum speed
float maxChange = maxSpeed * smoothTime;
float maxChangeSq = maxChange * maxChange;
float sqDist = change_x * change_x + change_y * change_y;
if(sqDist > maxChangeSq) {
float mag = std::sqrt(sqDist);
change_x = change_x / mag * maxChange;
change_y = change_y / mag * maxChange;
}
target.x = current.x - change_x;
target.y = current.y - change_y;
float temp_x = (currentVelocity.x + omega * change_x) * deltaTime;
float temp_y = (currentVelocity.y + omega * change_y) * deltaTime;
currentVelocity.x = (currentVelocity.x - omega * temp_x) * exp;
currentVelocity.y = (currentVelocity.y - omega * temp_y) * exp;
float output_x = target.x + (change_x + temp_x) * exp;
float output_y = target.y + (change_y + temp_y) * exp;
// Prevent overshooting
float origMinusCurrent_x = originalTo.x - current.x;
float origMinusCurrent_y = originalTo.y - current.y;
float outMinusOrig_x = output_x - originalTo.x;
float outMinusOrig_y = output_y - originalTo.y;
if(origMinusCurrent_x * outMinusOrig_x + origMinusCurrent_y * outMinusOrig_y > 0) {
output_x = originalTo.x;
output_y = originalTo.y;
currentVelocity.x = (output_x - originalTo.x) / deltaTime;
currentVelocity.y = (output_y - originalTo.y) / deltaTime;
}
return Vec2(output_x, output_y);
}
void Vec2::_register(VM* vm, PyObject* mod, PyObject* type) {
type->attr().set("ZERO", vm->new_user_object<Vec2>(0, 0));
type->attr().set("ONE", vm->new_user_object<Vec2>(1, 1));
vm->bind_func(type, __new__, 3, [](VM* vm, ArgsView args) {
float x = CAST_F(args[1]);
float y = CAST_F(args[2]);
return vm->new_object<Vec2>(PK_OBJ_GET(Type, args[0]), x, y);
});
// @staticmethod
vm->bind(
type,
"smooth_damp(current: vec2, target: vec2, current_velocity_: vec2, smooth_time: float, max_speed: float, delta_time: float) -> vec2",
[](VM* vm, ArgsView args) {
Vec2 current = CAST(Vec2, args[0]);
Vec2 target = CAST(Vec2, args[1]);
Vec2 current_velocity_ = CAST(Vec2, args[2]);
float smooth_time = CAST_F(args[3]);
float max_speed = CAST_F(args[4]);
float delta_time = CAST_F(args[5]);
Vec2 ret = SmoothDamp(current, target, current_velocity_, smooth_time, max_speed, delta_time);
return VAR(Tuple(VAR(ret), VAR(current_velocity_)));
},
{},
BindType_STATICMETHOD);
// @staticmethod
vm->bind(
type,
"angle(__from: vec2, __to: vec2) -> float",
[](VM* vm, ArgsView args) {
Vec2 __from = CAST(Vec2, args[0]);
Vec2 __to = CAST(Vec2, args[1]);
float val = atan2f(__to.y, __to.x) - atan2f(__from.y, __from.x);
const float PI = 3.1415926535897932384f;
if(val > PI) val -= 2 * PI;
if(val < -PI) val += 2 * PI;
return VAR(val);
},
{},
BindType_STATICMETHOD);
vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar obj) -> Str {
Vec2 self = _CAST(Vec2, obj);
SStream ss;
ss.setprecision(3);
ss << "vec2(" << self.x << ", " << self.y << ")";
return ss.str();
});
vm->bind_func(type, "rotate", 2, [](VM* vm, ArgsView args) {
Vec2 self = _CAST(Vec2, args[0]);
float radian = CAST(f64, args[1]);
return vm->new_user_object<Vec2>(self.rotate(radian));
});
PY_READONLY_FIELD(Vec2, "x", x)
PY_READONLY_FIELD(Vec2, "y", y)
BIND_VEC_VEC_OP(2, __add__, +)
BIND_VEC_VEC_OP(2, __sub__, -)
BIND_VEC_MUL_OP(2)
BIND_VEC_FLOAT_OP(2, __truediv__, /)
BIND_VEC_FUNCTION_1(Vec2, dot)
BIND_VEC_FUNCTION_1(Vec2, cross)
BIND_VEC_FUNCTION_0(Vec2, length)
BIND_VEC_FUNCTION_0(Vec2, length_squared)
BIND_VEC_FUNCTION_0(Vec2, normalize)
BIND_VEC_GETITEM(2)
BIND_SSO_VEC_COMMON(2)
}
void Vec3::_register(VM* vm, PyObject* mod, PyObject* type) {
type->attr().set("ZERO", vm->new_user_object<Vec3>(0, 0, 0));
type->attr().set("ONE", vm->new_user_object<Vec3>(1, 1, 1));
vm->bind_func(type, __new__, 4, [](VM* vm, ArgsView args) {
float x = CAST_F(args[1]);
float y = CAST_F(args[2]);
float z = CAST_F(args[3]);
return vm->new_object<Vec3>(PK_OBJ_GET(Type, args[0]), x, y, z);
});
vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar obj) -> Str {
Vec3 self = _CAST(Vec3, obj);
SStream ss;
ss.setprecision(3);
ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")";
return ss.str();
});
PY_READONLY_FIELD(Vec3, "x", x)
PY_READONLY_FIELD(Vec3, "y", y)
PY_READONLY_FIELD(Vec3, "z", z)
BIND_VEC_VEC_OP(3, __add__, +)
BIND_VEC_VEC_OP(3, __sub__, -)
BIND_VEC_MUL_OP(3)
BIND_VEC_FUNCTION_1(Vec3, dot)
BIND_VEC_FUNCTION_1(Vec3, cross)
BIND_VEC_FUNCTION_0(Vec3, length)
BIND_VEC_FUNCTION_0(Vec3, length_squared)
BIND_VEC_FUNCTION_0(Vec3, normalize)
BIND_VEC_GETITEM(3)
BIND_SSO_VEC_COMMON(3)
}
void Vec4::_register(VM* vm, PyObject* mod, PyObject* type) {
PY_STRUCT_LIKE(Vec4)
type->attr().set("ZERO", vm->new_user_object<Vec4>(0, 0, 0, 0));
type->attr().set("ONE", vm->new_user_object<Vec4>(1, 1, 1, 1));
vm->bind_func(type, __new__, 5, [](VM* vm, ArgsView args) {
float x = CAST_F(args[1]);
float y = CAST_F(args[2]);
float z = CAST_F(args[3]);
float w = CAST_F(args[4]);
return vm->new_object<Vec4>(PK_OBJ_GET(Type, args[0]), x, y, z, w);
});
vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar obj) -> Str {
Vec4 self = _CAST(Vec4&, obj);
SStream ss;
ss.setprecision(3);
ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")";
return ss.str();
});
PY_FIELD(Vec4, "x", x)
PY_FIELD(Vec4, "y", y)
PY_FIELD(Vec4, "z", z)
PY_FIELD(Vec4, "w", w)
BIND_VEC_VEC_OP(4, __add__, +)
BIND_VEC_VEC_OP(4, __sub__, -)
BIND_VEC_MUL_OP(4)
BIND_VEC_FUNCTION_1(Vec4&, dot)
BIND_VEC_FUNCTION_1(Vec4&, copy_)
BIND_VEC_FUNCTION_0(Vec4&, length)
BIND_VEC_FUNCTION_0(Vec4&, length_squared)
BIND_VEC_FUNCTION_0(Vec4&, normalize)
BIND_VEC_FUNCTION_0(Vec4&, normalize_)
BIND_VEC_GETITEM(4)
}
#undef BIND_VEC_VEC_OP
#undef BIND_VEC_MUL_OP
#undef BIND_VEC_FUNCTION_0
#undef BIND_VEC_FUNCTION_1
#undef BIND_VEC_GETITEM
void Mat3x3::_register(VM* vm, PyObject* mod, PyObject* type) {
PY_STRUCT_LIKE(Mat3x3)
vm->bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
if(args.size() == 1 + 0) return vm->new_object<Mat3x3>(PK_OBJ_GET(Type, args[0]), Mat3x3::zeros());
if(args.size() == 1 + 1) {
const List& list = CAST(List&, args[1]);
if(list.size() != 9) vm->TypeError("Mat3x3.__new__ takes a list of 9 floats");
Mat3x3 mat;
for(int i = 0; i < 9; i++)
mat.v[i] = CAST_F(list[i]);
return vm->new_object<Mat3x3>(PK_OBJ_GET(Type, args[0]), mat);
}
if(args.size() == 1 + 9) {
Mat3x3 mat;
for(int i = 0; i < 9; i++)
mat.v[i] = CAST_F(args[1 + i]);
return vm->new_object<Mat3x3>(PK_OBJ_GET(Type, args[0]), mat);
}
vm->TypeError(_S("Mat3x3.__new__ takes 0 or 1 or 9 arguments, got ", args.size() - 1));
return vm->None;
});
vm->bind_func(type, "copy_", 2, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
const Mat3x3& other = CAST(Mat3x3&, args[1]);
self = other;
return vm->None;
});
vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar obj) -> Str {
const Mat3x3& self = _CAST(Mat3x3&, obj);
SStream ss;
ss.setprecision(3);
ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n";
ss << " " << self._21 << ", " << self._22 << ", " << self._23 << ",\n";
ss << " " << self._31 << ", " << self._32 << ", " << self._33 << "])";
return ss.str();
});
vm->bind__getitem__(type->as<Type>(), [](VM* vm, PyVar obj, PyVar index) {
Mat3x3& self = _CAST(Mat3x3&, obj);
Tuple& t = CAST(Tuple&, index);
if(t.size() != 2) { vm->TypeError("Mat3x3.__getitem__ takes a tuple of 2 integers"); }
i64 i = CAST(i64, t[0]);
i64 j = CAST(i64, t[1]);
if(i < 0 || i >= 3 || j < 0 || j >= 3) { vm->IndexError("index out of range"); }
return VAR(self.m[i][j]);
});
vm->bind__setitem__(type->as<Type>(), [](VM* vm, PyVar obj, PyVar index, PyVar value) {
Mat3x3& self = _CAST(Mat3x3&, obj);
const Tuple& t = CAST(Tuple&, index);
if(t.size() != 2) { vm->TypeError("Mat3x3.__setitem__ takes a tuple of 2 integers"); }
i64 i = CAST(i64, t[0]);
i64 j = CAST(i64, t[1]);
if(i < 0 || i >= 3 || j < 0 || j >= 3) { vm->IndexError("index out of range"); }
self.m[i][j] = CAST_F(value);
});
vm->bind_field(type, "_11", &Mat3x3::_11);
vm->bind_field(type, "_12", &Mat3x3::_12);
vm->bind_field(type, "_13", &Mat3x3::_13);
vm->bind_field(type, "_21", &Mat3x3::_21);
vm->bind_field(type, "_22", &Mat3x3::_22);
vm->bind_field(type, "_23", &Mat3x3::_23);
vm->bind_field(type, "_31", &Mat3x3::_31);
vm->bind_field(type, "_32", &Mat3x3::_32);
vm->bind_field(type, "_33", &Mat3x3::_33);
vm->bind__add__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
Mat3x3& self = _CAST(Mat3x3&, _0);
Mat3x3& other = CAST(Mat3x3&, _1);
return vm->new_user_object<Mat3x3>(self + other);
});
vm->bind__sub__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
Mat3x3& self = _CAST(Mat3x3&, _0);
Mat3x3& other = CAST(Mat3x3&, _1);
return vm->new_user_object<Mat3x3>(self - other);
});
vm->bind__mul__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
Mat3x3& self = _CAST(Mat3x3&, _0);
f64 other = CAST_F(_1);
return vm->new_user_object<Mat3x3>(self * other);
});
vm->bind_func(type, "__rmul__", 2, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
f64 other = CAST_F(args[1]);
return vm->new_user_object<Mat3x3>(self * other);
});
vm->bind__truediv__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
Mat3x3& self = _CAST(Mat3x3&, _0);
f64 other = CAST_F(_1);
return vm->new_user_object<Mat3x3>(self / other);
});
vm->bind__matmul__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
Mat3x3& self = _CAST(Mat3x3&, _0);
if(vm->is_user_type<Mat3x3>(_1)) {
const Mat3x3& other = _CAST(Mat3x3&, _1);
return vm->new_user_object<Mat3x3>(self.matmul(other));
}
if(vm->is_user_type<Vec3>(_1)) {
const Vec3 other = _CAST(Vec3, _1);
return vm->new_user_object<Vec3>(self.matmul(other));
}
return vm->NotImplemented;
});
vm->bind(type, "matmul(self, other: mat3x3, out: mat3x3 = None)", [](VM* vm, ArgsView args) {
const Mat3x3& self = _CAST(Mat3x3&, args[0]);
const Mat3x3& other = CAST(Mat3x3&, args[1]);
if(is_none(args[2])) {
return vm->new_user_object<Mat3x3>(self.matmul(other));
} else {
Mat3x3& out = CAST(Mat3x3&, args[2]);
out = self.matmul(other);
return vm->None;
}
});
vm->bind_func(type, "determinant", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
return VAR(self.determinant());
});
vm->bind_func(type, "transpose", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
return vm->new_user_object<Mat3x3>(self.transpose());
});
vm->bind__invert__(type->as<Type>(), [](VM* vm, PyVar obj) {
Mat3x3& self = _CAST(Mat3x3&, obj);
Mat3x3 ret;
if(!self.inverse(ret)) vm->ValueError("matrix is not invertible");
return vm->new_user_object<Mat3x3>(ret);
});
vm->bind_func(type, "inverse", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
Mat3x3 ret;
if(!self.inverse(ret)) vm->ValueError("matrix is not invertible");
return vm->new_user_object<Mat3x3>(ret);
});
vm->bind_func(type, "inverse_", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
Mat3x3 ret;
if(!self.inverse(ret)) vm->ValueError("matrix is not invertible");
self = ret;
return vm->None;
});
vm->bind_func(type, "transpose_", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
self = self.transpose();
return vm->None;
});
// @staticmethod
vm->bind_func(
type,
"zeros",
0,
[](VM* vm, ArgsView args) {
return vm->new_user_object<Mat3x3>(Mat3x3::zeros());
},
{},
BindType_STATICMETHOD);
// @staticmethod
vm->bind_func(
type,
"ones",
0,
[](VM* vm, ArgsView args) {
return vm->new_user_object<Mat3x3>(Mat3x3::ones());
},
{},
BindType_STATICMETHOD);
// @staticmethod
vm->bind_func(
type,
"identity",
0,
[](VM* vm, ArgsView args) {
return vm->new_user_object<Mat3x3>(Mat3x3::identity());
},
{},
BindType_STATICMETHOD);
/*************** affine transformations ***************/
// @staticmethod
vm->bind(
type,
"trs(t: vec2, r: float, s: vec2)",
[](VM* vm, ArgsView args) {
Vec2 t = CAST(Vec2, args[0]);
f64 r = CAST_F(args[1]);
Vec2 s = CAST(Vec2, args[2]);
return vm->new_user_object<Mat3x3>(Mat3x3::trs(t, r, s));
},
{},
BindType_STATICMETHOD);
vm->bind(type, "copy_trs_(self, t: vec2, r: float, s: vec2)", [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
Vec2 t = CAST(Vec2, args[1]);
f64 r = CAST_F(args[2]);
Vec2 s = CAST(Vec2, args[3]);
self = Mat3x3::trs(t, r, s);
return vm->None;
});
vm->bind(type, "copy_t_(self, t: vec2)", [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
Vec2 t = CAST(Vec2, args[1]);
self = Mat3x3::trs(t, self._r(), self._s());
return vm->None;
});
vm->bind(type, "copy_r_(self, r: float)", [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
f64 r = CAST_F(args[1]);
self = Mat3x3::trs(self._t(), r, self._s());
return vm->None;
});
vm->bind(type, "copy_s_(self, s: vec2)", [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
Vec2 s = CAST(Vec2, args[1]);
self = Mat3x3::trs(self._t(), self._r(), s);
return vm->None;
});
vm->bind_func(type, "is_affine", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
return VAR(self.is_affine());
});
vm->bind_func(type, "_t", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
return vm->new_user_object<Vec2>(self._t());
});
vm->bind_func(type, "_r", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
return VAR(self._r());
});
vm->bind_func(type, "_s", 1, [](VM* vm, ArgsView args) {
Mat3x3& self = _CAST(Mat3x3&, args[0]);
return vm->new_user_object<Vec2>(self._s());
});
vm->bind_func(type, "transform_point", 2, [](VM* vm, ArgsView args) {
const Mat3x3& self = _CAST(Mat3x3&, args[0]);
Vec2 v = CAST(Vec2, args[1]);
Vec2 res(self._11 * v.x + self._12 * v.y + self._13, self._21 * v.x + self._22 * v.y + self._23);
return vm->new_user_object<Vec2>(res);
});
vm->bind_func(type, "inverse_transform_point", 2, [](VM* vm, ArgsView args) {
const Mat3x3& self = _CAST(Mat3x3&, args[0]);
Vec2 v = CAST(Vec2, args[1]);
Mat3x3 inv;
if(!self.inverse(inv)) vm->ValueError("matrix is not invertible");
Vec2 res(inv._11 * v.x + inv._12 * v.y + inv._13, inv._21 * v.x + inv._22 * v.y + inv._23);
return vm->new_user_object<Vec2>(res);
});
vm->bind_func(type, "transform_vector", 2, [](VM* vm, ArgsView args) {
const Mat3x3& self = _CAST(Mat3x3&, args[0]);
Vec2 v = CAST(Vec2, args[1]);
Vec2 res(self._11 * v.x + self._12 * v.y, self._21 * v.x + self._22 * v.y);
return vm->new_user_object<Vec2>(res);
});
vm->bind_func(type, "inverse_transform_vector", 2, [](VM* vm, ArgsView args) {
const Mat3x3& self = _CAST(Mat3x3&, args[0]);
Vec2 v = CAST(Vec2, args[1]);
Mat3x3 inv;
if(!self.inverse(inv)) vm->ValueError("matrix is not invertible");
Vec2 res(inv._11 * v.x + inv._12 * v.y, inv._21 * v.x + inv._22 * v.y);
return vm->new_user_object<Vec2>(res);
});
}
void add_module_linalg(VM* vm) {
PyObject* linalg = vm->new_module("linalg");
vm->register_user_class<Vec2>(linalg, "vec2", VM::tp_object);
vm->register_user_class<Vec3>(linalg, "vec3", VM::tp_object);
vm->register_user_class<Vec4>(linalg, "vec4", VM::tp_object, true);
vm->register_user_class<Mat3x3>(linalg, "mat3x3", VM::tp_object, true);
PyVar float_p = vm->_modules["c"]->attr("float_p");
linalg->attr().set("vec4_p", float_p);
linalg->attr().set("mat3x3_p", float_p);
}
/////////////// mat3x3 ///////////////
Mat3x3::Mat3x3() {}
Mat3x3::Mat3x3(float _11, float _12, float _13, float _21, float _22, float _23, float _31, float _32, float _33) :
_11(_11), _12(_12), _13(_13), _21(_21), _22(_22), _23(_23), _31(_31), _32(_32), _33(_33) {}
Mat3x3 Mat3x3::zeros() { return Mat3x3(0, 0, 0, 0, 0, 0, 0, 0, 0); }
Mat3x3 Mat3x3::ones() { return Mat3x3(1, 1, 1, 1, 1, 1, 1, 1, 1); }
Mat3x3 Mat3x3::identity() { return Mat3x3(1, 0, 0, 0, 1, 0, 0, 0, 1); }
Mat3x3 Mat3x3::operator+ (const Mat3x3& other) const {
Mat3x3 ret;
for(int i = 0; i < 9; ++i)
ret.v[i] = v[i] + other.v[i];
return ret;
}
Mat3x3 Mat3x3::operator- (const Mat3x3& other) const {
Mat3x3 ret;
for(int i = 0; i < 9; ++i)
ret.v[i] = v[i] - other.v[i];
return ret;
}
Mat3x3 Mat3x3::operator* (float scalar) const {
Mat3x3 ret;
for(int i = 0; i < 9; ++i)
ret.v[i] = v[i] * scalar;
return ret;
}
Mat3x3 Mat3x3::operator/ (float scalar) const {
Mat3x3 ret;
for(int i = 0; i < 9; ++i)
ret.v[i] = v[i] / scalar;
return ret;
}
bool Mat3x3::operator== (const Mat3x3& other) const {
for(int i = 0; i < 9; ++i) {
if(!isclose(v[i], other.v[i])) return false;
}
return true;
}
bool Mat3x3::operator!= (const Mat3x3& other) const {
for(int i = 0; i < 9; ++i) {
if(!isclose(v[i], other.v[i])) return true;
}
return false;
}
Mat3x3 Mat3x3::matmul(const Mat3x3& other) const {
Mat3x3 out;
out._11 = _11 * other._11 + _12 * other._21 + _13 * other._31;
out._12 = _11 * other._12 + _12 * other._22 + _13 * other._32;
out._13 = _11 * other._13 + _12 * other._23 + _13 * other._33;
out._21 = _21 * other._11 + _22 * other._21 + _23 * other._31;
out._22 = _21 * other._12 + _22 * other._22 + _23 * other._32;
out._23 = _21 * other._13 + _22 * other._23 + _23 * other._33;
out._31 = _31 * other._11 + _32 * other._21 + _33 * other._31;
out._32 = _31 * other._12 + _32 * other._22 + _33 * other._32;
out._33 = _31 * other._13 + _32 * other._23 + _33 * other._33;
return out;
}
Vec3 Mat3x3::matmul(const Vec3& other) const {
Vec3 out;
out.x = _11 * other.x + _12 * other.y + _13 * other.z;
out.y = _21 * other.x + _22 * other.y + _23 * other.z;
out.z = _31 * other.x + _32 * other.y + _33 * other.z;
return out;
}
float Mat3x3::determinant() const {
return _11 * _22 * _33 + _12 * _23 * _31 + _13 * _21 * _32 - _11 * _23 * _32 - _12 * _21 * _33 - _13 * _22 * _31;
}
Mat3x3 Mat3x3::transpose() const {
Mat3x3 ret;
ret._11 = _11;
ret._12 = _21;
ret._13 = _31;
ret._21 = _12;
ret._22 = _22;
ret._23 = _32;
ret._31 = _13;
ret._32 = _23;
ret._33 = _33;
return ret;
}
bool Mat3x3::inverse(Mat3x3& out) const {
float det = determinant();
if(isclose(det, 0)) return false;
float inv_det = 1.0f / det;
out._11 = (_22 * _33 - _23 * _32) * inv_det;
out._12 = (_13 * _32 - _12 * _33) * inv_det;
out._13 = (_12 * _23 - _13 * _22) * inv_det;
out._21 = (_23 * _31 - _21 * _33) * inv_det;
out._22 = (_11 * _33 - _13 * _31) * inv_det;
out._23 = (_13 * _21 - _11 * _23) * inv_det;
out._31 = (_21 * _32 - _22 * _31) * inv_det;
out._32 = (_12 * _31 - _11 * _32) * inv_det;
out._33 = (_11 * _22 - _12 * _21) * inv_det;
return true;
}
Mat3x3 Mat3x3::trs(Vec2 t, float radian, Vec2 s) {
float cr = cosf(radian);
float sr = sinf(radian);
return Mat3x3(s.x * cr, -s.y * sr, t.x, s.x * sr, s.y * cr, t.y, 0.0f, 0.0f, 1.0f);
}
bool Mat3x3::is_affine() const {
float det = _11 * _22 - _12 * _21;
if(isclose(det, 0)) return false;
return _31 == 0.0f && _32 == 0.0f && _33 == 1.0f;
}
Vec2 Mat3x3::_t() const { return Vec2(_13, _23); }
float Mat3x3::_r() const { return atan2f(_21, _11); }
Vec2 Mat3x3::_s() const { return Vec2(sqrtf(_11 * _11 + _21 * _21), sqrtf(_12 * _12 + _22 * _22)); }
} // namespace pkpy

View File

@ -1,355 +0,0 @@
#include "pocketpy/modules/modules.hpp"
#include "pocketpy/interpreter/bindings.hpp"
#include "pocketpy/common/export.h"
#include "pocketpy/common/_generated.h"
#include <chrono>
#include <cmath>
namespace pkpy {
struct PyStructTime {
int tm_year;
int tm_mon;
int tm_mday;
int tm_hour;
int tm_min;
int tm_sec;
int tm_wday;
int tm_yday;
int tm_isdst;
PyStructTime(std::time_t t) {
std::tm* tm = std::localtime(&t);
tm_year = tm->tm_year + 1900;
tm_mon = tm->tm_mon + 1;
tm_mday = tm->tm_mday;
tm_hour = tm->tm_hour;
tm_min = tm->tm_min;
tm_sec = tm->tm_sec;
tm_wday = (tm->tm_wday + 6) % 7;
tm_yday = tm->tm_yday + 1;
tm_isdst = tm->tm_isdst;
}
static void _register(VM* vm, PyObject* mod, PyObject* type) {
PY_READONLY_FIELD(PyStructTime, "tm_year", tm_year);
PY_READONLY_FIELD(PyStructTime, "tm_mon", tm_mon);
PY_READONLY_FIELD(PyStructTime, "tm_mday", tm_mday);
PY_READONLY_FIELD(PyStructTime, "tm_hour", tm_hour);
PY_READONLY_FIELD(PyStructTime, "tm_min", tm_min);
PY_READONLY_FIELD(PyStructTime, "tm_sec", tm_sec);
PY_READONLY_FIELD(PyStructTime, "tm_wday", tm_wday);
PY_READONLY_FIELD(PyStructTime, "tm_yday", tm_yday);
PY_READONLY_FIELD(PyStructTime, "tm_isdst", tm_isdst);
}
};
void add_module_time(VM* vm) {
PyObject* mod = vm->new_module("time");
vm->register_user_class<PyStructTime>(mod, "struct_time");
vm->bind_func(mod, "time", 0, [](VM* vm, ArgsView args) {
auto now = std::chrono::system_clock::now();
return VAR(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() / 1000.0);
});
vm->bind_func(mod, "sleep", 1, [](VM* vm, ArgsView args) {
f64 seconds = CAST_F(args[0]);
auto begin = std::chrono::system_clock::now();
while(true) {
auto now = std::chrono::system_clock::now();
f64 elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - begin).count() / 1000.0;
if(elapsed >= seconds) break;
}
return vm->None;
});
vm->bind_func(mod, "localtime", 0, [](VM* vm, ArgsView args) {
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
return vm->new_user_object<PyStructTime>(t);
});
}
void add_module_sys(VM* vm) {
PyObject* mod = vm->new_module("sys");
vm->setattr(mod, "version", VAR(PK_VERSION));
vm->setattr(mod, "platform", VAR(kPlatformStrings[PK_SYS_PLATFORM]));
PyObject* stdout_ = vm->new_object<DummyInstance>(vm->tp_object).get();
PyObject* stderr_ = vm->new_object<DummyInstance>(vm->tp_object).get();
vm->setattr(mod, "stdout", stdout_);
vm->setattr(mod, "stderr", stderr_);
vm->bind_func(stdout_, "write", 1, [](VM* vm, ArgsView args) {
Str& s = CAST(Str&, args[0]);
vm->stdout_write(s);
return vm->None;
});
vm->bind_func(stderr_, "write", 1, [](VM* vm, ArgsView args) {
Str& s = CAST(Str&, args[0]);
vm->stderr_write(s);
return vm->None;
});
}
void add_module_json(VM* vm) {
PyObject* mod = vm->new_module("json");
vm->bind_func(mod, "loads", 1, [](VM* vm, ArgsView args) {
std::string_view sv;
if(is_type(args[0], vm->tp_bytes)) {
const Bytes& b = PK_OBJ_GET(Bytes, args[0]);
sv = std::string_view((char*)b.data(), b.size());
} else {
sv = CAST(Str&, args[0]).sv();
}
CodeObject* code = vm->compile(sv, "<json>", JSON_MODE);
PyVar retval = vm->_exec(code, vm->callstack.top()._module);
CodeObject__delete(code);
return retval;
});
vm->bind_func(mod, "dumps", 1, [](VM* vm, ArgsView args) {
return VAR(vm->py_json(args[0]));
});
}
// https://docs.python.org/3.5/library/math.html
void add_module_math(VM* vm) {
PyObject* mod = vm->new_module("math");
mod->attr().set("pi", VAR(3.1415926535897932384));
mod->attr().set("e", VAR(2.7182818284590452354));
mod->attr().set("inf", VAR(std::numeric_limits<double>::infinity()));
mod->attr().set("nan", VAR(std::numeric_limits<double>::quiet_NaN()));
vm->bind_func(mod, "ceil", 1, PK_LAMBDA(VAR((i64)std::ceil(CAST_F(args[0])))));
vm->bind_func(mod, "fabs", 1, PK_LAMBDA(VAR(std::fabs(CAST_F(args[0])))));
vm->bind_func(mod, "floor", 1, PK_LAMBDA(VAR((i64)std::floor(CAST_F(args[0])))));
vm->bind_func(mod, "fsum", 1, [](VM* vm, ArgsView args) {
List& list = CAST(List&, args[0]);
double sum = 0;
double c = 0;
for(PyVar arg: list) {
double x = CAST_F(arg);
double y = x - c;
double t = sum + y;
c = (t - sum) - y;
sum = t;
}
return VAR(sum);
});
vm->bind_func(mod, "gcd", 2, [](VM* vm, ArgsView args) {
i64 a = CAST(i64, args[0]);
i64 b = CAST(i64, args[1]);
if(a < 0) a = -a;
if(b < 0) b = -b;
while(b != 0) {
i64 t = b;
b = a % b;
a = t;
}
return VAR(a);
});
vm->bind_func(mod, "isfinite", 1, PK_LAMBDA(VAR(std::isfinite(CAST_F(args[0])))));
vm->bind_func(mod, "isinf", 1, PK_LAMBDA(VAR(std::isinf(CAST_F(args[0])))));
vm->bind_func(mod, "isnan", 1, PK_LAMBDA(VAR(std::isnan(CAST_F(args[0])))));
vm->bind_func(mod, "isclose", 2, [](VM* vm, ArgsView args) {
f64 a = CAST_F(args[0]);
f64 b = CAST_F(args[1]);
return VAR(std::fabs(a - b) < 1e-9);
});
vm->bind_func(mod, "exp", 1, PK_LAMBDA(VAR(std::exp(CAST_F(args[0])))));
vm->bind(mod, "log(x, base=2.718281828459045)", [](VM* vm, ArgsView args) {
f64 x = CAST_F(args[0]);
f64 base = CAST_F(args[1]);
return VAR(std::log(x) / std::log(base));
});
vm->bind_func(mod, "log2", 1, PK_LAMBDA(VAR(std::log2(CAST_F(args[0])))));
vm->bind_func(mod, "log10", 1, PK_LAMBDA(VAR(std::log10(CAST_F(args[0])))));
vm->bind_func(mod, "pow", 2, PK_LAMBDA(VAR(std::pow(CAST_F(args[0]), CAST_F(args[1])))));
vm->bind_func(mod, "sqrt", 1, PK_LAMBDA(VAR(std::sqrt(CAST_F(args[0])))));
vm->bind_func(mod, "acos", 1, PK_LAMBDA(VAR(std::acos(CAST_F(args[0])))));
vm->bind_func(mod, "asin", 1, PK_LAMBDA(VAR(std::asin(CAST_F(args[0])))));
vm->bind_func(mod, "atan", 1, PK_LAMBDA(VAR(std::atan(CAST_F(args[0])))));
vm->bind_func(mod, "atan2", 2, PK_LAMBDA(VAR(std::atan2(CAST_F(args[0]), CAST_F(args[1])))));
vm->bind_func(mod, "cos", 1, PK_LAMBDA(VAR(std::cos(CAST_F(args[0])))));
vm->bind_func(mod, "sin", 1, PK_LAMBDA(VAR(std::sin(CAST_F(args[0])))));
vm->bind_func(mod, "tan", 1, PK_LAMBDA(VAR(std::tan(CAST_F(args[0])))));
vm->bind_func(mod, "degrees", 1, PK_LAMBDA(VAR(CAST_F(args[0]) * 180 / 3.1415926535897932384)));
vm->bind_func(mod, "radians", 1, PK_LAMBDA(VAR(CAST_F(args[0]) * 3.1415926535897932384 / 180)));
vm->bind_func(mod, "modf", 1, [](VM* vm, ArgsView args) {
f64 i;
f64 f = std::modf(CAST_F(args[0]), &i);
return VAR(Tuple(VAR(f), VAR(i)));
});
vm->bind_func(mod, "factorial", 1, [](VM* vm, ArgsView args) {
i64 n = CAST(i64, args[0]);
if(n < 0) vm->ValueError("factorial() not defined for negative values");
i64 r = 1;
for(i64 i = 2; i <= n; i++)
r *= i;
return VAR(r);
});
}
void add_module_traceback(VM* vm) {
PyObject* mod = vm->new_module("traceback");
vm->bind_func(mod, "print_exc", 0, [](VM* vm, ArgsView args) {
if(vm->__last_exception == nullptr) vm->ValueError("no exception");
Exception& e = vm->__last_exception->as<Exception>();
vm->stdout_write(e.summary());
return vm->None;
});
vm->bind_func(mod, "format_exc", 0, [](VM* vm, ArgsView args) {
if(vm->__last_exception == nullptr) vm->ValueError("no exception");
Exception& e = vm->__last_exception->as<Exception>();
return VAR(e.summary());
});
}
void add_module_dis(VM* vm) {
PyObject* mod = vm->new_module("dis");
vm->bind_func(mod, "dis", 1, [](VM* vm, ArgsView args) {
CodeObject* code;
bool need_delete = false;
PyVar obj = args[0];
if(is_type(obj, vm->tp_str)) {
const Str& source = CAST(Str, obj);
code = vm->compile(source, "<dis>", EXEC_MODE);
need_delete = true;
}
PyVar f = obj;
if(is_type(f, vm->tp_bound_method)) f = CAST(BoundMethod, obj).func;
code = CAST(Function&, f).decl->code;
vm->stdout_write(vm->disassemble(code));
if(need_delete) CodeObject__delete(code);
return vm->None;
});
}
void add_module_gc(VM* vm) {
PyObject* mod = vm->new_module("gc");
vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(pk_ManagedHeap__collect(&vm->heap))));
}
void add_module_enum(VM* vm) {
PyObject* mod = vm->new_module("enum");
CodeObject* code = vm->compile(kPythonLibs__enum, "enum.py", EXEC_MODE);
vm->_exec(code, mod);
CodeObject__delete(code);
PyVar Enum = mod->attr("Enum");
vm->_all_types[PK_OBJ_GET(Type, Enum)].on_end_subclass = [](VM* vm, PyTypeInfo* new_ti) {
new_ti->subclass_enabled = false; // Enum class cannot be subclassed twice
NameDict& attr = new_ti->obj->attr();
for(auto [k, v]: attr.items()) {
// wrap every attribute
std::string_view k_sv = k.sv();
if(k_sv.empty() || k_sv[0] == '_') continue;
attr.set(k, vm->call(new_ti->obj, VAR(k_sv), v));
}
};
}
void add_module___builtins(VM* vm) {
PyObject* mod = vm->new_module("__builtins");
vm->bind_func(mod, "next", 1, [](VM* vm, ArgsView args) {
return vm->py_next(args[0]);
});
vm->bind_func(mod, "_enable_instance_dict", 1, [](VM* vm, ArgsView args) {
PyVar self = args[0];
if(is_tagged(self)) vm->TypeError("object: tagged object cannot enable instance dict");
if(self->is_attr_valid()) vm->RuntimeError("object: instance dict is already enabled");
self->_attr = new NameDict();
return vm->None;
});
}
/************************************************/
#if PK_ENABLE_PROFILER
struct LineProfilerW;
struct _LpGuard {
PK_ALWAYS_PASS_BY_POINTER(_LpGuard)
LineProfilerW* lp;
VM* vm;
_LpGuard(LineProfilerW* lp, VM* vm);
~_LpGuard();
};
// line_profiler wrapper
struct LineProfilerW {
LineProfiler profiler;
static void _register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind_func(type, __new__, 1, [](VM* vm, ArgsView args) {
Type cls = PK_OBJ_GET(Type, args[0]);
return vm->new_object<LineProfilerW>(cls);
});
vm->bind(type, "add_function(self, func)", [](VM* vm, ArgsView args) {
LineProfilerW& self = PK_OBJ_GET(LineProfilerW, args[0]);
vm->check_type(args[1], VM::tp_function);
auto decl = PK_OBJ_GET(Function, args[1]).decl.get();
self.profiler.functions.push_back(decl);
return vm->None;
});
vm->bind(type, "runcall(self, func, *args)", [](VM* vm, ArgsView view) {
LineProfilerW& self = PK_OBJ_GET(LineProfilerW, view[0]);
PyVar func = view[1];
const Tuple& args = CAST(Tuple&, view[2]);
vm->s_data.push(func);
vm->s_data.push(PY_NULL);
for(PyVar arg: args)
vm->s_data.push(arg);
_LpGuard guard(&self, vm);
PyVar ret = vm->vectorcall(args.size());
return ret;
});
vm->bind(type, "print_stats(self)", [](VM* vm, ArgsView args) {
LineProfilerW& self = PK_OBJ_GET(LineProfilerW, args[0]);
vm->stdout_write(self.profiler.stats());
return vm->None;
});
}
};
_LpGuard::_LpGuard(LineProfilerW* lp, VM* vm) : lp(lp), vm(vm) {
if(vm->_profiler) { vm->ValueError("only one profiler can be enabled at a time"); }
vm->_profiler = &lp->profiler;
lp->profiler.begin();
}
_LpGuard::~_LpGuard() {
vm->_profiler = nullptr;
lp->profiler.end();
}
void add_module_line_profiler(VM* vm) {
PyObject* mod = vm->new_module("line_profiler");
vm->register_user_class<LineProfilerW>(mod, "LineProfiler");
}
#else
void add_module_line_profiler(VM* vm) { (void)vm; }
#endif
} // namespace pkpy

View File

@ -1,225 +0,0 @@
#include "pocketpy/modules/random.hpp"
#include "pocketpy/interpreter/bindings.hpp"
#include <chrono>
/* https://github.com/clibs/mt19937ar
Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima
University and The University of Tokyo. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the Hiroshima University nor the names of
its contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct mt19937 {
const static int N = 624;
const static int M = 397;
const uint32_t MATRIX_A = 0x9908b0dfUL; /* constant vector a */
const uint32_t UPPER_MASK = 0x80000000UL; /* most significant w-r bits */
const uint32_t LOWER_MASK = 0x7fffffffUL; /* least significant r bits */
uint32_t mt[N]; /* the array for the state vector */
int mti = N + 1; /* mti==N+1 means mt[N] is not initialized */
/* initializes mt[N] with a seed */
void seed(uint32_t s) {
mt[0] = s & 0xffffffffUL;
for(mti = 1; mti < N; mti++) {
mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
mt[mti] &= 0xffffffffUL;
/* for >32 bit machines */
}
}
/* generates a random number on [0,0xffffffff]-interval */
uint32_t next_uint32(void) {
uint32_t y;
static uint32_t mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if(mti >= N) { /* generate N words at one time */
int kk;
if(mti == N + 1) /* if init_genrand() has not been called, */
seed(5489UL); /* a default initial seed is used */
for(kk = 0; kk < N - M; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for(; kk < N - 1; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
y = mt[mti++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
uint64_t next_uint64(void) { return (uint64_t(next_uint32()) << 32) | next_uint32(); }
/* generates a random number on [0,1)-real-interval */
float random(void) { return next_uint32() * (1.0 / 4294967296.0); /* divided by 2^32 */ }
/* generates a random number on [a, b]-interval */
int64_t randint(int64_t a, int64_t b) {
uint64_t delta = b - a + 1;
if(delta < 0x80000000UL) {
return a + next_uint32() % (uint32_t)delta;
} else {
return a + next_uint64() % delta;
}
}
float uniform(float a, float b) { return a + random() * (b - a); }
};
namespace pkpy {
struct Random {
mt19937 gen;
Random() {
auto count = std::chrono::high_resolution_clock::now().time_since_epoch().count();
gen.seed((uint32_t)count);
}
static void _register(VM* vm, PyObject* mod, PyObject* type) {
vm->bind_func(type, __new__, 1, [](VM* vm, ArgsView args) {
Type cls = PK_OBJ_GET(Type, args[0]);
return vm->new_object<Random>(cls);
});
vm->bind_func(type, "seed", 2, [](VM* vm, ArgsView args) {
Random& self = PK_OBJ_GET(Random, args[0]);
self.gen.seed(CAST(i64, args[1]));
return vm->None;
});
vm->bind_func(type, "randint", 3, [](VM* vm, ArgsView args) {
Random& self = PK_OBJ_GET(Random, args[0]);
i64 a = CAST(i64, args[1]);
i64 b = CAST(i64, args[2]);
if(a > b) vm->ValueError("randint(a, b): a must be less than or equal to b");
return VAR(self.gen.randint(a, b));
});
vm->bind_func(type, "random", 1, [](VM* vm, ArgsView args) {
Random& self = PK_OBJ_GET(Random, args[0]);
return VAR(self.gen.random());
});
vm->bind_func(type, "uniform", 3, [](VM* vm, ArgsView args) {
Random& self = PK_OBJ_GET(Random, args[0]);
f64 a = CAST(f64, args[1]);
f64 b = CAST(f64, args[2]);
if(a > b) std::swap(a, b);
return VAR(self.gen.uniform(a, b));
});
vm->bind_func(type, "shuffle", 2, [](VM* vm, ArgsView args) {
Random& self = PK_OBJ_GET(Random, args[0]);
List& L = CAST(List&, args[1]);
for(int i = L.size() - 1; i > 0; i--) {
int j = self.gen.randint(0, i);
std::swap(L[i], L[j]);
}
return vm->None;
});
vm->bind_func(type, "choice", 2, [](VM* vm, ArgsView args) {
Random& self = PK_OBJ_GET(Random, args[0]);
ArgsView view = vm->cast_array_view(args[1]);
if(view.empty()) vm->IndexError("cannot choose from an empty sequence");
int index = self.gen.randint(0, view.size() - 1);
return view[index];
});
vm->bind(type, "choices(self, population, weights=None, k=1)", [](VM* vm, ArgsView args) {
Random& self = PK_OBJ_GET(Random, args[0]);
ArgsView view = vm->cast_array_view(args[1]);
PyVar* data = view.begin();
int size = view.size();
if(size == 0) vm->IndexError("cannot choose from an empty sequence");
array<f64> cum_weights(size);
if(is_none(args[2])) {
for(int i = 0; i < size; i++)
cum_weights[i] = i + 1;
} else {
ArgsView weights = vm->cast_array_view(args[2]);
if(weights.size() != size) vm->ValueError(_S("len(weights) != ", size));
cum_weights[0] = CAST(f64, weights[0]);
for(int i = 1; i < size; i++) {
cum_weights[i] = cum_weights[i - 1] + CAST(f64, weights[i]);
}
}
if(cum_weights[size - 1] <= 0) vm->ValueError("total of weights must be greater than zero");
int k = CAST(int, args[3]);
List result(k);
for(int i = 0; i < k; i++) {
f64 key = self.gen.uniform(0.0, cum_weights[size - 1]);
int index;
c11__lower_bound(f64, cum_weights.begin(), cum_weights.size(), key, c11__less, &index);
assert(index != cum_weights.size());
result[i] = data[index];
}
return VAR(std::move(result));
});
}
};
void add_module_random(VM* vm) {
PyObject* mod = vm->new_module("random");
vm->register_user_class<Random>(mod, "Random");
PyVar instance = vm->new_user_object<Random>();
mod->attr().set("seed", vm->getattr(instance, "seed"));
mod->attr().set("random", vm->getattr(instance, "random"));
mod->attr().set("uniform", vm->getattr(instance, "uniform"));
mod->attr().set("randint", vm->getattr(instance, "randint"));
mod->attr().set("shuffle", vm->getattr(instance, "shuffle"));
mod->attr().set("choice", vm->getattr(instance, "choice"));
mod->attr().set("choices", vm->getattr(instance, "choices"));
}
} // namespace pkpy

View File

@ -1,8 +1,6 @@
#include "pocketpy/objects/base.h" #include "pocketpy/objects/base.h"
struct pkpy_G pkpy_g; PyVar PY_NULL = {.type=0, .is_ptr=false, .extra=0, ._i64=0};
PyVar PY_OP_CALL = {.type=27, .is_ptr=false, .extra=0, ._i64=0};
PyVar pkpy_NULL = {.type=0, .is_ptr=false, .extra=0, ._i64=0}; PyVar PY_OP_YIELD = {.type=28, .is_ptr=false, .extra=0, ._i64=0};
PyVar pkpy_OP_CALL = {.type=27, .is_ptr=false, .extra=0, ._i64=0};
PyVar pkpy_OP_YIELD = {.type=28, .is_ptr=false, .extra=0, ._i64=0};

View File

@ -3,6 +3,6 @@
#define SMALLMAP_T__SOURCE #define SMALLMAP_T__SOURCE
#define K uint16_t #define K uint16_t
#define V PyVar #define V PyVar
#define NAME pkpy_NameDict #define NAME pk_NameDict
#include "pocketpy/xmacros/smallmap.h" #include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__SOURCE #undef SMALLMAP_T__SOURCE

View File

@ -1,21 +0,0 @@
#include "pocketpy/objects/base.h"
#include "pocketpy/interpreter/vm.hpp"
extern "C" {
bool py_eq(const PyVar* a, const PyVar* b){
auto vm = (pkpy::VM*)pkpy_g.vm;
return vm->py_eq(*a, *b);
}
bool py_le(const PyVar* a, const PyVar* b){
auto vm = (pkpy::VM*)pkpy_g.vm;
return vm->py_le(*a, *b);
}
int64_t py_hash(const PyVar* a){
auto vm = (pkpy::VM*)pkpy_g.vm;
return vm->py_hash(*a);
}
}

View File

@ -1,56 +0,0 @@
#include "pocketpy/objects/tuplelist.hpp"
namespace pkpy {
Tuple::Tuple(int n) {
if(n <= INLINED_SIZE) {
this->_args = _inlined;
} else {
this->_args = (PyVar*)std::malloc(n * sizeof(PyVar));
}
this->_size = n;
}
Tuple::Tuple(Tuple&& other) noexcept {
_size = other._size;
if(other.is_inlined()) {
_args = _inlined;
for(int i = 0; i < _size; i++)
_args[i] = other._args[i];
} else {
_args = other._args;
other._args = other._inlined;
other._size = 0;
}
}
Tuple::Tuple(PyVar _0, PyVar _1) : Tuple(2) {
_args[0] = _0;
_args[1] = _1;
}
Tuple::Tuple(PyVar _0, PyVar _1, PyVar _2) : Tuple(3) {
_args[0] = _0;
_args[1] = _1;
_args[2] = _2;
}
Tuple::~Tuple() {
if(!is_inlined()) std::free(_args);
}
List ArgsView::to_list() const {
List ret(size());
for(int i = 0; i < size(); i++)
ret[i] = _begin[i];
return ret;
}
Tuple ArgsView::to_tuple() const {
Tuple ret(size());
for(int i = 0; i < size(); i++)
ret[i] = _begin[i];
return ret;
}
} // namespace pkpy

View File

@ -1,9 +1,22 @@
#include "pocketpy/objects/public.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
pk_VM* pk_vm;
static pk_VM pk_default_vm;
void py_initialize(){ void py_initialize(){
// initialize the global VM Pools_initialize();
pk_StrName__initialize();
pk_vm = &pk_default_vm;
pk_VM__ctor(&pk_default_vm);
}
void py_finalize(){
pk_VM__dtor(&pk_default_vm);
pk_vm = NULL;
pk_StrName__finalize();
Pools_finalize();
} }
void py_newint(PyVar* self, int64_t val){ void py_newint(PyVar* self, int64_t val){

File diff suppressed because it is too large Load Diff

View File

@ -1,575 +0,0 @@
#include "pocketpy/objects/codeobject.h"
#ifndef PK_NO_EXPORT_C_API
#include "pocketpy/pocketpy.hpp"
#include "pocketpy/pocketpy_c.h"
#include <iostream>
namespace pkpy{
#define PK_ASSERT_N_EXTRA_ELEMENTS(n) \
int __ex_count = count_extra_elements(vm, n); \
if(__ex_count < n) { \
Str msg = _S("expected at least ", n, " elements, got ", __ex_count); \
pkpy_error(vm_handle, "StackError", pkpy_string(msg.c_str())); \
return false; \
}
#define PK_ASSERT_NO_ERROR() \
if(vm->__c.error != nullptr) return false;
static int count_extra_elements(VM* vm, int n) {
if(vm->callstack.empty()) { return vm->s_data.size(); }
assert(!vm->__c.s_view.empty());
return vm->s_data._sp - vm->__c.s_view.back().end();
}
static PyVar stack_item(VM* vm, int index) {
PyVar* begin;
PyVar* end = vm->s_data.end();
if(vm->callstack.empty()) {
begin = vm->s_data.begin();
} else {
assert(!vm->__c.s_view.empty());
begin = vm->__c.s_view.back().begin();
}
int size = end - begin;
if(index < 0) index += size;
assert(index >= 0 && index < size);
return begin[index];
}
#define PK_PROTECTED(__B) \
try { \
__B \
} catch(TopLevelException e) { \
vm->__c.error = (PyObject*)e.ptr->self; \
return false; \
} catch(const std::exception& re) { \
PyObject* e_t = vm->_t(vm->tp_exception); \
vm->__c.error = vm->call(e_t, VAR(re.what())).get(); \
return false; \
}
pkpy_vm* pkpy_new_vm(bool enable_os) { return (pkpy_vm*)new VM(enable_os); }
void pkpy_delete_vm(pkpy_vm* vm) { return delete (VM*)vm; }
bool pkpy_exec(pkpy_vm* vm_handle, const char* source) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar res;
PK_PROTECTED(
CodeObject* code = vm->compile(source, "main.py", EXEC_MODE);
res = vm->_exec(code, vm->_main);
CodeObject__delete(code);
)
return res != nullptr;
}
bool pkpy_exec_2(pkpy_vm* vm_handle, const char* source, const char* filename, int mode, const char* module) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar res;
PyObject* mod;
PK_PROTECTED(
if(module == nullptr){ mod = vm->_main;
}else{
mod = vm->_modules[module].get(); // may raise
}
CodeObject* code = vm->compile(source, filename, (CompileMode)mode);
res = vm->_exec(code, mod);
CodeObject__delete(code); // TODO: _exec may raise, so code may leak
)
return res != nullptr;
}
void pkpy_set_main_argv(pkpy_vm* vm_handle, int argc, char** argv) {
VM* vm = (VM*)vm_handle;
vm->set_main_argv(argc, argv);
}
bool pkpy_dup(pkpy_vm* vm_handle, int n) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, n);
vm->s_data.push(item);
)
return true;
}
bool pkpy_pop(pkpy_vm* vm_handle, int n) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(n)
vm->s_data.shrink(n);
return true;
}
bool pkpy_pop_top(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
vm->s_data.pop();
return true;
}
bool pkpy_dup_top(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
vm->s_data.push(vm->s_data.top());
return true;
}
bool pkpy_rot_two(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(2)
std::swap(vm->s_data.top(), vm->s_data.second());
return true;
}
int pkpy_stack_size(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
if(vm->callstack.empty()) { return vm->s_data.size(); }
if(vm->__c.s_view.empty()) exit(127);
return vm->s_data._sp - vm->__c.s_view.back().begin();
}
// int
bool pkpy_push_int(pkpy_vm* vm_handle, int value) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar res;
PK_PROTECTED(
// int may overflow so we should protect it
res = py_var(vm, value);
)
vm->s_data.push(res);
return true;
}
bool pkpy_is_int(pkpy_vm* vm_handle, int i) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
return is_int(stack_item(vm, i));
)
}
bool pkpy_to_int(pkpy_vm* vm_handle, int i, int* out) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
*out = py_cast<int>(vm, item);
)
return true;
}
// float
bool pkpy_push_float(pkpy_vm* vm_handle, double value) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar res = py_var(vm, value);
vm->s_data.push(res);
return true;
}
bool pkpy_is_float(pkpy_vm* vm_handle, int i) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
return is_float(item);
)
}
bool pkpy_to_float(pkpy_vm* vm_handle, int i, double* out) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
*out = py_cast<double>(vm, item);
)
return true;
}
// bool
bool pkpy_push_bool(pkpy_vm* vm_handle, bool value) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
vm->s_data.push(value ? vm->True : vm->False);
return true;
}
bool pkpy_is_bool(pkpy_vm* vm_handle, int i) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
return is_type(item, vm->tp_bool);
)
}
bool pkpy_to_bool(pkpy_vm* vm_handle, int i, bool* out) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
*out = py_cast<bool>(vm, item);
)
return true;
}
// string
bool pkpy_push_string(pkpy_vm* vm_handle, pkpy_CString value) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar res = py_var(vm, value);
vm->s_data.push(res);
return true;
}
bool pkpy_is_string(pkpy_vm* vm_handle, int i) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
return is_type(item, vm->tp_str);
)
}
bool pkpy_to_string(pkpy_vm* vm_handle, int i, pkpy_CString* out) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
const Str& s = py_cast<Str&>(vm, item);
*out = s.c_str();
)
return true;
}
// void_p
bool pkpy_push_voidp(pkpy_vm* vm_handle, void* value) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar res = py_var(vm, value);
vm->s_data.push(res);
return true;
}
bool pkpy_is_voidp(pkpy_vm* vm_handle, int i) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
return vm->is_user_type<VoidP>(item);
)
}
bool pkpy_to_voidp(pkpy_vm* vm_handle, int i, void** out) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
VoidP& vp = py_cast<VoidP&>(vm, item);
*out = vp.ptr;
)
return true;
}
// none
bool pkpy_push_none(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
vm->s_data.push(vm->None);
return true;
}
bool pkpy_is_none(pkpy_vm* vm_handle, int i) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar item = stack_item(vm, i);
return is_none(item);
)
}
// null
bool pkpy_push_null(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
vm->s_data.push(PY_NULL);
return true;
}
struct TempViewPopper {
VM* vm;
bool used;
TempViewPopper(VM* vm) : vm(vm), used(false) {}
void restore() noexcept {
if(used) return;
vm->__c.s_view.pop_back();
used = true;
}
~TempViewPopper() { restore(); }
};
// function
static PyVar c_function_wrapper(VM* vm, ArgsView args) {
pkpy_CFunction f = lambda_get_userdata<pkpy_CFunction>(args.begin());
PyVar* curr_sp = vm->s_data._sp;
vm->__c.s_view.push_back(args);
TempViewPopper _tvp(vm);
int retc = f((pkpy_vm*)vm); // may raise, _tvp will handle this via RAII
_tvp.restore();
// propagate_if_errored
if(vm->__c.error != nullptr) {
PyObject* e_obj = vm->__c.error;
vm->__c.error = nullptr;
vm->_error(e_obj);
return nullptr;
}
assert(retc == vm->s_data._sp - curr_sp);
if(retc == 0) return vm->None;
if(retc == 1) return vm->s_data.popx();
ArgsView ret_view(curr_sp, vm->s_data._sp);
return py_var(vm, ret_view.to_tuple());
}
bool pkpy_push_function(pkpy_vm* vm_handle, const char* sig, pkpy_CFunction f) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar f_obj;
PK_PROTECTED(
f_obj = vm->bind(nullptr, sig, c_function_wrapper, f);
)
vm->s_data.push(f_obj);
return true;
}
// special push
bool pkpy_push_module(pkpy_vm* vm_handle, const char* name) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyObject* module = vm->new_module(name);
vm->s_data.emplace(module);
)
return true;
}
// some opt
bool pkpy_getattr(pkpy_vm* vm_handle, pkpy_CName name) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
PyVar o = vm->s_data.top();
o = vm->getattr(o, StrName(name), false);
if(o == nullptr) return false;
vm->s_data.top() = o;
return true;
}
bool pkpy_setattr(pkpy_vm* vm_handle, pkpy_CName name) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(2)
PyVar a = vm->s_data.top();
PyVar val = vm->s_data.second();
PK_PROTECTED(
vm->setattr(a, StrName(name), val);
)
vm->s_data.shrink(2);
return true;
}
// get global will also get bulitins
bool pkpy_getglobal(pkpy_vm* vm_handle, pkpy_CName name) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar o = vm->_main->attr().try_get(StrName(name));
if(o == nullptr) {
o = vm->builtins->attr().try_get(StrName(name));
if(o == nullptr) return false;
}
vm->s_data.push(o);
return true;
}
bool pkpy_setglobal(pkpy_vm* vm_handle, pkpy_CName name) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
vm->_main->attr().set(StrName(name), vm->s_data.popx());
return true;
}
bool pkpy_eval(pkpy_vm* vm_handle, const char* source) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
CodeObject* code = vm->compile(source, "<eval>", EVAL_MODE);
PyVar ret = vm->_exec(code, vm->_main);
vm->s_data.push(ret);
CodeObject__delete(code);
)
return true;
}
bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
auto _lock = vm->gc_scope_lock();
PK_PROTECTED(
PyVar _0 = vm->py_iter(vm->s_data.popx());
for(int i=0; i<n; i++){
PyVar _1 = vm->py_next(_0);
if(_1 == vm->StopIteration) vm->ValueError("not enough values to unpack");
vm->s_data.push(_1);
}
if(vm->py_next(_0) != vm->StopIteration) vm->ValueError("too many values to unpack");
)
return true;
}
bool pkpy_get_unbound_method(pkpy_vm* vm_handle, pkpy_CName name) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
PyVar o = vm->s_data.top();
PyVar self;
PK_PROTECTED(
o = vm->get_unbound_method(o, StrName(name), &self);
)
vm->s_data.pop();
vm->s_data.push(o);
vm->s_data.push(self);
return true;
}
bool pkpy_py_repr(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
PyVar item = vm->s_data.top();
PK_PROTECTED(
item = VAR(vm->py_repr(item));
)
vm->s_data.top() = item;
return true;
}
bool pkpy_py_str(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(1)
PyVar item = vm->s_data.top();
PK_PROTECTED(
item = VAR(vm->py_str(item));
)
vm->s_data.top() = item;
return true;
}
bool pkpy_py_import(pkpy_vm* vm_handle, pkpy_CString name) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_PROTECTED(
PyVar module = vm->py_import(name);
vm->s_data.push(module);
)
return true;
}
/* Error Handling */
bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PyVar e_t = vm->_main->attr().try_get_likely_found(name);
if(e_t == nullptr) {
e_t = vm->builtins->attr().try_get_likely_found(name);
if(e_t == nullptr) {
e_t = vm->_t(vm->tp_exception);
std::cerr << "[warning] pkpy_error(): " << Str(name).escape() << " not found, fallback to 'Exception'"
<< std::endl;
}
}
vm->__c.error = vm->call(e_t, VAR(message)).get();
return false;
}
bool pkpy_check_error(pkpy_vm* vm_handle) {
VM* vm = (VM*)vm_handle;
return vm->__c.error != nullptr;
}
bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
VM* vm = (VM*)vm_handle;
// no error
if(vm->__c.error == nullptr) return false;
Exception& e = vm->__c.error->as<Exception>();
if(message != nullptr)
*message = strdup(e.summary().c_str());
else
std::cout << e.summary() << std::endl;
vm->__c.error = nullptr;
if(vm->callstack.empty()) {
vm->s_data.clear();
} else {
if(vm->__c.s_view.empty()) exit(127);
vm->s_data.reset(vm->__c.s_view.back().end());
}
return true;
}
bool pkpy_vectorcall(pkpy_vm* vm_handle, int argc) {
VM* vm = (VM*)vm_handle;
PK_ASSERT_NO_ERROR()
PK_ASSERT_N_EXTRA_ELEMENTS(argc + 2)
PyVar res;
PK_PROTECTED(
res = vm->vectorcall(argc);
)
vm->s_data.push(res);
return true;
}
/*****************************************************************/
void pkpy_free(void* p) { std::free(p); }
pkpy_CName pkpy_name(const char* name) { return StrName(name).index; }
pkpy_CString pkpy_name_to_string(pkpy_CName name) { return StrName(name).c_str(); }
void pkpy_set_output_handler(pkpy_vm* vm_handle, pkpy_COutputHandler handler) {
VM* vm = (VM*)vm_handle;
vm->_stdout = handler;
}
void pkpy_set_import_handler(pkpy_vm* vm_handle, pkpy_CImportHandler handler) {
VM* vm = (VM*)vm_handle;
vm->_import_handler = handler;
}
void* pkpy_new_repl(pkpy_vm* vm_handle) { return new REPL((VM*)vm_handle); }
bool pkpy_repl_input(void* r, const char* line) { return ((REPL*)r)->input(line); }
void pkpy_delete_repl(void* repl) { delete (REPL*)repl; }
#endif // PK_NO_EXPORT_C_API
} // namespace pkpy

42
src2/main.c Normal file
View File

@ -0,0 +1,42 @@
#include <stdio.h>
#include <stdlib.h>
#include "pocketpy.h"
char* read_file(const char* path) {
FILE* file = fopen(path, "r");
if(file == NULL) {
printf("Error: file not found\n");
return NULL;
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char* buffer = malloc(size + 1);
fread(buffer, 1, size, file);
buffer[size] = 0;
return buffer;
}
int main(int argc, char** argv) {
#if _WIN32
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
#endif
if(argc != 2) goto __HELP;
char* source = read_file(argv[1]);
py_initialize();
py_Error* err = py_exec_simple(source);
if(err){
py_Error__print(err);
py_Error__delete(err);
}
py_finalize();
free(source);
__HELP:
printf("Usage: pocketpy [filename]\n");
return 0;
}

View File

@ -1,265 +0,0 @@
#include "pocketpy/pocketpy_c.h"
#ifdef _WIN32
#pragma warning(disable: 4700)
#endif
pkpy_vm* pkpy_new_vm(bool enable_os) {
pkpy_vm* returnValue;
return returnValue;
}
void pkpy_delete_vm(pkpy_vm* vm) {
}
bool pkpy_exec(pkpy_vm* vm, const char* source) {
bool returnValue;
return returnValue;
}
bool pkpy_exec_2(pkpy_vm* vm, const char* source, const char* filename, int mode, const char* module) {
bool returnValue;
return returnValue;
}
void pkpy_set_main_argv(pkpy_vm* vm, int argc, char** argv){
}
bool pkpy_dup(pkpy_vm* vm, int i) {
bool returnValue;
return returnValue;
}
bool pkpy_pop(pkpy_vm* vm, int n) {
bool returnValue;
return returnValue;
}
bool pkpy_pop_top(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
bool pkpy_dup_top(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
bool pkpy_rot_two(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
int pkpy_stack_size(pkpy_vm* vm) {
int returnValue;
return returnValue;
}
bool pkpy_push_int(pkpy_vm* vm, int val) {
bool returnValue;
return returnValue;
}
bool pkpy_is_int(pkpy_vm* vm, int i) {
bool returnValue;
return returnValue;
}
bool pkpy_to_int(pkpy_vm* vm, int i, int* out) {
bool returnValue;
return returnValue;
}
bool pkpy_push_float(pkpy_vm* vm, double val) {
bool returnValue;
return returnValue;
}
bool pkpy_is_float(pkpy_vm* vm, int i) {
bool returnValue;
return returnValue;
}
bool pkpy_to_float(pkpy_vm* vm, int i, double* out) {
bool returnValue;
return returnValue;
}
bool pkpy_push_bool(pkpy_vm* vm, bool val) {
bool returnValue;
return returnValue;
}
bool pkpy_is_bool(pkpy_vm* vm, int i) {
bool returnValue;
return returnValue;
}
bool pkpy_to_bool(pkpy_vm* vm, int i, bool* out) {
bool returnValue;
return returnValue;
}
bool pkpy_push_string(pkpy_vm* vm, pkpy_CString val) {
bool returnValue;
return returnValue;
}
bool pkpy_is_string(pkpy_vm* vm, int i) {
bool returnValue;
return returnValue;
}
bool pkpy_to_string(pkpy_vm* vm, int i, pkpy_CString* out) {
bool returnValue;
return returnValue;
}
bool pkpy_push_voidp(pkpy_vm* vm, void* val) {
bool returnValue;
return returnValue;
}
bool pkpy_is_voidp(pkpy_vm* vm, int i) {
bool returnValue;
return returnValue;
}
bool pkpy_to_voidp(pkpy_vm* vm, int i, void** out) {
bool returnValue;
return returnValue;
}
bool pkpy_push_none(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
bool pkpy_is_none(pkpy_vm* vm, int i) {
bool returnValue;
return returnValue;
}
bool pkpy_push_null(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
bool pkpy_push_function(pkpy_vm* vm, const char* sig, pkpy_CFunction val) {
bool returnValue;
return returnValue;
}
bool pkpy_push_module(pkpy_vm* vm, const char* name) {
bool returnValue;
return returnValue;
}
bool pkpy_getattr(pkpy_vm* vm, pkpy_CName name) {
bool returnValue;
return returnValue;
}
bool pkpy_setattr(pkpy_vm* vm, pkpy_CName name) {
bool returnValue;
return returnValue;
}
bool pkpy_getglobal(pkpy_vm* vm, pkpy_CName name) {
bool returnValue;
return returnValue;
}
bool pkpy_setglobal(pkpy_vm* vm, pkpy_CName name) {
bool returnValue;
return returnValue;
}
bool pkpy_eval(pkpy_vm* vm, const char* source) {
bool returnValue;
return returnValue;
}
bool pkpy_unpack_sequence(pkpy_vm* vm, int size) {
bool returnValue;
return returnValue;
}
bool pkpy_get_unbound_method(pkpy_vm* vm, pkpy_CName name) {
bool returnValue;
return returnValue;
}
bool pkpy_py_repr(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
bool pkpy_py_str(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
bool pkpy_py_import(pkpy_vm* vm, pkpy_CString name) {
bool returnValue;
return returnValue;
}
bool pkpy_error(pkpy_vm* vm, const char* name, pkpy_CString msg) {
bool returnValue;
return returnValue;
}
bool pkpy_check_error(pkpy_vm* vm) {
bool returnValue;
return returnValue;
}
bool pkpy_clear_error(pkpy_vm* vm, char** message) {
bool returnValue;
return returnValue;
}
bool pkpy_vectorcall(pkpy_vm* vm, int argc) {
bool returnValue;
return returnValue;
}
void pkpy_free(void* p) {
}
pkpy_CName pkpy_name(const char* s) {
pkpy_CName returnValue;
return returnValue;
}
pkpy_CString pkpy_name_to_string(pkpy_CName name) {
pkpy_CString returnValue;
return returnValue;
}
void pkpy_set_output_handler(pkpy_vm* vm, pkpy_COutputHandler handler) {
}
void pkpy_set_import_handler(pkpy_vm* vm, pkpy_CImportHandler handler) {
}
void* pkpy_new_repl(pkpy_vm* vm) {
void* returnValue;
return returnValue;
}
bool pkpy_repl_input(void* r, const char* line) {
bool returnValue;
return returnValue;
}
void pkpy_delete_repl(void* repl) {
}