mirror of
https://github.com/pocketpy/pocketpy
synced 2026-06-10 11:03:36 +00:00
Format the world. (#259)
* add some clang-format offs. * add formats. * format the world. * AllowShortIfStatementsOnASingleLine * off REGION. * Rollback vm.hpp * Disable insert.
This commit is contained in:
parent
0b404a51cb
commit
1c82060daf
106
.clang-format
106
.clang-format
@ -1,6 +1,102 @@
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 4
|
||||
UseTab: Never
|
||||
# clang-format configuration
|
||||
# compatible with clang-format 18
|
||||
|
||||
IndentPPDirectives: BeforeHash
|
||||
UseTab: Never
|
||||
ColumnLimit: 120
|
||||
|
||||
# Indent
|
||||
IndentWidth: 4
|
||||
BracedInitializerIndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: true
|
||||
IndentExternBlock: Indent
|
||||
IndentGotoLabels: true
|
||||
IndentRequiresClause: true
|
||||
IndentWrappedFunctionNames: true
|
||||
NamespaceIndentation: None
|
||||
LambdaBodyIndentation: Signature
|
||||
BitFieldColonSpacing: Both
|
||||
|
||||
# Insert
|
||||
InsertBraces: false
|
||||
InsertNewlineAtEOF: true
|
||||
KeepEmptyLinesAtEOF: true
|
||||
|
||||
# Align
|
||||
AlignAfterOpenBracket: true
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
|
||||
AlignArrayOfStructures: Left
|
||||
PointerAlignment: Left
|
||||
|
||||
BreakAfterAttributes: Leave
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
BreakAdjacentStringLiterals: false
|
||||
BreakStringLiterals: false
|
||||
CompactNamespaces: false
|
||||
Cpp11BracedListStyle: true
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowBreakBeforeNoexceptSpecifier: Never
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortCompoundRequirementOnASingleLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
RequiresClausePosition: OwnLine
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
|
||||
# Space
|
||||
SeparateDefinitionBlocks: Always
|
||||
SpaceBeforeParens: Custom
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: false
|
||||
AfterForeachMacros: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterIfMacros: false
|
||||
AfterOverloadedOperator: true
|
||||
AfterRequiresInClause: true
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
|
||||
SpaceBeforeRangeBasedForLoopColon: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
|
||||
SpacesInParens: Custom
|
||||
SpacesInParensOptions:
|
||||
InConditionalStatements: false
|
||||
InCStyleCasts: false
|
||||
InEmptyParentheses: false
|
||||
Other: false
|
||||
|
||||
SpacesInSquareBrackets: false
|
||||
|
||||
# Order
|
||||
QualifierAlignment: Custom
|
||||
QualifierOrder: ["constexpr", "const", "inline", "static", "type"]
|
||||
SortIncludes: Never
|
||||
SortUsingDeclarations: LexicographicNumeric
|
||||
IncludeBlocks: Merge
|
||||
|
||||
WhitespaceSensitiveMacros: ["PK_PROTECTED", "LUA_PROTECTED"]
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/pocketpy.hpp"
|
||||
#include "pocketpy/pocketpy.hpp"
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/pocketpy.hpp"
|
||||
#include "pocketpy/pocketpy.hpp"
|
||||
|
||||
@ -10,22 +10,24 @@
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_any_sso_v = is_pod_v<T> && sizeof(T) <= sizeof(void*);
|
||||
template <typename T>
|
||||
constexpr inline bool is_any_sso_v = is_pod_v<T> && sizeof(T) <= sizeof(void*);
|
||||
|
||||
struct any{
|
||||
struct vtable{
|
||||
struct any {
|
||||
struct vtable {
|
||||
const std::type_index type;
|
||||
void (*deleter)(void*);
|
||||
|
||||
template<typename T>
|
||||
inline static vtable* get(){
|
||||
template <typename T>
|
||||
inline static vtable* get() {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
if constexpr (is_any_sso_v<T>){
|
||||
static vtable vt{ typeid(T), nullptr };
|
||||
if constexpr(is_any_sso_v<T>) {
|
||||
static vtable vt{typeid(T), nullptr};
|
||||
return &vt;
|
||||
}else{
|
||||
static vtable vt{ typeid(T), [](void* ptr){ delete static_cast<T*>(ptr); } };
|
||||
} else {
|
||||
static vtable vt{typeid(T), [](void* ptr) {
|
||||
delete static_cast<T*>(ptr);
|
||||
}};
|
||||
return &vt;
|
||||
}
|
||||
}
|
||||
@ -36,45 +38,45 @@ struct any{
|
||||
|
||||
any() : data(nullptr), _vt(nullptr) {}
|
||||
|
||||
explicit operator bool() const { return _vt != nullptr; }
|
||||
explicit operator bool () const { return _vt != nullptr; }
|
||||
|
||||
template<typename T>
|
||||
any(T&& value){
|
||||
template <typename T>
|
||||
any(T&& value) {
|
||||
using U = std::decay_t<T>;
|
||||
static_assert(!std::is_same_v<U, any>, "any(const any&) is deleted");
|
||||
static_assert(sizeof(U) == sizeof(T));
|
||||
if constexpr (is_any_sso_v<U>){
|
||||
if constexpr(is_any_sso_v<U>) {
|
||||
std::memcpy(&data, &value, sizeof(U));
|
||||
}else{
|
||||
} else {
|
||||
data = new U(std::forward<T>(value));
|
||||
}
|
||||
_vt = vtable::get<U>();
|
||||
}
|
||||
|
||||
any(any&& other) noexcept;
|
||||
any& operator=(any&& other) noexcept;
|
||||
any& operator= (any&& other) noexcept;
|
||||
|
||||
const std::type_index type_id() const{
|
||||
return _vt ? _vt->type : typeid(void);
|
||||
}
|
||||
const std::type_index type_id() const { return _vt ? _vt->type : typeid(void); }
|
||||
|
||||
any(const any& other) = delete;
|
||||
any& operator=(const any& other) = delete;
|
||||
any& operator= (const any& other) = delete;
|
||||
|
||||
~any() { if(_vt && _vt->deleter) _vt->deleter(data); }
|
||||
~any() {
|
||||
if(_vt && _vt->deleter) _vt->deleter(data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& _cast() const noexcept{
|
||||
template <typename T>
|
||||
T& _cast() const noexcept {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
if constexpr (is_any_sso_v<T>){
|
||||
if constexpr(is_any_sso_v<T>) {
|
||||
return *((T*)(&data));
|
||||
}else{
|
||||
} else {
|
||||
return *(static_cast<T*>(data));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& cast() const{
|
||||
template <typename T>
|
||||
T& cast() const {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
if(type_id() != typeid(T)) __bad_any_cast(typeid(T), type_id());
|
||||
return _cast<T>();
|
||||
@ -83,29 +85,29 @@ struct any{
|
||||
static void __bad_any_cast(const std::type_index expected, const std::type_index actual);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
struct function;
|
||||
|
||||
template<typename Ret, typename... Params>
|
||||
struct function<Ret(Params...)>{
|
||||
template <typename Ret, typename... Params>
|
||||
struct function<Ret(Params...)> {
|
||||
any _impl;
|
||||
Ret (*_wrapper)(const any&, Params...);
|
||||
|
||||
function(): _impl(), _wrapper(nullptr) {}
|
||||
function() : _impl(), _wrapper(nullptr) {}
|
||||
|
||||
explicit operator bool() const { return _wrapper != nullptr; }
|
||||
explicit operator bool () const { return _wrapper != nullptr; }
|
||||
|
||||
template<typename F>
|
||||
function(F&& f) : _impl(std::forward<F>(f)){
|
||||
_wrapper = [](const any& impl, Params... params) -> Ret{
|
||||
template <typename F>
|
||||
function(F&& f) : _impl(std::forward<F>(f)) {
|
||||
_wrapper = [](const any& impl, Params... params) -> Ret {
|
||||
return impl._cast<std::decay_t<F>>()(std::forward<Params>(params)...);
|
||||
};
|
||||
}
|
||||
|
||||
Ret operator()(Params... params) const{
|
||||
Ret operator() (Params... params) const {
|
||||
assert(_wrapper);
|
||||
return _wrapper(_impl, std::forward<Params>(params)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
// clang-format off
|
||||
|
||||
/*************** feature settings ***************/
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
// clang-format off
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
//define something for Windows (32-bit and 64-bit, this part is common)
|
||||
|
||||
@ -5,10 +5,13 @@
|
||||
#include <mutex>
|
||||
|
||||
struct GIL {
|
||||
inline static std::mutex _mutex;
|
||||
inline static std::mutex _mutex;
|
||||
|
||||
explicit GIL() { _mutex.lock(); }
|
||||
|
||||
~GIL() { _mutex.unlock(); }
|
||||
};
|
||||
|
||||
#define PK_GLOBAL_SCOPE_LOCK() GIL _lock;
|
||||
|
||||
#else
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
inline const int kPoolExprBlockSize = 128;
|
||||
inline const int kPoolFrameBlockSize = 80;
|
||||
const inline int kPoolExprBlockSize = 128;
|
||||
const inline int kPoolFrameBlockSize = 80;
|
||||
|
||||
void* PoolExpr_alloc() noexcept;
|
||||
void PoolExpr_dealloc(void*) noexcept;
|
||||
|
||||
@ -6,20 +6,22 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
template<typename T>
|
||||
constexpr T default_invalid_value(){
|
||||
if constexpr(std::is_same_v<int, T>) return -1;
|
||||
else return nullptr;
|
||||
template <typename T>
|
||||
constexpr T default_invalid_value() {
|
||||
if constexpr(std::is_same_v<int, T>)
|
||||
return -1;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
struct NameDictImpl {
|
||||
PK_ALWAYS_PASS_BY_POINTER(NameDictImpl)
|
||||
|
||||
using Item = std::pair<StrName, T>;
|
||||
static constexpr uint16_t kInitialCapacity = 16;
|
||||
constexpr static uint16_t kInitialCapacity = 16;
|
||||
static_assert(is_pod_v<T>);
|
||||
|
||||
float _load_factor;
|
||||
@ -31,26 +33,30 @@ struct NameDictImpl {
|
||||
|
||||
Item* _items;
|
||||
|
||||
#define HASH_PROBE_1(key, ok, i) \
|
||||
ok = false; \
|
||||
i = key.index & _mask; \
|
||||
while(!_items[i].first.empty()) { \
|
||||
if(_items[i].first == (key)) { ok = true; break; } \
|
||||
i = (i + 1) & _mask; \
|
||||
}
|
||||
#define HASH_PROBE_1(key, ok, i) \
|
||||
ok = false; \
|
||||
i = key.index & _mask; \
|
||||
while(!_items[i].first.empty()) { \
|
||||
if(_items[i].first == (key)) { \
|
||||
ok = true; \
|
||||
break; \
|
||||
} \
|
||||
i = (i + 1) & _mask; \
|
||||
}
|
||||
|
||||
#define HASH_PROBE_0 HASH_PROBE_1
|
||||
|
||||
NameDictImpl(float load_factor=PK_INST_ATTR_LOAD_FACTOR): _load_factor(load_factor), _size(0) {
|
||||
NameDictImpl(float load_factor = PK_INST_ATTR_LOAD_FACTOR) : _load_factor(load_factor), _size(0) {
|
||||
_set_capacity_and_alloc_items(kInitialCapacity);
|
||||
}
|
||||
|
||||
~NameDictImpl(){ std::free(_items); }
|
||||
~NameDictImpl() { std::free(_items); }
|
||||
|
||||
uint16_t size() const { return _size; }
|
||||
|
||||
uint16_t capacity() const { return _capacity; }
|
||||
|
||||
void _set_capacity_and_alloc_items(uint16_t val){
|
||||
void _set_capacity_and_alloc_items(uint16_t val) {
|
||||
_capacity = val;
|
||||
_critical_size = val * _load_factor;
|
||||
_mask = val - 1;
|
||||
@ -59,12 +65,13 @@ while(!_items[i].first.empty()) { \
|
||||
std::memset(_items, 0, _capacity * sizeof(Item));
|
||||
}
|
||||
|
||||
void set(StrName key, T val){
|
||||
bool ok; uint16_t i;
|
||||
void set(StrName key, T val) {
|
||||
bool ok;
|
||||
uint16_t i;
|
||||
HASH_PROBE_1(key, ok, i);
|
||||
if(!ok) {
|
||||
_size++;
|
||||
if(_size > _critical_size){
|
||||
if(_size > _critical_size) {
|
||||
_rehash_2x();
|
||||
HASH_PROBE_1(key, ok, i);
|
||||
}
|
||||
@ -73,13 +80,14 @@ while(!_items[i].first.empty()) { \
|
||||
_items[i].second = val;
|
||||
}
|
||||
|
||||
void _rehash_2x(){
|
||||
void _rehash_2x() {
|
||||
Item* old_items = _items;
|
||||
uint16_t old_capacity = _capacity;
|
||||
_set_capacity_and_alloc_items(_capacity * 2);
|
||||
for(uint16_t i=0; i<old_capacity; i++){
|
||||
for(uint16_t i = 0; i < old_capacity; i++) {
|
||||
if(old_items[i].first.empty()) continue;
|
||||
bool ok; uint16_t j;
|
||||
bool ok;
|
||||
uint16_t j;
|
||||
HASH_PROBE_1(old_items[i].first, ok, j);
|
||||
assert(!ok);
|
||||
_items[j] = old_items[i];
|
||||
@ -87,21 +95,23 @@ while(!_items[i].first.empty()) { \
|
||||
std::free(old_items);
|
||||
}
|
||||
|
||||
T try_get(StrName key) const{
|
||||
bool ok; uint16_t i;
|
||||
T try_get(StrName key) const {
|
||||
bool ok;
|
||||
uint16_t i;
|
||||
HASH_PROBE_0(key, ok, i);
|
||||
if(!ok) return default_invalid_value<T>();
|
||||
return _items[i].second;
|
||||
}
|
||||
|
||||
T* try_get_2(StrName key) const{
|
||||
bool ok; uint16_t i;
|
||||
T* try_get_2(StrName key) const {
|
||||
bool ok;
|
||||
uint16_t i;
|
||||
HASH_PROBE_0(key, ok, i);
|
||||
if(!ok) return nullptr;
|
||||
return &_items[i].second;
|
||||
}
|
||||
|
||||
T try_get_likely_found(StrName key) const{
|
||||
T try_get_likely_found(StrName key) const {
|
||||
uint16_t i = key.index & _mask;
|
||||
if(_items[i].first == key) return _items[i].second;
|
||||
i = (i + 1) & _mask;
|
||||
@ -109,7 +119,7 @@ while(!_items[i].first.empty()) { \
|
||||
return try_get(key);
|
||||
}
|
||||
|
||||
T* try_get_2_likely_found(StrName key) const{
|
||||
T* try_get_2_likely_found(StrName key) const {
|
||||
uint16_t i = key.index & _mask;
|
||||
if(_items[i].first == key) return &_items[i].second;
|
||||
i = (i + 1) & _mask;
|
||||
@ -117,8 +127,9 @@ while(!_items[i].first.empty()) { \
|
||||
return try_get_2(key);
|
||||
}
|
||||
|
||||
bool del(StrName key){
|
||||
bool ok; uint16_t i;
|
||||
bool del(StrName key) {
|
||||
bool ok;
|
||||
uint16_t i;
|
||||
HASH_PROBE_0(key, ok, i);
|
||||
if(!ok) return false;
|
||||
_items[i].first = StrName();
|
||||
@ -127,7 +138,7 @@ while(!_items[i].first.empty()) { \
|
||||
// tidy
|
||||
uint16_t pre_z = i;
|
||||
uint16_t z = (i + 1) & _mask;
|
||||
while(!_items[z].first.empty()){
|
||||
while(!_items[z].first.empty()) {
|
||||
uint16_t h = _items[z].first.index & _mask;
|
||||
if(h != i) break;
|
||||
std::swap(_items[pre_z], _items[z]);
|
||||
@ -137,56 +148,56 @@ while(!_items[i].first.empty()) { \
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename __Func>
|
||||
template <typename __Func>
|
||||
void apply(__Func func) const {
|
||||
for(uint16_t i=0; i<_capacity; i++){
|
||||
for(uint16_t i = 0; i < _capacity; i++) {
|
||||
if(_items[i].first.empty()) continue;
|
||||
func(_items[i].first, _items[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(StrName key) const {
|
||||
bool ok; uint16_t i;
|
||||
bool ok;
|
||||
uint16_t i;
|
||||
HASH_PROBE_0(key, ok, i);
|
||||
return ok;
|
||||
}
|
||||
|
||||
T operator[](StrName key) const {
|
||||
T operator[] (StrName key) const {
|
||||
T* val = try_get_2_likely_found(key);
|
||||
if(val == nullptr){
|
||||
throw std::runtime_error(_S("NameDict key not found: ", key.escape()).str());
|
||||
}
|
||||
if(val == nullptr) { throw std::runtime_error(_S("NameDict key not found: ", key.escape()).str()); }
|
||||
return *val;
|
||||
}
|
||||
|
||||
array<StrName> keys() const {
|
||||
array<StrName> v(_size);
|
||||
int j = 0;
|
||||
for(uint16_t i=0; i<_capacity; i++){
|
||||
for(uint16_t i = 0; i < _capacity; i++) {
|
||||
if(_items[i].first.empty()) continue;
|
||||
new (&v[j++]) StrName(_items[i].first);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
array<Item> items() const{
|
||||
array<Item> items() const {
|
||||
array<Item> v(_size);
|
||||
int j = 0;
|
||||
apply([&](StrName key, T val){
|
||||
new(&v[j++]) Item(key, val);
|
||||
apply([&](StrName key, T val) {
|
||||
new (&v[j++]) Item(key, val);
|
||||
});
|
||||
return v;
|
||||
}
|
||||
|
||||
void clear(){
|
||||
for(uint16_t i=0; i<_capacity; i++){
|
||||
void clear() {
|
||||
for(uint16_t i = 0; i < _capacity; i++) {
|
||||
_items[i].first = StrName();
|
||||
_items[i].second = nullptr;
|
||||
}
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
#undef HASH_PROBE_0
|
||||
#undef HASH_PROBE_1
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
int utf8len(unsigned char c, bool suppress=false);
|
||||
int utf8len(unsigned char c, bool suppress = false);
|
||||
struct SStream;
|
||||
|
||||
struct Str{
|
||||
struct Str {
|
||||
int size;
|
||||
bool is_ascii;
|
||||
char* data;
|
||||
@ -26,60 +26,70 @@ struct Str{
|
||||
Str(std::string_view s);
|
||||
Str(const char* s);
|
||||
Str(const char* s, int len);
|
||||
Str(std::pair<char *, int>);
|
||||
Str(std::pair<char*, int>);
|
||||
Str(const Str& other);
|
||||
Str(Str&& other);
|
||||
|
||||
operator std::string_view() const { return sv(); }
|
||||
operator std::string_view () const { return sv(); }
|
||||
|
||||
const char* begin() const { return data; }
|
||||
|
||||
const char* end() const { return data + size; }
|
||||
char operator[](int idx) const { return data[idx]; }
|
||||
|
||||
char operator[] (int idx) const { return data[idx]; }
|
||||
|
||||
int length() const { return size; }
|
||||
|
||||
bool empty() const { return size == 0; }
|
||||
size_t hash() const{ return std::hash<std::string_view>()(sv()); }
|
||||
|
||||
Str& operator=(const Str&);
|
||||
Str operator+(const Str&) const;
|
||||
Str operator+(const char*) const;
|
||||
friend Str operator+(const char*, const Str&);
|
||||
size_t hash() const { return std::hash<std::string_view>()(sv()); }
|
||||
|
||||
bool operator==(const std::string_view other) const;
|
||||
bool operator!=(const std::string_view other) const;
|
||||
bool operator<(const std::string_view other) const;
|
||||
friend bool operator<(const std::string_view other, const Str& str);
|
||||
Str& operator= (const Str&);
|
||||
Str operator+ (const Str&) const;
|
||||
Str operator+ (const char*) const;
|
||||
friend Str operator+ (const char*, const Str&);
|
||||
|
||||
bool operator==(const char* p) const;
|
||||
bool operator!=(const char* p) const;
|
||||
bool operator== (const std::string_view other) const;
|
||||
bool operator!= (const std::string_view other) const;
|
||||
bool operator< (const std::string_view other) const;
|
||||
friend bool operator< (const std::string_view other, const Str& str);
|
||||
|
||||
bool operator==(const Str& other) const;
|
||||
bool operator!=(const Str& other) const;
|
||||
bool operator<(const Str& other) const;
|
||||
bool operator>(const Str& other) const;
|
||||
bool operator<=(const Str& other) const;
|
||||
bool operator>=(const Str& other) const;
|
||||
bool operator== (const char* p) const;
|
||||
bool operator!= (const char* p) const;
|
||||
|
||||
bool operator== (const Str& other) const;
|
||||
bool operator!= (const Str& other) const;
|
||||
bool operator< (const Str& other) const;
|
||||
bool operator> (const Str& other) const;
|
||||
bool operator<= (const Str& other) const;
|
||||
bool operator>= (const Str& other) const;
|
||||
|
||||
~Str();
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Str& str);
|
||||
friend std::ostream& operator<< (std::ostream& os, const Str& str);
|
||||
|
||||
const char* c_str() const { return data; }
|
||||
|
||||
std::string_view sv() const { return std::string_view(data, size); }
|
||||
|
||||
std::string str() const { return std::string(data, size); }
|
||||
|
||||
Str substr(int start, int len) const;
|
||||
Str substr(int start) const;
|
||||
Str strip(bool left, bool right, const Str& chars) const;
|
||||
Str strip(bool left=true, bool right=true) const;
|
||||
Str strip(bool left = true, bool right = true) const;
|
||||
|
||||
Str lstrip() const { return strip(true, false); }
|
||||
|
||||
Str rstrip() const { return strip(false, true); }
|
||||
|
||||
Str lower() const;
|
||||
Str upper() const;
|
||||
Str escape(bool single_quote=true) const;
|
||||
void escape_(SStream& ss, bool single_quote=true) const;
|
||||
int index(const Str& sub, int start=0) const;
|
||||
Str escape(bool single_quote = true) const;
|
||||
void escape_(SStream& ss, bool single_quote = true) const;
|
||||
int index(const Str& sub, int start = 0) const;
|
||||
Str replace(char old, char new_) const;
|
||||
Str replace(const Str& old, const Str& new_, int count=-1) const;
|
||||
Str replace(const Str& old, const Str& new_, int count = -1) const;
|
||||
vector<std::string_view> split(const Str& sep) const;
|
||||
vector<std::string_view> split(char sep) const;
|
||||
int count(const Str& sub) const;
|
||||
@ -95,32 +105,29 @@ struct Str{
|
||||
struct StrName {
|
||||
uint16_t index;
|
||||
|
||||
StrName(): index(0) {}
|
||||
explicit StrName(uint16_t index): index(index) {}
|
||||
StrName(const char* s): index(get(s).index) {}
|
||||
StrName(const Str& s): index(get(s.sv()).index) {}
|
||||
StrName() : index(0) {}
|
||||
|
||||
explicit StrName(uint16_t index) : index(index) {}
|
||||
|
||||
StrName(const char* s) : index(get(s).index) {}
|
||||
|
||||
StrName(const Str& s) : index(get(s.sv()).index) {}
|
||||
|
||||
std::string_view sv() const { return _r_interned()[index]; }
|
||||
|
||||
std::string_view sv() const { return _r_interned()[index];}
|
||||
const char* c_str() const { return _r_interned()[index].c_str(); }
|
||||
|
||||
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 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(); }
|
||||
|
||||
bool operator>(const StrName& other) const noexcept {
|
||||
return sv() > other.sv();
|
||||
}
|
||||
bool operator> (const StrName& other) const noexcept { return sv() > other.sv(); }
|
||||
|
||||
static StrName get(std::string_view s);
|
||||
static std::map<std::string_view, uint16_t>& _interned();
|
||||
@ -128,32 +135,34 @@ struct StrName {
|
||||
static uint32_t _pesudo_random_index;
|
||||
};
|
||||
|
||||
struct SStream{
|
||||
struct SStream {
|
||||
PK_ALWAYS_PASS_BY_POINTER(SStream)
|
||||
|
||||
vector<char> buffer;
|
||||
int _precision = -1;
|
||||
|
||||
bool empty() const { return buffer.empty(); }
|
||||
|
||||
void setprecision(int precision) { _precision = precision; }
|
||||
|
||||
SStream() {}
|
||||
SStream(int guess_size) { buffer.reserve(guess_size);}
|
||||
|
||||
SStream(int guess_size) { buffer.reserve(guess_size); }
|
||||
|
||||
Str str();
|
||||
|
||||
SStream& operator<<(const Str&);
|
||||
SStream& operator<<(const char*);
|
||||
SStream& operator<<(int);
|
||||
SStream& operator<<(size_t);
|
||||
SStream& operator<<(i64);
|
||||
SStream& operator<<(f64);
|
||||
SStream& operator<<(const std::string&);
|
||||
SStream& operator<<(std::string_view);
|
||||
SStream& operator<<(char);
|
||||
SStream& operator<<(StrName);
|
||||
SStream& operator<< (const Str&);
|
||||
SStream& operator<< (const char*);
|
||||
SStream& operator<< (int);
|
||||
SStream& operator<< (size_t);
|
||||
SStream& operator<< (i64);
|
||||
SStream& operator<< (f64);
|
||||
SStream& operator<< (const std::string&);
|
||||
SStream& operator<< (std::string_view);
|
||||
SStream& operator<< (char);
|
||||
SStream& operator<< (StrName);
|
||||
|
||||
void write_hex(unsigned char, bool non_zero=false);
|
||||
void write_hex(unsigned char, bool non_zero = false);
|
||||
void write_hex(void*);
|
||||
void write_hex(i64);
|
||||
};
|
||||
@ -162,17 +171,19 @@ struct SStream{
|
||||
#undef _S
|
||||
#endif
|
||||
|
||||
template<typename... Args>
|
||||
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; }
|
||||
struct CString {
|
||||
const char* ptr;
|
||||
|
||||
CString(const char* ptr) : ptr(ptr) {}
|
||||
|
||||
operator const char* () const { return ptr; }
|
||||
};
|
||||
|
||||
// unary operators
|
||||
@ -234,4 +245,4 @@ extern const StrName pk_id_complex;
|
||||
|
||||
#define DEF_SNAME(name) const static StrName name(#name)
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,35 +2,39 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
// is_pod_v<> for c++17 and c++20
|
||||
template<typename T>
|
||||
inline constexpr bool is_pod_v = std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
|
||||
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>
|
||||
inline constexpr bool is_integral_v = !std::is_same_v<T, bool> && std::is_integral_v<T>;
|
||||
template <typename T>
|
||||
constexpr inline bool is_integral_v = !std::is_same_v<T, bool> && std::is_integral_v<T>;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_floating_point_v = std::is_same_v<T, float> || std::is_same_v<T, double>;
|
||||
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>
|
||||
inline constexpr bool is_sso_v = is_integral_v<T> || is_floating_point_v<T>;
|
||||
template <typename T>
|
||||
constexpr inline bool is_sso_v = is_integral_v<T> || is_floating_point_v<T>;
|
||||
|
||||
// if is_sso_v<T> is true, return T, else return T&
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
using obj_get_t = std::conditional_t<is_sso_v<T>, 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 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, typename = void>
|
||||
struct has_gc_marker : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr int py_sizeof = 16 + sizeof(T);
|
||||
} // namespace pkpy
|
||||
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);
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
using i64 = int64_t; // always 64-bit
|
||||
using f64 = double; // always 64-bit
|
||||
using i64 = int64_t; // always 64-bit
|
||||
using f64 = double; // always 64-bit
|
||||
|
||||
static_assert(sizeof(i64) == 8);
|
||||
static_assert(sizeof(f64) == 8);
|
||||
@ -16,13 +16,15 @@ struct explicit_copy_t {
|
||||
};
|
||||
|
||||
// Dummy types
|
||||
struct DummyInstance { };
|
||||
struct DummyModule { };
|
||||
struct NoReturn { };
|
||||
struct DummyInstance {};
|
||||
|
||||
struct DummyModule {};
|
||||
|
||||
struct NoReturn {};
|
||||
|
||||
// Forward declarations
|
||||
struct PyObject;
|
||||
struct Frame;
|
||||
class VM;
|
||||
|
||||
}; // namespace pkpy
|
||||
}; // namespace pkpy
|
||||
|
||||
@ -1,34 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#define PK_REGION(name) 1
|
||||
#define PK_REGION(name) 1
|
||||
|
||||
#define PK_ALWAYS_PASS_BY_POINTER(T) \
|
||||
T(const T&) = delete; \
|
||||
T& operator=(const T&) = delete; \
|
||||
T(T&&) = delete; \
|
||||
T& operator=(T&&) = delete;
|
||||
#define PK_ALWAYS_PASS_BY_POINTER(T) \
|
||||
T(const T&) = delete; \
|
||||
T& operator= (const T&) = delete; \
|
||||
T(T&&) = delete; \
|
||||
T& operator= (T&&) = delete;
|
||||
|
||||
#define PK_SLICE_LOOP(i, start, stop, step) for(int i=start; step>0?i<stop:i>stop; i+=step)
|
||||
#define PK_SLICE_LOOP(i, start, stop, step) for(int i = start; step > 0 ? i < stop : i > stop; i += step)
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
// global constants
|
||||
inline const char* PK_HEX_TABLE = "0123456789abcdef";
|
||||
const inline char* PK_HEX_TABLE = "0123456789abcdef";
|
||||
|
||||
inline const char* kPlatformStrings[] = {
|
||||
"win32", // 0
|
||||
"emscripten", // 1
|
||||
"ios", // 2
|
||||
"darwin", // 3
|
||||
"android", // 4
|
||||
"linux", // 5
|
||||
"unknown" // 6
|
||||
const inline char* kPlatformStrings[] = {
|
||||
"win32", // 0
|
||||
"emscripten", // 1
|
||||
"ios", // 2
|
||||
"darwin", // 3
|
||||
"android", // 4
|
||||
"linux", // 5
|
||||
"unknown" // 6
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define PK_UNREACHABLE() __assume(0);
|
||||
#define PK_UNREACHABLE() __assume(0);
|
||||
#else
|
||||
#define PK_UNREACHABLE() __builtin_unreachable();
|
||||
#define PK_UNREACHABLE() __builtin_unreachable();
|
||||
#endif
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -18,21 +18,27 @@ struct array {
|
||||
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];
|
||||
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) {
|
||||
array& operator= (array&& other) noexcept {
|
||||
if(_data) {
|
||||
std::destroy(begin(), end());
|
||||
std::free(_data);
|
||||
}
|
||||
@ -43,14 +49,14 @@ struct array {
|
||||
return *this;
|
||||
}
|
||||
|
||||
array& operator=(const array& other) = delete;
|
||||
array& operator= (const array& other) = delete;
|
||||
|
||||
T& operator[](int i) {
|
||||
T& operator[] (int i) {
|
||||
assert(i >= 0 && i < _size);
|
||||
return _data[i];
|
||||
}
|
||||
|
||||
const T& operator[](int i) const {
|
||||
const T& operator[] (int i) const {
|
||||
assert(i >= 0 && i < _size);
|
||||
return _data[i];
|
||||
}
|
||||
@ -58,7 +64,9 @@ struct array {
|
||||
int size() const { return _size; }
|
||||
|
||||
T* begin() const { return _data; }
|
||||
|
||||
T* end() const { return _data + _size; }
|
||||
|
||||
T* data() const { return _data; }
|
||||
|
||||
std::pair<T*, int> detach() noexcept {
|
||||
@ -69,7 +77,7 @@ struct array {
|
||||
}
|
||||
|
||||
~array() {
|
||||
if (_data) {
|
||||
if(_data) {
|
||||
std::destroy(begin(), end());
|
||||
std::free(_data);
|
||||
}
|
||||
@ -85,58 +93,63 @@ struct vector {
|
||||
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) {
|
||||
|
||||
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)
|
||||
: _data((T*)std::malloc(sizeof(T) * other._size)),
|
||||
_capacity(other._size),
|
||||
_size(other._size) {
|
||||
for (int i = 0; i < _size; i++) _data[i] = other._data[i];
|
||||
|
||||
vector(explicit_copy_t, const vector& other) :
|
||||
_data((T*)std::malloc(sizeof(T) * other._size)), _capacity(other._size), _size(other._size) {
|
||||
for(int i = 0; i < _size; i++)
|
||||
_data[i] = other._data[i];
|
||||
}
|
||||
|
||||
// allow move
|
||||
vector& operator=(vector&& other) noexcept {
|
||||
if (_data) {
|
||||
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;
|
||||
vector& operator= (const vector& other) = delete;
|
||||
|
||||
bool empty() const { return _size == 0; }
|
||||
|
||||
int size() const { return _size; }
|
||||
|
||||
int capacity() const { return _capacity; }
|
||||
|
||||
T& back() { return _data[_size - 1]; }
|
||||
|
||||
T* begin() const { return _data; }
|
||||
|
||||
T* end() const { return _data + _size; }
|
||||
|
||||
T* data() const { return _data; }
|
||||
|
||||
void reserve(int cap) {
|
||||
if (cap < 4) cap = 4; // minimum capacity
|
||||
if (cap <= capacity()) return;
|
||||
if(cap < 4) cap = 4; // minimum capacity
|
||||
if(cap <= capacity()) return;
|
||||
T* new_data = (T*)std::malloc(sizeof(T) * cap);
|
||||
if constexpr (is_trivially_relocatable_v<T>) {
|
||||
if constexpr(is_trivially_relocatable_v<T>) {
|
||||
std::memcpy(new_data, _data, sizeof(T) * _size);
|
||||
} else {
|
||||
for (int i = 0; i < _size; i++) {
|
||||
for(int i = 0; i < _size; i++) {
|
||||
new (&new_data[i]) T(std::move(_data[i]));
|
||||
_data[i].~T();
|
||||
}
|
||||
}
|
||||
if (_data) std::free(_data);
|
||||
if(_data) std::free(_data);
|
||||
_data = new_data;
|
||||
_capacity = cap;
|
||||
}
|
||||
@ -147,46 +160,49 @@ struct vector {
|
||||
}
|
||||
|
||||
void push_back(const T& t) {
|
||||
if (_size == _capacity) reserve(_capacity * 2);
|
||||
if(_size == _capacity) reserve(_capacity * 2);
|
||||
new (&_data[_size++]) T(t);
|
||||
}
|
||||
|
||||
void push_back(T&& t) {
|
||||
if (_size == _capacity) reserve(_capacity * 2);
|
||||
if(_size == _capacity) reserve(_capacity * 2);
|
||||
new (&_data[_size++]) T(std::move(t));
|
||||
}
|
||||
|
||||
bool contains(const T& t) const {
|
||||
for (int i = 0; i < _size; i++) {
|
||||
if (_data[i] == t) return true;
|
||||
for(int i = 0; i < _size; i++) {
|
||||
if(_data[i] == t) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args) {
|
||||
if (_size == _capacity) reserve(_capacity * 2);
|
||||
if(_size == _capacity) reserve(_capacity * 2);
|
||||
new (&_data[_size++]) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
T& operator[](int i) { return _data[i]; }
|
||||
const T& operator[](int i) const { return _data[i]; }
|
||||
T& operator[] (int i) { return _data[i]; }
|
||||
|
||||
const T& operator[] (int i) const { return _data[i]; }
|
||||
|
||||
void extend(T* begin, T* end) {
|
||||
int n = end - begin;
|
||||
reserve(_size + n);
|
||||
for (int i = 0; i < n; i++) new (&_data[_size++]) T(begin[i]);
|
||||
for(int i = 0; i < n; i++)
|
||||
new (&_data[_size++]) T(begin[i]);
|
||||
}
|
||||
|
||||
void insert(int index, const T& t) {
|
||||
if (_size == _capacity) reserve(_capacity * 2);
|
||||
for (int i = _size; i > index; i--) _data[i] = std::move(_data[i - 1]);
|
||||
if(_size == _capacity) reserve(_capacity * 2);
|
||||
for(int i = _size; i > index; i--)
|
||||
_data[i] = std::move(_data[i - 1]);
|
||||
_data[index] = t;
|
||||
_size++;
|
||||
}
|
||||
|
||||
void erase(int index) {
|
||||
for (int i = index; i < _size - 1; i++)
|
||||
for(int i = index; i < _size - 1; i++)
|
||||
_data[i] = std::move(_data[i + 1]);
|
||||
_size--;
|
||||
}
|
||||
@ -194,9 +210,7 @@ struct vector {
|
||||
void pop_back() {
|
||||
assert(_size > 0);
|
||||
_size--;
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
_data[_size].~T();
|
||||
}
|
||||
if constexpr(!std::is_trivially_destructible_v<T>) { _data[_size].~T(); }
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@ -219,7 +233,7 @@ struct vector {
|
||||
}
|
||||
|
||||
~vector() {
|
||||
if (_data) {
|
||||
if(_data) {
|
||||
std::destroy(begin(), end());
|
||||
std::free(_data);
|
||||
}
|
||||
@ -230,37 +244,49 @@ template <typename T, typename Container = vector<T>>
|
||||
class stack {
|
||||
Container vec;
|
||||
|
||||
public:
|
||||
public:
|
||||
void push(const T& t) { vec.push_back(t); }
|
||||
|
||||
void push(T&& t) { vec.push_back(std::move(t)); }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args&&... args) {
|
||||
vec.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void pop() { vec.pop_back(); }
|
||||
|
||||
void clear() { vec.clear(); }
|
||||
|
||||
bool empty() const { return vec.empty(); }
|
||||
|
||||
typename Container::size_type size() const { return vec.size(); }
|
||||
|
||||
T& top() { return vec.back(); }
|
||||
|
||||
const T& top() const { return vec.back(); }
|
||||
|
||||
T popx() {
|
||||
T t = std::move(vec.back());
|
||||
vec.pop_back();
|
||||
return t;
|
||||
}
|
||||
|
||||
void reserve(int n) { vec.reserve(n); }
|
||||
|
||||
Container& container() { return vec; }
|
||||
|
||||
const Container& container() const { return vec; }
|
||||
};
|
||||
|
||||
template <typename T, typename Container = vector<T>>
|
||||
class stack_no_copy : public stack<T, Container> {
|
||||
public:
|
||||
public:
|
||||
stack_no_copy() = default;
|
||||
stack_no_copy(const stack_no_copy& other) = delete;
|
||||
stack_no_copy& operator=(const stack_no_copy& other) = delete;
|
||||
stack_no_copy& operator= (const stack_no_copy& other) = delete;
|
||||
stack_no_copy(stack_no_copy&& other) noexcept = default;
|
||||
stack_no_copy& operator=(stack_no_copy&& other) noexcept = default;
|
||||
stack_no_copy& operator= (stack_no_copy&& other) noexcept = default;
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
@ -273,7 +299,7 @@ class small_vector {
|
||||
T* m_end;
|
||||
T* m_max;
|
||||
|
||||
public:
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = int;
|
||||
using difference_type = int;
|
||||
@ -286,74 +312,82 @@ class small_vector {
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
[[nodiscard]] bool is_small() const {
|
||||
return m_begin == reinterpret_cast<const T*>(m_buffer);
|
||||
}
|
||||
[[nodiscard]] bool is_small() const { return m_begin == reinterpret_cast<const T*>(m_buffer); }
|
||||
|
||||
[[nodiscard]] size_type size() const { return m_end - m_begin; }
|
||||
|
||||
[[nodiscard]] size_type capacity() const { return m_max - m_begin; }
|
||||
|
||||
[[nodiscard]] bool empty() const { return m_begin == m_end; }
|
||||
|
||||
pointer data() { return m_begin; }
|
||||
const_pointer data() const { return m_begin; }
|
||||
reference operator[](size_type index) { return m_begin[index]; }
|
||||
const_reference operator[](size_type index) const { return m_begin[index]; }
|
||||
iterator begin() { return m_begin; }
|
||||
const_iterator begin() const { return m_begin; }
|
||||
iterator end() { return m_end; }
|
||||
const_iterator end() const { return m_end; }
|
||||
reference front() { return *begin(); }
|
||||
const_reference front() const { return *begin(); }
|
||||
reference back() { return *(end() - 1); }
|
||||
const_reference back() const { return *(end() - 1); }
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
const_reverse_iterator rbegin() const {
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rend() const {
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
private:
|
||||
const_pointer data() const { return m_begin; }
|
||||
|
||||
reference operator[] (size_type index) { return m_begin[index]; }
|
||||
|
||||
const_reference operator[] (size_type index) const { return m_begin[index]; }
|
||||
|
||||
iterator begin() { return m_begin; }
|
||||
|
||||
const_iterator begin() const { return m_begin; }
|
||||
|
||||
iterator end() { return m_end; }
|
||||
|
||||
const_iterator end() const { return m_end; }
|
||||
|
||||
reference front() { return *begin(); }
|
||||
|
||||
const_reference front() const { return *begin(); }
|
||||
|
||||
reference back() { return *(end() - 1); }
|
||||
|
||||
const_reference back() const { return *(end() - 1); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
||||
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
||||
|
||||
private:
|
||||
static void uninitialized_copy_n(const void* src, size_type n, void* dest) {
|
||||
if constexpr (std::is_trivially_copyable_v<T>) {
|
||||
if constexpr(std::is_trivially_copyable_v<T>) {
|
||||
std::memcpy(dest, src, sizeof(T) * n);
|
||||
} else {
|
||||
for (size_type i = 0; i < n; i++) {
|
||||
for(size_type i = 0; i < n; i++) {
|
||||
::new ((T*)dest + i) T(*((const T*)src + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uninitialized_relocate_n(void* src, size_type n, void* dest) {
|
||||
if constexpr (is_trivially_relocatable_v<T>) {
|
||||
if constexpr(is_trivially_relocatable_v<T>) {
|
||||
std::memcpy(dest, src, sizeof(T) * n);
|
||||
} else {
|
||||
for (size_type i = 0; i < n; i++) {
|
||||
for(size_type i = 0; i < n; i++) {
|
||||
::new ((T*)dest + i) T(std::move(*((T*)src + i)));
|
||||
((T*)src + i)->~T();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
small_vector()
|
||||
: m_begin(reinterpret_cast<T*>(m_buffer)),
|
||||
m_end(m_begin),
|
||||
m_max(m_begin + N) {}
|
||||
public:
|
||||
small_vector() : m_begin(reinterpret_cast<T*>(m_buffer)), m_end(m_begin), m_max(m_begin + N) {}
|
||||
|
||||
small_vector(const small_vector& other) noexcept {
|
||||
const auto size = other.size();
|
||||
const auto capacity = other.capacity();
|
||||
m_begin = reinterpret_cast<T*>(
|
||||
other.is_small() ? m_buffer : std::malloc(sizeof(T) * capacity));
|
||||
m_begin = reinterpret_cast<T*>(other.is_small() ? m_buffer : std::malloc(sizeof(T) * capacity));
|
||||
uninitialized_copy_n(other.m_begin, size, this->m_begin);
|
||||
m_end = m_begin + size;
|
||||
m_max = m_begin + capacity;
|
||||
}
|
||||
|
||||
small_vector(small_vector&& other) noexcept {
|
||||
if (other.is_small()) {
|
||||
if(other.is_small()) {
|
||||
m_begin = reinterpret_cast<T*>(m_buffer);
|
||||
uninitialized_relocate_n(other.m_buffer, other.size(), m_buffer);
|
||||
m_end = m_begin + other.size();
|
||||
@ -368,16 +402,16 @@ class small_vector {
|
||||
other.m_max = other.m_begin + N;
|
||||
}
|
||||
|
||||
small_vector& operator=(const small_vector& other) noexcept {
|
||||
if (this != &other) {
|
||||
small_vector& operator= (const small_vector& other) noexcept {
|
||||
if(this != &other) {
|
||||
~small_vector();
|
||||
::new (this) small_vector(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
small_vector& operator=(small_vector&& other) noexcept {
|
||||
if (this != &other) {
|
||||
small_vector& operator= (small_vector&& other) noexcept {
|
||||
if(this != &other) {
|
||||
~small_vector();
|
||||
::new (this) small_vector(std::move(other));
|
||||
}
|
||||
@ -386,21 +420,19 @@ class small_vector {
|
||||
|
||||
~small_vector() {
|
||||
std::destroy(m_begin, m_end);
|
||||
if (!is_small()) std::free(m_begin);
|
||||
if(!is_small()) std::free(m_begin);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args) noexcept {
|
||||
if (m_end == m_max) {
|
||||
if(m_end == m_max) {
|
||||
const auto new_capacity = capacity() * 2;
|
||||
const auto size = this->size();
|
||||
if (!is_small()) {
|
||||
if constexpr (is_trivially_relocatable_v<T>) {
|
||||
m_begin = (pointer)std::realloc(m_begin,
|
||||
sizeof(T) * new_capacity);
|
||||
if(!is_small()) {
|
||||
if constexpr(is_trivially_relocatable_v<T>) {
|
||||
m_begin = (pointer)std::realloc(m_begin, sizeof(T) * new_capacity);
|
||||
} else {
|
||||
auto new_data =
|
||||
(pointer)std::malloc(sizeof(T) * new_capacity);
|
||||
auto new_data = (pointer)std::malloc(sizeof(T) * new_capacity);
|
||||
uninitialized_relocate_n(m_begin, size, new_data);
|
||||
std::free(m_begin);
|
||||
m_begin = new_data;
|
||||
@ -418,13 +450,12 @@ class small_vector {
|
||||
}
|
||||
|
||||
void push_back(const T& value) { emplace_back(value); }
|
||||
|
||||
void push_back(T&& value) { emplace_back(std::move(value)); }
|
||||
|
||||
void pop_back() {
|
||||
m_end--;
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
m_end->~T();
|
||||
}
|
||||
if constexpr(!std::is_trivially_destructible_v<T>) { m_end->~T(); }
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@ -435,11 +466,11 @@ class small_vector {
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
class small_vector_2 : public small_vector<T, N> {
|
||||
public:
|
||||
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& operator= (const small_vector_2& other) = delete;
|
||||
small_vector_2(small_vector_2&& other) = delete;
|
||||
small_vector_2& operator=(small_vector_2&& other) = delete;
|
||||
small_vector_2& operator= (small_vector_2&& other) = delete;
|
||||
};
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
// clang-format off
|
||||
|
||||
#define PK_VERSION "2.0.0"
|
||||
#define PK_VERSION_MAJOR 2
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
|
||||
#include "pocketpy/compiler/expr.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
class Compiler;
|
||||
typedef void (Compiler::*PrattCallback)();
|
||||
|
||||
struct PrattRule{
|
||||
struct PrattRule {
|
||||
PrattCallback prefix;
|
||||
PrattCallback infix;
|
||||
Precedence precedence;
|
||||
@ -21,22 +21,28 @@ class Compiler {
|
||||
Lexer lexer;
|
||||
stack_no_copy<CodeEmitContext> contexts;
|
||||
VM* vm;
|
||||
bool unknown_global_scope; // for eval/exec() call
|
||||
bool unknown_global_scope; // for eval/exec() call
|
||||
// for parsing token stream
|
||||
int i = 0;
|
||||
vector<Token> tokens;
|
||||
|
||||
const Token& prev() const{ return tokens[i-1]; }
|
||||
const Token& curr() const{ return tokens[i]; }
|
||||
const Token& next() const{ return tokens[i+1]; }
|
||||
const Token& err() const{
|
||||
const Token& prev() const { return tokens[i - 1]; }
|
||||
|
||||
const Token& curr() const { return tokens[i]; }
|
||||
|
||||
const Token& next() const { return tokens[i + 1]; }
|
||||
|
||||
const Token& err() const {
|
||||
if(i >= tokens.size()) return prev();
|
||||
return curr();
|
||||
}
|
||||
void advance(int delta=1) { i += delta; }
|
||||
|
||||
void advance(int delta = 1) { i += delta; }
|
||||
|
||||
CodeEmitContext* ctx() { return &contexts.top(); }
|
||||
CompileMode mode() const{ return lexer.src->mode; }
|
||||
|
||||
CompileMode mode() const { return lexer.src->mode; }
|
||||
|
||||
NameScope name_scope() const;
|
||||
CodeObject_ push_global_context();
|
||||
FuncDecl_ push_f_context(Str name);
|
||||
@ -48,13 +54,13 @@ class Compiler {
|
||||
void consume(TokenIndex expected);
|
||||
bool match_newlines_repl();
|
||||
|
||||
bool match_newlines(bool repl_throw=false);
|
||||
bool match_newlines(bool repl_throw = false);
|
||||
bool match_end_stmt();
|
||||
void consume_end_stmt();
|
||||
|
||||
/*************************************************/
|
||||
void EXPR();
|
||||
void EXPR_TUPLE(bool allow_slice=false);
|
||||
void EXPR_TUPLE(bool allow_slice = false);
|
||||
Expr_ EXPR_VARS(); // special case for `for loop` and `comp`
|
||||
|
||||
template <typename T, typename... Args>
|
||||
@ -91,11 +97,11 @@ class Compiler {
|
||||
void exprSubscr();
|
||||
void exprLiteral0();
|
||||
|
||||
void compile_block_body(void (Compiler::*callback)()=nullptr);
|
||||
void compile_block_body(void (Compiler::*callback)() = nullptr);
|
||||
void compile_normal_import();
|
||||
void compile_from_import();
|
||||
bool is_expression(bool allow_slice=false);
|
||||
void parse_expression(int precedence, bool allow_slice=false);
|
||||
bool is_expression(bool allow_slice = false);
|
||||
void parse_expression(int precedence, bool allow_slice = false);
|
||||
void compile_if_stmt();
|
||||
void compile_while_loop();
|
||||
void compile_for_loop();
|
||||
@ -106,32 +112,42 @@ class Compiler {
|
||||
void compile_stmt();
|
||||
void consume_type_hints();
|
||||
void _add_decorators(const Expr_vector& decorators);
|
||||
void compile_class(const Expr_vector& decorators={});
|
||||
void compile_class(const Expr_vector& decorators = {});
|
||||
void _compile_f_args(FuncDecl_ decl, bool enable_type_hints);
|
||||
void compile_function(const Expr_vector& decorators={});
|
||||
void compile_function(const Expr_vector& decorators = {});
|
||||
|
||||
PyVar to_object(const TokenValue& value);
|
||||
PyVar read_literal();
|
||||
|
||||
void SyntaxError(Str msg){ lexer.throw_err("SyntaxError", msg, err().line, err().start); }
|
||||
void SyntaxError(){ lexer.throw_err("SyntaxError", "invalid syntax", err().line, err().start); }
|
||||
void IndentationError(Str msg){ lexer.throw_err("IndentationError", msg, err().line, err().start); }
|
||||
void SyntaxError(Str msg) { lexer.throw_err("SyntaxError", msg, err().line, err().start); }
|
||||
|
||||
void SyntaxError() { lexer.throw_err("SyntaxError", "invalid syntax", err().line, err().start); }
|
||||
|
||||
void IndentationError(Str msg) { lexer.throw_err("IndentationError", msg, err().line, err().start); }
|
||||
|
||||
public:
|
||||
Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope=false);
|
||||
Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope = false);
|
||||
Str precompile();
|
||||
void from_precompiled(const char* source);
|
||||
CodeObject_ compile();
|
||||
};
|
||||
|
||||
struct TokenDeserializer{
|
||||
struct TokenDeserializer {
|
||||
const char* curr;
|
||||
const char* source;
|
||||
|
||||
TokenDeserializer(const char* source): curr(source), source(source) {}
|
||||
char read_char(){ return *curr++; }
|
||||
bool match_char(char c){ if(*curr == c) { curr++; return true; } return false; }
|
||||
|
||||
TokenDeserializer(const char* source) : curr(source), source(source) {}
|
||||
|
||||
char read_char() { return *curr++; }
|
||||
|
||||
bool match_char(char c) {
|
||||
if(*curr == c) {
|
||||
curr++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string_view read_string(char c);
|
||||
Str read_string_from_hex(char c);
|
||||
int read_count();
|
||||
@ -139,4 +155,4 @@ struct TokenDeserializer{
|
||||
f64 read_float(char c);
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,43 +3,64 @@
|
||||
#include "pocketpy/objects/codeobject.hpp"
|
||||
#include "pocketpy/compiler/lexer.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct CodeEmitContext;
|
||||
struct Expr;
|
||||
|
||||
template<typename T>
|
||||
class unique_ptr_128{
|
||||
template <typename T>
|
||||
class unique_ptr_128 {
|
||||
T* ptr;
|
||||
|
||||
public:
|
||||
unique_ptr_128(): ptr(nullptr) {}
|
||||
unique_ptr_128(T* ptr): ptr(ptr) {}
|
||||
unique_ptr_128() : ptr(nullptr) {}
|
||||
|
||||
unique_ptr_128(T* ptr) : ptr(ptr) {}
|
||||
|
||||
T* operator->() const { return ptr; }
|
||||
|
||||
T* get() const { return ptr; }
|
||||
T* detach() { T* p = ptr; ptr = nullptr; return p; }
|
||||
|
||||
T* detach() {
|
||||
T* p = ptr;
|
||||
ptr = nullptr;
|
||||
return p;
|
||||
}
|
||||
|
||||
unique_ptr_128(const unique_ptr_128&) = delete;
|
||||
unique_ptr_128& operator=(const unique_ptr_128&) = delete;
|
||||
unique_ptr_128& operator= (const unique_ptr_128&) = delete;
|
||||
|
||||
bool operator==(std::nullptr_t) const { return ptr == nullptr; }
|
||||
bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
|
||||
bool operator== (std::nullptr_t) const { return ptr == nullptr; }
|
||||
|
||||
~unique_ptr_128(){ if(ptr) { ptr->~T(); PoolExpr_dealloc(ptr); } }
|
||||
bool operator!= (std::nullptr_t) const { return ptr != nullptr; }
|
||||
|
||||
template<typename U>
|
||||
unique_ptr_128(unique_ptr_128<U>&& other): ptr(other.detach()) {}
|
||||
~unique_ptr_128() {
|
||||
if(ptr) {
|
||||
ptr->~T();
|
||||
PoolExpr_dealloc(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const { return ptr != nullptr; }
|
||||
template <typename U>
|
||||
unique_ptr_128(unique_ptr_128<U>&& other) : ptr(other.detach()) {}
|
||||
|
||||
template<typename U>
|
||||
unique_ptr_128& operator=(unique_ptr_128<U>&& other) {
|
||||
if(ptr) { ptr->~T(); PoolExpr_dealloc(ptr); };
|
||||
operator bool () const { return ptr != nullptr; }
|
||||
|
||||
template <typename U>
|
||||
unique_ptr_128& operator= (unique_ptr_128<U>&& other) {
|
||||
if(ptr) {
|
||||
ptr->~T();
|
||||
PoolExpr_dealloc(ptr);
|
||||
};
|
||||
ptr = other.detach();
|
||||
return *this;
|
||||
}
|
||||
|
||||
unique_ptr_128& operator=(std::nullptr_t) {
|
||||
if(ptr) { ptr->~T(); PoolExpr_dealloc(ptr); }
|
||||
unique_ptr_128& operator= (std::nullptr_t) {
|
||||
if(ptr) {
|
||||
ptr->~T();
|
||||
PoolExpr_dealloc(ptr);
|
||||
}
|
||||
ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
@ -48,52 +69,54 @@ public:
|
||||
typedef unique_ptr_128<Expr> Expr_;
|
||||
typedef small_vector<Expr_, 4> Expr_vector;
|
||||
|
||||
template<>
|
||||
template <>
|
||||
constexpr inline bool is_trivially_relocatable_v<Expr_> = true;
|
||||
|
||||
struct Expr{
|
||||
struct Expr {
|
||||
int line = 0;
|
||||
virtual ~Expr() = default;
|
||||
virtual void emit_(CodeEmitContext* ctx) = 0;
|
||||
|
||||
virtual bool is_literal() const { return false; }
|
||||
|
||||
virtual bool is_json_object() const { return false; }
|
||||
|
||||
virtual bool is_attrib() const { return false; }
|
||||
|
||||
virtual bool is_subscr() const { return false; }
|
||||
|
||||
virtual bool is_compare() const { return false; }
|
||||
|
||||
virtual int star_level() const { return 0; }
|
||||
|
||||
virtual bool is_tuple() const { return false; }
|
||||
|
||||
virtual bool is_name() const { return false; }
|
||||
|
||||
bool is_starred() const { return star_level() > 0; }
|
||||
|
||||
// for OP_DELETE_XXX
|
||||
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) {
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
|
||||
|
||||
// for OP_STORE_XXX
|
||||
[[nodiscard]] virtual bool emit_store(CodeEmitContext* ctx) {
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] virtual bool emit_store(CodeEmitContext* ctx) { return false; }
|
||||
|
||||
virtual void emit_inplace(CodeEmitContext* ctx) {
|
||||
emit_(ctx);
|
||||
}
|
||||
virtual void emit_inplace(CodeEmitContext* ctx) { emit_(ctx); }
|
||||
|
||||
[[nodiscard]] virtual bool emit_store_inplace(CodeEmitContext* ctx) {
|
||||
return emit_store(ctx);
|
||||
}
|
||||
[[nodiscard]] virtual bool emit_store_inplace(CodeEmitContext* ctx) { return emit_store(ctx); }
|
||||
};
|
||||
|
||||
struct CodeEmitContext{
|
||||
struct CodeEmitContext {
|
||||
VM* vm;
|
||||
FuncDecl_ func; // optional
|
||||
CodeObject_ co; // 1 CodeEmitContext <=> 1 CodeObject_
|
||||
FuncDecl_ func; // optional
|
||||
CodeObject_ co; // 1 CodeEmitContext <=> 1 CodeObject_
|
||||
// some bugs on MSVC (error C2280) when using Expr_vector
|
||||
// so we use stack_no_copy instead
|
||||
stack_no_copy<Expr_> s_expr;
|
||||
int level;
|
||||
vector<Str> global_names;
|
||||
CodeEmitContext(VM* vm, CodeObject_ co, int level): vm(vm), co(co), level(level) {}
|
||||
|
||||
CodeEmitContext(VM* vm, CodeObject_ co, int level) : vm(vm), co(co), level(level) {}
|
||||
|
||||
int curr_iblock = 0;
|
||||
bool is_compiling_class = false;
|
||||
@ -104,8 +127,8 @@ struct CodeEmitContext{
|
||||
int get_loop() const;
|
||||
CodeBlock* enter_block(CodeBlockType type);
|
||||
void exit_block();
|
||||
void emit_expr(); // clear the expression stack and generate bytecode
|
||||
int emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual=false);
|
||||
void emit_expr(); // clear the expression stack and generate bytecode
|
||||
int emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual = false);
|
||||
void revert_last_emit_();
|
||||
int emit_int(i64 value, int line);
|
||||
void patch_jump(int index);
|
||||
@ -118,150 +141,189 @@ struct CodeEmitContext{
|
||||
void try_merge_for_iter_store(int);
|
||||
};
|
||||
|
||||
struct NameExpr: Expr{
|
||||
struct NameExpr : Expr {
|
||||
StrName name;
|
||||
NameScope scope;
|
||||
NameExpr(StrName name, NameScope scope): name(name), scope(scope) {}
|
||||
|
||||
NameExpr(StrName name, NameScope scope) : name(name), scope(scope) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
|
||||
bool is_name() const override { return true; }
|
||||
};
|
||||
|
||||
struct InvertExpr: Expr{
|
||||
struct InvertExpr : Expr {
|
||||
Expr_ child;
|
||||
InvertExpr(Expr_&& child): child(std::move(child)) {}
|
||||
|
||||
InvertExpr(Expr_&& child) : child(std::move(child)) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct StarredExpr: Expr{
|
||||
struct StarredExpr : Expr {
|
||||
int level;
|
||||
Expr_ child;
|
||||
StarredExpr(int level, Expr_&& child): level(level), child(std::move(child)) {}
|
||||
|
||||
StarredExpr(int level, Expr_&& child) : level(level), child(std::move(child)) {}
|
||||
|
||||
int star_level() const override { return level; }
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct NotExpr: Expr{
|
||||
struct NotExpr : Expr {
|
||||
Expr_ child;
|
||||
NotExpr(Expr_&& child): child(std::move(child)) {}
|
||||
|
||||
NotExpr(Expr_&& child) : child(std::move(child)) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct AndExpr: Expr{
|
||||
struct AndExpr : Expr {
|
||||
Expr_ lhs;
|
||||
Expr_ rhs;
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct OrExpr: Expr{
|
||||
struct OrExpr : Expr {
|
||||
Expr_ lhs;
|
||||
Expr_ rhs;
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
// [None, True, False, ...]
|
||||
struct Literal0Expr: Expr{
|
||||
struct Literal0Expr : Expr {
|
||||
TokenIndex token;
|
||||
Literal0Expr(TokenIndex token): token(token) {}
|
||||
|
||||
Literal0Expr(TokenIndex token) : token(token) {}
|
||||
|
||||
bool is_json_object() const override { return true; }
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct LongExpr: Expr{
|
||||
struct LongExpr : Expr {
|
||||
Str s;
|
||||
LongExpr(const Str& s): s(s) {}
|
||||
|
||||
LongExpr(const Str& s) : s(s) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct BytesExpr: Expr{
|
||||
struct BytesExpr : Expr {
|
||||
Str s;
|
||||
BytesExpr(const Str& s): s(s) {}
|
||||
|
||||
BytesExpr(const Str& s) : s(s) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct ImagExpr: Expr{
|
||||
struct ImagExpr : Expr {
|
||||
f64 value;
|
||||
ImagExpr(f64 value): value(value) {}
|
||||
|
||||
ImagExpr(f64 value) : value(value) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
// @num, @str which needs to invoke OP_LOAD_CONST
|
||||
struct LiteralExpr: Expr{
|
||||
struct LiteralExpr : Expr {
|
||||
TokenValue value;
|
||||
LiteralExpr(TokenValue value): value(value) {}
|
||||
|
||||
LiteralExpr(TokenValue value) : value(value) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
|
||||
bool is_literal() const override { return true; }
|
||||
|
||||
bool is_json_object() const override { return true; }
|
||||
};
|
||||
|
||||
struct NegatedExpr: Expr{
|
||||
struct NegatedExpr : Expr {
|
||||
Expr_ child;
|
||||
NegatedExpr(Expr_&& child): child(std::move(child)) {}
|
||||
|
||||
NegatedExpr(Expr_&& child) : child(std::move(child)) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
|
||||
bool is_json_object() const override { return child->is_literal(); }
|
||||
};
|
||||
|
||||
struct SliceExpr: Expr{
|
||||
struct SliceExpr : Expr {
|
||||
Expr_ start;
|
||||
Expr_ stop;
|
||||
Expr_ step;
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct DictItemExpr: Expr{
|
||||
Expr_ key; // maybe nullptr if it is **kwargs
|
||||
struct DictItemExpr : Expr {
|
||||
Expr_ key; // maybe nullptr if it is **kwargs
|
||||
Expr_ value;
|
||||
|
||||
int star_level() const override { return value->star_level(); }
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct SequenceExpr: Expr{
|
||||
struct SequenceExpr : Expr {
|
||||
Expr_vector items;
|
||||
SequenceExpr(Expr_vector&& items): items(std::move(items)) {}
|
||||
|
||||
SequenceExpr(Expr_vector&& items) : items(std::move(items)) {}
|
||||
|
||||
virtual Opcode opcode() const = 0;
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override {
|
||||
for(auto& item: items) item->emit_(ctx);
|
||||
for(auto& item: items)
|
||||
item->emit_(ctx);
|
||||
ctx->emit_(opcode(), items.size(), line);
|
||||
}
|
||||
};
|
||||
|
||||
struct ListExpr: SequenceExpr{
|
||||
struct ListExpr : SequenceExpr {
|
||||
using SequenceExpr::SequenceExpr;
|
||||
|
||||
Opcode opcode() const override {
|
||||
for(auto& e: items) if(e->is_starred()) return OP_BUILD_LIST_UNPACK;
|
||||
for(auto& e: items)
|
||||
if(e->is_starred()) return OP_BUILD_LIST_UNPACK;
|
||||
return OP_BUILD_LIST;
|
||||
}
|
||||
|
||||
bool is_json_object() const override { return true; }
|
||||
};
|
||||
|
||||
struct DictExpr: SequenceExpr{
|
||||
struct DictExpr : SequenceExpr {
|
||||
using SequenceExpr::SequenceExpr;
|
||||
|
||||
Opcode opcode() const override {
|
||||
for(auto& e: items) if(e->is_starred()) return OP_BUILD_DICT_UNPACK;
|
||||
for(auto& e: items)
|
||||
if(e->is_starred()) return OP_BUILD_DICT_UNPACK;
|
||||
return OP_BUILD_DICT;
|
||||
}
|
||||
|
||||
bool is_json_object() const override { return true; }
|
||||
};
|
||||
|
||||
struct SetExpr: SequenceExpr{
|
||||
struct SetExpr : SequenceExpr {
|
||||
using SequenceExpr::SequenceExpr;
|
||||
|
||||
Opcode opcode() const override {
|
||||
for(auto& e: items) if(e->is_starred()) return OP_BUILD_SET_UNPACK;
|
||||
for(auto& e: items)
|
||||
if(e->is_starred()) return OP_BUILD_SET_UNPACK;
|
||||
return OP_BUILD_SET;
|
||||
}
|
||||
};
|
||||
|
||||
struct TupleExpr: SequenceExpr{
|
||||
struct TupleExpr : SequenceExpr {
|
||||
using SequenceExpr::SequenceExpr;
|
||||
|
||||
bool is_tuple() const override { return true; }
|
||||
|
||||
Opcode opcode() const override {
|
||||
for(auto& e: items) if(e->is_starred()) return OP_BUILD_TUPLE_UNPACK;
|
||||
for(auto& e: items)
|
||||
if(e->is_starred()) return OP_BUILD_TUPLE_UNPACK;
|
||||
return OP_BUILD_TUPLE;
|
||||
}
|
||||
|
||||
@ -269,11 +331,11 @@ struct TupleExpr: SequenceExpr{
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct CompExpr: Expr{
|
||||
Expr_ expr; // loop expr
|
||||
Expr_ vars; // loop vars
|
||||
Expr_ iter; // loop iter
|
||||
Expr_ cond; // optional if condition
|
||||
struct CompExpr : Expr {
|
||||
Expr_ expr; // loop expr
|
||||
Expr_ vars; // loop vars
|
||||
Expr_ iter; // loop iter
|
||||
Expr_ cond; // optional if condition
|
||||
|
||||
virtual Opcode op0() = 0;
|
||||
virtual Opcode op1() = 0;
|
||||
@ -281,25 +343,28 @@ struct CompExpr: Expr{
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct ListCompExpr: CompExpr{
|
||||
struct ListCompExpr : CompExpr {
|
||||
Opcode op0() override { return OP_BUILD_LIST; }
|
||||
|
||||
Opcode op1() override { return OP_LIST_APPEND; }
|
||||
};
|
||||
|
||||
struct DictCompExpr: CompExpr{
|
||||
struct DictCompExpr : CompExpr {
|
||||
Opcode op0() override { return OP_BUILD_DICT; }
|
||||
|
||||
Opcode op1() override { return OP_DICT_ADD; }
|
||||
};
|
||||
|
||||
struct SetCompExpr: CompExpr{
|
||||
struct SetCompExpr : CompExpr {
|
||||
Opcode op0() override { return OP_BUILD_SET; }
|
||||
|
||||
Opcode op1() override { return OP_SET_ADD; }
|
||||
};
|
||||
|
||||
struct LambdaExpr: Expr{
|
||||
struct LambdaExpr : Expr {
|
||||
FuncDecl_ decl;
|
||||
|
||||
LambdaExpr(FuncDecl_ decl): decl(decl) {}
|
||||
LambdaExpr(FuncDecl_ decl) : decl(decl) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override {
|
||||
int index = ctx->add_func_decl(decl);
|
||||
@ -307,17 +372,21 @@ struct LambdaExpr: Expr{
|
||||
}
|
||||
};
|
||||
|
||||
struct FStringExpr: Expr{
|
||||
struct FStringExpr : Expr {
|
||||
Str src;
|
||||
FStringExpr(const Str& src): src(src) {}
|
||||
|
||||
FStringExpr(const Str& src) : src(src) {}
|
||||
|
||||
void _load_simple_expr(CodeEmitContext* ctx, Str expr);
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct SubscrExpr: Expr{
|
||||
struct SubscrExpr : Expr {
|
||||
Expr_ a;
|
||||
Expr_ b;
|
||||
|
||||
bool is_subscr() const override { return true; }
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
@ -326,10 +395,11 @@ struct SubscrExpr: Expr{
|
||||
bool emit_store_inplace(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct AttribExpr: Expr{
|
||||
struct AttribExpr : Expr {
|
||||
Expr_ a;
|
||||
StrName b;
|
||||
AttribExpr(Expr_ a, StrName b): a(std::move(a)), b(b) {}
|
||||
|
||||
AttribExpr(Expr_ a, StrName b) : a(std::move(a)), b(b) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
@ -337,11 +407,12 @@ struct AttribExpr: Expr{
|
||||
void emit_method(CodeEmitContext* ctx);
|
||||
|
||||
bool is_attrib() const override { return true; }
|
||||
|
||||
void emit_inplace(CodeEmitContext* ctx) override;
|
||||
bool emit_store_inplace(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct CallExpr: Expr{
|
||||
struct CallExpr : Expr {
|
||||
Expr_ callable;
|
||||
Expr_vector args;
|
||||
// **a will be interpreted as a special keyword argument: {"**": a}
|
||||
@ -349,42 +420,36 @@ struct CallExpr: Expr{
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct GroupedExpr: Expr{
|
||||
struct GroupedExpr : Expr {
|
||||
Expr_ a;
|
||||
GroupedExpr(Expr_&& a): a(std::move(a)) {}
|
||||
|
||||
void emit_(CodeEmitContext* ctx) override{
|
||||
a->emit_(ctx);
|
||||
}
|
||||
GroupedExpr(Expr_&& a) : a(std::move(a)) {}
|
||||
|
||||
bool emit_del(CodeEmitContext* ctx) override {
|
||||
return a->emit_del(ctx);
|
||||
}
|
||||
void emit_(CodeEmitContext* ctx) override { a->emit_(ctx); }
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
return a->emit_store(ctx);
|
||||
}
|
||||
bool emit_del(CodeEmitContext* ctx) override { return a->emit_del(ctx); }
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override { return a->emit_store(ctx); }
|
||||
};
|
||||
|
||||
struct BinaryExpr: Expr{
|
||||
struct BinaryExpr : Expr {
|
||||
TokenIndex op;
|
||||
Expr_ lhs;
|
||||
Expr_ rhs;
|
||||
bool inplace;
|
||||
|
||||
BinaryExpr(bool inplace=false): inplace(inplace) {}
|
||||
BinaryExpr(bool inplace = false) : inplace(inplace) {}
|
||||
|
||||
bool is_compare() const override;
|
||||
void _emit_compare(CodeEmitContext*, small_vector_2<int, 6>&);
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
|
||||
struct TernaryExpr: Expr{
|
||||
struct TernaryExpr : Expr {
|
||||
Expr_ cond;
|
||||
Expr_ true_expr;
|
||||
Expr_ false_expr;
|
||||
void emit_(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -5,10 +5,11 @@
|
||||
|
||||
#include <variant>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
typedef uint8_t TokenIndex;
|
||||
|
||||
// clang-format off
|
||||
constexpr const char* kTokens[] = {
|
||||
"is not", "not in", "yield from",
|
||||
"@eof", "@eol", "@sof",
|
||||
@ -28,67 +29,71 @@ constexpr const char* kTokens[] = {
|
||||
"None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally",
|
||||
"while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise"
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
using TokenValue = std::variant<std::monostate, i64, f64, Str>;
|
||||
const int kTokenCount = sizeof(kTokens) / sizeof(kTokens[0]);
|
||||
|
||||
constexpr TokenIndex TK(const char token[]) {
|
||||
for(int k=0; k<kTokenCount; k++){
|
||||
for(int k = 0; k < kTokenCount; k++) {
|
||||
const char* i = kTokens[k];
|
||||
const char* j = token;
|
||||
while(*i && *j && *i == *j) { i++; j++;}
|
||||
while(*i && *j && *i == *j) {
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
if(*i == *j) return k;
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
|
||||
inline constexpr bool is_raw_string_used(TokenIndex t){
|
||||
return t == TK("@id") || t == TK("@long");
|
||||
}
|
||||
constexpr inline bool is_raw_string_used(TokenIndex t) { return t == TK("@id") || t == TK("@long"); }
|
||||
|
||||
#define TK_STR(t) kTokens[t]
|
||||
const std::map<std::string_view, TokenIndex> kTokenKwMap = [](){
|
||||
const std::map<std::string_view, TokenIndex> kTokenKwMap = []() {
|
||||
std::map<std::string_view, TokenIndex> map;
|
||||
for(int k=TK("class"); k<kTokenCount; k++) map[kTokens[k]] = k;
|
||||
for(int k = TK("class"); k < kTokenCount; k++)
|
||||
map[kTokens[k]] = k;
|
||||
return map;
|
||||
}();
|
||||
|
||||
struct Token{
|
||||
TokenIndex type;
|
||||
const char* start;
|
||||
int length;
|
||||
int line;
|
||||
int brackets_level;
|
||||
TokenValue value;
|
||||
struct Token {
|
||||
TokenIndex type;
|
||||
const char* start;
|
||||
int length;
|
||||
int line;
|
||||
int brackets_level;
|
||||
TokenValue value;
|
||||
|
||||
Str str() const { return Str(start, length);}
|
||||
std::string_view sv() const { return std::string_view(start, length);}
|
||||
Str str() const { return Str(start, length); }
|
||||
|
||||
std::string_view sv() const { return std::string_view(start, length); }
|
||||
};
|
||||
|
||||
// https://docs.python.org/3/reference/expressions.html#operator-precedence
|
||||
enum Precedence {
|
||||
PREC_LOWEST,
|
||||
PREC_LAMBDA, // lambda
|
||||
PREC_TERNARY, // ?:
|
||||
PREC_LOGICAL_OR, // or
|
||||
PREC_LOGICAL_AND, // and
|
||||
PREC_LOGICAL_NOT, // not
|
||||
/* https://docs.python.org/3/reference/expressions.html#comparisons
|
||||
* Unlike C, all comparison operations in Python have the same priority,
|
||||
* which is lower than that of any arithmetic, shifting or bitwise operation.
|
||||
* Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics.
|
||||
*/
|
||||
PREC_COMPARISION, // < > <= >= != ==, in / is / is not / not in
|
||||
PREC_BITWISE_OR, // |
|
||||
PREC_BITWISE_XOR, // ^
|
||||
PREC_BITWISE_AND, // &
|
||||
PREC_BITWISE_SHIFT, // << >>
|
||||
PREC_TERM, // + -
|
||||
PREC_FACTOR, // * / % // @
|
||||
PREC_UNARY, // - not ~
|
||||
PREC_EXPONENT, // **
|
||||
PREC_PRIMARY, // f() x[] a.b 1:2
|
||||
PREC_HIGHEST,
|
||||
PREC_LOWEST,
|
||||
PREC_LAMBDA, // lambda
|
||||
PREC_TERNARY, // ?:
|
||||
PREC_LOGICAL_OR, // or
|
||||
PREC_LOGICAL_AND, // and
|
||||
PREC_LOGICAL_NOT, // not
|
||||
/* https://docs.python.org/3/reference/expressions.html#comparisons
|
||||
* Unlike C, all comparison operations in Python have the same priority,
|
||||
* which is lower than that of any arithmetic, shifting or bitwise operation.
|
||||
* Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics.
|
||||
*/
|
||||
PREC_COMPARISION, // < > <= >= != ==, in / is / is not / not in
|
||||
PREC_BITWISE_OR, // |
|
||||
PREC_BITWISE_XOR, // ^
|
||||
PREC_BITWISE_AND, // &
|
||||
PREC_BITWISE_SHIFT, // << >>
|
||||
PREC_TERM, // + -
|
||||
PREC_FACTOR, // * / % // @
|
||||
PREC_UNARY, // - not ~
|
||||
PREC_EXPONENT, // **
|
||||
PREC_PRIMARY, // f() x[] a.b 1:2
|
||||
PREC_HIGHEST,
|
||||
};
|
||||
|
||||
enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
|
||||
@ -103,7 +108,8 @@ struct Lexer {
|
||||
stack_no_copy<int, small_vector_2<int, 8>> indents;
|
||||
int brackets_level = 0;
|
||||
|
||||
char peekchar() const{ return *curr_char; }
|
||||
char peekchar() const { return *curr_char; }
|
||||
|
||||
bool match_n_chars(int n, char c0);
|
||||
bool match_string(const char* s);
|
||||
int eat_spaces();
|
||||
@ -114,7 +120,7 @@ struct Lexer {
|
||||
int eat_name();
|
||||
void skip_line_comment();
|
||||
bool matchchar(char c);
|
||||
void add_token(TokenIndex type, TokenValue value={});
|
||||
void add_token(TokenIndex type, TokenValue value = {});
|
||||
void add_token_2(char c, TokenIndex one, TokenIndex two);
|
||||
Str eat_string_until(char quote, bool raw);
|
||||
void eat_string(char quote, StringType type);
|
||||
@ -125,16 +131,18 @@ struct Lexer {
|
||||
/***** Error Reporter *****/
|
||||
[[noreturn]] void throw_err(StrName type, Str msg);
|
||||
[[noreturn]] void throw_err(StrName type, Str msg, int lineno, const char* cursor);
|
||||
[[noreturn]] void SyntaxError(Str msg){ throw_err("SyntaxError", msg); }
|
||||
[[noreturn]] void SyntaxError(){ throw_err("SyntaxError", "invalid syntax"); }
|
||||
[[noreturn]] void IndentationError(Str msg){ throw_err("IndentationError", msg); }
|
||||
|
||||
|
||||
[[noreturn]] void SyntaxError(Str msg) { throw_err("SyntaxError", msg); }
|
||||
|
||||
[[noreturn]] void SyntaxError() { throw_err("SyntaxError", "invalid syntax"); }
|
||||
|
||||
[[noreturn]] void IndentationError(Str msg) { throw_err("IndentationError", msg); }
|
||||
|
||||
Lexer(VM* vm, std::shared_ptr<SourceData> src);
|
||||
vector<Token> run();
|
||||
};
|
||||
|
||||
|
||||
enum class IntParsingResult{
|
||||
enum class IntParsingResult {
|
||||
Success,
|
||||
Failure,
|
||||
Overflow,
|
||||
@ -142,4 +150,4 @@ enum class IntParsingResult{
|
||||
|
||||
IntParsingResult parse_uint(std::string_view text, i64* out, int base);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,104 +2,108 @@
|
||||
|
||||
#include "pocketpy/interpreter/cffi.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
struct NativeProxyFuncCBase {
|
||||
virtual PyVar operator()(VM* vm, ArgsView args) = 0;
|
||||
virtual PyVar operator() (VM* vm, ArgsView args) = 0;
|
||||
};
|
||||
|
||||
template<typename Ret, typename... Params>
|
||||
struct NativeProxyFuncC final: NativeProxyFuncCBase {
|
||||
static constexpr int N = sizeof...(Params);
|
||||
using _Fp = Ret(*)(Params...);
|
||||
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 {
|
||||
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>){
|
||||
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{
|
||||
} 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 {
|
||||
static constexpr int N = sizeof...(Params);
|
||||
using _Fp = Ret(T::*)(Params...);
|
||||
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);
|
||||
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])...);
|
||||
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])...);
|
||||
} 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){
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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(':');
|
||||
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{
|
||||
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){
|
||||
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]);
|
||||
@ -114,94 +118,112 @@ PyObject* VM::bind_field(PyObject* obj, const char* name, F T::*field){
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
#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_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_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_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_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_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 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; })
|
||||
#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
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/interpreter/vm.hpp"
|
||||
// dummy header for ceval.cpp
|
||||
// dummy header for ceval.cpp
|
||||
|
||||
@ -4,28 +4,30 @@
|
||||
|
||||
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); \
|
||||
}
|
||||
#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{
|
||||
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; }
|
||||
VoidP(const void* ptr) : ptr(const_cast<void*>(ptr)) {}
|
||||
|
||||
Str hex() const{
|
||||
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_hex(ptr);
|
||||
return ss.str();
|
||||
@ -34,11 +36,11 @@ struct VoidP{
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
};
|
||||
|
||||
#define POINTER_VAR(Tp, NAME) \
|
||||
inline PyVar py_var(VM* vm, Tp val){ \
|
||||
const static std::pair<StrName, StrName> P("c", NAME); \
|
||||
PyVar type = vm->_modules[P.first]->attr(P.second); \
|
||||
return vm->new_object<VoidP>(type->as<Type>(), val); \
|
||||
#define POINTER_VAR(Tp, NAME) \
|
||||
inline PyVar py_var(VM* vm, Tp val) { \
|
||||
const static std::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")
|
||||
@ -58,43 +60,46 @@ POINTER_VAR(const bool*, "bool_p")
|
||||
|
||||
#undef POINTER_VAR
|
||||
|
||||
|
||||
struct Struct{
|
||||
static constexpr int INLINE_SIZE = 24;
|
||||
struct Struct {
|
||||
constexpr static int INLINE_SIZE = 24;
|
||||
|
||||
char _inlined[INLINE_SIZE];
|
||||
char* p;
|
||||
int size;
|
||||
|
||||
Struct(int new_size, bool zero_init=true){
|
||||
Struct(int new_size, bool zero_init = true) {
|
||||
this->size = new_size;
|
||||
if(size <= INLINE_SIZE){
|
||||
if(size <= INLINE_SIZE) {
|
||||
p = _inlined;
|
||||
}else{
|
||||
} else {
|
||||
p = (char*)std::malloc(size);
|
||||
}
|
||||
if(zero_init) std::memset(p, 0, size);
|
||||
}
|
||||
|
||||
Struct(void* p, int size): Struct(size, false){
|
||||
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); }
|
||||
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){
|
||||
template <typename Tp>
|
||||
Tp to_void_p(VM* vm, PyVar var) {
|
||||
static_assert(std::is_pointer_v<Tp>);
|
||||
if(var == vm->None) return nullptr; // None can be casted to any pointer implicitly
|
||||
if(var == vm->None) 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
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,25 +2,27 @@
|
||||
|
||||
#include "pocketpy/objects/codeobject.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
// weak reference fast locals
|
||||
struct FastLocals{
|
||||
struct FastLocals {
|
||||
// this is a weak reference
|
||||
const CodeObject* co;
|
||||
PyVar* a;
|
||||
|
||||
int size() const{ return co->nlocals;}
|
||||
int size() const { return co->nlocals; }
|
||||
|
||||
PyVar& operator[](int i){ return a[i]; }
|
||||
PyVar operator[](int i) const { return a[i]; }
|
||||
PyVar& operator[] (int i) { return a[i]; }
|
||||
|
||||
FastLocals(const CodeObject* co, PyVar* a): co(co), a(a) {}
|
||||
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(); }
|
||||
};
|
||||
|
||||
@ -28,49 +30,72 @@ 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 _begin[PK_VM_STACK_SIZE + PK_VM_STACK_SIZE / 128];
|
||||
PyVar* _sp;
|
||||
PyVar* _max_end;
|
||||
|
||||
static constexpr size_t max_size() { return PK_VM_STACK_SIZE; }
|
||||
constexpr static size_t max_size() { return PK_VM_STACK_SIZE; }
|
||||
|
||||
ValueStack(): _sp(_begin), _max_end(_begin + PK_VM_STACK_SIZE) {}
|
||||
ValueStack() : _sp(_begin), _max_end(_begin + PK_VM_STACK_SIZE) {}
|
||||
|
||||
PyVar& top() { return _sp[-1]; }
|
||||
|
||||
PyVar& top(){ return _sp[-1]; }
|
||||
PyVar top() const { return _sp[-1]; }
|
||||
PyVar& second(){ return _sp[-2]; }
|
||||
|
||||
PyVar& second() { return _sp[-2]; }
|
||||
|
||||
PyVar second() const { return _sp[-2]; }
|
||||
PyVar& third(){ return _sp[-3]; }
|
||||
|
||||
PyVar& third() { return _sp[-3]; }
|
||||
|
||||
PyVar third() const { return _sp[-3]; }
|
||||
PyVar& peek(int n){ return _sp[-n]; }
|
||||
|
||||
PyVar& peek(int n) { return _sp[-n]; }
|
||||
|
||||
PyVar peek(int n) const { return _sp[-n]; }
|
||||
void push(PyVar v){ *_sp++ = v; }
|
||||
|
||||
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; }
|
||||
|
||||
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)...);
|
||||
template <typename... Args>
|
||||
void emplace(Args&&... args) {
|
||||
new (_sp) PyVar(std::forward<Args>(args)...);
|
||||
++_sp;
|
||||
}
|
||||
};
|
||||
|
||||
struct UnwindTarget{
|
||||
struct UnwindTarget {
|
||||
UnwindTarget* next;
|
||||
int iblock;
|
||||
int offset;
|
||||
|
||||
UnwindTarget(int iblock, int offset): next(nullptr), iblock(iblock), offset(offset) {}
|
||||
UnwindTarget(int iblock, int offset) : next(nullptr), iblock(iblock), offset(offset) {}
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
@ -82,36 +107,42 @@ struct Frame {
|
||||
|
||||
const CodeObject* co;
|
||||
PyObject* _module;
|
||||
PyObject* _callable; // a function object or nullptr (global scope)
|
||||
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 - co->codes.data(); }
|
||||
|
||||
// function scope
|
||||
Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, PyVar* _locals_base)
|
||||
: _ip(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, _locals_base), _uw_list(nullptr) { }
|
||||
Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, PyVar* _locals_base) :
|
||||
_ip(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(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals), _uw_list(nullptr) { }
|
||||
Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, FastLocals _locals) :
|
||||
_ip(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(co->codes.data()-1), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr), _locals(co.get(), p0), _uw_list(nullptr) { }
|
||||
Frame(PyVar* p0, const CodeObject_& co, PyObject* _module) :
|
||||
_ip(co->codes.data() - 1), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr),
|
||||
_locals(co.get(), 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){
|
||||
[[nodiscard]] int prepare_loop_break(ValueStack* s_data) {
|
||||
int target = co->_get_block_codei(ip()).end;
|
||||
prepare_jump_break(s_data, target);
|
||||
return target;
|
||||
@ -126,29 +157,35 @@ struct Frame {
|
||||
~Frame();
|
||||
};
|
||||
|
||||
struct LinkedFrame{
|
||||
struct LinkedFrame {
|
||||
LinkedFrame* f_back;
|
||||
Frame frame;
|
||||
template<typename... Args>
|
||||
|
||||
template <typename... Args>
|
||||
LinkedFrame(LinkedFrame* f_back, Args&&... args) : f_back(f_back), frame(std::forward<Args>(args)...) {}
|
||||
};
|
||||
|
||||
|
||||
struct CallStack{
|
||||
struct CallStack {
|
||||
static_assert(sizeof(LinkedFrame) <= 128);
|
||||
|
||||
LinkedFrame* _tail;
|
||||
int _size;
|
||||
CallStack(): _tail(nullptr), _size(0) {}
|
||||
|
||||
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){
|
||||
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)...);
|
||||
_tail = new (PoolFrame_alloc()) LinkedFrame(_tail, std::forward<Args>(args)...);
|
||||
++_size;
|
||||
}
|
||||
|
||||
@ -161,12 +198,13 @@ struct CallStack{
|
||||
return _tail->frame;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void apply(Func&& f){
|
||||
for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back) f(p->frame);
|
||||
template <typename Func>
|
||||
void apply(Func&& f) {
|
||||
for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back)
|
||||
f(p->frame);
|
||||
}
|
||||
|
||||
~CallStack(){ clear(); }
|
||||
~CallStack() { clear(); }
|
||||
};
|
||||
|
||||
}; // namespace pkpy
|
||||
}; // namespace pkpy
|
||||
|
||||
@ -6,54 +6,52 @@
|
||||
#include "pocketpy/objects/object.hpp"
|
||||
|
||||
namespace pkpy {
|
||||
struct ManagedHeap{
|
||||
struct ManagedHeap {
|
||||
vector<PyObject*> _no_gc;
|
||||
vector<PyObject*> gen;
|
||||
VM* vm;
|
||||
void (*_gc_on_delete)(VM*, PyObject*) = nullptr;
|
||||
void (*_gc_marker_ex)(VM*) = nullptr;
|
||||
|
||||
ManagedHeap(VM* vm): vm(vm) {}
|
||||
|
||||
ManagedHeap(VM* vm) : vm(vm) {}
|
||||
|
||||
int gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||
int gc_counter = 0;
|
||||
|
||||
/********************/
|
||||
int _gc_lock_counter = 0;
|
||||
struct ScopeLock{
|
||||
|
||||
struct ScopeLock {
|
||||
PK_ALWAYS_PASS_BY_POINTER(ScopeLock)
|
||||
|
||||
|
||||
ManagedHeap* heap;
|
||||
ScopeLock(ManagedHeap* heap): heap(heap){
|
||||
heap->_gc_lock_counter++;
|
||||
}
|
||||
~ScopeLock(){
|
||||
heap->_gc_lock_counter--;
|
||||
}
|
||||
|
||||
ScopeLock(ManagedHeap* heap) : heap(heap) { heap->_gc_lock_counter++; }
|
||||
|
||||
~ScopeLock() { heap->_gc_lock_counter--; }
|
||||
};
|
||||
|
||||
ScopeLock gc_scope_lock(){
|
||||
return ScopeLock(this);
|
||||
}
|
||||
ScopeLock gc_scope_lock() { return ScopeLock(this); }
|
||||
|
||||
/********************/
|
||||
|
||||
template<typename T, typename... Args>
|
||||
PyObject* gcnew(Type type, Args&&... args){
|
||||
template <typename T, typename... Args>
|
||||
PyObject* gcnew(Type type, Args&&... args) {
|
||||
using __T = std::decay_t<T>;
|
||||
static_assert(!is_sso_v<__T>, "gcnew cannot be used with SSO types");
|
||||
// https://github.com/pocketpy/pocketpy/issues/94#issuecomment-1594784476
|
||||
PyObject* p = new(PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
|
||||
PyObject* p = new (PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
|
||||
p->placement_new<__T>(std::forward<Args>(args)...);
|
||||
gen.push_back(p);
|
||||
gc_counter++;
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
PyObject* _new(Type type, Args&&... args){
|
||||
template <typename T, typename... Args>
|
||||
PyObject* _new(Type type, Args&&... args) {
|
||||
using __T = std::decay_t<T>;
|
||||
static_assert(!is_sso_v<__T>);
|
||||
PyObject* p = new(PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
|
||||
PyObject* p = new (PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
|
||||
p->placement_new<__T>(std::forward<Args>(args)...);
|
||||
_no_gc.push_back(p);
|
||||
return p;
|
||||
@ -67,9 +65,11 @@ struct ManagedHeap{
|
||||
|
||||
int sweep();
|
||||
void _auto_collect();
|
||||
|
||||
bool _should_auto_collect() const { return gc_counter >= gc_threshold; }
|
||||
|
||||
int collect();
|
||||
void mark();
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,51 +2,57 @@
|
||||
|
||||
#include "pocketpy/interpreter/bindings.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct RangeIter{ // step > 0
|
||||
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
|
||||
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{
|
||||
struct ArrayIter {
|
||||
PyObject* ref;
|
||||
PyVar* end;
|
||||
PyVar* current;
|
||||
|
||||
ArrayIter(PyObject* ref, PyVar* begin, PyVar* end)
|
||||
: ref(ref), end(end), current(begin) {}
|
||||
ArrayIter(PyObject* ref, PyVar* begin, PyVar* end) : ref(ref), end(end), current(begin) {}
|
||||
|
||||
void _gc_mark(VM* vm) const { vm->__obj_gc_mark(ref); }
|
||||
|
||||
void _gc_mark(VM* vm) const{ vm->__obj_gc_mark(ref); }
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
};
|
||||
|
||||
struct StringIter{
|
||||
struct StringIter {
|
||||
PyVar ref;
|
||||
int i; // byte index
|
||||
int i; // byte index
|
||||
|
||||
StringIter(PyVar ref) : ref(ref), i(0) {}
|
||||
void _gc_mark(VM* vm) const{ vm->obj_gc_mark(ref); }
|
||||
|
||||
void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }
|
||||
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
};
|
||||
|
||||
struct Generator{
|
||||
struct Generator {
|
||||
LinkedFrame* lf;
|
||||
int state; // 0,1,2
|
||||
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);
|
||||
Generator(LinkedFrame* lf, ArgsView buffer) : lf(lf), state(0) {
|
||||
for(PyVar obj: buffer)
|
||||
s_backup.push_back(obj);
|
||||
}
|
||||
|
||||
void _gc_mark(VM* vm) {
|
||||
@ -58,22 +64,23 @@ struct Generator{
|
||||
PyVar next(VM* vm);
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
|
||||
~Generator(){
|
||||
if(lf){
|
||||
~Generator() {
|
||||
if(lf) {
|
||||
lf->~LinkedFrame();
|
||||
PoolFrame_dealloc(lf);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DictItemsIter{
|
||||
struct DictItemsIter {
|
||||
PyVar ref;
|
||||
int i;
|
||||
DictItemsIter(PyVar ref) : ref(ref) {
|
||||
i = PK_OBJ_GET(Dict, ref)._head_idx;
|
||||
}
|
||||
void _gc_mark(VM* vm) const{ vm->obj_gc_mark(ref); }
|
||||
|
||||
DictItemsIter(PyVar ref) : ref(ref) { i = PK_OBJ_GET(Dict, ref)._head_idx; }
|
||||
|
||||
void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }
|
||||
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -6,23 +6,24 @@
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
struct _LineRecord{
|
||||
struct _LineRecord {
|
||||
int line;
|
||||
i64 hits;
|
||||
clock_t time;
|
||||
|
||||
_LineRecord(): line(-1), hits(0), time(0) {}
|
||||
_LineRecord() : line(-1), hits(0), time(0) {}
|
||||
|
||||
bool is_valid() const { return line != -1; }
|
||||
};
|
||||
|
||||
struct _FrameRecord{
|
||||
struct _FrameRecord {
|
||||
int callstack_size;
|
||||
Frame* frame;
|
||||
clock_t prev_time;
|
||||
_LineRecord* prev_record;
|
||||
};
|
||||
|
||||
struct LineProfiler{
|
||||
struct LineProfiler {
|
||||
// filename -> records
|
||||
std::map<std::string_view, vector<_LineRecord>> records;
|
||||
stack_no_copy<_FrameRecord> frames;
|
||||
@ -35,4 +36,4 @@ struct LineProfiler{
|
||||
Str stats();
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -11,67 +11,76 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
/* Stack manipulation macros */
|
||||
// https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
|
||||
#define TOP() (s_data.top())
|
||||
#define SECOND() (s_data.second())
|
||||
#define THIRD() (s_data.third())
|
||||
#define STACK_SHRINK(n) (s_data.shrink(n))
|
||||
#define PUSH(v) (s_data.push(v))
|
||||
#define POP() (s_data.pop())
|
||||
#define POPX() (s_data.popx())
|
||||
#define STACK_VIEW(n) (s_data.view(n))
|
||||
#define TOP() (s_data.top())
|
||||
#define SECOND() (s_data.second())
|
||||
#define THIRD() (s_data.third())
|
||||
#define STACK_SHRINK(n) (s_data.shrink(n))
|
||||
#define PUSH(v) (s_data.push(v))
|
||||
#define POP() (s_data.pop())
|
||||
#define POPX() (s_data.popx())
|
||||
#define STACK_VIEW(n) (s_data.view(n))
|
||||
|
||||
typedef PyVar (*BinaryFuncC)(VM*, PyVar, PyVar);
|
||||
typedef void (*RegisterFunc)(VM*, PyObject*, PyObject*);
|
||||
|
||||
#if PK_ENABLE_PROFILER
|
||||
struct NextBreakpoint{
|
||||
struct NextBreakpoint {
|
||||
int callstack_size;
|
||||
int lineno;
|
||||
bool should_step_into;
|
||||
NextBreakpoint(): callstack_size(0) {}
|
||||
NextBreakpoint(int callstack_size, int lineno, bool should_step_into): callstack_size(callstack_size), lineno(lineno), should_step_into(should_step_into) {}
|
||||
|
||||
NextBreakpoint() : callstack_size(0) {}
|
||||
|
||||
NextBreakpoint(int callstack_size, int lineno, bool should_step_into) :
|
||||
callstack_size(callstack_size), lineno(lineno), should_step_into(should_step_into) {}
|
||||
|
||||
void _step(VM* vm);
|
||||
|
||||
bool empty() const { return callstack_size == 0; }
|
||||
};
|
||||
#endif
|
||||
|
||||
struct PyTypeInfo{
|
||||
struct Vt{
|
||||
struct PyTypeInfo {
|
||||
struct Vt {
|
||||
void (*_dtor)(void*);
|
||||
void (*_gc_mark)(void*, VM*);
|
||||
|
||||
Vt(): _dtor(nullptr), _gc_mark(nullptr) {}
|
||||
Vt() : _dtor(nullptr), _gc_mark(nullptr) {}
|
||||
|
||||
operator bool() const { return _dtor || _gc_mark; }
|
||||
operator bool () const { return _dtor || _gc_mark; }
|
||||
|
||||
template<typename T>
|
||||
inline static Vt get(){
|
||||
template <typename T>
|
||||
inline static Vt get() {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
Vt vt;
|
||||
if constexpr(!std::is_trivially_destructible_v<T>){
|
||||
vt._dtor = [](void* p){ ((T*)p)->~T(); };
|
||||
if constexpr(!std::is_trivially_destructible_v<T>) {
|
||||
vt._dtor = [](void* p) {
|
||||
((T*)p)->~T();
|
||||
};
|
||||
}
|
||||
if constexpr(has_gc_marker<T>::value){
|
||||
vt._gc_mark = [](void* p, VM* vm){ ((T*)p)->_gc_mark(vm); };
|
||||
if constexpr(has_gc_marker<T>::value) {
|
||||
vt._gc_mark = [](void* p, VM* vm) {
|
||||
((T*)p)->_gc_mark(vm);
|
||||
};
|
||||
}
|
||||
return vt;
|
||||
}
|
||||
};
|
||||
|
||||
PyObject* obj; // never be garbage collected
|
||||
PyObject* obj; // never be garbage collected
|
||||
Type base;
|
||||
PyObject* mod; // never be garbage collected
|
||||
PyObject* mod; // never be garbage collected
|
||||
StrName name;
|
||||
bool subclass_enabled;
|
||||
Vt vt;
|
||||
|
||||
PyTypeInfo(PyObject* obj, Type base, PyObject* mod, StrName name, bool subclass_enabled, Vt vt={}):
|
||||
PyTypeInfo(PyObject* obj, Type base, PyObject* mod, StrName name, bool subclass_enabled, Vt vt = {}) :
|
||||
obj(obj), base(base), mod(mod), name(name), subclass_enabled(subclass_enabled), vt(vt) {}
|
||||
|
||||
|
||||
vector<StrName> annotated_fields = {};
|
||||
|
||||
// unary operators
|
||||
@ -122,52 +131,53 @@ struct PyTypeInfo{
|
||||
void (*on_end_subclass)(VM* vm, PyTypeInfo*) = nullptr;
|
||||
};
|
||||
|
||||
struct ImportContext{
|
||||
struct ImportContext {
|
||||
PK_ALWAYS_PASS_BY_POINTER(ImportContext)
|
||||
|
||||
vector<Str> pending;
|
||||
vector<bool> pending_is_init; // a.k.a __init__.py
|
||||
vector<bool> pending_is_init; // a.k.a __init__.py
|
||||
|
||||
ImportContext() {}
|
||||
|
||||
struct Temp{
|
||||
struct Temp {
|
||||
PK_ALWAYS_PASS_BY_POINTER(Temp)
|
||||
|
||||
ImportContext* ctx;
|
||||
Temp(ImportContext* ctx, Str name, bool is_init) : ctx(ctx){
|
||||
|
||||
Temp(ImportContext* ctx, Str name, bool is_init) : ctx(ctx) {
|
||||
ctx->pending.push_back(name);
|
||||
ctx->pending_is_init.push_back(is_init);
|
||||
}
|
||||
~Temp(){
|
||||
|
||||
~Temp() {
|
||||
ctx->pending.pop_back();
|
||||
ctx->pending_is_init.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
Temp scope(Str name, bool is_init){
|
||||
return {this, name, is_init};
|
||||
}
|
||||
Temp scope(Str name, bool is_init) { return {this, name, is_init}; }
|
||||
};
|
||||
|
||||
class VM {
|
||||
PK_ALWAYS_PASS_BY_POINTER(VM)
|
||||
|
||||
VM* vm; // self reference to simplify code
|
||||
|
||||
VM* vm; // self reference to simplify code
|
||||
|
||||
public:
|
||||
ManagedHeap heap;
|
||||
ValueStack s_data;
|
||||
CallStack callstack;
|
||||
vector<PyTypeInfo> _all_types;
|
||||
|
||||
NameDict _modules; // loaded modules
|
||||
std::map<StrName, Str> _lazy_modules; // lazy loaded modules
|
||||
|
||||
struct{
|
||||
NameDict _modules; // loaded modules
|
||||
std::map<StrName, Str> _lazy_modules; // lazy loaded modules
|
||||
|
||||
struct {
|
||||
PyObject* error;
|
||||
stack_no_copy<ArgsView> s_view;
|
||||
} __c;
|
||||
|
||||
PyVar StopIteration; // a special Exception class
|
||||
PyVar StopIteration; // a special Exception class
|
||||
PyObject* builtins;
|
||||
PyObject* _main;
|
||||
|
||||
@ -191,34 +201,36 @@ public:
|
||||
#endif
|
||||
|
||||
void (*_ceval_on_step)(VM*, Frame*, Bytecode bc);
|
||||
void(*_stdout)(const char*, int);
|
||||
void(*_stderr)(const char*, int);
|
||||
void (*_stdout)(const char*, int);
|
||||
void (*_stderr)(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
|
||||
static constexpr Type tp_object=Type(1), tp_type=Type(2);
|
||||
static constexpr Type tp_int=Type(kTpIntIndex), tp_float=Type(kTpFloatIndex), tp_bool=Type(5), tp_str=Type(6);
|
||||
static constexpr Type tp_list=Type(7), tp_tuple=Type(8);
|
||||
static constexpr Type tp_slice=Type(9), tp_range=Type(10), tp_module=Type(11);
|
||||
static constexpr Type tp_function=Type(12), tp_native_func=Type(13), tp_bound_method=Type(14);
|
||||
static constexpr Type tp_super=Type(15), tp_exception=Type(16), tp_bytes=Type(17), tp_mappingproxy=Type(18);
|
||||
static constexpr Type tp_dict=Type(19), tp_property=Type(20), tp_star_wrapper=Type(21);
|
||||
static constexpr Type tp_staticmethod=Type(22), tp_classmethod=Type(23);
|
||||
static constexpr Type tp_none_type=Type(24), tp_not_implemented=Type(25), tp_ellipsis=Type(26);
|
||||
static constexpr Type tp_stack_memory=Type(kTpStackMemoryIndex);
|
||||
|
||||
static constexpr PyVar True{const_sso_var(), tp_bool, 1};
|
||||
static constexpr PyVar False{const_sso_var(), tp_bool, 0};
|
||||
static constexpr PyVar None{const_sso_var(), tp_none_type, 0};
|
||||
static constexpr PyVar NotImplemented{const_sso_var(), tp_not_implemented, 0};
|
||||
static constexpr PyVar Ellipsis{const_sso_var(), tp_ellipsis, 0};
|
||||
// for quick access
|
||||
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),
|
||||
tp_str = Type(6);
|
||||
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_function = Type(12), tp_native_func = Type(13), tp_bound_method = Type(14);
|
||||
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_staticmethod = Type(22), tp_classmethod = Type(23);
|
||||
constexpr static Type tp_none_type = Type(24), tp_not_implemented = Type(25), tp_ellipsis = Type(26);
|
||||
constexpr static Type tp_stack_memory = Type(kTpStackMemoryIndex);
|
||||
|
||||
constexpr static PyVar True{const_sso_var(), tp_bool, 1};
|
||||
constexpr static PyVar False{const_sso_var(), tp_bool, 0};
|
||||
constexpr static PyVar None{const_sso_var(), tp_none_type, 0};
|
||||
constexpr static PyVar NotImplemented{const_sso_var(), tp_not_implemented, 0};
|
||||
constexpr static PyVar Ellipsis{const_sso_var(), tp_ellipsis, 0};
|
||||
|
||||
const bool enable_os;
|
||||
VM(bool enable_os=true);
|
||||
VM(bool enable_os = true);
|
||||
|
||||
// clang-format off
|
||||
#if PK_REGION("Python Equivalents")
|
||||
Str py_str(PyVar obj); // x -> str(x)
|
||||
Str py_repr(PyVar obj); // x -> repr(x)
|
||||
@ -453,18 +465,19 @@ public:
|
||||
vm->s_data.emplace(p->type, p);
|
||||
}
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
template<typename T>
|
||||
Type _find_type_in_cxx_typeid_map(){
|
||||
template <typename T>
|
||||
Type _find_type_in_cxx_typeid_map() {
|
||||
auto it = _cxx_typeid_map.find(typeid(T));
|
||||
if(it == _cxx_typeid_map.end()){
|
||||
#if __GNUC__ || __clang__
|
||||
if(it == _cxx_typeid_map.end()) {
|
||||
#if __GNUC__ || __clang__
|
||||
throw std::runtime_error(__PRETTY_FUNCTION__ + std::string(" failed: T not found"));
|
||||
#elif _MSC_VER
|
||||
#elif _MSC_VER
|
||||
throw std::runtime_error(__FUNCSIG__ + std::string(" failed: T not found"));
|
||||
#else
|
||||
#else
|
||||
throw std::runtime_error("_find_type_in_cxx_typeid_map() failed: T not found");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
@ -485,17 +498,35 @@ public:
|
||||
void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl_&);
|
||||
void __unpack_as_list(ArgsView args, List& list);
|
||||
void __unpack_as_dict(ArgsView args, Dict& dict);
|
||||
[[noreturn]] void __raise_exc(bool re_raise=false);
|
||||
[[noreturn]] void __raise_exc(bool re_raise = false);
|
||||
[[noreturn]] void __builtin_error(StrName type);
|
||||
[[noreturn]] void __builtin_error(StrName type, PyVar arg);
|
||||
[[noreturn]] void __builtin_error(StrName type, const Str& msg);
|
||||
void __init_builtin_types();
|
||||
void __post_init_builtin_types();
|
||||
void __push_varargs(){}
|
||||
void __push_varargs(PyVar _0){ PUSH(_0); }
|
||||
void __push_varargs(PyVar _0, PyVar _1){ PUSH(_0); PUSH(_1); }
|
||||
void __push_varargs(PyVar _0, PyVar _1, PyVar _2){ PUSH(_0); PUSH(_1); PUSH(_2); }
|
||||
void __push_varargs(PyVar _0, PyVar _1, PyVar _2, PyVar _3){ PUSH(_0); PUSH(_1); PUSH(_2); PUSH(_3); }
|
||||
|
||||
void __push_varargs() {}
|
||||
|
||||
void __push_varargs(PyVar _0) { PUSH(_0); }
|
||||
|
||||
void __push_varargs(PyVar _0, PyVar _1) {
|
||||
PUSH(_0);
|
||||
PUSH(_1);
|
||||
}
|
||||
|
||||
void __push_varargs(PyVar _0, PyVar _1, PyVar _2) {
|
||||
PUSH(_0);
|
||||
PUSH(_1);
|
||||
PUSH(_2);
|
||||
}
|
||||
|
||||
void __push_varargs(PyVar _0, PyVar _1, PyVar _2, PyVar _3) {
|
||||
PUSH(_0);
|
||||
PUSH(_1);
|
||||
PUSH(_2);
|
||||
PUSH(_3);
|
||||
}
|
||||
|
||||
PyVar __pack_next_retval(unsigned);
|
||||
PyVar __minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key);
|
||||
bool __py_bool_non_trivial(PyVar);
|
||||
@ -504,128 +535,201 @@ public:
|
||||
void* __stack_alloc(int size);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_immutable_v =
|
||||
is_integral_v<T> || is_floating_point_v<T> || std::is_same_v<T, Str> || std::is_same_v<T, Tuple> ||
|
||||
std::is_same_v<T, Bytes> || std::is_same_v<T, bool> || std::is_same_v<T, Range> || std::is_same_v<T, Slice> ||
|
||||
std::is_pointer_v<T> || std::is_enum_v<T>;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_immutable_v = is_integral_v<T> || is_floating_point_v<T>
|
||||
|| std::is_same_v<T, Str> || std::is_same_v<T, Tuple> || std::is_same_v<T, Bytes> || std::is_same_v<T, bool>
|
||||
|| std::is_same_v<T, Range> || std::is_same_v<T, Slice>
|
||||
|| std::is_pointer_v<T> || std::is_enum_v<T>;
|
||||
template <typename T>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map() {
|
||||
return Type();
|
||||
}
|
||||
|
||||
template<typename T> constexpr Type _find_type_in_const_cxx_typeid_map(){ return Type(); }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Str>(){ return VM::tp_str; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<List>(){ return VM::tp_list; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Tuple>(){ return VM::tp_tuple; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Function>(){ return VM::tp_function; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<NativeFunc>(){ return VM::tp_native_func; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<BoundMethod>(){ return VM::tp_bound_method; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Range>(){ return VM::tp_range; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Slice>(){ return VM::tp_slice; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Exception>(){ return VM::tp_exception; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Bytes>(){ return VM::tp_bytes; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<MappingProxy>(){ return VM::tp_mappingproxy; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Dict>(){ return VM::tp_dict; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<Property>(){ return VM::tp_property; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<StarWrapper>(){ return VM::tp_star_wrapper; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<StaticMethod>(){ return VM::tp_staticmethod; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<ClassMethod>(){ return VM::tp_classmethod; }
|
||||
template<> constexpr Type _find_type_in_const_cxx_typeid_map<StackMemory>(){ return VM::tp_stack_memory; }
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Str>() {
|
||||
return VM::tp_str;
|
||||
}
|
||||
|
||||
template<typename __T>
|
||||
PyVar py_var(VM* vm, __T&& value){
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<List>() {
|
||||
return VM::tp_list;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Tuple>() {
|
||||
return VM::tp_tuple;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Function>() {
|
||||
return VM::tp_function;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<NativeFunc>() {
|
||||
return VM::tp_native_func;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<BoundMethod>() {
|
||||
return VM::tp_bound_method;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Range>() {
|
||||
return VM::tp_range;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Slice>() {
|
||||
return VM::tp_slice;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Exception>() {
|
||||
return VM::tp_exception;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Bytes>() {
|
||||
return VM::tp_bytes;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<MappingProxy>() {
|
||||
return VM::tp_mappingproxy;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Dict>() {
|
||||
return VM::tp_dict;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<Property>() {
|
||||
return VM::tp_property;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<StarWrapper>() {
|
||||
return VM::tp_star_wrapper;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<StaticMethod>() {
|
||||
return VM::tp_staticmethod;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<ClassMethod>() {
|
||||
return VM::tp_classmethod;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr Type _find_type_in_const_cxx_typeid_map<StackMemory>() {
|
||||
return VM::tp_stack_memory;
|
||||
}
|
||||
|
||||
template <typename __T>
|
||||
PyVar py_var(VM* vm, __T&& value) {
|
||||
using T = std::decay_t<__T>;
|
||||
|
||||
static_assert(!std::is_same_v<T, PyVar>, "py_var(VM*, PyVar) is not allowed");
|
||||
|
||||
if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>){
|
||||
if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view>) {
|
||||
// str (shortcuts)
|
||||
return VAR(Str(std::forward<__T>(value)));
|
||||
}else if constexpr(std::is_same_v<T, NoReturn>){
|
||||
} else if constexpr(std::is_same_v<T, NoReturn>) {
|
||||
// NoneType
|
||||
return vm->None;
|
||||
}else if constexpr(std::is_same_v<T, bool>){
|
||||
} else if constexpr(std::is_same_v<T, bool>) {
|
||||
// bool
|
||||
return value ? vm->True : vm->False;
|
||||
}else if constexpr(is_integral_v<T>){
|
||||
} else if constexpr(is_integral_v<T>) {
|
||||
// int
|
||||
return PyVar(VM::tp_int, static_cast<i64>(value));
|
||||
}else if constexpr(is_floating_point_v<T>){
|
||||
} else if constexpr(is_floating_point_v<T>) {
|
||||
// float
|
||||
return PyVar(VM::tp_float, static_cast<f64>(value));
|
||||
}else if constexpr(std::is_pointer_v<T>){
|
||||
} else if constexpr(std::is_pointer_v<T>) {
|
||||
return from_void_p(vm, (void*)value);
|
||||
}else{
|
||||
} else {
|
||||
constexpr Type const_type = _find_type_in_const_cxx_typeid_map<T>();
|
||||
if constexpr((bool)const_type){
|
||||
if constexpr(is_sso_v<T>) return PyVar(const_type, value);
|
||||
else return vm->heap.gcnew<T>(const_type, std::forward<__T>(value));
|
||||
}else{
|
||||
if constexpr((bool)const_type) {
|
||||
if constexpr(is_sso_v<T>)
|
||||
return PyVar(const_type, value);
|
||||
else
|
||||
return vm->heap.gcnew<T>(const_type, std::forward<__T>(value));
|
||||
} else {
|
||||
Type type = vm->_find_type_in_cxx_typeid_map<T>();
|
||||
if constexpr(is_sso_v<T>) return PyVar(type, value);
|
||||
else return vm->heap.gcnew<T>(type, std::forward<__T>(value));
|
||||
if constexpr(is_sso_v<T>)
|
||||
return PyVar(type, value);
|
||||
else
|
||||
return vm->heap.gcnew<T>(type, std::forward<__T>(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fast path for bool if py_var<> cannot be inlined
|
||||
inline PyVar py_var(VM* vm, bool value){
|
||||
return value ? vm->True : vm->False;
|
||||
}
|
||||
inline PyVar py_var(VM* vm, bool value) { return value ? vm->True : vm->False; }
|
||||
|
||||
template<typename __T, bool with_check>
|
||||
template <typename __T, bool with_check>
|
||||
__T _py_cast__internal(VM* vm, PyVar obj) {
|
||||
static_assert(!std::is_rvalue_reference_v<__T>, "rvalue reference is not allowed");
|
||||
using T = std::decay_t<__T>;
|
||||
static_assert(!(is_sso_v<T> && std::is_reference_v<__T>), "SSO types cannot be reference");
|
||||
|
||||
if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, CString>){
|
||||
if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, CString>) {
|
||||
static_assert(!std::is_reference_v<__T>);
|
||||
// str (shortcuts)
|
||||
if(obj == vm->None) return nullptr;
|
||||
if constexpr(with_check) vm->check_type(obj, vm->tp_str);
|
||||
return PK_OBJ_GET(Str, obj).c_str();
|
||||
}else if constexpr(std::is_same_v<T, bool>){
|
||||
} else if constexpr(std::is_same_v<T, bool>) {
|
||||
static_assert(!std::is_reference_v<__T>);
|
||||
// bool
|
||||
if constexpr(with_check){
|
||||
if constexpr(with_check) {
|
||||
if(obj == vm->True) return true;
|
||||
if(obj == vm->False) return false;
|
||||
vm->TypeError("expected 'bool', got " + _type_name(vm, vm->_tp(obj)).escape());
|
||||
}
|
||||
return obj == vm->True;
|
||||
}else if constexpr(is_integral_v<T>){
|
||||
} else if constexpr(is_integral_v<T>) {
|
||||
static_assert(!std::is_reference_v<__T>);
|
||||
// int
|
||||
if constexpr(with_check){
|
||||
if constexpr(with_check) {
|
||||
if(is_int(obj)) return (T)obj.as<i64>();
|
||||
vm->TypeError("expected 'int', got " + _type_name(vm, vm->_tp(obj)).escape());
|
||||
}
|
||||
return (T)obj.as<i64>();
|
||||
}else if constexpr(is_floating_point_v<T>){
|
||||
} else if constexpr(is_floating_point_v<T>) {
|
||||
static_assert(!std::is_reference_v<__T>);
|
||||
if(is_float(obj)) return (T)obj.as<f64>();
|
||||
if(is_int(obj)) return (T)obj.as<i64>();
|
||||
vm->TypeError("expected 'int' or 'float', got " + _type_name(vm, vm->_tp(obj)).escape());
|
||||
return 0.0f;
|
||||
}else if constexpr(std::is_enum_v<T>){
|
||||
} else if constexpr(std::is_enum_v<T>) {
|
||||
static_assert(!std::is_reference_v<__T>);
|
||||
return (__T)_py_cast__internal<i64, with_check>(vm, obj);
|
||||
}else if constexpr(std::is_pointer_v<T>){
|
||||
} else if constexpr(std::is_pointer_v<T>) {
|
||||
static_assert(!std::is_reference_v<__T>);
|
||||
return to_void_p<T>(vm, obj);
|
||||
}else{
|
||||
} else {
|
||||
constexpr Type const_type = _find_type_in_const_cxx_typeid_map<T>();
|
||||
if constexpr((bool)const_type){
|
||||
if constexpr(with_check){
|
||||
if constexpr(std::is_same_v<T, Exception>){
|
||||
if constexpr((bool)const_type) {
|
||||
if constexpr(with_check) {
|
||||
if constexpr(std::is_same_v<T, Exception>) {
|
||||
// Exception is `subclass_enabled`
|
||||
vm->check_compatible_type(obj, const_type);
|
||||
}else{
|
||||
} else {
|
||||
vm->check_type(obj, const_type);
|
||||
}
|
||||
}
|
||||
return PK_OBJ_GET(T, obj);
|
||||
}else{
|
||||
if constexpr(with_check){
|
||||
} else {
|
||||
if constexpr(with_check) {
|
||||
Type type = vm->_find_type_in_cxx_typeid_map<T>();
|
||||
vm->check_compatible_type(obj, type);
|
||||
}
|
||||
@ -634,25 +738,31 @@ __T _py_cast__internal(VM* vm, PyVar obj) {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename __T>
|
||||
__T py_cast(VM* vm, PyVar obj) { return _py_cast__internal<__T, true>(vm, obj); }
|
||||
template<typename __T>
|
||||
__T _py_cast(VM* vm, PyVar obj) { return _py_cast__internal<__T, false>(vm, obj); }
|
||||
template <typename __T>
|
||||
__T py_cast(VM* vm, PyVar obj) {
|
||||
return _py_cast__internal<__T, true>(vm, obj);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PyObject* VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _register, Type base, bool subclass_enabled){
|
||||
template <typename __T>
|
||||
__T _py_cast(VM* vm, PyVar obj) {
|
||||
return _py_cast__internal<__T, false>(vm, obj);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PyObject*
|
||||
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>());
|
||||
mod->attr().set(name, type);
|
||||
_cxx_typeid_map[typeid(T)] = type->as<Type>();
|
||||
_register(this, mod, type);
|
||||
if(!type->attr().contains(__new__)){
|
||||
if(!type->attr().contains(__new__)) {
|
||||
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>();
|
||||
return vm->new_object<T>(cls_t);
|
||||
});
|
||||
}else{
|
||||
bind_func(type, __new__, -1, [](VM* vm, ArgsView args){
|
||||
} else {
|
||||
bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
|
||||
vm->NotImplementedError();
|
||||
return vm->None;
|
||||
});
|
||||
@ -661,9 +771,9 @@ PyObject* VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _reg
|
||||
return type;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PyObject* VM::register_user_class(PyObject* mod, StrName name, Type base, bool subclass_enabled){
|
||||
template <typename T>
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -6,4 +6,4 @@ namespace pkpy {
|
||||
|
||||
void add_module_array2d(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -6,4 +6,4 @@ namespace pkpy {
|
||||
|
||||
void add_module_base64(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -6,4 +6,4 @@ namespace pkpy {
|
||||
|
||||
void add_module_csv(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
#include "pocketpy/common/types.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
void add_module_dataclasses(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
#include "pocketpy/common/types.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
void add_module_easing(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
#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 {
|
||||
unsigned char* _default_import_handler(const char*, int*);
|
||||
void add_module_os(VM* vm);
|
||||
void add_module_io(VM* vm);
|
||||
} // namespace pkpy
|
||||
|
||||
@ -5,90 +5,167 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
inline bool isclose(float a, float b){ return std::fabs(a - b) < 1e-4; }
|
||||
inline bool isclose(float a, float b) { return std::fabs(a - b) < 1e-4; }
|
||||
|
||||
struct Vec2{
|
||||
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]; }
|
||||
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); }
|
||||
|
||||
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{
|
||||
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]; }
|
||||
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); }
|
||||
|
||||
Vec3 normalize() const {
|
||||
float l = length();
|
||||
return Vec3(x / l, y / l, z / l);
|
||||
}
|
||||
};
|
||||
|
||||
struct Vec4{
|
||||
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]; }
|
||||
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 {}; }
|
||||
|
||||
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{
|
||||
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 _11, _12, _13;
|
||||
float _21, _22, _23;
|
||||
float _31, _32, _33;
|
||||
};
|
||||
|
||||
float m[3][3];
|
||||
float v[9];
|
||||
};
|
||||
@ -100,14 +177,14 @@ struct Mat3x3{
|
||||
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;
|
||||
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;
|
||||
|
||||
bool operator==(const Mat3x3& other) const;
|
||||
bool operator!=(const Mat3x3& other) const;
|
||||
|
||||
Mat3x3 matmul(const Mat3x3& other) const;
|
||||
Vec3 matmul(const Vec3& other) const;
|
||||
|
||||
@ -130,9 +207,9 @@ static_assert(is_pod_v<Vec3>);
|
||||
static_assert(is_pod_v<Vec4>);
|
||||
static_assert(is_pod_v<Mat3x3>);
|
||||
|
||||
template<>
|
||||
inline constexpr bool is_sso_v<Vec2> = true;
|
||||
template<>
|
||||
inline constexpr bool is_sso_v<Vec3> = true;
|
||||
template <>
|
||||
constexpr inline bool is_sso_v<Vec2> = true;
|
||||
template <>
|
||||
constexpr inline bool is_sso_v<Vec3> = true;
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "pocketpy/common/types.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
void add_module_time(VM* vm);
|
||||
void add_module_sys(VM* vm);
|
||||
@ -15,4 +15,4 @@ void add_module_line_profiler(VM* vm);
|
||||
void add_module_enum(VM* vm);
|
||||
void add_module___builtins(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
#include "pocketpy/common/types.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
void add_module_random(VM* vm);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -8,25 +8,31 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct Type {
|
||||
int16_t index;
|
||||
constexpr Type(): index(0) {}
|
||||
explicit constexpr Type(int 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 int() const { return index; }
|
||||
int16_t index;
|
||||
|
||||
constexpr Type() : index(0) {}
|
||||
|
||||
explicit constexpr Type(int 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 int () const { return index; }
|
||||
};
|
||||
|
||||
struct const_sso_var {};
|
||||
|
||||
struct PyVar final{
|
||||
struct PyVar final {
|
||||
Type type;
|
||||
bool is_ptr;
|
||||
uint8_t flags;
|
||||
// 12 bytes SSO
|
||||
int _0; i64 _1;
|
||||
int _0;
|
||||
i64 _1;
|
||||
|
||||
// uninitialized
|
||||
PyVar() = default;
|
||||
@ -36,45 +42,50 @@ struct PyVar final{
|
||||
|
||||
/* We must initialize all members to allow == operator to work correctly */
|
||||
// constexpr initialized
|
||||
constexpr PyVar(const const_sso_var&, Type type, int value): type(type), is_ptr(false), flags(0), _0(value), _1(0) {}
|
||||
constexpr PyVar(const const_sso_var&, Type type, int value) :
|
||||
type(type), is_ptr(false), flags(0), _0(value), _1(0) {}
|
||||
|
||||
// zero initialized
|
||||
constexpr PyVar(std::nullptr_t): type(0), is_ptr(false), flags(0), _0(0), _1(0) {}
|
||||
constexpr PyVar(std::nullptr_t) : type(0), is_ptr(false), flags(0), _0(0), _1(0) {}
|
||||
|
||||
// PyObject* initialized (is_sso = false)
|
||||
PyVar(Type type, PyObject* p): type(type), is_ptr(true), flags(0), _0(0), _1(reinterpret_cast<i64>(p)) {}
|
||||
PyVar(Type type, PyObject* p) : type(type), is_ptr(true), flags(0), _0(0), _1(reinterpret_cast<i64>(p)) {}
|
||||
|
||||
// SSO initialized (is_sso = true)
|
||||
template<typename T>
|
||||
PyVar(Type type, T value): type(type), is_ptr(false), flags(0), _0(0), _1(0) {
|
||||
template <typename T>
|
||||
PyVar(Type type, T value) : type(type), is_ptr(false), flags(0), _0(0), _1(0) {
|
||||
static_assert(sizeof(T) <= 12, "SSO size exceeded");
|
||||
as<T>() = value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& as(){
|
||||
template <typename T>
|
||||
T& as() {
|
||||
static_assert(!std::is_reference_v<T>);
|
||||
if constexpr(sizeof(T) <= 8){
|
||||
if constexpr(sizeof(T) <= 8) {
|
||||
return reinterpret_cast<T&>(_1);
|
||||
}else{
|
||||
} else {
|
||||
return reinterpret_cast<T&>(_0);
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)type; }
|
||||
explicit operator bool () const { return (bool)type; }
|
||||
|
||||
void set_null() { _qword(0) = 0; _qword(1) = 0; }
|
||||
void set_null() {
|
||||
_qword(0) = 0;
|
||||
_qword(1) = 0;
|
||||
}
|
||||
|
||||
i64 _qword(int i) const { return ((const i64*)this)[i]; }
|
||||
|
||||
i64& _qword(int i) { return ((i64*)this)[i]; }
|
||||
|
||||
bool operator==(const PyVar& other) const {
|
||||
return _qword(0) == other._qword(0) && _qword(1) == other._qword(1);
|
||||
}
|
||||
bool operator== (const PyVar& other) const { return _qword(0) == other._qword(0) && _qword(1) == other._qword(1); }
|
||||
|
||||
bool operator!=(const PyVar& other) const {
|
||||
return _qword(0) != other._qword(0) || _qword(1) != other._qword(1);
|
||||
}
|
||||
bool operator!= (const PyVar& other) const { return _qword(0) != other._qword(0) || _qword(1) != other._qword(1); }
|
||||
|
||||
bool operator==(std::nullptr_t) const { return !(bool)type; }
|
||||
bool operator!=(std::nullptr_t) const { return (bool)type; }
|
||||
bool operator== (std::nullptr_t) const { return !(bool)type; }
|
||||
|
||||
bool operator!= (std::nullptr_t) const { return (bool)type; }
|
||||
|
||||
PyObject* get() const {
|
||||
assert(is_ptr);
|
||||
@ -88,14 +99,12 @@ struct PyVar final{
|
||||
|
||||
i64 hash() const { return _0 + _1; }
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
obj_get_t<T> obj_get();
|
||||
|
||||
// for std::map<>
|
||||
bool operator<(const PyVar& other) const {
|
||||
return memcmp(this, &other, sizeof(PyVar)) < 0;
|
||||
}
|
||||
bool operator< (const PyVar& other) const { return memcmp(this, &other, sizeof(PyVar)) < 0; }
|
||||
};
|
||||
|
||||
static_assert(sizeof(PyVar) == 16 && is_pod_v<PyVar>);
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,31 +3,39 @@
|
||||
#include "pocketpy/common/vector.hpp"
|
||||
#include "pocketpy/objects/object.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct BoundMethod {
|
||||
PyVar self;
|
||||
PyVar func;
|
||||
|
||||
BoundMethod(PyVar self, PyVar func) : self(self), func(func) {}
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
struct StaticMethod{
|
||||
struct StaticMethod {
|
||||
PyVar func;
|
||||
|
||||
StaticMethod(PyVar func) : func(func) {}
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
struct ClassMethod{
|
||||
struct ClassMethod {
|
||||
PyVar func;
|
||||
|
||||
ClassMethod(PyVar func) : func(func) {}
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
struct Property{
|
||||
struct Property {
|
||||
PyVar getter;
|
||||
PyVar setter;
|
||||
|
||||
Property(PyVar getter, PyVar setter) : getter(getter), setter(setter) {}
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
@ -37,20 +45,23 @@ struct Range {
|
||||
i64 step = 1;
|
||||
};
|
||||
|
||||
|
||||
struct StarWrapper{
|
||||
int level; // either 1 or 2
|
||||
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{
|
||||
struct Super {
|
||||
PyVar first;
|
||||
Type second;
|
||||
|
||||
Super(PyVar first, Type second) : first(first), second(second) {}
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
@ -58,16 +69,19 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
inline const int kTpIntIndex = 3;
|
||||
inline const int kTpFloatIndex = 4;
|
||||
const inline int kTpIntIndex = 3;
|
||||
const inline int kTpFloatIndex = 4;
|
||||
|
||||
inline bool is_tagged(PyVar p) noexcept { return !p.is_ptr; }
|
||||
|
||||
inline bool is_float(PyVar p) noexcept { return p.type.index == kTpFloatIndex; }
|
||||
|
||||
inline bool is_int(PyVar p) noexcept { return p.type.index == kTpIntIndex; }
|
||||
|
||||
inline bool is_type(PyVar obj, Type type) {
|
||||
@ -80,23 +94,26 @@ inline bool is_type(PyObject* p, Type type) {
|
||||
return p->type == type;
|
||||
}
|
||||
|
||||
struct MappingProxy{
|
||||
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);
|
||||
template <typename T>
|
||||
T to_void_p(VM*, PyVar);
|
||||
PyVar from_void_p(VM*, void*);
|
||||
|
||||
|
||||
template<typename T>
|
||||
obj_get_t<T> PyVar::obj_get(){
|
||||
if constexpr(is_sso_v<T>){
|
||||
template <typename T>
|
||||
obj_get_t<T> PyVar::obj_get() {
|
||||
if constexpr(is_sso_v<T>) {
|
||||
return as<T>();
|
||||
}else{
|
||||
} else {
|
||||
assert(is_ptr);
|
||||
void* v = ((PyObject*)_1)->_value_ptr();
|
||||
return *reinterpret_cast<T*>(v);
|
||||
@ -119,6 +136,6 @@ obj_get_t<T> PyVar::obj_get(){
|
||||
|
||||
#define PY_NULL nullptr
|
||||
extern PyVar const PY_OP_CALL;
|
||||
extern PyVar const PY_OP_YIELD;
|
||||
extern const PyVar PY_OP_YIELD;
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include "pocketpy/objects/object.hpp"
|
||||
#include "pocketpy/objects/sourcedata.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
#if PK_ENABLE_STD_FUNCTION
|
||||
using NativeFuncC = function<PyVar(VM*, ArgsView)>;
|
||||
@ -13,7 +13,7 @@ using NativeFuncC = function<PyVar(VM*, ArgsView)>;
|
||||
typedef PyVar (*NativeFuncC)(VM*, ArgsView);
|
||||
#endif
|
||||
|
||||
enum class BindType{
|
||||
enum class BindType {
|
||||
DEFAULT,
|
||||
STATICMETHOD,
|
||||
CLASSMETHOD,
|
||||
@ -21,24 +21,23 @@ enum class BindType{
|
||||
|
||||
enum NameScope { NAME_LOCAL, NAME_GLOBAL, NAME_GLOBAL_UNKNOWN };
|
||||
|
||||
enum Opcode: uint8_t {
|
||||
#define OPCODE(name) OP_##name,
|
||||
#include "pocketpy/opcodes.h"
|
||||
#undef OPCODE
|
||||
enum Opcode : uint8_t {
|
||||
|
||||
#define OPCODE(name) OP_##name,
|
||||
#include "pocketpy/opcodes.h"
|
||||
#undef OPCODE
|
||||
};
|
||||
|
||||
struct Bytecode{
|
||||
struct Bytecode {
|
||||
uint8_t op;
|
||||
uint16_t arg;
|
||||
|
||||
void set_signed_arg(int arg){
|
||||
void set_signed_arg(int arg) {
|
||||
assert(arg >= INT16_MIN && arg <= INT16_MAX);
|
||||
this->arg = (int16_t)arg;
|
||||
}
|
||||
|
||||
bool is_forward_jump() const{
|
||||
return op >= OP_JUMP_FORWARD && op <= OP_LOOP_BREAK;
|
||||
}
|
||||
bool is_forward_jump() const { return op >= OP_JUMP_FORWARD && op <= OP_LOOP_BREAK; }
|
||||
};
|
||||
|
||||
enum class CodeBlockType {
|
||||
@ -49,20 +48,20 @@ enum class CodeBlockType {
|
||||
TRY_EXCEPT,
|
||||
};
|
||||
|
||||
inline const uint8_t BC_NOARG = 0;
|
||||
inline const int BC_KEEPLINE = -1;
|
||||
const inline uint8_t BC_NOARG = 0;
|
||||
const inline int BC_KEEPLINE = -1;
|
||||
|
||||
struct CodeBlock {
|
||||
CodeBlockType type;
|
||||
int parent; // parent index in blocks
|
||||
int start; // start index of this block in codes, inclusive
|
||||
int end; // end index of this block in codes, exclusive
|
||||
int end2; // ...
|
||||
int parent; // parent index in blocks
|
||||
int start; // start index of this block in codes, inclusive
|
||||
int end; // end index of this block in codes, exclusive
|
||||
int end2; // ...
|
||||
|
||||
CodeBlock(CodeBlockType type, int parent, int start):
|
||||
CodeBlock(CodeBlockType type, int parent, int start) :
|
||||
type(type), parent(parent), start(start), end(-1), end2(-1) {}
|
||||
|
||||
int get_break_end() const{
|
||||
int get_break_end() const {
|
||||
if(end2 != -1) return end2;
|
||||
return end;
|
||||
}
|
||||
@ -74,10 +73,10 @@ using CodeObject_ = std::shared_ptr<CodeObject>;
|
||||
using FuncDecl_ = std::shared_ptr<FuncDecl>;
|
||||
|
||||
struct CodeObject {
|
||||
struct LineInfo{
|
||||
int lineno; // line number for each bytecode
|
||||
bool is_virtual; // whether this bytecode is virtual (not in source code)
|
||||
int iblock; // block index
|
||||
struct LineInfo {
|
||||
int lineno; // line number for each bytecode
|
||||
bool is_virtual; // whether this bytecode is virtual (not in source code)
|
||||
int iblock; // block index
|
||||
};
|
||||
|
||||
std::shared_ptr<SourceData> src;
|
||||
@ -85,10 +84,10 @@ struct CodeObject {
|
||||
|
||||
vector<Bytecode> codes;
|
||||
vector<LineInfo> lines;
|
||||
|
||||
small_vector_2<PyVar, 8> consts; // constants
|
||||
small_vector_2<StrName, 8> varnames; // local variables
|
||||
int nlocals; // varnames.size()
|
||||
|
||||
small_vector_2<PyVar, 8> consts; // constants
|
||||
small_vector_2<StrName, 8> varnames; // local variables
|
||||
int nlocals; // varnames.size()
|
||||
|
||||
NameDictInt varnames_inv;
|
||||
vector<CodeBlock> blocks;
|
||||
@ -98,15 +97,13 @@ struct CodeObject {
|
||||
int start_line;
|
||||
int end_line;
|
||||
|
||||
const CodeBlock& _get_block_codei(int codei) const{
|
||||
return blocks[lines[codei].iblock];
|
||||
}
|
||||
const CodeBlock& _get_block_codei(int codei) const { return blocks[lines[codei].iblock]; }
|
||||
|
||||
CodeObject(std::shared_ptr<SourceData> src, const Str& name);
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
enum class FuncType{
|
||||
enum class FuncType {
|
||||
UNSET,
|
||||
NORMAL,
|
||||
SIMPLE,
|
||||
@ -116,63 +113,68 @@ enum class FuncType{
|
||||
|
||||
struct FuncDecl {
|
||||
struct KwArg {
|
||||
int index; // index in co->varnames
|
||||
StrName key; // name of this argument
|
||||
PyVar value; // default value
|
||||
int index; // index in co->varnames
|
||||
StrName key; // name of this argument
|
||||
PyVar value; // default value
|
||||
};
|
||||
CodeObject_ code; // code object of this function
|
||||
|
||||
CodeObject_ code; // code object of this function
|
||||
|
||||
small_vector_2<int, 6> args; // indices in co->varnames
|
||||
small_vector_2<KwArg, 6> kwargs; // indices in co->varnames
|
||||
|
||||
int starred_arg = -1; // index in co->varnames, -1 if no *arg
|
||||
int starred_kwarg = -1; // index in co->varnames, -1 if no **kwarg
|
||||
bool nested = false; // whether this function is nested
|
||||
int starred_arg = -1; // index in co->varnames, -1 if no *arg
|
||||
int starred_kwarg = -1; // index in co->varnames, -1 if no **kwarg
|
||||
bool nested = false; // whether this function is nested
|
||||
|
||||
const char* docstring; // docstring of this function (weak ref)
|
||||
const char* docstring; // docstring of this function (weak ref)
|
||||
|
||||
FuncType type = FuncType::UNSET;
|
||||
|
||||
NameDictInt kw_to_index;
|
||||
|
||||
void add_kwarg(int index, StrName key, PyVar value){
|
||||
void add_kwarg(int index, StrName key, PyVar value) {
|
||||
kw_to_index.set(key, index);
|
||||
kwargs.push_back(KwArg{index, key, value});
|
||||
}
|
||||
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
struct NativeFunc {
|
||||
NativeFuncC f;
|
||||
int argc; // old style argc-based call
|
||||
FuncDecl_ decl; // new style decl-based call
|
||||
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)) {}
|
||||
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;
|
||||
};
|
||||
|
||||
struct Function{
|
||||
struct Function {
|
||||
FuncDecl_ decl;
|
||||
PyObject* _module; // weak ref
|
||||
PyObject* _class; // weak ref
|
||||
NameDict_ _closure;
|
||||
|
||||
explicit Function(FuncDecl_ decl, PyObject* _module, PyObject* _class, NameDict_ _closure):
|
||||
explicit Function(FuncDecl_ decl, PyObject* _module, PyObject* _class, NameDict_ _closure) :
|
||||
decl(decl), _module(_module), _class(_class), _closure(_closure) {}
|
||||
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T& lambda_get_userdata(PyVar* p){
|
||||
template <typename T>
|
||||
T& lambda_get_userdata(PyVar* p) {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
int offset = p[-1] != nullptr ? -1 : -2;
|
||||
return p[offset].obj_get<NativeFunc>()._userdata.cast<T>();
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,32 +3,32 @@
|
||||
#include "pocketpy/objects/base.hpp"
|
||||
#include "pocketpy/objects/tuplelist.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct Dict{
|
||||
struct Item{
|
||||
struct Dict {
|
||||
struct Item {
|
||||
PyVar first;
|
||||
PyVar second;
|
||||
int prev;
|
||||
int next;
|
||||
};
|
||||
|
||||
static constexpr int __Capacity = 8;
|
||||
static constexpr float __LoadFactor = 0.67f;
|
||||
constexpr static int __Capacity = 8;
|
||||
constexpr static float __LoadFactor = 0.67f;
|
||||
|
||||
int _capacity;
|
||||
int _mask;
|
||||
int _size;
|
||||
int _critical_size;
|
||||
int _head_idx; // for order preserving
|
||||
int _tail_idx; // for order preserving
|
||||
int _head_idx; // for order preserving
|
||||
int _tail_idx; // for order preserving
|
||||
Item* _items;
|
||||
|
||||
Dict();
|
||||
Dict(Dict&& other);
|
||||
Dict(const Dict& other);
|
||||
Dict& operator=(const Dict&) = delete;
|
||||
Dict& operator=(Dict&&) = delete;
|
||||
Dict& operator= (const Dict&) = delete;
|
||||
Dict& operator= (Dict&&) = delete;
|
||||
|
||||
int size() const { return _size; }
|
||||
|
||||
@ -44,10 +44,10 @@ struct Dict{
|
||||
bool del(VM* vm, PyVar key);
|
||||
void update(VM* vm, const Dict& other);
|
||||
|
||||
template<typename __Func>
|
||||
template <typename __Func>
|
||||
void apply(__Func f) const {
|
||||
int i = _head_idx;
|
||||
while(i != -1){
|
||||
while(i != -1) {
|
||||
f(_items[i].first, _items[i].second);
|
||||
i = _items[i].next;
|
||||
}
|
||||
@ -63,4 +63,4 @@ struct Dict{
|
||||
void _gc_mark(VM*) const;
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,22 +3,23 @@
|
||||
#include "pocketpy/common/str.hpp"
|
||||
#include "pocketpy/objects/sourcedata.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
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
|
||||
};
|
||||
enum class InternalExceptionType : int { Null, Handled, Unhandled, ToBeRaised };
|
||||
|
||||
struct InternalException final{
|
||||
struct InternalException final {
|
||||
InternalExceptionType type;
|
||||
int arg;
|
||||
InternalException(): type(InternalExceptionType::Null), arg(-1) {}
|
||||
InternalException(InternalExceptionType type, int arg=-1): type(type), arg(arg) {}
|
||||
|
||||
InternalException() : type(InternalExceptionType::Null), arg(-1) {}
|
||||
|
||||
InternalException(InternalExceptionType type, int arg = -1) : type(type), arg(arg) {}
|
||||
};
|
||||
|
||||
struct Exception {
|
||||
@ -29,9 +30,9 @@ struct Exception {
|
||||
int _ip_on_error;
|
||||
void* _code_on_error;
|
||||
|
||||
PyObject* _self; // weak reference
|
||||
PyObject* _self; // weak reference
|
||||
|
||||
struct Frame{
|
||||
struct Frame {
|
||||
std::shared_ptr<SourceData> src;
|
||||
int lineno;
|
||||
const char* cursor;
|
||||
@ -39,20 +40,21 @@ struct Exception {
|
||||
|
||||
Str snapshot() const { return src->snapshot(lineno, cursor, name); }
|
||||
|
||||
Frame(std::shared_ptr<SourceData> src, int lineno, const char* cursor, std::string_view name):
|
||||
Frame(std::shared_ptr<SourceData> src, int lineno, const char* cursor, std::string_view name) :
|
||||
src(src), lineno(lineno), cursor(cursor), name(name) {}
|
||||
};
|
||||
|
||||
stack<Frame> stacktrace;
|
||||
Exception(StrName type): type(type), is_re(true), _ip_on_error(-1), _code_on_error(nullptr), _self(nullptr) {}
|
||||
|
||||
PyObject* self() const{
|
||||
Exception(StrName type) : type(type), is_re(true), _ip_on_error(-1), _code_on_error(nullptr), _self(nullptr) {}
|
||||
|
||||
PyObject* self() const {
|
||||
assert(_self != nullptr);
|
||||
return _self;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void st_push(Args&&... args){
|
||||
template <typename... Args>
|
||||
void st_push(Args&&... args) {
|
||||
if(stacktrace.size() >= 7) return;
|
||||
stacktrace.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
@ -60,10 +62,11 @@ struct Exception {
|
||||
Str summary() const;
|
||||
};
|
||||
|
||||
struct TopLevelException: std::exception{
|
||||
struct TopLevelException : std::exception {
|
||||
VM* vm;
|
||||
Exception* ptr;
|
||||
TopLevelException(VM* vm, Exception* ptr): vm(vm), ptr(ptr) {}
|
||||
|
||||
TopLevelException(VM* vm, Exception* ptr) : vm(vm), ptr(ptr) {}
|
||||
|
||||
Str summary() const { return ptr->summary(); }
|
||||
|
||||
@ -74,4 +77,4 @@ struct TopLevelException: std::exception{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,22 +3,24 @@
|
||||
#include "pocketpy/common/namedict.hpp"
|
||||
#include "pocketpy/objects/base.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
using NameDict = NameDictImpl<PyVar>;
|
||||
using NameDict_ = std::shared_ptr<NameDict>;
|
||||
using NameDictInt = NameDictImpl<int>;
|
||||
|
||||
static_assert(sizeof(NameDict) <= 128);
|
||||
|
||||
struct PyObject final{
|
||||
bool gc_marked; // whether this object is marked
|
||||
Type type; // we have a duplicated type here for convenience
|
||||
NameDict* _attr; // gc will delete this on destruction
|
||||
struct PyObject final {
|
||||
bool gc_marked; // whether this object is marked
|
||||
Type type; // we have a duplicated type here for convenience
|
||||
NameDict* _attr; // gc will delete this on destruction
|
||||
|
||||
bool is_attr_valid() const noexcept { return _attr != nullptr; }
|
||||
|
||||
void* _value_ptr() noexcept { return (char*)this + 16; }
|
||||
|
||||
template<typename T> T& as() noexcept {
|
||||
template <typename T>
|
||||
T& as() noexcept {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
return *reinterpret_cast<T*>(_value_ptr());
|
||||
}
|
||||
@ -35,17 +37,17 @@ struct PyObject final{
|
||||
|
||||
PyObject(Type type) : gc_marked(false), type(type), _attr(nullptr) {}
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
void placement_new(Args&&... args){
|
||||
template <typename T, typename... Args>
|
||||
void placement_new(Args&&... args) {
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
new(_value_ptr()) T(std::forward<Args>(args)...);
|
||||
new (_value_ptr()) T(std::forward<Args>(args)...);
|
||||
|
||||
// backdoor for important builtin types
|
||||
if constexpr(std::is_same_v<T, DummyInstance>){
|
||||
if constexpr(std::is_same_v<T, DummyInstance>) {
|
||||
_attr = new NameDict();
|
||||
}else if constexpr(std::is_same_v<T, Type>){
|
||||
} else if constexpr(std::is_same_v<T, Type>) {
|
||||
_attr = new NameDict(PK_TYPE_ATTR_LOAD_FACTOR);
|
||||
}else if constexpr(std::is_same_v<T, DummyModule>){
|
||||
} else if constexpr(std::is_same_v<T, DummyModule>) {
|
||||
_attr = new NameDict(PK_TYPE_ATTR_LOAD_FACTOR);
|
||||
}
|
||||
}
|
||||
@ -53,4 +55,4 @@ struct PyObject final{
|
||||
|
||||
static_assert(sizeof(PyObject) <= 16);
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,15 +3,9 @@
|
||||
#include "pocketpy/common/utils.hpp"
|
||||
#include "pocketpy/common/str.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
enum CompileMode {
|
||||
EXEC_MODE,
|
||||
EVAL_MODE,
|
||||
REPL_MODE,
|
||||
JSON_MODE,
|
||||
CELL_MODE
|
||||
};
|
||||
enum CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, JSON_MODE, CELL_MODE };
|
||||
|
||||
struct SourceData {
|
||||
PK_ALWAYS_PASS_BY_POINTER(SourceData)
|
||||
@ -24,12 +18,12 @@ struct SourceData {
|
||||
|
||||
bool is_precompiled;
|
||||
vector<Str> _precompiled_tokens;
|
||||
|
||||
|
||||
SourceData(std::string_view source, const Str& filename, CompileMode mode);
|
||||
SourceData(const Str& filename, CompileMode mode);
|
||||
std::pair<const char*,const char*> _get_line(int lineno) const;
|
||||
std::pair<const char*, const char*> _get_line(int lineno) const;
|
||||
std::string_view get_line(int lineno) const;
|
||||
Str snapshot(int lineno, const char* cursor, std::string_view name) const;
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,16 +2,17 @@
|
||||
|
||||
#include "pocketpy/common/traits.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct StackMemory{
|
||||
struct StackMemory {
|
||||
int count;
|
||||
|
||||
StackMemory(int count) : count(count) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool constexpr is_sso_v<StackMemory> = true;
|
||||
template <>
|
||||
constexpr inline bool is_sso_v<StackMemory> = true;
|
||||
|
||||
inline const int kTpStackMemoryIndex = 27;
|
||||
const inline int kTpStackMemoryIndex = 27;
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
namespace pkpy {
|
||||
|
||||
struct Tuple {
|
||||
static const int INLINED_SIZE = 3;
|
||||
const static int INLINED_SIZE = 3;
|
||||
|
||||
PyVar* _args;
|
||||
PyVar _inlined[INLINED_SIZE];
|
||||
@ -15,52 +15,63 @@ struct Tuple {
|
||||
Tuple(int n);
|
||||
Tuple(Tuple&& other) noexcept;
|
||||
Tuple(const Tuple& other) = delete;
|
||||
Tuple& operator=(const Tuple& other) = delete;
|
||||
Tuple& operator=(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]; }
|
||||
|
||||
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>{
|
||||
struct List : public vector<PyVar> {
|
||||
using vector<PyVar>::vector;
|
||||
void _gc_mark(VM*) const;
|
||||
|
||||
Tuple to_tuple() const{
|
||||
Tuple to_tuple() const {
|
||||
Tuple ret(size());
|
||||
for(int i=0; i<size(); i++) ret[i] = (*this)[i];
|
||||
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{
|
||||
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]; }
|
||||
|
||||
PyVar operator[] (int i) const { return _begin[i]; }
|
||||
|
||||
List to_list() const;
|
||||
Tuple to_tuple() const;
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -153,4 +153,4 @@ OPCODE(INC_FAST)
|
||||
OPCODE(DEC_FAST)
|
||||
OPCODE(INC_GLOBAL)
|
||||
OPCODE(DEC_GLOBAL)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -9,17 +9,17 @@
|
||||
#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
|
||||
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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#ifndef POCKETPY_C_H
|
||||
#ifndef POCKETPY_C_H
|
||||
#define POCKETPY_C_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -10,98 +10,97 @@ extern "C" {
|
||||
|
||||
#include "pocketpy/common/export.h"
|
||||
|
||||
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;
|
||||
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);
|
||||
/* 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*);
|
||||
/* 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
/* 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);
|
||||
/* Callables */
|
||||
PK_EXPORT bool pkpy_vectorcall(pkpy_vm*, int argc);
|
||||
|
||||
/* Special APIs */
|
||||
PK_EXPORT void pkpy_free(void* p);
|
||||
/* 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);
|
||||
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);
|
||||
/* 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
|
||||
|
||||
@ -2,16 +2,17 @@
|
||||
|
||||
#include "pocketpy/interpreter/vm.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
class REPL {
|
||||
protected:
|
||||
int need_more_lines = 0;
|
||||
std::string buffer;
|
||||
VM* vm;
|
||||
|
||||
public:
|
||||
REPL(VM* vm);
|
||||
bool input(std::string line);
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/pocketpy_c.h"
|
||||
#include "pocketpy/pocketpy_c.h"
|
||||
|
||||
@ -3,115 +3,106 @@
|
||||
#include "types.h"
|
||||
|
||||
namespace pybind11 {
|
||||
inline void exec(const char* code, handle global = {}, handle local = {}) {
|
||||
vm->py_exec(code, global.ptr(), local.ptr());
|
||||
}
|
||||
inline void exec(const char* code, handle global = {}, handle local = {}) {
|
||||
vm->py_exec(code, global.ptr(), local.ptr());
|
||||
}
|
||||
|
||||
// wrapper for builtin functions in Python
|
||||
inline bool hasattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
return vm->getattr(obj.ptr(), key, false) != nullptr;
|
||||
}
|
||||
// wrapper for builtin functions in Python
|
||||
inline bool hasattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
return vm->getattr(obj.ptr(), key, false) != nullptr;
|
||||
}
|
||||
|
||||
inline bool hasattr(const handle& obj, const char* name) {
|
||||
return vm->getattr(obj.ptr(), name, false) != nullptr;
|
||||
}
|
||||
inline bool hasattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name, false) != nullptr; }
|
||||
|
||||
inline void delattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
vm->delattr(obj.ptr(), key);
|
||||
}
|
||||
inline void delattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
vm->delattr(obj.ptr(), key);
|
||||
}
|
||||
|
||||
inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
|
||||
inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
|
||||
|
||||
inline object getattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
return reinterpret_borrow<object>(vm->getattr(obj.ptr(), key));
|
||||
}
|
||||
inline object getattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
return reinterpret_borrow<object>(vm->getattr(obj.ptr(), key));
|
||||
}
|
||||
|
||||
inline object getattr(const handle& obj, const char* name) {
|
||||
return reinterpret_borrow<object>(vm->getattr(obj.ptr(), name));
|
||||
}
|
||||
inline object getattr(const handle& obj, const char* name) {
|
||||
return reinterpret_borrow<object>(vm->getattr(obj.ptr(), name));
|
||||
}
|
||||
|
||||
inline object getattr(const handle& obj, const handle& name, const handle& default_) {
|
||||
if(!hasattr(obj, name)) {
|
||||
return reinterpret_borrow<object>(default_);
|
||||
inline object getattr(const handle& obj, const handle& name, const handle& default_) {
|
||||
if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
|
||||
return getattr(obj, name);
|
||||
}
|
||||
|
||||
inline object getattr(const handle& obj, const char* name, const handle& default_) {
|
||||
if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
|
||||
return getattr(obj, name);
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
vm->setattr(obj.ptr(), key, value.ptr());
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const char* name, const handle& value) {
|
||||
vm->setattr(obj.ptr(), name, value.ptr());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool isinstance(const handle& obj) {
|
||||
pkpy::Type cls = _builtin_cast<pkpy::Type>(type::handle_of<T>().ptr());
|
||||
return vm->isinstance(obj.ptr(), cls);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool isinstance<handle>(const handle&) = delete;
|
||||
|
||||
template <>
|
||||
inline bool isinstance<iterable>(const handle& obj) {
|
||||
return hasattr(obj, "__iter__");
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool isinstance<iterator>(const handle& obj) {
|
||||
return hasattr(obj, "__iter__") && hasattr(obj, "__next__");
|
||||
}
|
||||
|
||||
inline bool isinstance(const handle& obj, const handle& type) {
|
||||
return vm->isinstance(obj.ptr(), _builtin_cast<pkpy::Type>(type));
|
||||
}
|
||||
|
||||
inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct type_caster;
|
||||
|
||||
template <typename T>
|
||||
handle
|
||||
_cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
|
||||
using U = std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>;
|
||||
return type_caster<U>::cast(std::forward<T>(value), policy, parent);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
object
|
||||
cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
|
||||
return reinterpret_borrow<object>(_cast(std::forward<T>(value), policy, parent));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T cast(handle obj, bool convert = false) {
|
||||
using Caster = type_caster<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
|
||||
Caster caster;
|
||||
|
||||
if(caster.load(obj, convert)) {
|
||||
if constexpr(std::is_rvalue_reference_v<T>) {
|
||||
return std::move(caster.value);
|
||||
} else {
|
||||
return caster.value;
|
||||
}
|
||||
return getattr(obj, name);
|
||||
}
|
||||
|
||||
inline object getattr(const handle& obj, const char* name, const handle& default_) {
|
||||
if(!hasattr(obj, name)) {
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
return getattr(obj, name);
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
vm->setattr(obj.ptr(), key, value.ptr());
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const char* name, const handle& value) {
|
||||
vm->setattr(obj.ptr(), name, value.ptr());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool isinstance(const handle& obj) {
|
||||
pkpy::Type cls = _builtin_cast<pkpy::Type>(type::handle_of<T>().ptr());
|
||||
return vm->isinstance(obj.ptr(), cls);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool isinstance<handle>(const handle&) = delete;
|
||||
|
||||
template <>
|
||||
inline bool isinstance<iterable>(const handle& obj) {
|
||||
return hasattr(obj, "__iter__");
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool isinstance<iterator>(const handle& obj) {
|
||||
return hasattr(obj, "__iter__") && hasattr(obj, "__next__");
|
||||
}
|
||||
|
||||
inline bool isinstance(const handle& obj, const handle& type) {
|
||||
return vm->isinstance(obj.ptr(), _builtin_cast<pkpy::Type>(type));
|
||||
}
|
||||
|
||||
inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct type_caster;
|
||||
|
||||
template <typename T>
|
||||
handle _cast(T&& value,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
handle parent = handle()) {
|
||||
using U = std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>;
|
||||
return type_caster<U>::cast(std::forward<T>(value), policy, parent);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
object cast(T&& value,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
handle parent = handle()) {
|
||||
return reinterpret_borrow<object>(_cast(std::forward<T>(value), policy, parent));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T cast(handle obj, bool convert = false) {
|
||||
using Caster =
|
||||
type_caster<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
|
||||
Caster caster;
|
||||
|
||||
if(caster.load(obj, convert)) {
|
||||
if constexpr(std::is_rvalue_reference_v<T>) {
|
||||
return std::move(caster.value);
|
||||
} else {
|
||||
return caster.value;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Unable to cast Python instance to C++ type");
|
||||
}
|
||||
throw std::runtime_error("Unable to cast Python instance to C++ type");
|
||||
}
|
||||
} // namespace pybind11
|
||||
|
||||
@ -6,168 +6,161 @@
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
using pkpy::is_floating_point_v;
|
||||
using pkpy::is_integral_v;
|
||||
using pkpy::is_floating_point_v;
|
||||
using pkpy::is_integral_v;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_string_v =
|
||||
std::is_same_v<T, char*> || std::is_same_v<T, const char*> ||
|
||||
std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
|
||||
template <typename T>
|
||||
constexpr inline bool is_string_v = std::is_same_v<T, char*> || std::is_same_v<T, const char*> ||
|
||||
std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_pyobject_v = std::is_base_of_v<handle, T>;
|
||||
template <typename T>
|
||||
constexpr bool is_pyobject_v = std::is_base_of_v<handle, T>;
|
||||
|
||||
template <typename T, typename>
|
||||
struct type_caster;
|
||||
template <typename T, typename>
|
||||
struct type_caster;
|
||||
|
||||
template <>
|
||||
struct type_caster<bool> {
|
||||
bool value;
|
||||
template <>
|
||||
struct type_caster<bool> {
|
||||
bool value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::bool_>(src)) {
|
||||
value = pkpy::_py_cast<bool>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::bool_>(src)) {
|
||||
value = pkpy::_py_cast<bool>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(bool src, return_value_policy, handle) {
|
||||
return src ? vm->True : vm->False;
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(bool src, return_value_policy, handle) { return src ? vm->True : vm->False; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::float_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
if(convert && isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<int64_t>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::str>(src)) {
|
||||
// FIXME: support other kinds of string
|
||||
value = pkpy::_py_cast<std::string>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(const std::string& src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<T>(src)) {
|
||||
value = reinterpret_borrow<T>(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& src, return_value_policy, handle) {
|
||||
return std::forward<U>(src);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
struct type_caster {
|
||||
value_wrapper<T> value;
|
||||
|
||||
using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if(isinstance<underlying_type>(src)) {
|
||||
auto& i = _builtin_cast<instance>(src);
|
||||
value.pointer = &i.cast<underlying_type>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
|
||||
// TODO: support implicit cast
|
||||
const auto& info = typeid(underlying_type);
|
||||
bool existed = vm->_cxx_typeid_map.find(info) != vm->_cxx_typeid_map.end();
|
||||
if(existed) {
|
||||
auto type = vm->_cxx_typeid_map[info];
|
||||
return instance::create(std::forward<U>(value), type, policy, parent.ptr());
|
||||
}
|
||||
vm->TypeError("type not registered");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<std::is_pointer_v<T> || std::is_reference_v<T>>> {
|
||||
using underlying = std::conditional_t<std::is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<T>>;
|
||||
|
||||
struct wrapper {
|
||||
type_caster<underlying> caster;
|
||||
|
||||
operator T () {
|
||||
if constexpr(std::is_pointer_v<T>) {
|
||||
return caster.value.pointer;
|
||||
} else {
|
||||
return caster.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
|
||||
T value;
|
||||
wrapper value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::float_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
if(convert && isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<int64_t>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::str>(src)) {
|
||||
// FIXME: support other kinds of string
|
||||
value = pkpy::_py_cast<std::string>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(const std::string& src, return_value_policy, handle) {
|
||||
return pkpy::py_var(vm, src);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<T>(src)) {
|
||||
value = reinterpret_borrow<T>(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& src, return_value_policy, handle) {
|
||||
return std::forward<U>(src);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
struct type_caster {
|
||||
value_wrapper<T> value;
|
||||
|
||||
using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if(isinstance<underlying_type>(src)) {
|
||||
auto& i = _builtin_cast<instance>(src);
|
||||
value.pointer = &i.cast<underlying_type>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
|
||||
// TODO: support implicit cast
|
||||
const auto& info = typeid(underlying_type);
|
||||
bool existed = vm->_cxx_typeid_map.find(info) != vm->_cxx_typeid_map.end();
|
||||
if(existed) {
|
||||
auto type = vm->_cxx_typeid_map[info];
|
||||
return instance::create(std::forward<U>(value), type, policy, parent.ptr());
|
||||
}
|
||||
vm->TypeError("type not registered");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<std::is_pointer_v<T> || std::is_reference_v<T>>> {
|
||||
using underlying = std::conditional_t<std::is_pointer_v<T>,
|
||||
std::remove_pointer_t<T>,
|
||||
std::remove_reference_t<T>>;
|
||||
|
||||
struct wrapper {
|
||||
type_caster<underlying> caster;
|
||||
|
||||
operator T () {
|
||||
if constexpr(std::is_pointer_v<T>) {
|
||||
return caster.value.pointer;
|
||||
} else {
|
||||
return caster.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
wrapper value;
|
||||
|
||||
bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& value, return_value_policy policy, const handle& parent) {
|
||||
return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
|
||||
}
|
||||
};
|
||||
template <typename U>
|
||||
static handle cast(U&& value, return_value_policy policy, const handle& parent) {
|
||||
return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
||||
|
||||
@ -4,181 +4,174 @@
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
class module : public object {
|
||||
class module : public object {
|
||||
|
||||
public:
|
||||
using object::object;
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
static module import(const char* name) {
|
||||
if(name == std::string_view{"__main__"}) {
|
||||
return module{vm->_main, true};
|
||||
} else {
|
||||
return module{vm->py_import(name, false), true};
|
||||
}
|
||||
static module import(const char* name) {
|
||||
if(name == std::string_view{"__main__"}) {
|
||||
return module{vm->_main, true};
|
||||
} else {
|
||||
return module{vm->py_import(name, false), true};
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// TODO:
|
||||
// 1. inheritance
|
||||
// 2. virtual function
|
||||
// 3. factory function
|
||||
// TODO:
|
||||
// 1. inheritance
|
||||
// 2. virtual function
|
||||
// 3. factory function
|
||||
|
||||
template <typename T, typename... Others>
|
||||
class class_ : public type {
|
||||
public:
|
||||
using type::type;
|
||||
template <typename T, typename... Others>
|
||||
class class_ : public type {
|
||||
public:
|
||||
using type::type;
|
||||
|
||||
template <typename... Args>
|
||||
class_(const handle& scope, const char* name, Args&&... args) :
|
||||
type(vm->new_type_object(scope.ptr(),
|
||||
name,
|
||||
vm->tp_object,
|
||||
false,
|
||||
pkpy::PyTypeInfo::Vt::get<instance>()),
|
||||
true) {
|
||||
pkpy::PyVar mod = scope.ptr();
|
||||
mod->attr().set(name, m_ptr);
|
||||
vm->_cxx_typeid_map[typeid(T)] = _builtin_cast<pkpy::Type>(m_ptr);
|
||||
vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
|
||||
auto cls = _builtin_cast<pkpy::Type>(args[0]);
|
||||
return instance::create<T>(cls);
|
||||
});
|
||||
}
|
||||
|
||||
/// bind constructor
|
||||
template <typename... Args, typename... Extra>
|
||||
class_& def(init<Args...>, const Extra&... extra) {
|
||||
if constexpr(!std::is_constructible_v<T, Args...>) {
|
||||
static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
|
||||
} else {
|
||||
bind_function(
|
||||
*this,
|
||||
"__init__",
|
||||
[](T* self, Args... args) { new (self) T(args...); },
|
||||
pkpy::BindType::DEFAULT,
|
||||
extra...);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
/// bind member function
|
||||
template <typename Fn, typename... Extra>
|
||||
class_& def(const char* name, Fn&& f, const Extra&... extra) {
|
||||
using first = std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>;
|
||||
constexpr bool is_first_base_of_v =
|
||||
std::is_reference_v<first> && std::is_base_of_v<T, remove_cvref_t<first>>;
|
||||
|
||||
if constexpr(!is_first_base_of_v) {
|
||||
static_assert(
|
||||
is_first_base_of_v,
|
||||
"If you want to bind member function, the first argument must be the base class");
|
||||
} else {
|
||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
|
||||
}
|
||||
template <typename... Args>
|
||||
class_(const handle& scope, const char* name, Args&&... args) :
|
||||
type(vm->new_type_object(scope.ptr(), name, vm->tp_object, false, pkpy::PyTypeInfo::Vt::get<instance>()),
|
||||
true) {
|
||||
pkpy::PyVar mod = scope.ptr();
|
||||
mod->attr().set(name, m_ptr);
|
||||
vm->_cxx_typeid_map[typeid(T)] = _builtin_cast<pkpy::Type>(m_ptr);
|
||||
vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
|
||||
auto cls = _builtin_cast<pkpy::Type>(args[0]);
|
||||
return instance::create<T>(cls);
|
||||
});
|
||||
}
|
||||
|
||||
/// bind constructor
|
||||
template <typename... Args, typename... Extra>
|
||||
class_& def(init<Args...>, const Extra&... extra) {
|
||||
if constexpr(!std::is_constructible_v<T, Args...>) {
|
||||
static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
|
||||
} else {
|
||||
bind_function(
|
||||
*this,
|
||||
"__init__",
|
||||
[](T* self, Args... args) {
|
||||
new (self) T(args...);
|
||||
},
|
||||
pkpy::BindType::DEFAULT,
|
||||
extra...);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
/// bind operators
|
||||
template <typename Operator, typename... Extras>
|
||||
class_& def(Operator op, const Extras&... extras) {
|
||||
op.execute(*this, extras...);
|
||||
return *this;
|
||||
/// bind member function
|
||||
template <typename Fn, typename... Extra>
|
||||
class_& def(const char* name, Fn&& f, const Extra&... extra) {
|
||||
using first = std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>;
|
||||
constexpr bool is_first_base_of_v = std::is_reference_v<first> && std::is_base_of_v<T, remove_cvref_t<first>>;
|
||||
|
||||
if constexpr(!is_first_base_of_v) {
|
||||
static_assert(is_first_base_of_v,
|
||||
"If you want to bind member function, the first argument must be the base class");
|
||||
} else {
|
||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
|
||||
}
|
||||
|
||||
// TODO: factory function
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// bind static function
|
||||
template <typename Fn, typename... Extra>
|
||||
class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
|
||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
|
||||
return *this;
|
||||
/// bind operators
|
||||
template <typename Operator, typename... Extras>
|
||||
class_& def(Operator op, const Extras&... extras) {
|
||||
op.execute(*this, extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO: factory function
|
||||
|
||||
/// bind static function
|
||||
template <typename Fn, typename... Extra>
|
||||
class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
|
||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>, "def_readwrite only supports pointer to data member");
|
||||
} else {
|
||||
bind_property(*this, name, mp, mp, extras...);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>,
|
||||
"def_readwrite only supports pointer to data member");
|
||||
} else {
|
||||
bind_property(*this, name, mp, mp, extras...);
|
||||
}
|
||||
return *this;
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>, "def_readonly only supports pointer to data member");
|
||||
} else {
|
||||
bind_property(*this, name, mp, nullptr, extras...);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>,
|
||||
"def_readonly only supports pointer to data member");
|
||||
} else {
|
||||
bind_property(*this, name, mp, nullptr, extras...);
|
||||
}
|
||||
return *this;
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename... Extras>
|
||||
class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
|
||||
bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_& def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Getter>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Others>
|
||||
class enum_ : public class_<T, Others...> {
|
||||
std::map<const char*, pkpy::PyVar> m_values;
|
||||
|
||||
public:
|
||||
using class_<T, Others...>::class_;
|
||||
|
||||
template <typename... Args>
|
||||
enum_(const handle& scope, const char* name, Args&&... args) :
|
||||
class_<T, Others...>(scope, name, std::forward<Args>(args)...) {}
|
||||
|
||||
enum_& value(const char* name, T value) {
|
||||
handle var = type_caster<T>::cast(value, return_value_policy::copy);
|
||||
this->m_ptr->attr().set(name, var.ptr());
|
||||
m_values[name] = var.ptr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum_& export_values() {
|
||||
pkpy::PyVar mod = this->m_ptr->attr("__module__");
|
||||
for(auto& [name, value]: m_values) {
|
||||
mod->attr().set(name, value);
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename... Extras>
|
||||
class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
|
||||
bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_&
|
||||
def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Getter>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Others>
|
||||
class enum_ : public class_<T, Others...> {
|
||||
std::map<const char*, pkpy::PyVar> m_values;
|
||||
|
||||
public:
|
||||
using class_<T, Others...>::class_;
|
||||
|
||||
template <typename... Args>
|
||||
enum_(const handle& scope, const char* name, Args&&... args) :
|
||||
class_<T, Others...>(scope, name, std::forward<Args>(args)...) {}
|
||||
|
||||
enum_& value(const char* name, T value) {
|
||||
handle var = type_caster<T>::cast(value, return_value_policy::copy);
|
||||
this->m_ptr->attr().set(name, var.ptr());
|
||||
m_values[name] = var.ptr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum_& export_values() {
|
||||
pkpy::PyVar mod = this->m_ptr->attr("__module__");
|
||||
for(auto& [name, value]: m_values) {
|
||||
mod->attr().set(name, value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
||||
@ -5,379 +5,354 @@
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
template <std::size_t Nurse, std::size_t... Patients>
|
||||
struct keep_alive {};
|
||||
template <std::size_t Nurse, std::size_t... Patients>
|
||||
struct keep_alive {};
|
||||
|
||||
template <typename T>
|
||||
struct call_guard {
|
||||
static_assert(std::is_default_constructible_v<T>,
|
||||
"call_guard must be default constructible");
|
||||
template <typename T>
|
||||
struct call_guard {
|
||||
static_assert(std::is_default_constructible_v<T>, "call_guard must be default constructible");
|
||||
};
|
||||
|
||||
// append the overload to the beginning of the overload list
|
||||
struct prepend {};
|
||||
|
||||
template <typename... Args>
|
||||
struct init {};
|
||||
|
||||
// TODO: support more customized tags
|
||||
// struct kw_only {};
|
||||
//
|
||||
// struct pos_only {};
|
||||
//
|
||||
// struct default_arg {};
|
||||
//
|
||||
// struct arg {
|
||||
// const char* name;
|
||||
// const char* description;
|
||||
// };
|
||||
//
|
||||
// struct default_arg {
|
||||
// const char* name;
|
||||
// const char* description;
|
||||
// const char* value;
|
||||
// };
|
||||
|
||||
template <typename Fn,
|
||||
typename Extra,
|
||||
typename Args = callable_args_t<std::decay_t<Fn>>,
|
||||
typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
|
||||
struct generator;
|
||||
|
||||
class function_record {
|
||||
union {
|
||||
void* data;
|
||||
char buffer[16];
|
||||
};
|
||||
|
||||
// append the overload to the beginning of the overload list
|
||||
struct prepend {};
|
||||
// TODO: optimize the function_record size to reduce memory usage
|
||||
const char* name;
|
||||
function_record* next;
|
||||
void (*destructor)(function_record*);
|
||||
return_value_policy policy = return_value_policy::automatic;
|
||||
handle (*wrapper)(function_record&, pkpy::ArgsView, bool convert, handle parent);
|
||||
|
||||
template <typename... Args>
|
||||
struct init {};
|
||||
template <typename Fn, typename Extra, typename Args, typename IndexSequence>
|
||||
friend struct generator;
|
||||
|
||||
// TODO: support more customized tags
|
||||
// struct kw_only {};
|
||||
//
|
||||
// struct pos_only {};
|
||||
//
|
||||
// struct default_arg {};
|
||||
//
|
||||
// struct arg {
|
||||
// const char* name;
|
||||
// const char* description;
|
||||
// };
|
||||
//
|
||||
// struct default_arg {
|
||||
// const char* name;
|
||||
// const char* description;
|
||||
// const char* value;
|
||||
// };
|
||||
public:
|
||||
template <typename Fn, typename... Extras>
|
||||
function_record(Fn&& f, const char* name, const Extras&... extras) : name(name), next(nullptr) {
|
||||
|
||||
template <typename Fn,
|
||||
typename Extra,
|
||||
typename Args = callable_args_t<std::decay_t<Fn>>,
|
||||
typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
|
||||
struct generator;
|
||||
|
||||
class function_record {
|
||||
union {
|
||||
void* data;
|
||||
char buffer[16];
|
||||
};
|
||||
|
||||
// TODO: optimize the function_record size to reduce memory usage
|
||||
const char* name;
|
||||
function_record* next;
|
||||
void (*destructor)(function_record*);
|
||||
return_value_policy policy = return_value_policy::automatic;
|
||||
handle (*wrapper)(function_record&, pkpy::ArgsView, bool convert, handle parent);
|
||||
|
||||
template <typename Fn, typename Extra, typename Args, typename IndexSequence>
|
||||
friend struct generator;
|
||||
|
||||
public:
|
||||
template <typename Fn, typename... Extras>
|
||||
function_record(Fn&& f, const char* name, const Extras&... extras) :
|
||||
name(name), next(nullptr) {
|
||||
|
||||
if constexpr(sizeof(f) <= sizeof(buffer)) {
|
||||
new (buffer) auto(std::forward<Fn>(f));
|
||||
destructor = [](function_record* self) {
|
||||
reinterpret_cast<Fn*>(self->buffer)->~Fn();
|
||||
};
|
||||
} else {
|
||||
data = new auto(std::forward<Fn>(f));
|
||||
destructor = [](function_record* self) { delete static_cast<Fn*>(self->data); };
|
||||
}
|
||||
|
||||
using Generator = generator<std::decay_t<Fn>, std::tuple<Extras...>>;
|
||||
Generator::initialize(*this, extras...);
|
||||
wrapper = Generator::generate();
|
||||
}
|
||||
|
||||
~function_record() { destructor(this); }
|
||||
|
||||
template <typename Fn>
|
||||
auto& cast() {
|
||||
if constexpr(sizeof(Fn) <= sizeof(buffer)) {
|
||||
return *reinterpret_cast<Fn*>(buffer);
|
||||
} else {
|
||||
return *static_cast<Fn*>(data);
|
||||
}
|
||||
}
|
||||
|
||||
void append(function_record* record) {
|
||||
function_record* p = this;
|
||||
while(p->next != nullptr) {
|
||||
p = p->next;
|
||||
}
|
||||
p->next = record;
|
||||
}
|
||||
|
||||
handle operator() (pkpy::ArgsView view) {
|
||||
function_record* p = this;
|
||||
// foreach function record and call the function with not convert
|
||||
while(p != nullptr) {
|
||||
handle result = p->wrapper(*this, view, false, {});
|
||||
if(result) {
|
||||
return result;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
p = this;
|
||||
// foreach function record and call the function with convert
|
||||
while(p != nullptr) {
|
||||
handle result = p->wrapper(*this, view, true, {});
|
||||
if(result) {
|
||||
return result;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
vm->TypeError("no matching function found");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn, std::size_t... Is, typename... Args>
|
||||
handle invoke(Fn&& fn,
|
||||
std::index_sequence<Is...>,
|
||||
std::tuple<type_caster<Args>...>& casters,
|
||||
return_value_policy policy,
|
||||
handle parent) {
|
||||
using underlying_type = std::decay_t<Fn>;
|
||||
using ret = callable_return_t<underlying_type>;
|
||||
|
||||
// if the return type is void, return None
|
||||
if constexpr(std::is_void_v<ret>) {
|
||||
// resolve the member function pointer
|
||||
if constexpr(std::is_member_function_pointer_v<underlying_type>) {
|
||||
[&](class_type_t<underlying_type>& self, auto&... args) {
|
||||
(self.*fn)(args...);
|
||||
}(std::get<Is>(casters).value...);
|
||||
} else {
|
||||
fn(std::get<Is>(casters).value...);
|
||||
}
|
||||
return vm->None;
|
||||
if constexpr(sizeof(f) <= sizeof(buffer)) {
|
||||
new (buffer) auto(std::forward<Fn>(f));
|
||||
destructor = [](function_record* self) {
|
||||
reinterpret_cast<Fn*>(self->buffer)->~Fn();
|
||||
};
|
||||
} else {
|
||||
// resolve the member function pointer
|
||||
if constexpr(std::is_member_function_pointer_v<remove_cvref_t<Fn>>) {
|
||||
return type_caster<ret>::cast(
|
||||
[&](class_type_t<underlying_type>& self, auto&... args) {
|
||||
return (self.*fn)(args...);
|
||||
}(std::get<Is>(casters).value...),
|
||||
policy,
|
||||
parent);
|
||||
} else {
|
||||
return type_caster<ret>::cast(fn(std::get<Is>(casters).value...), policy, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Args, std::size_t... Is, typename... Extras>
|
||||
struct generator<Fn, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
|
||||
static void initialize(function_record& record, const Extras&... extras) {}
|
||||
|
||||
static auto generate() {
|
||||
return +[](function_record& self, pkpy::ArgsView view, bool convert, handle parent) {
|
||||
// FIXME:
|
||||
// Temporarily, args and kwargs must be at the end of the arguments list
|
||||
// Named arguments are not supported yet
|
||||
constexpr bool has_args = types_count_v<args, remove_cvref_t<Args>...> != 0;
|
||||
constexpr bool has_kwargs = types_count_v<kwargs, remove_cvref_t<Args>...> != 0;
|
||||
constexpr std::size_t count = sizeof...(Args) - has_args - has_kwargs;
|
||||
|
||||
handle stack[sizeof...(Args)] = {};
|
||||
|
||||
// initialize the stack
|
||||
|
||||
if(!has_args && (view.size() != count)) {
|
||||
return handle();
|
||||
}
|
||||
|
||||
if(has_args && (view.size() < count)) {
|
||||
return handle();
|
||||
}
|
||||
|
||||
for(std::size_t i = 0; i < count; ++i) {
|
||||
stack[i] = view[i];
|
||||
}
|
||||
|
||||
// pack the args and kwargs
|
||||
if constexpr(has_args) {
|
||||
const auto n = view.size() - count;
|
||||
pkpy::PyVar var = vm->new_object<pkpy::Tuple>(vm->tp_tuple, n);
|
||||
auto& tuple = var.obj_get<pkpy::Tuple>();
|
||||
for(std::size_t i = 0; i < n; ++i) {
|
||||
tuple[i] = view[count + i];
|
||||
}
|
||||
stack[count] = var;
|
||||
}
|
||||
|
||||
if constexpr(has_kwargs) {
|
||||
const auto n = vm->s_data._sp - view.end();
|
||||
pkpy::PyVar var = vm->new_object<pkpy::Dict>(vm->tp_dict);
|
||||
auto& dict = var.obj_get<pkpy::Dict>();
|
||||
|
||||
for(std::size_t i = 0; i < n; i += 2) {
|
||||
pkpy::i64 index = pkpy::_py_cast<pkpy::i64>(vm, view[count + i]);
|
||||
pkpy::PyVar str =
|
||||
vm->new_object<pkpy::Str>(vm->tp_str, pkpy::StrName(index).sv());
|
||||
dict.set(vm, str, view[count + i + 1]);
|
||||
}
|
||||
|
||||
stack[count + 1] = var;
|
||||
}
|
||||
|
||||
// check if all the arguments are not valid
|
||||
for(std::size_t i = 0; i < sizeof...(Args); ++i) {
|
||||
if(!stack[i]) {
|
||||
return handle();
|
||||
}
|
||||
}
|
||||
|
||||
// ok, all the arguments are valid, call the function
|
||||
std::tuple<type_caster<Args>...> casters;
|
||||
|
||||
// check type compatibility
|
||||
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
|
||||
return invoke(self.cast<Fn>(),
|
||||
std::index_sequence<Is...>{},
|
||||
casters,
|
||||
self.policy,
|
||||
parent);
|
||||
}
|
||||
|
||||
return handle();
|
||||
data = new auto(std::forward<Fn>(f));
|
||||
destructor = [](function_record* self) {
|
||||
delete static_cast<Fn*>(self->data);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) {
|
||||
auto& record = pkpy::lambda_get_userdata<function_record>(view.begin());
|
||||
return record(view).ptr();
|
||||
};
|
||||
|
||||
class cpp_function : public function {
|
||||
public:
|
||||
template <typename Fn, typename... Extras>
|
||||
cpp_function(Fn&& f, const Extras&... extras) {
|
||||
pkpy::any userdata = function_record(std::forward<Fn>(f), "anonymous", extras...);
|
||||
m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata));
|
||||
inc_ref();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn, typename... Extras>
|
||||
handle bind_function(const handle& obj,
|
||||
const char* name,
|
||||
Fn&& fn,
|
||||
pkpy::BindType type,
|
||||
const Extras&... extras) {
|
||||
// do not use cpp_function directly to avoid unnecessary reference count change
|
||||
pkpy::PyVar var = obj.ptr();
|
||||
pkpy::PyVar callable = var->attr().try_get(name);
|
||||
|
||||
// if the function is not bound yet, bind it
|
||||
if(!callable) {
|
||||
pkpy::any userdata = function_record(std::forward<Fn>(fn), name, extras...);
|
||||
callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata));
|
||||
} else {
|
||||
auto& userdata = callable.obj_get<pkpy::NativeFunc>()._userdata;
|
||||
function_record* record = new function_record(std::forward<Fn>(fn), name, extras...);
|
||||
|
||||
constexpr bool is_prepend = (types_count_v<prepend, Extras...> != 0);
|
||||
if constexpr(is_prepend) {
|
||||
// if prepend is specified, append the new record to the beginning of the list
|
||||
function_record* last = (function_record*)userdata.data;
|
||||
userdata.data = record;
|
||||
record->append(last);
|
||||
} else {
|
||||
// otherwise, append the new record to the end of the list
|
||||
function_record* last = (function_record*)userdata.data;
|
||||
last->append(record);
|
||||
}
|
||||
}
|
||||
return callable;
|
||||
using Generator = generator<std::decay_t<Fn>, std::tuple<Extras...>>;
|
||||
Generator::initialize(*this, extras...);
|
||||
wrapper = Generator::generate();
|
||||
}
|
||||
|
||||
template <typename Getter_, typename Setter_, typename... Extras>
|
||||
handle bind_property(const handle& obj,
|
||||
const char* name,
|
||||
Getter_&& getter_,
|
||||
Setter_&& setter_,
|
||||
const Extras&... extras) {
|
||||
pkpy::PyVar var = obj.ptr();
|
||||
pkpy::PyVar getter = vm->None;
|
||||
pkpy::PyVar setter = vm->None;
|
||||
using Getter = std::decay_t<Getter_>;
|
||||
using Setter = std::decay_t<Setter_>;
|
||||
~function_record() { destructor(this); }
|
||||
|
||||
getter = vm->new_object<pkpy::NativeFunc>(
|
||||
vm->tp_native_func,
|
||||
[](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
|
||||
auto& getter = pkpy::lambda_get_userdata<Getter>(view.begin());
|
||||
template <typename Fn>
|
||||
auto& cast() {
|
||||
if constexpr(sizeof(Fn) <= sizeof(buffer)) {
|
||||
return *reinterpret_cast<Fn*>(buffer);
|
||||
} else {
|
||||
return *static_cast<Fn*>(data);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(std::is_member_pointer_v<Getter>) {
|
||||
using Self = class_type_t<Getter>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
void append(function_record* record) {
|
||||
function_record* p = this;
|
||||
while(p->next != nullptr) {
|
||||
p = p->next;
|
||||
}
|
||||
p->next = record;
|
||||
}
|
||||
|
||||
if constexpr(std::is_member_object_pointer_v<Getter>) {
|
||||
return type_caster<member_type_t<Getter>>::cast(
|
||||
self.*getter,
|
||||
return_value_policy::reference_internal,
|
||||
view[0])
|
||||
.ptr();
|
||||
} else {
|
||||
return type_caster<callable_return_t<Getter>>::cast(
|
||||
(self.*getter)(),
|
||||
return_value_policy::reference_internal,
|
||||
view[0])
|
||||
.ptr();
|
||||
}
|
||||
handle operator() (pkpy::ArgsView view) {
|
||||
function_record* p = this;
|
||||
// foreach function record and call the function with not convert
|
||||
while(p != nullptr) {
|
||||
handle result = p->wrapper(*this, view, false, {});
|
||||
if(result) { return result; }
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
p = this;
|
||||
// foreach function record and call the function with convert
|
||||
while(p != nullptr) {
|
||||
handle result = p->wrapper(*this, view, true, {});
|
||||
if(result) { return result; }
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
vm->TypeError("no matching function found");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn, std::size_t... Is, typename... Args>
|
||||
handle invoke(Fn&& fn,
|
||||
std::index_sequence<Is...>,
|
||||
std::tuple<type_caster<Args>...>& casters,
|
||||
return_value_policy policy,
|
||||
handle parent) {
|
||||
using underlying_type = std::decay_t<Fn>;
|
||||
using ret = callable_return_t<underlying_type>;
|
||||
|
||||
// if the return type is void, return None
|
||||
if constexpr(std::is_void_v<ret>) {
|
||||
// resolve the member function pointer
|
||||
if constexpr(std::is_member_function_pointer_v<underlying_type>) {
|
||||
[&](class_type_t<underlying_type>& self, auto&... args) {
|
||||
(self.*fn)(args...);
|
||||
}(std::get<Is>(casters).value...);
|
||||
} else {
|
||||
fn(std::get<Is>(casters).value...);
|
||||
}
|
||||
return vm->None;
|
||||
} else {
|
||||
// resolve the member function pointer
|
||||
if constexpr(std::is_member_function_pointer_v<remove_cvref_t<Fn>>) {
|
||||
return type_caster<ret>::cast(
|
||||
[&](class_type_t<underlying_type>& self, auto&... args) {
|
||||
return (self.*fn)(args...);
|
||||
}(std::get<Is>(casters).value...),
|
||||
policy,
|
||||
parent);
|
||||
} else {
|
||||
return type_caster<ret>::cast(fn(std::get<Is>(casters).value...), policy, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Args, std::size_t... Is, typename... Extras>
|
||||
struct generator<Fn, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
|
||||
static void initialize(function_record& record, const Extras&... extras) {}
|
||||
|
||||
static auto generate() {
|
||||
return +[](function_record& self, pkpy::ArgsView view, bool convert, handle parent) {
|
||||
// FIXME:
|
||||
// Temporarily, args and kwargs must be at the end of the arguments list
|
||||
// Named arguments are not supported yet
|
||||
constexpr bool has_args = types_count_v<args, remove_cvref_t<Args>...> != 0;
|
||||
constexpr bool has_kwargs = types_count_v<kwargs, remove_cvref_t<Args>...> != 0;
|
||||
constexpr std::size_t count = sizeof...(Args) - has_args - has_kwargs;
|
||||
|
||||
handle stack[sizeof...(Args)] = {};
|
||||
|
||||
// initialize the stack
|
||||
|
||||
if(!has_args && (view.size() != count)) { return handle(); }
|
||||
|
||||
if(has_args && (view.size() < count)) { return handle(); }
|
||||
|
||||
for(std::size_t i = 0; i < count; ++i) {
|
||||
stack[i] = view[i];
|
||||
}
|
||||
|
||||
// pack the args and kwargs
|
||||
if constexpr(has_args) {
|
||||
const auto n = view.size() - count;
|
||||
pkpy::PyVar var = vm->new_object<pkpy::Tuple>(vm->tp_tuple, n);
|
||||
auto& tuple = var.obj_get<pkpy::Tuple>();
|
||||
for(std::size_t i = 0; i < n; ++i) {
|
||||
tuple[i] = view[count + i];
|
||||
}
|
||||
stack[count] = var;
|
||||
}
|
||||
|
||||
if constexpr(has_kwargs) {
|
||||
const auto n = vm->s_data._sp - view.end();
|
||||
pkpy::PyVar var = vm->new_object<pkpy::Dict>(vm->tp_dict);
|
||||
auto& dict = var.obj_get<pkpy::Dict>();
|
||||
|
||||
for(std::size_t i = 0; i < n; i += 2) {
|
||||
pkpy::i64 index = pkpy::_py_cast<pkpy::i64>(vm, view[count + i]);
|
||||
pkpy::PyVar str = vm->new_object<pkpy::Str>(vm->tp_str, pkpy::StrName(index).sv());
|
||||
dict.set(vm, str, view[count + i + 1]);
|
||||
}
|
||||
|
||||
stack[count + 1] = var;
|
||||
}
|
||||
|
||||
// check if all the arguments are not valid
|
||||
for(std::size_t i = 0; i < sizeof...(Args); ++i) {
|
||||
if(!stack[i]) { return handle(); }
|
||||
}
|
||||
|
||||
// ok, all the arguments are valid, call the function
|
||||
std::tuple<type_caster<Args>...> casters;
|
||||
|
||||
// check type compatibility
|
||||
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
|
||||
return invoke(self.cast<Fn>(), std::index_sequence<Is...>{}, casters, self.policy, parent);
|
||||
}
|
||||
|
||||
return handle();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) {
|
||||
auto& record = pkpy::lambda_get_userdata<function_record>(view.begin());
|
||||
return record(view).ptr();
|
||||
};
|
||||
|
||||
class cpp_function : public function {
|
||||
public:
|
||||
template <typename Fn, typename... Extras>
|
||||
cpp_function(Fn&& f, const Extras&... extras) {
|
||||
pkpy::any userdata = function_record(std::forward<Fn>(f), "anonymous", extras...);
|
||||
m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata));
|
||||
inc_ref();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn, typename... Extras>
|
||||
handle bind_function(const handle& obj, const char* name, Fn&& fn, pkpy::BindType type, const Extras&... extras) {
|
||||
// do not use cpp_function directly to avoid unnecessary reference count change
|
||||
pkpy::PyVar var = obj.ptr();
|
||||
pkpy::PyVar callable = var->attr().try_get(name);
|
||||
|
||||
// if the function is not bound yet, bind it
|
||||
if(!callable) {
|
||||
pkpy::any userdata = function_record(std::forward<Fn>(fn), name, extras...);
|
||||
callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata));
|
||||
} else {
|
||||
auto& userdata = callable.obj_get<pkpy::NativeFunc>()._userdata;
|
||||
function_record* record = new function_record(std::forward<Fn>(fn), name, extras...);
|
||||
|
||||
constexpr bool is_prepend = (types_count_v<prepend, Extras...> != 0);
|
||||
if constexpr(is_prepend) {
|
||||
// if prepend is specified, append the new record to the beginning of the list
|
||||
function_record* last = (function_record*)userdata.data;
|
||||
userdata.data = record;
|
||||
record->append(last);
|
||||
} else {
|
||||
// otherwise, append the new record to the end of the list
|
||||
function_record* last = (function_record*)userdata.data;
|
||||
last->append(record);
|
||||
}
|
||||
}
|
||||
return callable;
|
||||
}
|
||||
|
||||
template <typename Getter_, typename Setter_, typename... Extras>
|
||||
handle
|
||||
bind_property(const handle& obj, const char* name, Getter_&& getter_, Setter_&& setter_, const Extras&... extras) {
|
||||
pkpy::PyVar var = obj.ptr();
|
||||
pkpy::PyVar getter = vm->None;
|
||||
pkpy::PyVar setter = vm->None;
|
||||
using Getter = std::decay_t<Getter_>;
|
||||
using Setter = std::decay_t<Setter_>;
|
||||
|
||||
getter = vm->new_object<pkpy::NativeFunc>(
|
||||
vm->tp_native_func,
|
||||
[](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
|
||||
auto& getter = pkpy::lambda_get_userdata<Getter>(view.begin());
|
||||
|
||||
if constexpr(std::is_member_pointer_v<Getter>) {
|
||||
using Self = class_type_t<Getter>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
|
||||
if constexpr(std::is_member_object_pointer_v<Getter>) {
|
||||
return type_caster<member_type_t<Getter>>::cast(self.*getter,
|
||||
return_value_policy::reference_internal,
|
||||
view[0])
|
||||
.ptr();
|
||||
} else {
|
||||
using Self = std::tuple_element_t<0, callable_args_t<Getter>>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
|
||||
return type_caster<callable_return_t<Getter>>::cast(
|
||||
getter(self),
|
||||
return_value_policy::reference_internal,
|
||||
view[0])
|
||||
return type_caster<callable_return_t<Getter>>::cast((self.*getter)(),
|
||||
return_value_policy::reference_internal,
|
||||
view[0])
|
||||
.ptr();
|
||||
}
|
||||
},
|
||||
1,
|
||||
std::forward<Getter_>(getter_));
|
||||
|
||||
if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
|
||||
setter = vm->new_object<pkpy::NativeFunc>(
|
||||
vm->tp_native_func,
|
||||
[](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
|
||||
auto& setter = pkpy::lambda_get_userdata<Setter>(view.begin());
|
||||
} else {
|
||||
using Self = std::tuple_element_t<0, callable_args_t<Getter>>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
|
||||
if constexpr(std::is_member_pointer_v<Setter>) {
|
||||
using Self = class_type_t<Setter>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
return type_caster<callable_return_t<Getter>>::cast(getter(self),
|
||||
return_value_policy::reference_internal,
|
||||
view[0])
|
||||
.ptr();
|
||||
}
|
||||
},
|
||||
1,
|
||||
std::forward<Getter_>(getter_));
|
||||
|
||||
if constexpr(std::is_member_object_pointer_v<Setter>) {
|
||||
type_caster<member_type_t<Setter>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
self.*setter = caster.value;
|
||||
return vm->None;
|
||||
}
|
||||
} else {
|
||||
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
(self.*setter)(caster.value);
|
||||
return vm->None;
|
||||
}
|
||||
if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
|
||||
setter = vm->new_object<pkpy::NativeFunc>(
|
||||
vm->tp_native_func,
|
||||
[](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
|
||||
auto& setter = pkpy::lambda_get_userdata<Setter>(view.begin());
|
||||
|
||||
if constexpr(std::is_member_pointer_v<Setter>) {
|
||||
using Self = class_type_t<Setter>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
|
||||
if constexpr(std::is_member_object_pointer_v<Setter>) {
|
||||
type_caster<member_type_t<Setter>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
self.*setter = caster.value;
|
||||
return vm->None;
|
||||
}
|
||||
} else {
|
||||
using Self = std::tuple_element_t<0, callable_args_t<Setter>>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
|
||||
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
setter(self, caster.value);
|
||||
(self.*setter)(caster.value);
|
||||
return vm->None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
using Self = std::tuple_element_t<0, callable_args_t<Setter>>;
|
||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
||||
|
||||
vm->TypeError("invalid argument");
|
||||
},
|
||||
2,
|
||||
std::forward<Setter_>(setter_));
|
||||
}
|
||||
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
setter(self, caster.value);
|
||||
return vm->None;
|
||||
}
|
||||
}
|
||||
|
||||
pkpy::PyVar property = vm->new_object<pkpy::Property>(vm->tp_property, getter, setter);
|
||||
var->attr().set(name, property);
|
||||
return property;
|
||||
vm->TypeError("invalid argument");
|
||||
},
|
||||
2,
|
||||
std::forward<Setter_>(setter_));
|
||||
}
|
||||
|
||||
pkpy::PyVar property = vm->new_object<pkpy::Property>(vm->tp_property, getter, setter);
|
||||
var->attr().set(name, property);
|
||||
return property;
|
||||
}
|
||||
|
||||
} // namespace pybind11
|
||||
|
||||
@ -3,145 +3,143 @@
|
||||
#include "kernel.h"
|
||||
|
||||
namespace pybind11 {
|
||||
struct type_info {
|
||||
const char* name;
|
||||
std::size_t size;
|
||||
std::size_t alignment;
|
||||
void (*destructor)(void*);
|
||||
void (*copy)(void*, const void*);
|
||||
void (*move)(void*, void*);
|
||||
const std::type_info* type;
|
||||
struct type_info {
|
||||
const char* name;
|
||||
std::size_t size;
|
||||
std::size_t alignment;
|
||||
void (*destructor)(void*);
|
||||
void (*copy)(void*, const void*);
|
||||
void (*move)(void*, void*);
|
||||
const std::type_info* type;
|
||||
|
||||
template <typename T>
|
||||
static type_info& of() {
|
||||
static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
|
||||
"T must not be a reference type or const type.");
|
||||
static type_info info = {
|
||||
typeid(T).name(),
|
||||
sizeof(T),
|
||||
alignof(T),
|
||||
[](void* ptr) {
|
||||
((T*)ptr)->~T();
|
||||
operator delete (ptr);
|
||||
},
|
||||
[](void* dst, const void* src) { new (dst) T(*(const T*)src); },
|
||||
[](void* dst, void* src) { new (dst) T(std::move(*(T*)src)); },
|
||||
&typeid(T),
|
||||
};
|
||||
return info;
|
||||
}
|
||||
};
|
||||
|
||||
// all registered C++ class will be ensured as instance type.
|
||||
class instance {
|
||||
public:
|
||||
// use to record the type information of C++ class.
|
||||
|
||||
private:
|
||||
enum Flag {
|
||||
None = 0,
|
||||
Own = 1 << 0, // if the instance is owned by C++ side.
|
||||
Ref = 1 << 1, // need to mark the parent object.
|
||||
template <typename T>
|
||||
static type_info& of() {
|
||||
static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
|
||||
"T must not be a reference type or const type.");
|
||||
static type_info info = {
|
||||
typeid(T).name(),
|
||||
sizeof(T),
|
||||
alignof(T),
|
||||
[](void* ptr) {
|
||||
((T*)ptr)->~T();
|
||||
operator delete (ptr);
|
||||
},
|
||||
[](void* dst, const void* src) {
|
||||
new (dst) T(*(const T*)src);
|
||||
},
|
||||
[](void* dst, void* src) {
|
||||
new (dst) T(std::move(*(T*)src));
|
||||
},
|
||||
&typeid(T),
|
||||
};
|
||||
return info;
|
||||
}
|
||||
};
|
||||
|
||||
Flag flag;
|
||||
void* data;
|
||||
const type_info* type;
|
||||
pkpy::PyVar parent;
|
||||
// pkpy::PyVar
|
||||
// all registered C++ class will be ensured as instance type.
|
||||
class instance {
|
||||
public:
|
||||
// use to record the type information of C++ class.
|
||||
|
||||
public:
|
||||
instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
|
||||
|
||||
instance(const instance&) = delete;
|
||||
|
||||
instance(instance&& other) noexcept :
|
||||
flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
|
||||
other.flag = Flag::None;
|
||||
other.data = nullptr;
|
||||
other.type = nullptr;
|
||||
other.parent = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static pkpy::PyVar create(pkpy::Type type) {
|
||||
instance instance;
|
||||
instance.type = &type_info::of<T>();
|
||||
instance.data = operator new (sizeof(T));
|
||||
instance.flag = Flag::Own;
|
||||
return vm->new_object<pybind11::instance>(type, std::move(instance));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static pkpy::PyVar
|
||||
create(T&& value,
|
||||
pkpy::Type type,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
pkpy::PyVar parent = nullptr) noexcept {
|
||||
using underlying_type = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
// resolve for automatic policy.
|
||||
if(policy == return_value_policy::automatic) {
|
||||
policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
|
||||
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||
: return_value_policy::move;
|
||||
} else if(policy == return_value_policy::automatic_reference) {
|
||||
policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
|
||||
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||
: return_value_policy::move;
|
||||
}
|
||||
|
||||
auto& _value = [&]() -> auto& {
|
||||
/**
|
||||
* note that, pybind11 will ignore the const qualifier.
|
||||
* in fact, try to modify a const value will result in undefined behavior.
|
||||
*/
|
||||
if constexpr(std::is_pointer_v<underlying_type>) {
|
||||
return *reinterpret_cast<underlying_type*>(value);
|
||||
} else {
|
||||
return const_cast<underlying_type&>(value);
|
||||
}
|
||||
}();
|
||||
|
||||
instance instance;
|
||||
instance.type = &type_info::of<underlying_type>();
|
||||
|
||||
if(policy == return_value_policy::take_ownership) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::copy) {
|
||||
instance.data = ::new auto(_value);
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::move) {
|
||||
instance.data = ::new auto(std::move(_value));
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::reference) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::None;
|
||||
} else if(policy == return_value_policy::reference_internal) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Ref;
|
||||
instance.parent = parent;
|
||||
}
|
||||
|
||||
return vm->new_object<pybind11::instance>(type, std::move(instance));
|
||||
}
|
||||
|
||||
~instance() {
|
||||
if(flag & Flag::Own) {
|
||||
type->destructor(data);
|
||||
}
|
||||
}
|
||||
|
||||
void _gc_mark(pkpy::VM* vm) const noexcept {
|
||||
if(parent && (flag & Flag::Ref)) {
|
||||
PK_OBJ_MARK(parent);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& cast() noexcept {
|
||||
return *static_cast<T*>(data);
|
||||
}
|
||||
private:
|
||||
enum Flag {
|
||||
None = 0,
|
||||
Own = 1 << 0, // if the instance is owned by C++ side.
|
||||
Ref = 1 << 1, // need to mark the parent object.
|
||||
};
|
||||
|
||||
Flag flag;
|
||||
void* data;
|
||||
const type_info* type;
|
||||
pkpy::PyVar parent;
|
||||
// pkpy::PyVar
|
||||
|
||||
public:
|
||||
instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
|
||||
|
||||
instance(const instance&) = delete;
|
||||
|
||||
instance(instance&& other) noexcept : flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
|
||||
other.flag = Flag::None;
|
||||
other.data = nullptr;
|
||||
other.type = nullptr;
|
||||
other.parent = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static pkpy::PyVar create(pkpy::Type type) {
|
||||
instance instance;
|
||||
instance.type = &type_info::of<T>();
|
||||
instance.data = operator new (sizeof(T));
|
||||
instance.flag = Flag::Own;
|
||||
return vm->new_object<pybind11::instance>(type, std::move(instance));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static pkpy::PyVar create(T&& value,
|
||||
pkpy::Type type,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
pkpy::PyVar parent = nullptr) noexcept {
|
||||
using underlying_type = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
// resolve for automatic policy.
|
||||
if(policy == return_value_policy::automatic) {
|
||||
policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
|
||||
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||
: return_value_policy::move;
|
||||
} else if(policy == return_value_policy::automatic_reference) {
|
||||
policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
|
||||
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||
: return_value_policy::move;
|
||||
}
|
||||
|
||||
auto& _value = [&]() -> auto& {
|
||||
/**
|
||||
* note that, pybind11 will ignore the const qualifier.
|
||||
* in fact, try to modify a const value will result in undefined behavior.
|
||||
*/
|
||||
if constexpr(std::is_pointer_v<underlying_type>) {
|
||||
return *reinterpret_cast<underlying_type*>(value);
|
||||
} else {
|
||||
return const_cast<underlying_type&>(value);
|
||||
}
|
||||
}();
|
||||
|
||||
instance instance;
|
||||
instance.type = &type_info::of<underlying_type>();
|
||||
|
||||
if(policy == return_value_policy::take_ownership) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::copy) {
|
||||
instance.data = ::new auto(_value);
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::move) {
|
||||
instance.data = ::new auto(std::move(_value));
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::reference) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::None;
|
||||
} else if(policy == return_value_policy::reference_internal) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Ref;
|
||||
instance.parent = parent;
|
||||
}
|
||||
|
||||
return vm->new_object<pybind11::instance>(type, std::move(instance));
|
||||
}
|
||||
|
||||
~instance() {
|
||||
if(flag & Flag::Own) { type->destructor(data); }
|
||||
}
|
||||
|
||||
void _gc_mark(pkpy::VM* vm) const noexcept {
|
||||
if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& cast() noexcept {
|
||||
return *static_cast<T*>(data);
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
||||
@ -2,107 +2,98 @@
|
||||
|
||||
#include <pocketpy.h>
|
||||
|
||||
namespace pybind11
|
||||
{
|
||||
inline pkpy::VM* vm = nullptr;
|
||||
inline std::map<pkpy::PyVar, int*>* _ref_counts_map = nullptr;
|
||||
namespace pybind11 {
|
||||
inline pkpy::VM* vm = nullptr;
|
||||
inline std::map<pkpy::PyVar, int*>* _ref_counts_map = nullptr;
|
||||
|
||||
inline void initialize(bool enable_os = true)
|
||||
{
|
||||
vm = new pkpy::VM(enable_os);
|
||||
_ref_counts_map = new std::map<pkpy::PyVar, int*>();
|
||||
inline void initialize(bool enable_os = true) {
|
||||
vm = new pkpy::VM(enable_os);
|
||||
_ref_counts_map = new std::map<pkpy::PyVar, int*>();
|
||||
|
||||
// use to keep alive PyObject, when the object is hold by C++ side.
|
||||
vm->heap._gc_marker_ex = [](pkpy::VM* vm)
|
||||
{
|
||||
for(auto iter = _ref_counts_map->begin(); iter != _ref_counts_map->end();)
|
||||
{
|
||||
auto ref_count = iter->second;
|
||||
if(*ref_count != 0)
|
||||
{
|
||||
// if ref count is not zero, then mark it.
|
||||
PK_OBJ_MARK(iter->first);
|
||||
++iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if ref count is zero, then delete it.
|
||||
iter = _ref_counts_map->erase(iter);
|
||||
delete ref_count;
|
||||
}
|
||||
// use to keep alive PyObject, when the object is hold by C++ side.
|
||||
vm->heap._gc_marker_ex = [](pkpy::VM* vm) {
|
||||
for(auto iter = _ref_counts_map->begin(); iter != _ref_counts_map->end();) {
|
||||
auto ref_count = iter->second;
|
||||
if(*ref_count != 0) {
|
||||
// if ref count is not zero, then mark it.
|
||||
PK_OBJ_MARK(iter->first);
|
||||
++iter;
|
||||
} else {
|
||||
// if ref count is zero, then delete it.
|
||||
iter = _ref_counts_map->erase(iter);
|
||||
delete ref_count;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline void finalize()
|
||||
{
|
||||
delete _ref_counts_map;
|
||||
delete vm;
|
||||
}
|
||||
|
||||
enum class return_value_policy : uint8_t
|
||||
{
|
||||
/**
|
||||
* This is the default return value policy, which falls back to the policy
|
||||
* return_value_policy::take_ownership when the return value is a pointer.
|
||||
* Otherwise, it uses return_value::move or return_value::copy for rvalue
|
||||
* and lvalue references, respectively. See below for a description of what
|
||||
* all of these different policies do.
|
||||
*/
|
||||
automatic = 0,
|
||||
|
||||
/**
|
||||
* As above, but use policy return_value_policy::reference when the return
|
||||
* value is a pointer. This is the default conversion policy for function
|
||||
* arguments when calling Python functions manually from C++ code (i.e. via
|
||||
* handle::operator()). You probably won't need to use this.
|
||||
*/
|
||||
automatic_reference,
|
||||
|
||||
/**
|
||||
* Reference an existing object (i.e. do not create a new copy) and take
|
||||
* ownership. Python will call the destructor and delete operator when the
|
||||
* object's reference count reaches zero. Undefined behavior ensues when
|
||||
* the C++ side does the same..
|
||||
*/
|
||||
take_ownership,
|
||||
|
||||
/**
|
||||
* Create a new copy of the returned object, which will be owned by
|
||||
* Python. This policy is comparably safe because the lifetimes of the two
|
||||
* instances are decoupled.
|
||||
*/
|
||||
copy,
|
||||
|
||||
/**
|
||||
* Use std::move to move the return value contents into a new instance
|
||||
* that will be owned by Python. This policy is comparably safe because the
|
||||
* lifetimes of the two instances (move source and destination) are
|
||||
* decoupled.
|
||||
*/
|
||||
move,
|
||||
|
||||
/**
|
||||
* Reference an existing object, but do not take ownership. The C++ side
|
||||
* is responsible for managing the object's lifetime and deallocating it
|
||||
* when it is no longer used. Warning: undefined behavior will ensue when
|
||||
* the C++ side deletes an object that is still referenced and used by
|
||||
* Python.
|
||||
*/
|
||||
reference,
|
||||
|
||||
/**
|
||||
* This policy only applies to methods and properties. It references the
|
||||
* object without taking ownership similar to the above
|
||||
* return_value_policy::reference policy. In contrast to that policy, the
|
||||
* function or property's implicit this argument (called the parent) is
|
||||
* considered to be the the owner of the return value (the child).
|
||||
* pybind11 then couples the lifetime of the parent to the child via a
|
||||
* reference relationship that ensures that the parent cannot be garbage
|
||||
* collected while Python is still using the child. More advanced
|
||||
* variations of this scheme are also possible using combinations of
|
||||
* return_value_policy::reference and the keep_alive call policy
|
||||
*/
|
||||
reference_internal
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline void finalize() {
|
||||
delete _ref_counts_map;
|
||||
delete vm;
|
||||
}
|
||||
|
||||
enum class return_value_policy : uint8_t {
|
||||
/**
|
||||
* This is the default return value policy, which falls back to the policy
|
||||
* return_value_policy::take_ownership when the return value is a pointer.
|
||||
* Otherwise, it uses return_value::move or return_value::copy for rvalue
|
||||
* and lvalue references, respectively. See below for a description of what
|
||||
* all of these different policies do.
|
||||
*/
|
||||
automatic = 0,
|
||||
|
||||
/**
|
||||
* As above, but use policy return_value_policy::reference when the return
|
||||
* value is a pointer. This is the default conversion policy for function
|
||||
* arguments when calling Python functions manually from C++ code (i.e. via
|
||||
* handle::operator()). You probably won't need to use this.
|
||||
*/
|
||||
automatic_reference,
|
||||
|
||||
/**
|
||||
* Reference an existing object (i.e. do not create a new copy) and take
|
||||
* ownership. Python will call the destructor and delete operator when the
|
||||
* object's reference count reaches zero. Undefined behavior ensues when
|
||||
* the C++ side does the same..
|
||||
*/
|
||||
take_ownership,
|
||||
|
||||
/**
|
||||
* Create a new copy of the returned object, which will be owned by
|
||||
* Python. This policy is comparably safe because the lifetimes of the two
|
||||
* instances are decoupled.
|
||||
*/
|
||||
copy,
|
||||
|
||||
/**
|
||||
* Use std::move to move the return value contents into a new instance
|
||||
* that will be owned by Python. This policy is comparably safe because the
|
||||
* lifetimes of the two instances (move source and destination) are
|
||||
* decoupled.
|
||||
*/
|
||||
move,
|
||||
|
||||
/**
|
||||
* Reference an existing object, but do not take ownership. The C++ side
|
||||
* is responsible for managing the object's lifetime and deallocating it
|
||||
* when it is no longer used. Warning: undefined behavior will ensue when
|
||||
* the C++ side deletes an object that is still referenced and used by
|
||||
* Python.
|
||||
*/
|
||||
reference,
|
||||
|
||||
/**
|
||||
* This policy only applies to methods and properties. It references the
|
||||
* object without taking ownership similar to the above
|
||||
* return_value_policy::reference policy. In contrast to that policy, the
|
||||
* function or property's implicit this argument (called the parent) is
|
||||
* considered to be the the owner of the return value (the child).
|
||||
* pybind11 then couples the lifetime of the parent to the child via a
|
||||
* reference relationship that ensures that the parent cannot be garbage
|
||||
* collected while Python is still using the child. More advanced
|
||||
* variations of this scheme are also possible using combinations of
|
||||
* return_value_policy::reference and the keep_alive call policy
|
||||
*/
|
||||
reference_internal
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
||||
@ -3,253 +3,249 @@
|
||||
#include "kernel.h"
|
||||
|
||||
namespace pybind11 {
|
||||
class handle;
|
||||
class object;
|
||||
class attr_accessor;
|
||||
class item_accessor;
|
||||
class iterator;
|
||||
class str;
|
||||
class bytes;
|
||||
class iterable;
|
||||
class tuple;
|
||||
class dict;
|
||||
class list;
|
||||
class set;
|
||||
class function;
|
||||
class module;
|
||||
class type;
|
||||
class bool_;
|
||||
class int_;
|
||||
class float_;
|
||||
class str;
|
||||
class bytes;
|
||||
class handle;
|
||||
class object;
|
||||
class attr_accessor;
|
||||
class item_accessor;
|
||||
class iterator;
|
||||
class str;
|
||||
class bytes;
|
||||
class iterable;
|
||||
class tuple;
|
||||
class dict;
|
||||
class list;
|
||||
class set;
|
||||
class function;
|
||||
class module;
|
||||
class type;
|
||||
class bool_;
|
||||
class int_;
|
||||
class float_;
|
||||
class str;
|
||||
class bytes;
|
||||
|
||||
template <typename T>
|
||||
T& _builtin_cast(const handle& obj);
|
||||
template <typename T>
|
||||
T& _builtin_cast(const handle& obj);
|
||||
|
||||
template <typename T>
|
||||
T reinterpret_borrow(const handle& h);
|
||||
template <typename T>
|
||||
T reinterpret_borrow(const handle& h);
|
||||
|
||||
template <typename T>
|
||||
T reinterpret_steal(const handle& h);
|
||||
template <typename T>
|
||||
T reinterpret_steal(const handle& h);
|
||||
|
||||
class handle {
|
||||
protected:
|
||||
pkpy::PyVar m_ptr = nullptr;
|
||||
mutable int* ref_count = nullptr;
|
||||
class handle {
|
||||
protected:
|
||||
pkpy::PyVar m_ptr = nullptr;
|
||||
mutable int* ref_count = nullptr;
|
||||
|
||||
public:
|
||||
handle() = default;
|
||||
handle(const handle& h) = default;
|
||||
handle& operator= (const handle& other) = default;
|
||||
public:
|
||||
handle() = default;
|
||||
handle(const handle& h) = default;
|
||||
handle& operator= (const handle& other) = default;
|
||||
|
||||
handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
|
||||
handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
|
||||
|
||||
pkpy::PyVar ptr() const { return m_ptr; }
|
||||
pkpy::PyVar ptr() const { return m_ptr; }
|
||||
|
||||
int reference_count() const { return ref_count == nullptr ? 0 : *ref_count; }
|
||||
int reference_count() const { return ref_count == nullptr ? 0 : *ref_count; }
|
||||
|
||||
const handle& inc_ref() const {
|
||||
assert(m_ptr != nullptr);
|
||||
if(ref_count == nullptr) {
|
||||
auto iter = _ref_counts_map->find(m_ptr);
|
||||
if(iter == _ref_counts_map->end()) {
|
||||
ref_count = ::new int(1);
|
||||
_ref_counts_map->insert({m_ptr, ref_count});
|
||||
} else {
|
||||
ref_count = iter->second;
|
||||
*ref_count += 1;
|
||||
}
|
||||
const handle& inc_ref() const {
|
||||
assert(m_ptr != nullptr);
|
||||
if(ref_count == nullptr) {
|
||||
auto iter = _ref_counts_map->find(m_ptr);
|
||||
if(iter == _ref_counts_map->end()) {
|
||||
ref_count = ::new int(1);
|
||||
_ref_counts_map->insert({m_ptr, ref_count});
|
||||
} else {
|
||||
ref_count = iter->second;
|
||||
*ref_count += 1;
|
||||
}
|
||||
return *this;
|
||||
} else {
|
||||
*ref_count += 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const handle& dec_ref() const {
|
||||
assert(m_ptr != nullptr);
|
||||
assert(ref_count != nullptr);
|
||||
const handle& dec_ref() const {
|
||||
assert(m_ptr != nullptr);
|
||||
assert(ref_count != nullptr);
|
||||
|
||||
*ref_count -= 1;
|
||||
try {
|
||||
if(*ref_count == 0) {
|
||||
_ref_counts_map->erase(m_ptr);
|
||||
::delete ref_count;
|
||||
ref_count = nullptr;
|
||||
}
|
||||
} catch(std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
|
||||
*ref_count -= 1;
|
||||
try {
|
||||
if(*ref_count == 0) {
|
||||
_ref_counts_map->erase(m_ptr);
|
||||
::delete ref_count;
|
||||
ref_count = nullptr;
|
||||
}
|
||||
} catch(std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
T cast() const;
|
||||
|
||||
explicit operator bool () const { return m_ptr.operator bool (); }
|
||||
|
||||
bool is(const handle& other) const { return m_ptr == other.m_ptr; }
|
||||
|
||||
bool is_none() const { return m_ptr == vm->None; }
|
||||
|
||||
bool in(const handle& other) const {
|
||||
return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr));
|
||||
}
|
||||
|
||||
bool contains(const handle& other) const {
|
||||
return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), m_ptr, other.m_ptr));
|
||||
}
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
str doc() const;
|
||||
|
||||
attr_accessor attr(const char* name) const;
|
||||
attr_accessor attr(const handle& name) const;
|
||||
attr_accessor attr(object&& name) const;
|
||||
|
||||
item_accessor operator[] (int64_t key) const;
|
||||
item_accessor operator[] (const char* key) const;
|
||||
item_accessor operator[] (const handle& key) const;
|
||||
item_accessor operator[] (object&& key) const;
|
||||
|
||||
object operator- () const;
|
||||
object operator~() const;
|
||||
|
||||
template <return_value_policy policy = return_value_policy::automatic, typename... Args>
|
||||
object operator() (Args&&... args) const;
|
||||
|
||||
private:
|
||||
friend object operator+ (const handle& lhs, const handle& rhs);
|
||||
friend object operator- (const handle& lhs, const handle& rhs);
|
||||
friend object operator* (const handle& lhs, const handle& rhs);
|
||||
friend object operator% (const handle& lhs, const handle& rhs);
|
||||
friend object operator/ (const handle& lhs, const handle& rhs);
|
||||
friend object operator| (const handle& lhs, const handle& rhs);
|
||||
friend object operator& (const handle& lhs, const handle& rhs);
|
||||
friend object operator^ (const handle& lhs, const handle& rhs);
|
||||
friend object operator<< (const handle& lhs, const handle& rhs);
|
||||
friend object operator>> (const handle& lhs, const handle& rhs);
|
||||
|
||||
friend object operator+= (const handle& lhs, const handle& rhs);
|
||||
friend object operator-= (const handle& lhs, const handle& rhs);
|
||||
friend object operator*= (const handle& lhs, const handle& rhs);
|
||||
friend object operator/= (const handle& lhs, const handle& rhs);
|
||||
friend object operator%= (const handle& lhs, const handle& rhs);
|
||||
friend object operator|= (const handle& lhs, const handle& rhs);
|
||||
friend object operator&= (const handle& lhs, const handle& rhs);
|
||||
friend object operator^= (const handle& lhs, const handle& rhs);
|
||||
friend object operator<<= (const handle& lhs, const handle& rhs);
|
||||
friend object operator>>= (const handle& lhs, const handle& rhs);
|
||||
|
||||
friend object operator== (const handle& lhs, const handle& rhs);
|
||||
friend object operator!= (const handle& lhs, const handle& rhs);
|
||||
friend object operator< (const handle& lhs, const handle& rhs);
|
||||
friend object operator> (const handle& lhs, const handle& rhs);
|
||||
friend object operator<= (const handle& lhs, const handle& rhs);
|
||||
friend object operator>= (const handle& lhs, const handle& rhs);
|
||||
|
||||
template <typename T>
|
||||
friend T& _builtin_cast(const handle& obj) {
|
||||
// FIXME: 2.0 does not use Py_<T> anymore
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
|
||||
return obj.ptr().obj_get<T>();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<handle>);
|
||||
|
||||
class object : public handle {
|
||||
public:
|
||||
object(const object& other) : handle(other) { inc_ref(); }
|
||||
|
||||
object(object&& other) noexcept : handle(other) {
|
||||
other.m_ptr = nullptr;
|
||||
other.ref_count = nullptr;
|
||||
}
|
||||
|
||||
object& operator= (const object& other) {
|
||||
if(this != &other) {
|
||||
dec_ref();
|
||||
m_ptr = other.m_ptr;
|
||||
ref_count = other.ref_count;
|
||||
inc_ref();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
T cast() const;
|
||||
|
||||
explicit operator bool () const { return m_ptr.operator bool (); }
|
||||
|
||||
bool is(const handle& other) const { return m_ptr == other.m_ptr; }
|
||||
|
||||
bool is_none() const { return m_ptr == vm->None; }
|
||||
|
||||
bool in(const handle& other) const {
|
||||
return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr));
|
||||
}
|
||||
|
||||
bool contains(const handle& other) const {
|
||||
return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), m_ptr, other.m_ptr));
|
||||
}
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
str doc() const;
|
||||
|
||||
attr_accessor attr(const char* name) const;
|
||||
attr_accessor attr(const handle& name) const;
|
||||
attr_accessor attr(object&& name) const;
|
||||
|
||||
item_accessor operator[] (int64_t key) const;
|
||||
item_accessor operator[] (const char* key) const;
|
||||
item_accessor operator[] (const handle& key) const;
|
||||
item_accessor operator[] (object&& key) const;
|
||||
|
||||
object operator- () const;
|
||||
object operator~() const;
|
||||
|
||||
template <return_value_policy policy = return_value_policy::automatic, typename... Args>
|
||||
object operator() (Args&&... args) const;
|
||||
|
||||
private:
|
||||
friend object operator+ (const handle& lhs, const handle& rhs);
|
||||
friend object operator- (const handle& lhs, const handle& rhs);
|
||||
friend object operator* (const handle& lhs, const handle& rhs);
|
||||
friend object operator% (const handle& lhs, const handle& rhs);
|
||||
friend object operator/ (const handle& lhs, const handle& rhs);
|
||||
friend object operator| (const handle& lhs, const handle& rhs);
|
||||
friend object operator& (const handle& lhs, const handle& rhs);
|
||||
friend object operator^ (const handle& lhs, const handle& rhs);
|
||||
friend object operator<< (const handle& lhs, const handle& rhs);
|
||||
friend object operator>> (const handle& lhs, const handle& rhs);
|
||||
|
||||
friend object operator+= (const handle& lhs, const handle& rhs);
|
||||
friend object operator-= (const handle& lhs, const handle& rhs);
|
||||
friend object operator*= (const handle& lhs, const handle& rhs);
|
||||
friend object operator/= (const handle& lhs, const handle& rhs);
|
||||
friend object operator%= (const handle& lhs, const handle& rhs);
|
||||
friend object operator|= (const handle& lhs, const handle& rhs);
|
||||
friend object operator&= (const handle& lhs, const handle& rhs);
|
||||
friend object operator^= (const handle& lhs, const handle& rhs);
|
||||
friend object operator<<= (const handle& lhs, const handle& rhs);
|
||||
friend object operator>>= (const handle& lhs, const handle& rhs);
|
||||
|
||||
friend object operator== (const handle& lhs, const handle& rhs);
|
||||
friend object operator!= (const handle& lhs, const handle& rhs);
|
||||
friend object operator< (const handle& lhs, const handle& rhs);
|
||||
friend object operator> (const handle& lhs, const handle& rhs);
|
||||
friend object operator<= (const handle& lhs, const handle& rhs);
|
||||
friend object operator>= (const handle& lhs, const handle& rhs);
|
||||
|
||||
template <typename T>
|
||||
friend T& _builtin_cast(const handle& obj) {
|
||||
// FIXME: 2.0 does not use Py_<T> anymore
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
|
||||
return obj.ptr().obj_get<T>();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<handle>);
|
||||
|
||||
class object : public handle {
|
||||
public:
|
||||
object(const object& other) : handle(other) { inc_ref(); }
|
||||
|
||||
object(object&& other) noexcept : handle(other) {
|
||||
object& operator= (object&& other) noexcept {
|
||||
if(this != &other) {
|
||||
dec_ref();
|
||||
m_ptr = other.m_ptr;
|
||||
ref_count = other.ref_count;
|
||||
other.m_ptr = nullptr;
|
||||
other.ref_count = nullptr;
|
||||
}
|
||||
|
||||
object& operator= (const object& other) {
|
||||
if(this != &other) {
|
||||
dec_ref();
|
||||
m_ptr = other.m_ptr;
|
||||
ref_count = other.ref_count;
|
||||
inc_ref();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
object& operator= (object&& other) noexcept {
|
||||
if(this != &other) {
|
||||
dec_ref();
|
||||
m_ptr = other.m_ptr;
|
||||
ref_count = other.ref_count;
|
||||
other.m_ptr = nullptr;
|
||||
other.ref_count = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~object() {
|
||||
if(m_ptr != nullptr) {
|
||||
dec_ref();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
object(const handle& h, bool borrow) : handle(h) {
|
||||
if(borrow) {
|
||||
inc_ref();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend T reinterpret_borrow(const handle& h) {
|
||||
return {h, true};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend T reinterpret_steal(const handle& h) {
|
||||
return {h, false};
|
||||
}
|
||||
};
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value);
|
||||
inline void setitem(const handle& obj, const handle& key, const handle& value);
|
||||
|
||||
#define PYBIND11_BINARY_OPERATOR(OP, NAME) \
|
||||
inline object operator OP (const handle& lhs, const handle& rhs) { \
|
||||
return reinterpret_borrow<object>(vm->call(vm->py_op(NAME), lhs.m_ptr, rhs.m_ptr)); \
|
||||
return *this;
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(+, "add");
|
||||
PYBIND11_BINARY_OPERATOR(-, "sub");
|
||||
PYBIND11_BINARY_OPERATOR(*, "mul");
|
||||
PYBIND11_BINARY_OPERATOR(/, "truediv");
|
||||
PYBIND11_BINARY_OPERATOR(%, "mod");
|
||||
PYBIND11_BINARY_OPERATOR(|, "or_");
|
||||
PYBIND11_BINARY_OPERATOR(&, "and_");
|
||||
PYBIND11_BINARY_OPERATOR(^, "xor");
|
||||
PYBIND11_BINARY_OPERATOR(<<, "lshift");
|
||||
PYBIND11_BINARY_OPERATOR(>>, "rshift");
|
||||
~object() {
|
||||
if(m_ptr != nullptr) { dec_ref(); }
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(+=, "iadd");
|
||||
PYBIND11_BINARY_OPERATOR(-=, "isub");
|
||||
PYBIND11_BINARY_OPERATOR(*=, "imul");
|
||||
PYBIND11_BINARY_OPERATOR(/=, "itruediv");
|
||||
PYBIND11_BINARY_OPERATOR(%=, "imod");
|
||||
PYBIND11_BINARY_OPERATOR(|=, "ior");
|
||||
PYBIND11_BINARY_OPERATOR(&=, "iand");
|
||||
PYBIND11_BINARY_OPERATOR(^=, "ixor");
|
||||
PYBIND11_BINARY_OPERATOR(<<=, "ilshift");
|
||||
PYBIND11_BINARY_OPERATOR(>>=, "irshift");
|
||||
protected:
|
||||
object(const handle& h, bool borrow) : handle(h) {
|
||||
if(borrow) { inc_ref(); }
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(==, "eq");
|
||||
PYBIND11_BINARY_OPERATOR(!=, "ne");
|
||||
PYBIND11_BINARY_OPERATOR(<, "lt");
|
||||
PYBIND11_BINARY_OPERATOR(>, "gt");
|
||||
PYBIND11_BINARY_OPERATOR(<=, "le");
|
||||
PYBIND11_BINARY_OPERATOR(>=, "ge");
|
||||
template <typename T>
|
||||
friend T reinterpret_borrow(const handle& h) {
|
||||
return {h, true};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
friend T reinterpret_steal(const handle& h) {
|
||||
return {h, false};
|
||||
}
|
||||
};
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value);
|
||||
inline void setitem(const handle& obj, const handle& key, const handle& value);
|
||||
|
||||
#define PYBIND11_BINARY_OPERATOR(OP, NAME) \
|
||||
inline object operator OP (const handle& lhs, const handle& rhs) { \
|
||||
return reinterpret_borrow<object>(vm->call(vm->py_op(NAME), lhs.m_ptr, rhs.m_ptr)); \
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(+, "add");
|
||||
PYBIND11_BINARY_OPERATOR(-, "sub");
|
||||
PYBIND11_BINARY_OPERATOR(*, "mul");
|
||||
PYBIND11_BINARY_OPERATOR(/, "truediv");
|
||||
PYBIND11_BINARY_OPERATOR(%, "mod");
|
||||
PYBIND11_BINARY_OPERATOR(|, "or_");
|
||||
PYBIND11_BINARY_OPERATOR(&, "and_");
|
||||
PYBIND11_BINARY_OPERATOR(^, "xor");
|
||||
PYBIND11_BINARY_OPERATOR(<<, "lshift");
|
||||
PYBIND11_BINARY_OPERATOR(>>, "rshift");
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(+=, "iadd");
|
||||
PYBIND11_BINARY_OPERATOR(-=, "isub");
|
||||
PYBIND11_BINARY_OPERATOR(*=, "imul");
|
||||
PYBIND11_BINARY_OPERATOR(/=, "itruediv");
|
||||
PYBIND11_BINARY_OPERATOR(%=, "imod");
|
||||
PYBIND11_BINARY_OPERATOR(|=, "ior");
|
||||
PYBIND11_BINARY_OPERATOR(&=, "iand");
|
||||
PYBIND11_BINARY_OPERATOR(^=, "ixor");
|
||||
PYBIND11_BINARY_OPERATOR(<<=, "ilshift");
|
||||
PYBIND11_BINARY_OPERATOR(>>=, "irshift");
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(==, "eq");
|
||||
PYBIND11_BINARY_OPERATOR(!=, "ne");
|
||||
PYBIND11_BINARY_OPERATOR(<, "lt");
|
||||
PYBIND11_BINARY_OPERATOR(>, "gt");
|
||||
PYBIND11_BINARY_OPERATOR(<=, "le");
|
||||
PYBIND11_BINARY_OPERATOR(>=, "ge");
|
||||
|
||||
#undef PYBIND11_BINARY_OPERATOR
|
||||
|
||||
|
||||
@ -4,154 +4,154 @@
|
||||
#include <type_traits>
|
||||
|
||||
namespace pybind11 {
|
||||
template <typename T>
|
||||
constexpr bool dependent_false = false;
|
||||
template <typename T>
|
||||
constexpr bool dependent_false = false;
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct tuple_push_front;
|
||||
template <typename T, typename Tuple>
|
||||
struct tuple_push_front;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
struct tuple_push_front<T, std::tuple<Ts...>> {
|
||||
using type = std::tuple<T, Ts...>;
|
||||
template <typename T, typename... Ts>
|
||||
struct tuple_push_front<T, std::tuple<Ts...>> {
|
||||
using type = std::tuple<T, Ts...>;
|
||||
};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
using tuple_push_front_t = typename tuple_push_front<T, Tuple>::type;
|
||||
|
||||
// traits for function types
|
||||
template <typename Fn>
|
||||
struct function_traits {
|
||||
static_assert(dependent_false<Fn>, "unsupported function type");
|
||||
};
|
||||
|
||||
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \
|
||||
template <typename R, typename... Args> \
|
||||
struct function_traits<R(Args...) qualifiers> { \
|
||||
using return_type = R; \
|
||||
using args_type = std::tuple<Args...>; \
|
||||
constexpr static std::size_t args_count = sizeof...(Args); \
|
||||
};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
using tuple_push_front_t = typename tuple_push_front<T, Tuple>::type;
|
||||
|
||||
// traits for function types
|
||||
template <typename Fn>
|
||||
struct function_traits {
|
||||
static_assert(dependent_false<Fn>, "unsupported function type");
|
||||
};
|
||||
|
||||
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \
|
||||
template <typename R, typename... Args> \
|
||||
struct function_traits<R(Args...) qualifiers> { \
|
||||
using return_type = R; \
|
||||
using args_type = std::tuple<Args...>; \
|
||||
constexpr static std::size_t args_count = sizeof...(Args); \
|
||||
};
|
||||
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE()
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(&)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const&)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(& noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const& noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE()
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(&)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const&)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(& noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const& noexcept)
|
||||
|
||||
#undef PYBIND11_FUNCTION_TRAITS_SPECIALIZE
|
||||
|
||||
template <typename T>
|
||||
using function_return_t = typename function_traits<T>::return_type;
|
||||
template <typename T>
|
||||
using function_return_t = typename function_traits<T>::return_type;
|
||||
|
||||
template <typename T>
|
||||
using function_args_t = typename function_traits<T>::args_type;
|
||||
template <typename T>
|
||||
using function_args_t = typename function_traits<T>::args_type;
|
||||
|
||||
template <typename T>
|
||||
constexpr std::size_t function_args_count = function_traits<T>::args_count;
|
||||
template <typename T>
|
||||
constexpr std::size_t function_args_count = function_traits<T>::args_count;
|
||||
|
||||
// traits for member pointers
|
||||
template <typename T>
|
||||
struct member_traits;
|
||||
// traits for member pointers
|
||||
template <typename T>
|
||||
struct member_traits;
|
||||
|
||||
template <typename M, typename C>
|
||||
struct member_traits<M C::*> {
|
||||
using member_type = M;
|
||||
using class_type = C;
|
||||
};
|
||||
template <typename M, typename C>
|
||||
struct member_traits<M C::*> {
|
||||
using member_type = M;
|
||||
using class_type = C;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using member_type_t = typename member_traits<T>::member_type;
|
||||
template <typename T>
|
||||
using member_type_t = typename member_traits<T>::member_type;
|
||||
|
||||
template <typename T>
|
||||
using class_type_t = typename member_traits<T>::class_type;
|
||||
template <typename T>
|
||||
using class_type_t = typename member_traits<T>::class_type;
|
||||
|
||||
// some traits for distinguishing between function pointers, member function pointers and
|
||||
// functors
|
||||
using std::is_member_function_pointer_v;
|
||||
using std::is_member_object_pointer_v;
|
||||
// some traits for distinguishing between function pointers, member function pointers and
|
||||
// functors
|
||||
using std::is_member_function_pointer_v;
|
||||
using std::is_member_object_pointer_v;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
|
||||
template <typename T>
|
||||
constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
|
||||
|
||||
template <typename T, typename U = void>
|
||||
constexpr bool is_functor_v = false;
|
||||
template <typename T, typename U = void>
|
||||
constexpr bool is_functor_v = false;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
|
||||
template <typename T>
|
||||
constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct callable_traits;
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct callable_traits;
|
||||
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
|
||||
using args_type = tuple_push_front_t<class_type_t<T>&, function_args_t<member_type_t<T>>>;
|
||||
using return_type = function_return_t<member_type_t<T>>;
|
||||
};
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
|
||||
using args_type = tuple_push_front_t<class_type_t<T>&, function_args_t<member_type_t<T>>>;
|
||||
using return_type = function_return_t<member_type_t<T>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
|
||||
using args_type = function_args_t<std::remove_pointer<T>>;
|
||||
using return_type = function_return_t<std::remove_pointer<T>>;
|
||||
};
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
|
||||
using args_type = function_args_t<std::remove_pointer<T>>;
|
||||
using return_type = function_return_t<std::remove_pointer<T>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_functor_v<T>>> {
|
||||
using args_type = function_args_t<member_type_t<decltype(&T::operator())>>;
|
||||
using return_type = function_return_t<member_type_t<decltype(&T::operator())>>;
|
||||
};
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_functor_v<T>>> {
|
||||
using args_type = function_args_t<member_type_t<decltype(&T::operator())>>;
|
||||
using return_type = function_return_t<member_type_t<decltype(&T::operator())>>;
|
||||
};
|
||||
|
||||
template <typename Callable>
|
||||
using callable_args_t = typename callable_traits<Callable>::args_type;
|
||||
template <typename Callable>
|
||||
using callable_args_t = typename callable_traits<Callable>::args_type;
|
||||
|
||||
template <typename Callable>
|
||||
using callable_return_t = typename callable_traits<Callable>::return_type;
|
||||
template <typename Callable>
|
||||
using callable_return_t = typename callable_traits<Callable>::return_type;
|
||||
|
||||
template <typename Callable>
|
||||
constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
|
||||
template <typename Callable>
|
||||
constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
|
||||
|
||||
template <typename T>
|
||||
struct type_identity {
|
||||
using type = T;
|
||||
};
|
||||
template <typename T>
|
||||
struct type_identity {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
|
||||
template <typename T, typename... Ts>
|
||||
constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
|
||||
|
||||
template <typename T>
|
||||
constexpr inline std::size_t types_count_v<T> = 0;
|
||||
template <typename T>
|
||||
constexpr inline std::size_t types_count_v<T> = 0;
|
||||
|
||||
template <typename T>
|
||||
struct value_wrapper {
|
||||
T* pointer;
|
||||
template <typename T>
|
||||
struct value_wrapper {
|
||||
T* pointer;
|
||||
|
||||
operator T& () { return *pointer; }
|
||||
};
|
||||
operator T& () { return *pointer; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct value_wrapper<T*> {
|
||||
T* pointer;
|
||||
template <typename T>
|
||||
struct value_wrapper<T*> {
|
||||
T* pointer;
|
||||
|
||||
operator T* () { return pointer; }
|
||||
};
|
||||
operator T* () { return pointer; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct value_wrapper<T&> {
|
||||
T* pointer;
|
||||
template <typename T>
|
||||
struct value_wrapper<T&> {
|
||||
T* pointer;
|
||||
|
||||
operator T& () { return *pointer; }
|
||||
};
|
||||
operator T& () { return *pointer; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct value_wrapper<T&&> {
|
||||
T* pointer;
|
||||
template <typename T>
|
||||
struct value_wrapper<T&&> {
|
||||
T* pointer;
|
||||
|
||||
operator T&& () { return std::move(*pointer); }
|
||||
};
|
||||
operator T&& () { return std::move(*pointer); }
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
||||
|
||||
@ -3,216 +3,206 @@
|
||||
#include "object.h"
|
||||
|
||||
namespace pybind11 {
|
||||
class type : public object {
|
||||
public:
|
||||
using object::object;
|
||||
template <typename T>
|
||||
static handle handle_of();
|
||||
};
|
||||
class type : public object {
|
||||
public:
|
||||
using object::object;
|
||||
template <typename T>
|
||||
static handle handle_of();
|
||||
};
|
||||
|
||||
class iterable : public object {
|
||||
public:
|
||||
using object::object;
|
||||
iterable() = delete;
|
||||
};
|
||||
class iterable : public object {
|
||||
public:
|
||||
using object::object;
|
||||
iterable() = delete;
|
||||
};
|
||||
|
||||
class iterator : public object {
|
||||
public:
|
||||
using object::object;
|
||||
iterator() = delete;
|
||||
};
|
||||
class iterator : public object {
|
||||
public:
|
||||
using object::object;
|
||||
iterator() = delete;
|
||||
};
|
||||
|
||||
class list : public object {
|
||||
public:
|
||||
using object::object;
|
||||
class list : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
list() : object(vm->new_object<pkpy::List>(pkpy::VM::tp_list), true) {}
|
||||
};
|
||||
list() : object(vm->new_object<pkpy::List>(pkpy::VM::tp_list), true) {}
|
||||
};
|
||||
|
||||
class tuple : public object {
|
||||
public:
|
||||
using object::object;
|
||||
class tuple : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
tuple(int n) : object(vm->new_object<pkpy::Tuple>(pkpy::VM::tp_tuple, n), true) {}
|
||||
tuple(int n) : object(vm->new_object<pkpy::Tuple>(pkpy::VM::tp_tuple, n), true) {}
|
||||
|
||||
//& operator[](int i){ return _args[i]; }
|
||||
// PyVar operator[](int i) const { return _args[i]; }
|
||||
};
|
||||
//& operator[](int i){ return _args[i]; }
|
||||
// PyVar operator[](int i) const { return _args[i]; }
|
||||
};
|
||||
|
||||
class set : public object {
|
||||
public:
|
||||
using object::object;
|
||||
// set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
|
||||
};
|
||||
class set : public object {
|
||||
public:
|
||||
using object::object;
|
||||
// set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
|
||||
};
|
||||
|
||||
class dict : public object {
|
||||
public:
|
||||
using object::object;
|
||||
class dict : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
dict() : object(vm->new_object<pkpy::Dict>(pkpy::VM::tp_dict), true) {}
|
||||
};
|
||||
dict() : object(vm->new_object<pkpy::Dict>(pkpy::VM::tp_dict), true) {}
|
||||
};
|
||||
|
||||
class str : public object {
|
||||
class str : public object {
|
||||
|
||||
public:
|
||||
using object::object;
|
||||
str(const char* c, int len) :
|
||||
object(vm->new_object<pkpy::Str>(pkpy::VM::tp_str, c, len), true) {
|
||||
public:
|
||||
using object::object;
|
||||
str(const char* c, int len) :
|
||||
object(vm->new_object<pkpy::Str>(pkpy::VM::tp_str, c, len), true) {
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
str(const char* c = "") : str(c, strlen(c)) {}
|
||||
str(const char* c = "") : str(c, strlen(c)) {}
|
||||
|
||||
str(const std::string& s) : str(s.data(), s.size()) {}
|
||||
str(const std::string& s) : str(s.data(), s.size()) {}
|
||||
|
||||
str(std::string_view sv) : str(sv.data(), sv.size()) {}
|
||||
str(std::string_view sv) : str(sv.data(), sv.size()) {}
|
||||
|
||||
explicit str(const bytes& b);
|
||||
explicit str(handle h);
|
||||
operator std::string () const;
|
||||
explicit str(const bytes& b);
|
||||
explicit str(handle h);
|
||||
operator std::string () const;
|
||||
|
||||
template <typename... Args>
|
||||
str format(Args&&... args) const;
|
||||
};
|
||||
template <typename... Args>
|
||||
str format(Args&&... args) const;
|
||||
};
|
||||
|
||||
class int_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
class int_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
int_(int64_t value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
int_(int64_t value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
|
||||
class float_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
class float_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
float_(double value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
float_(double value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
|
||||
class bool_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
class bool_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
bool_(bool value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
bool_(bool value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
|
||||
class function : public object {
|
||||
public:
|
||||
using object::object;
|
||||
};
|
||||
class function : public object {
|
||||
public:
|
||||
using object::object;
|
||||
};
|
||||
|
||||
class attr_accessor : public object {
|
||||
private:
|
||||
object key;
|
||||
class attr_accessor : public object {
|
||||
private:
|
||||
object key;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
|
||||
|
||||
template <typename T>
|
||||
attr_accessor& operator= (T&& value) & {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>,
|
||||
"T must be derived from object");
|
||||
m_ptr = std::forward<T>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
attr_accessor& operator= (T&& value) && {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>,
|
||||
"T must be derived from object");
|
||||
setattr(*this, key, std::forward<T>(value));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
inline attr_accessor handle::attr(const char* name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), str(name));
|
||||
}
|
||||
|
||||
inline attr_accessor handle::attr(const handle& name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(name));
|
||||
}
|
||||
|
||||
inline attr_accessor handle::attr(object&& name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), std::move(name));
|
||||
}
|
||||
|
||||
class item_accessor : public object {
|
||||
public:
|
||||
object key;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
item_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
|
||||
|
||||
template <typename T>
|
||||
item_accessor& operator= (T&& value) & {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>,
|
||||
"T must be derived from object");
|
||||
m_ptr = std::forward<T>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
item_accessor& operator= (object&& value) && {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>,
|
||||
"T must be derived from object");
|
||||
setitem(*this, key, std::forward<T>(value));
|
||||
}
|
||||
};
|
||||
|
||||
inline item_accessor handle::operator[] (int64_t key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), int_(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (const char* key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), str(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (const handle& key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (object&& key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), std::move(key));
|
||||
}
|
||||
|
||||
class args : public tuple {
|
||||
using tuple::tuple;
|
||||
};
|
||||
|
||||
class kwargs : public dict {
|
||||
using dict::dict;
|
||||
};
|
||||
public:
|
||||
template <typename T>
|
||||
attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
|
||||
|
||||
template <typename T>
|
||||
handle type::handle_of() {
|
||||
if constexpr(std::is_same_v<T, object>) {
|
||||
return vm->_t(vm->tp_object);
|
||||
}
|
||||
#define PYBIND11_TYPE_MAPPER(type, tp) \
|
||||
else if constexpr(std::is_same_v<T, type>) { \
|
||||
return vm->_t(vm->tp); \
|
||||
attr_accessor& operator= (T&& value) & {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
||||
m_ptr = std::forward<T>(value);
|
||||
return *this;
|
||||
}
|
||||
PYBIND11_TYPE_MAPPER(type, tp_type)
|
||||
PYBIND11_TYPE_MAPPER(str, tp_str)
|
||||
PYBIND11_TYPE_MAPPER(int_, tp_int)
|
||||
PYBIND11_TYPE_MAPPER(float_, tp_float)
|
||||
PYBIND11_TYPE_MAPPER(bool_, tp_bool)
|
||||
PYBIND11_TYPE_MAPPER(list, tp_list)
|
||||
PYBIND11_TYPE_MAPPER(tuple, tp_tuple)
|
||||
PYBIND11_TYPE_MAPPER(args, tp_tuple)
|
||||
PYBIND11_TYPE_MAPPER(dict, tp_dict)
|
||||
PYBIND11_TYPE_MAPPER(kwargs, tp_dict)
|
||||
#undef PYBIND11_TYPE_MAPPER
|
||||
else {
|
||||
auto result = vm->_cxx_typeid_map.find(typeid(T));
|
||||
if(result != vm->_cxx_typeid_map.end()) {
|
||||
return vm->_t(result->second);
|
||||
}
|
||||
|
||||
vm->TypeError("Type not registered");
|
||||
}
|
||||
template <typename T>
|
||||
attr_accessor& operator= (T&& value) && {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
||||
setattr(*this, key, std::forward<T>(value));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
inline attr_accessor handle::attr(const char* name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), str(name));
|
||||
}
|
||||
|
||||
inline attr_accessor handle::attr(const handle& name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(name));
|
||||
}
|
||||
|
||||
inline attr_accessor handle::attr(object&& name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), std::move(name));
|
||||
}
|
||||
|
||||
class item_accessor : public object {
|
||||
public:
|
||||
object key;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
item_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
|
||||
|
||||
template <typename T>
|
||||
item_accessor& operator= (T&& value) & {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
||||
m_ptr = std::forward<T>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
item_accessor& operator= (object&& value) && {
|
||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
||||
setitem(*this, key, std::forward<T>(value));
|
||||
}
|
||||
};
|
||||
|
||||
inline item_accessor handle::operator[] (int64_t key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), int_(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (const char* key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), str(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (const handle& key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (object&& key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), std::move(key));
|
||||
}
|
||||
|
||||
class args : public tuple {
|
||||
using tuple::tuple;
|
||||
};
|
||||
|
||||
class kwargs : public dict {
|
||||
using dict::dict;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
handle type::handle_of() {
|
||||
if constexpr(std::is_same_v<T, object>) { return vm->_t(vm->tp_object); }
|
||||
#define PYBIND11_TYPE_MAPPER(type, tp) \
|
||||
else if constexpr(std::is_same_v<T, type>) { return vm->_t(vm->tp); }
|
||||
PYBIND11_TYPE_MAPPER(type, tp_type)
|
||||
PYBIND11_TYPE_MAPPER(str, tp_str)
|
||||
PYBIND11_TYPE_MAPPER(int_, tp_int)
|
||||
PYBIND11_TYPE_MAPPER(float_, tp_float)
|
||||
PYBIND11_TYPE_MAPPER(bool_, tp_bool)
|
||||
PYBIND11_TYPE_MAPPER(list, tp_list)
|
||||
PYBIND11_TYPE_MAPPER(tuple, tp_tuple)
|
||||
PYBIND11_TYPE_MAPPER(args, tp_tuple)
|
||||
PYBIND11_TYPE_MAPPER(dict, tp_dict)
|
||||
PYBIND11_TYPE_MAPPER(kwargs, tp_dict)
|
||||
#undef PYBIND11_TYPE_MAPPER
|
||||
else {
|
||||
auto result = vm->_cxx_typeid_map.find(typeid(T));
|
||||
if(result != vm->_cxx_typeid_map.end()) { return vm->_t(result->second); }
|
||||
|
||||
vm->TypeError("Type not registered");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pybind11
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "internal/class.h"
|
||||
#include "internal/class.h"
|
||||
|
||||
@ -16,7 +16,7 @@ def get_all_files(root: str):
|
||||
|
||||
if __name__ == '__main__':
|
||||
files = []
|
||||
# files.extend(get_all_files('include'))
|
||||
# files.extend(get_all_files('src'))
|
||||
files.extend(get_all_files('include'))
|
||||
files.extend(get_all_files('src'))
|
||||
files.extend(get_all_files('src2'))
|
||||
subprocess.run(['clang-format', '-i'] + files, check=True)
|
||||
|
||||
@ -3,20 +3,20 @@
|
||||
#include <stdexcept>
|
||||
#include <cstdio>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
void any::__bad_any_cast(const std::type_index expected, const std::type_index actual){
|
||||
void any::__bad_any_cast(const std::type_index expected, const std::type_index actual) {
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "bad_any_cast: expected %s, got %s", expected.name(), actual.name());
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
any::any(any&& other) noexcept: data(other.data), _vt(other._vt){
|
||||
any::any(any&& other) noexcept : data(other.data), _vt(other._vt) {
|
||||
other.data = nullptr;
|
||||
other._vt = nullptr;
|
||||
}
|
||||
|
||||
any& any::operator=(any&& other) noexcept{
|
||||
any& any::operator= (any&& other) noexcept {
|
||||
if(data) _vt->deleter(data);
|
||||
data = other.data;
|
||||
_vt = other._vt;
|
||||
@ -25,4 +25,4 @@ any& any::operator=(any&& other) noexcept{
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -6,28 +6,28 @@
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct LinkedListNode{
|
||||
struct LinkedListNode {
|
||||
LinkedListNode* prev;
|
||||
LinkedListNode* next;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct DoubleLinkedList{
|
||||
template <typename T>
|
||||
struct DoubleLinkedList {
|
||||
static_assert(std::is_base_of_v<LinkedListNode, T>);
|
||||
int _size;
|
||||
LinkedListNode head;
|
||||
LinkedListNode tail;
|
||||
|
||||
DoubleLinkedList(): _size(0){
|
||||
|
||||
DoubleLinkedList() : _size(0) {
|
||||
head.prev = nullptr;
|
||||
head.next = &tail;
|
||||
tail.prev = &head;
|
||||
tail.next = nullptr;
|
||||
}
|
||||
|
||||
void push_back(T* node){
|
||||
void push_back(T* node) {
|
||||
node->prev = tail.prev;
|
||||
node->next = &tail;
|
||||
tail.prev->next = node;
|
||||
@ -35,7 +35,7 @@ struct DoubleLinkedList{
|
||||
_size++;
|
||||
}
|
||||
|
||||
void push_front(T* node){
|
||||
void push_front(T* node) {
|
||||
node->prev = &head;
|
||||
node->next = head.next;
|
||||
head.next->prev = node;
|
||||
@ -43,14 +43,14 @@ struct DoubleLinkedList{
|
||||
_size++;
|
||||
}
|
||||
|
||||
void pop_back(){
|
||||
void pop_back() {
|
||||
assert(!empty());
|
||||
tail.prev->prev->next = &tail;
|
||||
tail.prev = tail.prev->prev;
|
||||
_size--;
|
||||
}
|
||||
|
||||
void pop_front(){
|
||||
void pop_front() {
|
||||
assert(!empty());
|
||||
head.next->next->prev = &head;
|
||||
head.next = head.next->next;
|
||||
@ -67,22 +67,20 @@ struct DoubleLinkedList{
|
||||
return static_cast<T*>(head.next);
|
||||
}
|
||||
|
||||
void erase(T* node){
|
||||
void erase(T* node) {
|
||||
node->prev->next = node->next;
|
||||
node->next->prev = node->prev;
|
||||
_size--;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
bool empty() const { return _size == 0; }
|
||||
|
||||
int size() const { return _size; }
|
||||
|
||||
template<typename Func>
|
||||
void apply(Func func){
|
||||
template <typename Func>
|
||||
void apply(Func func) {
|
||||
LinkedListNode* p = head.next;
|
||||
while(p != &tail){
|
||||
while(p != &tail) {
|
||||
LinkedListNode* next = p->next;
|
||||
func(static_cast<T*>(p));
|
||||
p = next;
|
||||
@ -90,42 +88,41 @@ struct DoubleLinkedList{
|
||||
}
|
||||
};
|
||||
|
||||
template<int __BlockSize>
|
||||
struct MemoryPool{
|
||||
static const int __MaxBlocks = 256*1024 / __BlockSize;
|
||||
static const int __MinArenaCount = PK_GC_MIN_THRESHOLD*100 / (256*1024);
|
||||
template <int __BlockSize>
|
||||
struct MemoryPool {
|
||||
const static int __MaxBlocks = 256 * 1024 / __BlockSize;
|
||||
const static int __MinArenaCount = PK_GC_MIN_THRESHOLD * 100 / (256 * 1024);
|
||||
|
||||
struct Block{
|
||||
struct Block {
|
||||
void* arena;
|
||||
char data[__BlockSize];
|
||||
};
|
||||
|
||||
struct Arena: LinkedListNode{
|
||||
struct Arena : LinkedListNode {
|
||||
Block _blocks[__MaxBlocks];
|
||||
Block* _free_list[__MaxBlocks];
|
||||
int _free_list_size;
|
||||
|
||||
Arena(): _free_list_size(__MaxBlocks) {
|
||||
for(int i=0; i<__MaxBlocks; i++){
|
||||
|
||||
Arena() : _free_list_size(__MaxBlocks) {
|
||||
for(int i = 0; i < __MaxBlocks; i++) {
|
||||
_blocks[i].arena = this;
|
||||
_free_list[i] = &_blocks[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const { return _free_list_size == 0; }
|
||||
|
||||
bool full() const { return _free_list_size == __MaxBlocks; }
|
||||
|
||||
size_t allocated_size() const{
|
||||
return __BlockSize * (__MaxBlocks - _free_list_size);
|
||||
}
|
||||
size_t allocated_size() const { return __BlockSize * (__MaxBlocks - _free_list_size); }
|
||||
|
||||
Block* alloc(){
|
||||
Block* alloc() {
|
||||
assert(!empty());
|
||||
_free_list_size--;
|
||||
return _free_list[_free_list_size];
|
||||
}
|
||||
|
||||
void dealloc(Block* block){
|
||||
void dealloc(Block* block) {
|
||||
assert(!full());
|
||||
_free_list[_free_list_size] = block;
|
||||
_free_list_size++;
|
||||
@ -134,71 +131,73 @@ struct MemoryPool{
|
||||
|
||||
MemoryPool() = default;
|
||||
MemoryPool(const MemoryPool&) = delete;
|
||||
MemoryPool& operator=(const MemoryPool&) = delete;
|
||||
MemoryPool& operator= (const MemoryPool&) = delete;
|
||||
MemoryPool(MemoryPool&&) = delete;
|
||||
MemoryPool& operator=(MemoryPool&&) = delete;
|
||||
MemoryPool& operator= (MemoryPool&&) = delete;
|
||||
|
||||
DoubleLinkedList<Arena> _arenas;
|
||||
DoubleLinkedList<Arena> _empty_arenas;
|
||||
|
||||
void* alloc(size_t size){
|
||||
void* alloc(size_t size) {
|
||||
PK_GLOBAL_SCOPE_LOCK();
|
||||
if(size > __BlockSize){
|
||||
if(size > __BlockSize) {
|
||||
void* p = std::malloc(sizeof(void*) + size);
|
||||
std::memset(p, 0, sizeof(void*));
|
||||
return (char*)p + sizeof(void*);
|
||||
}
|
||||
|
||||
if(_arenas.empty()){
|
||||
_arenas.push_back(new Arena());
|
||||
}
|
||||
if(_arenas.empty()) { _arenas.push_back(new Arena()); }
|
||||
Arena* arena = _arenas.back();
|
||||
void* p = arena->alloc()->data;
|
||||
if(arena->empty()){
|
||||
if(arena->empty()) {
|
||||
_arenas.pop_back();
|
||||
_empty_arenas.push_back(arena);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void dealloc(void* p){
|
||||
void dealloc(void* p) {
|
||||
PK_GLOBAL_SCOPE_LOCK();
|
||||
assert(p != nullptr);
|
||||
Block* block = (Block*)((char*)p - sizeof(void*));
|
||||
if(block->arena == nullptr){
|
||||
if(block->arena == nullptr) {
|
||||
std::free(block);
|
||||
}else{
|
||||
} else {
|
||||
Arena* arena = (Arena*)block->arena;
|
||||
if(arena->empty()){
|
||||
if(arena->empty()) {
|
||||
_empty_arenas.erase(arena);
|
||||
_arenas.push_front(arena);
|
||||
arena->dealloc(block);
|
||||
}else{
|
||||
} else {
|
||||
arena->dealloc(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shrink_to_fit(){
|
||||
void shrink_to_fit() {
|
||||
PK_GLOBAL_SCOPE_LOCK();
|
||||
if(_arenas.size() < __MinArenaCount) return;
|
||||
_arenas.apply([this](Arena* arena){
|
||||
if(arena->full()){
|
||||
_arenas.apply([this](Arena* arena) {
|
||||
if(arena->full()) {
|
||||
_arenas.erase(arena);
|
||||
delete arena;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~MemoryPool(){
|
||||
_arenas.apply([](Arena* arena){ delete arena; });
|
||||
_empty_arenas.apply([](Arena* arena){ delete arena; });
|
||||
~MemoryPool() {
|
||||
_arenas.apply([](Arena* arena) {
|
||||
delete arena;
|
||||
});
|
||||
_empty_arenas.apply([](Arena* arena) {
|
||||
delete arena;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template<int BlockSize, int BlockCount>
|
||||
struct FixedMemoryPool{
|
||||
struct Block{
|
||||
template <int BlockSize, int BlockCount>
|
||||
struct FixedMemoryPool {
|
||||
struct Block {
|
||||
char data[BlockSize];
|
||||
};
|
||||
|
||||
@ -211,31 +210,29 @@ struct FixedMemoryPool{
|
||||
|
||||
FixedMemoryPool() {
|
||||
_free_list_end = _free_list + BlockCount;
|
||||
for(int i = 0; i < BlockCount; ++i){
|
||||
for(int i = 0; i < BlockCount; ++i) {
|
||||
_free_list[i] = _blocks + i;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid(void* p){
|
||||
return p >= _blocks && p < _blocks + BlockCount;
|
||||
}
|
||||
bool is_valid(void* p) { return p >= _blocks && p < _blocks + BlockCount; }
|
||||
|
||||
void* alloc(){
|
||||
void* alloc() {
|
||||
PK_GLOBAL_SCOPE_LOCK()
|
||||
if(_free_list_end != _free_list){
|
||||
if(_free_list_end != _free_list) {
|
||||
--_free_list_end;
|
||||
return *_free_list_end;
|
||||
}else{
|
||||
} else {
|
||||
return std::malloc(BlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
void dealloc(void* p){
|
||||
void dealloc(void* p) {
|
||||
PK_GLOBAL_SCOPE_LOCK()
|
||||
if(is_valid(p)){
|
||||
if(is_valid(p)) {
|
||||
*_free_list_end = static_cast<Block*>(p);
|
||||
++_free_list_end;
|
||||
}else{
|
||||
} else {
|
||||
std::free(p);
|
||||
}
|
||||
}
|
||||
@ -246,12 +243,17 @@ static FixedMemoryPool<kPoolFrameBlockSize, 128> PoolFrame;
|
||||
static MemoryPool<80> PoolObject;
|
||||
|
||||
void* PoolExpr_alloc() noexcept { return PoolExpr.alloc(); }
|
||||
|
||||
void PoolExpr_dealloc(void* p) noexcept { PoolExpr.dealloc(p); }
|
||||
|
||||
void* PoolFrame_alloc() noexcept { return PoolFrame.alloc(); }
|
||||
|
||||
void PoolFrame_dealloc(void* p) noexcept { PoolFrame.dealloc(p); }
|
||||
|
||||
void* PoolObject_alloc(size_t size) noexcept { return PoolObject.alloc(size); }
|
||||
|
||||
void PoolObject_dealloc(void* p) noexcept { PoolObject.dealloc(p); }
|
||||
|
||||
void PoolObject_shrink_to_fit() noexcept { PoolObject.shrink_to_fit(); }
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
int utf8len(unsigned char c, bool suppress){
|
||||
int utf8len(unsigned char c, bool suppress) {
|
||||
if((c & 0b10000000) == 0) return 1;
|
||||
if((c & 0b11100000) == 0b11000000) return 2;
|
||||
if((c & 0b11110000) == 0b11100000) return 3;
|
||||
@ -19,531 +19,499 @@ int utf8len(unsigned char c, bool suppress){
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PK_STR_ALLOCATE() \
|
||||
if(this->size < (int)sizeof(this->_inlined)){ \
|
||||
this->data = this->_inlined; \
|
||||
}else{ \
|
||||
this->data = (char*)std::malloc(this->size+1); \
|
||||
#define PK_STR_ALLOCATE() \
|
||||
if(this->size < (int)sizeof(this->_inlined)) { \
|
||||
this->data = this->_inlined; \
|
||||
} else { \
|
||||
this->data = (char*)std::malloc(this->size + 1); \
|
||||
}
|
||||
|
||||
#define PK_STR_COPY_INIT(__s) \
|
||||
for(int i = 0; i < this->size; i++) { \
|
||||
this->data[i] = __s[i]; \
|
||||
if(!isascii(__s[i])) is_ascii = false; \
|
||||
} \
|
||||
this->data[this->size] = '\0';
|
||||
|
||||
Str::Str() : size(0), is_ascii(true), data(_inlined) { _inlined[0] = '\0'; }
|
||||
|
||||
Str::Str(int size, bool is_ascii) :
|
||||
size(size), is_ascii(is_ascii){PK_STR_ALLOCATE()}
|
||||
|
||||
Str::Str(const std::string& s) :
|
||||
size(s.size()), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(std::string_view s) :
|
||||
size(s.size()), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(const char* s) :
|
||||
size(strlen(s)), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(const char* s, int len) :
|
||||
size(len), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
|
||||
|
||||
Str::Str(std::pair<char*, int> detached) : size(detached.second), is_ascii(true) {
|
||||
this->data = detached.first;
|
||||
for(int i = 0; i < size; i++) {
|
||||
if(!isascii(data[i])) {
|
||||
is_ascii = false;
|
||||
break;
|
||||
}
|
||||
|
||||
#define PK_STR_COPY_INIT(__s) \
|
||||
for(int i=0; i<this->size; i++){ \
|
||||
this->data[i] = __s[i]; \
|
||||
if(!isascii(__s[i])) is_ascii = false; \
|
||||
} \
|
||||
this->data[this->size] = '\0';
|
||||
|
||||
Str::Str(): size(0), is_ascii(true), data(_inlined) {
|
||||
_inlined[0] = '\0';
|
||||
}
|
||||
assert(data[size] == '\0');
|
||||
}
|
||||
|
||||
Str::Str(int size, bool is_ascii): size(size), is_ascii(is_ascii) {
|
||||
PK_STR_ALLOCATE()
|
||||
}
|
||||
Str::Str(const Str& other) : size(other.size), is_ascii(other.is_ascii) {
|
||||
PK_STR_ALLOCATE()
|
||||
std::memcpy(data, other.data, size);
|
||||
data[size] = '\0';
|
||||
}
|
||||
|
||||
Str::Str(const std::string& s): size(s.size()), is_ascii(true) {
|
||||
PK_STR_ALLOCATE()
|
||||
PK_STR_COPY_INIT(s)
|
||||
}
|
||||
|
||||
Str::Str(std::string_view s): size(s.size()), is_ascii(true) {
|
||||
PK_STR_ALLOCATE()
|
||||
PK_STR_COPY_INIT(s)
|
||||
}
|
||||
|
||||
Str::Str(const char* s): size(strlen(s)), is_ascii(true) {
|
||||
PK_STR_ALLOCATE()
|
||||
PK_STR_COPY_INIT(s)
|
||||
}
|
||||
|
||||
Str::Str(const char* s, int len): size(len), is_ascii(true) {
|
||||
PK_STR_ALLOCATE()
|
||||
PK_STR_COPY_INIT(s)
|
||||
}
|
||||
|
||||
Str::Str(std::pair<char *, int> detached): size(detached.second), is_ascii(true) {
|
||||
this->data = detached.first;
|
||||
for(int i=0; i<size; i++){
|
||||
if(!isascii(data[i])){ is_ascii = false; break; }
|
||||
}
|
||||
assert(data[size] == '\0');
|
||||
}
|
||||
|
||||
Str::Str(const Str& other): size(other.size), is_ascii(other.is_ascii) {
|
||||
PK_STR_ALLOCATE()
|
||||
std::memcpy(data, other.data, size);
|
||||
Str::Str(Str&& other) : size(other.size), is_ascii(other.is_ascii) {
|
||||
if(other.is_inlined()) {
|
||||
data = _inlined;
|
||||
for(int i = 0; i < size; i++)
|
||||
_inlined[i] = other._inlined[i];
|
||||
data[size] = '\0';
|
||||
} else {
|
||||
data = other.data;
|
||||
// zero out `other`
|
||||
other.data = other._inlined;
|
||||
other.data[0] = '\0';
|
||||
other.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Str::Str(Str&& other): size(other.size), is_ascii(other.is_ascii) {
|
||||
if(other.is_inlined()){
|
||||
data = _inlined;
|
||||
for(int i=0; i<size; i++) _inlined[i] = other._inlined[i];
|
||||
data[size] = '\0';
|
||||
}else{
|
||||
data = other.data;
|
||||
// zero out `other`
|
||||
other.data = other._inlined;
|
||||
other.data[0] = '\0';
|
||||
other.size = 0;
|
||||
}
|
||||
Str operator+ (const char* p, const Str& str) {
|
||||
Str other(p);
|
||||
return other + str;
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const Str& str) { return os << str.sv(); }
|
||||
|
||||
bool operator< (const std::string_view other, const Str& str) { return other < str.sv(); }
|
||||
|
||||
Str& Str::operator= (const Str& other) {
|
||||
if(!is_inlined()) std::free(data);
|
||||
size = other.size;
|
||||
is_ascii = other.is_ascii;
|
||||
PK_STR_ALLOCATE()
|
||||
std::memcpy(data, other.data, size);
|
||||
data[size] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str Str::operator+ (const Str& other) const {
|
||||
Str ret(size + other.size, is_ascii && other.is_ascii);
|
||||
std::memcpy(ret.data, data, size);
|
||||
std::memcpy(ret.data + size, other.data, other.size);
|
||||
ret.data[ret.size] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
Str Str::operator+ (const char* p) const {
|
||||
Str other(p);
|
||||
return *this + other;
|
||||
}
|
||||
|
||||
bool Str::operator== (const Str& other) const {
|
||||
if(size != other.size) return false;
|
||||
return memcmp(data, other.data, size) == 0;
|
||||
}
|
||||
|
||||
bool Str::operator!= (const Str& other) const {
|
||||
if(size != other.size) return true;
|
||||
return memcmp(data, other.data, size) != 0;
|
||||
}
|
||||
|
||||
bool Str::operator== (const std::string_view other) const {
|
||||
if(size != (int)other.size()) return false;
|
||||
return memcmp(data, other.data(), size) == 0;
|
||||
}
|
||||
|
||||
bool Str::operator!= (const std::string_view other) const {
|
||||
if(size != (int)other.size()) return true;
|
||||
return memcmp(data, other.data(), size) != 0;
|
||||
}
|
||||
|
||||
bool Str::operator== (const char* p) const { return *this == std::string_view(p); }
|
||||
|
||||
bool Str::operator!= (const char* p) const { return *this != std::string_view(p); }
|
||||
|
||||
bool Str::operator< (const Str& other) const { return this->sv() < other.sv(); }
|
||||
|
||||
bool Str::operator< (const std::string_view other) const { return this->sv() < other; }
|
||||
|
||||
bool Str::operator> (const Str& other) const { return this->sv() > other.sv(); }
|
||||
|
||||
bool Str::operator<= (const Str& other) const { return this->sv() <= other.sv(); }
|
||||
|
||||
bool Str::operator>= (const Str& other) const { return this->sv() >= other.sv(); }
|
||||
|
||||
Str::~Str() {
|
||||
if(!is_inlined()) std::free(data);
|
||||
}
|
||||
|
||||
Str Str::substr(int start, int len) const {
|
||||
Str ret(len, is_ascii);
|
||||
std::memcpy(ret.data, data + start, len);
|
||||
ret.data[len] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
Str Str::substr(int start) const { return substr(start, size - start); }
|
||||
|
||||
Str Str::strip(bool left, bool right, const Str& chars) const {
|
||||
int L = 0;
|
||||
int R = u8_length();
|
||||
if(left) {
|
||||
while(L < R && chars.index(u8_getitem(L)) != -1)
|
||||
L++;
|
||||
}
|
||||
|
||||
Str operator+(const char* p, const Str& str){
|
||||
Str other(p);
|
||||
return other + str;
|
||||
if(right) {
|
||||
while(L < R && chars.index(u8_getitem(R - 1)) != -1)
|
||||
R--;
|
||||
}
|
||||
return u8_slice(L, R, 1);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Str& str){
|
||||
return os << str.sv();
|
||||
}
|
||||
|
||||
bool operator<(const std::string_view other, const Str& str){
|
||||
return other < str.sv();
|
||||
}
|
||||
|
||||
Str& Str::operator=(const Str& other){
|
||||
if(!is_inlined()) std::free(data);
|
||||
size = other.size;
|
||||
is_ascii = other.is_ascii;
|
||||
PK_STR_ALLOCATE()
|
||||
std::memcpy(data, other.data, size);
|
||||
data[size] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str Str::operator+(const Str& other) const {
|
||||
Str ret(size + other.size, is_ascii && other.is_ascii);
|
||||
std::memcpy(ret.data, data, size);
|
||||
std::memcpy(ret.data + size, other.data, other.size);
|
||||
ret.data[ret.size] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
Str Str::operator+(const char* p) const {
|
||||
Str other(p);
|
||||
return *this + other;
|
||||
}
|
||||
|
||||
bool Str::operator==(const Str& other) const {
|
||||
if(size != other.size) return false;
|
||||
return memcmp(data, other.data, size) == 0;
|
||||
}
|
||||
|
||||
bool Str::operator!=(const Str& other) const {
|
||||
if(size != other.size) return true;
|
||||
return memcmp(data, other.data, size) != 0;
|
||||
}
|
||||
|
||||
bool Str::operator==(const std::string_view other) const {
|
||||
if(size != (int)other.size()) return false;
|
||||
return memcmp(data, other.data(), size) == 0;
|
||||
}
|
||||
|
||||
bool Str::operator!=(const std::string_view other) const {
|
||||
if(size != (int)other.size()) return true;
|
||||
return memcmp(data, other.data(), size) != 0;
|
||||
}
|
||||
|
||||
bool Str::operator==(const char* p) const {
|
||||
return *this == std::string_view(p);
|
||||
}
|
||||
|
||||
bool Str::operator!=(const char* p) const {
|
||||
return *this != std::string_view(p);
|
||||
}
|
||||
|
||||
bool Str::operator<(const Str& other) const {
|
||||
return this->sv() < other.sv();
|
||||
}
|
||||
|
||||
bool Str::operator<(const std::string_view other) const {
|
||||
return this->sv() < other;
|
||||
}
|
||||
|
||||
bool Str::operator>(const Str& other) const {
|
||||
return this->sv() > other.sv();
|
||||
}
|
||||
|
||||
bool Str::operator<=(const Str& other) const {
|
||||
return this->sv() <= other.sv();
|
||||
}
|
||||
|
||||
bool Str::operator>=(const Str& other) const {
|
||||
return this->sv() >= other.sv();
|
||||
}
|
||||
|
||||
Str::~Str(){
|
||||
if(!is_inlined()) std::free(data);
|
||||
}
|
||||
|
||||
Str Str::substr(int start, int len) const {
|
||||
Str ret(len, is_ascii);
|
||||
std::memcpy(ret.data, data + start, len);
|
||||
ret.data[len] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
Str Str::substr(int start) const {
|
||||
return substr(start, size - start);
|
||||
}
|
||||
|
||||
Str Str::strip(bool left, bool right, const Str& chars) const {
|
||||
Str Str::strip(bool left, bool right) const {
|
||||
if(is_ascii) {
|
||||
int L = 0;
|
||||
int R = u8_length();
|
||||
if(left){
|
||||
while(L < R && chars.index(u8_getitem(L)) != -1) L++;
|
||||
int R = size;
|
||||
if(left) {
|
||||
while(L < R && (data[L] == ' ' || data[L] == '\t' || data[L] == '\n' || data[L] == '\r'))
|
||||
L++;
|
||||
}
|
||||
if(right){
|
||||
while(L < R && chars.index(u8_getitem(R-1)) != -1) R--;
|
||||
if(right) {
|
||||
while(L < R && (data[R - 1] == ' ' || data[R - 1] == '\t' || data[R - 1] == '\n' || data[R - 1] == '\r'))
|
||||
R--;
|
||||
}
|
||||
return u8_slice(L, R, 1);
|
||||
return substr(L, R - L);
|
||||
} else {
|
||||
return strip(left, right, " \t\n\r");
|
||||
}
|
||||
}
|
||||
|
||||
Str Str::strip(bool left, bool right) const {
|
||||
if(is_ascii){
|
||||
int L = 0;
|
||||
int R = size;
|
||||
if(left){
|
||||
while(L < R && (data[L] == ' ' || data[L] == '\t' || data[L] == '\n' || data[L] == '\r')) L++;
|
||||
}
|
||||
if(right){
|
||||
while(L < R && (data[R-1] == ' ' || data[R-1] == '\t' || data[R-1] == '\n' || data[R-1] == '\r')) R--;
|
||||
}
|
||||
return substr(L, R - L);
|
||||
}else{
|
||||
return strip(left, right, " \t\n\r");
|
||||
Str Str::lower() const {
|
||||
std::string copy(data, size);
|
||||
std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) {
|
||||
if('A' <= c && c <= 'Z') return c + ('a' - 'A');
|
||||
return (int)c;
|
||||
});
|
||||
return Str(copy);
|
||||
}
|
||||
|
||||
Str Str::upper() const {
|
||||
std::string copy(data, size);
|
||||
std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) {
|
||||
if('a' <= c && c <= 'z') return c - ('a' - 'A');
|
||||
return (int)c;
|
||||
});
|
||||
return Str(copy);
|
||||
}
|
||||
|
||||
Str Str::escape(bool single_quote) const {
|
||||
SStream ss;
|
||||
escape_(ss, single_quote);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void Str::escape_(SStream& ss, bool single_quote) const {
|
||||
ss << (single_quote ? '\'' : '"');
|
||||
for(int i = 0; i < length(); i++) {
|
||||
char c = this->operator[] (i);
|
||||
switch(c) {
|
||||
case '"':
|
||||
if(!single_quote) ss << '\\';
|
||||
ss << '"';
|
||||
break;
|
||||
case '\'':
|
||||
if(single_quote) ss << '\\';
|
||||
ss << '\'';
|
||||
break;
|
||||
case '\\': ss << '\\' << '\\'; break;
|
||||
case '\n': ss << "\\n"; break;
|
||||
case '\r': ss << "\\r"; break;
|
||||
case '\t': ss << "\\t"; break;
|
||||
case '\b': ss << "\\b"; break;
|
||||
default:
|
||||
if('\x00' <= c && c <= '\x1f') {
|
||||
ss << "\\x"; // << std::hex << std::setw(2) << std::setfill('0') << (int)c;
|
||||
ss << PK_HEX_TABLE[c >> 4];
|
||||
ss << PK_HEX_TABLE[c & 0xf];
|
||||
} else {
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
ss << (single_quote ? '\'' : '"');
|
||||
}
|
||||
|
||||
Str Str::lower() const{
|
||||
std::string copy(data, size);
|
||||
std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c){
|
||||
if('A' <= c && c <= 'Z') return c + ('a' - 'A');
|
||||
return (int)c;
|
||||
});
|
||||
return Str(copy);
|
||||
int Str::index(const Str& sub, int start) const {
|
||||
auto p = std::search(data + start, data + size, sub.data, sub.data + sub.size);
|
||||
if(p == data + size) return -1;
|
||||
return p - data;
|
||||
}
|
||||
|
||||
Str Str::replace(char old, char new_) const {
|
||||
Str copied = *this;
|
||||
for(int i = 0; i < copied.size; i++) {
|
||||
if(copied.data[i] == old) copied.data[i] = new_;
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
Str Str::upper() const{
|
||||
std::string copy(data, size);
|
||||
std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c){
|
||||
if('a' <= c && c <= 'z') return c - ('a' - 'A');
|
||||
return (int)c;
|
||||
});
|
||||
return Str(copy);
|
||||
Str Str::replace(const Str& old, const Str& new_, int count) const {
|
||||
SStream ss;
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = index(old, start);
|
||||
if(i == -1) break;
|
||||
ss << substr(start, i - start);
|
||||
ss << new_;
|
||||
start = i + old.size;
|
||||
if(count != -1 && --count == 0) break;
|
||||
}
|
||||
ss << substr(start, size - start);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Str Str::escape(bool single_quote) const{
|
||||
SStream ss;
|
||||
escape_(ss, single_quote);
|
||||
return ss.str();
|
||||
int Str::_unicode_index_to_byte(int i) const {
|
||||
if(is_ascii) return i;
|
||||
int j = 0;
|
||||
while(i > 0) {
|
||||
j += utf8len(data[j]);
|
||||
i--;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
void Str::escape_(SStream& ss, bool single_quote) const {
|
||||
ss << (single_quote ? '\'' : '"');
|
||||
for (int i=0; i<length(); i++) {
|
||||
char c = this->operator[](i);
|
||||
switch (c) {
|
||||
case '"':
|
||||
if(!single_quote) ss << '\\';
|
||||
ss << '"';
|
||||
break;
|
||||
case '\'':
|
||||
if(single_quote) ss << '\\';
|
||||
ss << '\'';
|
||||
break;
|
||||
case '\\': ss << '\\' << '\\'; break;
|
||||
case '\n': ss << "\\n"; break;
|
||||
case '\r': ss << "\\r"; break;
|
||||
case '\t': ss << "\\t"; break;
|
||||
case '\b': ss << "\\b"; break;
|
||||
default:
|
||||
if ('\x00' <= c && c <= '\x1f') {
|
||||
ss << "\\x"; // << std::hex << std::setw(2) << std::setfill('0') << (int)c;
|
||||
ss << PK_HEX_TABLE[c >> 4];
|
||||
ss << PK_HEX_TABLE[c & 0xf];
|
||||
} else {
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
ss << (single_quote ? '\'' : '"');
|
||||
int Str::_byte_index_to_unicode(int n) const {
|
||||
if(is_ascii) return n;
|
||||
int cnt = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
if((data[i] & 0xC0) != 0x80) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int Str::index(const Str& sub, int start) const {
|
||||
auto p = std::search(data + start, data + size, sub.data, sub.data + sub.size);
|
||||
if(p == data + size) return -1;
|
||||
return p - data;
|
||||
Str Str::u8_getitem(int i) const {
|
||||
i = _unicode_index_to_byte(i);
|
||||
return substr(i, utf8len(data[i]));
|
||||
}
|
||||
|
||||
Str Str::u8_slice(int start, int stop, int step) const {
|
||||
SStream ss;
|
||||
if(is_ascii) {
|
||||
PK_SLICE_LOOP(i, start, stop, step) ss << data[i];
|
||||
} else {
|
||||
PK_SLICE_LOOP(i, start, stop, step) ss << u8_getitem(i);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Str Str::replace(char old, char new_) const{
|
||||
Str copied = *this;
|
||||
for(int i=0; i<copied.size; i++){
|
||||
if(copied.data[i] == old) copied.data[i] = new_;
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
int Str::u8_length() const { return _byte_index_to_unicode(size); }
|
||||
|
||||
Str Str::replace(const Str& old, const Str& new_, int count) const {
|
||||
SStream ss;
|
||||
int start = 0;
|
||||
while(true){
|
||||
int i = index(old, start);
|
||||
if(i == -1) break;
|
||||
ss << substr(start, i - start);
|
||||
ss << new_;
|
||||
start = i + old.size;
|
||||
if(count != -1 && --count == 0) break;
|
||||
}
|
||||
ss << substr(start, size - start);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
int Str::_unicode_index_to_byte(int i) const{
|
||||
if(is_ascii) return i;
|
||||
int j = 0;
|
||||
while(i > 0){
|
||||
j += utf8len(data[j]);
|
||||
i--;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
int Str::_byte_index_to_unicode(int n) const{
|
||||
if(is_ascii) return n;
|
||||
int cnt = 0;
|
||||
for(int i=0; i<n; i++){
|
||||
if((data[i] & 0xC0) != 0x80) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
Str Str::u8_getitem(int i) const{
|
||||
i = _unicode_index_to_byte(i);
|
||||
return substr(i, utf8len(data[i]));
|
||||
}
|
||||
|
||||
Str Str::u8_slice(int start, int stop, int step) const{
|
||||
SStream ss;
|
||||
if(is_ascii){
|
||||
PK_SLICE_LOOP(i, start, stop, step) ss << data[i];
|
||||
}else{
|
||||
PK_SLICE_LOOP(i, start, stop, step) ss << u8_getitem(i);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int Str::u8_length() const {
|
||||
return _byte_index_to_unicode(size);
|
||||
}
|
||||
|
||||
vector<std::string_view> Str::split(const Str& sep) const{
|
||||
vector<std::string_view> result;
|
||||
std::string_view tmp;
|
||||
int start = 0;
|
||||
while(true){
|
||||
int i = index(sep, start);
|
||||
if(i == -1) break;
|
||||
tmp = sv().substr(start, i - start);
|
||||
if(!tmp.empty()) result.push_back(tmp);
|
||||
start = i + sep.size;
|
||||
}
|
||||
tmp = sv().substr(start, size - start);
|
||||
vector<std::string_view> Str::split(const Str& sep) const {
|
||||
vector<std::string_view> result;
|
||||
std::string_view tmp;
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = index(sep, start);
|
||||
if(i == -1) break;
|
||||
tmp = sv().substr(start, i - start);
|
||||
if(!tmp.empty()) result.push_back(tmp);
|
||||
return result;
|
||||
start = i + sep.size;
|
||||
}
|
||||
tmp = sv().substr(start, size - start);
|
||||
if(!tmp.empty()) result.push_back(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<std::string_view> Str::split(char sep) const{
|
||||
vector<std::string_view> result;
|
||||
int i = 0;
|
||||
for(int j = 0; j < size; j++){
|
||||
if(data[j] == sep){
|
||||
if(j > i) result.emplace_back(data+i, j-i);
|
||||
i = j + 1;
|
||||
continue;
|
||||
}
|
||||
vector<std::string_view> Str::split(char sep) const {
|
||||
vector<std::string_view> result;
|
||||
int i = 0;
|
||||
for(int j = 0; j < size; j++) {
|
||||
if(data[j] == sep) {
|
||||
if(j > i) result.emplace_back(data + i, j - i);
|
||||
i = j + 1;
|
||||
continue;
|
||||
}
|
||||
if(size > i) result.emplace_back(data+i, size-i);
|
||||
return result;
|
||||
}
|
||||
if(size > i) result.emplace_back(data + i, size - i);
|
||||
return result;
|
||||
}
|
||||
|
||||
int Str::count(const Str& sub) const{
|
||||
if(sub.empty()) return size + 1;
|
||||
int cnt = 0;
|
||||
int start = 0;
|
||||
while(true){
|
||||
int i = index(sub, start);
|
||||
if(i == -1) break;
|
||||
cnt++;
|
||||
start = i + sub.size;
|
||||
}
|
||||
return cnt;
|
||||
int Str::count(const Str& sub) const {
|
||||
if(sub.empty()) return size + 1;
|
||||
int cnt = 0;
|
||||
int start = 0;
|
||||
while(true) {
|
||||
int i = index(sub, start);
|
||||
if(i == -1) break;
|
||||
cnt++;
|
||||
start = i + sub.size;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
std::map<std::string_view, uint16_t>& StrName::_interned(){
|
||||
static std::map<std::string_view, uint16_t> interned;
|
||||
return interned;
|
||||
}
|
||||
std::map<std::string_view, uint16_t>& StrName::_interned() {
|
||||
static std::map<std::string_view, uint16_t> interned;
|
||||
return interned;
|
||||
}
|
||||
|
||||
std::map<uint16_t, std::string>& StrName::_r_interned(){
|
||||
static std::map<uint16_t, std::string> r_interned;
|
||||
return r_interned;
|
||||
}
|
||||
std::map<uint16_t, std::string>& StrName::_r_interned() {
|
||||
static std::map<uint16_t, std::string> r_interned;
|
||||
return r_interned;
|
||||
}
|
||||
|
||||
uint32_t StrName::_pesudo_random_index = 0;
|
||||
uint32_t StrName::_pesudo_random_index = 0;
|
||||
|
||||
StrName StrName::get(std::string_view s){
|
||||
auto it = _interned().find(s);
|
||||
if(it != _interned().end()) return StrName(it->second);
|
||||
// generate new index
|
||||
// https://github.com/python/cpython/blob/3.12/Objects/dictobject.c#L175
|
||||
uint16_t index = ((_pesudo_random_index*5) + 1) & 65535;
|
||||
if(index == 0) throw std::runtime_error("StrName index overflow");
|
||||
auto res = _r_interned().emplace(index, s);
|
||||
assert(res.second);
|
||||
s = std::string_view(res.first->second);
|
||||
_interned()[s] = index;
|
||||
_pesudo_random_index = index;
|
||||
return StrName(index);
|
||||
}
|
||||
StrName StrName::get(std::string_view s) {
|
||||
auto it = _interned().find(s);
|
||||
if(it != _interned().end()) return StrName(it->second);
|
||||
// generate new index
|
||||
// https://github.com/python/cpython/blob/3.12/Objects/dictobject.c#L175
|
||||
uint16_t index = ((_pesudo_random_index * 5) + 1) & 65535;
|
||||
if(index == 0) throw std::runtime_error("StrName index overflow");
|
||||
auto res = _r_interned().emplace(index, s);
|
||||
assert(res.second);
|
||||
s = std::string_view(res.first->second);
|
||||
_interned()[s] = index;
|
||||
_pesudo_random_index = index;
|
||||
return StrName(index);
|
||||
}
|
||||
|
||||
Str SStream::str(){
|
||||
// after this call, the buffer is no longer valid
|
||||
buffer.reserve(buffer.size() + 1); // allocate one more byte for '\0'
|
||||
buffer[buffer.size()] = '\0'; // set '\0'
|
||||
return Str(buffer.detach());
|
||||
}
|
||||
Str SStream::str() {
|
||||
// after this call, the buffer is no longer valid
|
||||
buffer.reserve(buffer.size() + 1); // allocate one more byte for '\0'
|
||||
buffer[buffer.size()] = '\0'; // set '\0'
|
||||
return Str(buffer.detach());
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(const Str& s){
|
||||
for(char c: s) buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(const char* s){
|
||||
while(*s) buffer.push_back(*s++);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(const std::string& s){
|
||||
for(char c: s) buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(std::string_view s){
|
||||
for(char c: s) buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(char c){
|
||||
SStream& SStream::operator<< (const Str& s) {
|
||||
for(char c: s)
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (const char* s) {
|
||||
while(*s)
|
||||
buffer.push_back(*s++);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (const std::string& s) {
|
||||
for(char c: s)
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (std::string_view s) {
|
||||
for(char c: s)
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (char c) {
|
||||
buffer.push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (StrName sn) { return *this << sn.sv(); }
|
||||
|
||||
SStream& SStream::operator<< (size_t val) {
|
||||
// size_t could be out of range of `i64`, use `std::to_string` instead
|
||||
return (*this) << std::to_string(val);
|
||||
}
|
||||
|
||||
SStream& SStream::operator<< (int val) { return (*this) << static_cast<i64>(val); }
|
||||
|
||||
SStream& SStream::operator<< (i64 val) {
|
||||
// str(-2**64).__len__() == 21
|
||||
buffer.reserve(buffer.size() + 24);
|
||||
if(val == 0) {
|
||||
buffer.push_back('0');
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(StrName sn){
|
||||
return *this << sn.sv();
|
||||
if(val < 0) {
|
||||
buffer.push_back('-');
|
||||
val = -val;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(size_t val){
|
||||
// size_t could be out of range of `i64`, use `std::to_string` instead
|
||||
return (*this) << std::to_string(val);
|
||||
auto begin = buffer.end();
|
||||
while(val) {
|
||||
buffer.push_back('0' + val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
std::reverse(begin, buffer.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(int val){
|
||||
return (*this) << static_cast<i64>(val);
|
||||
SStream& SStream::operator<< (f64 val) {
|
||||
if(std::isinf(val)) { return (*this) << (val > 0 ? "inf" : "-inf"); }
|
||||
if(std::isnan(val)) { return (*this) << "nan"; }
|
||||
char b[32];
|
||||
if(_precision == -1) {
|
||||
int prec = std::numeric_limits<f64>::max_digits10 - 1;
|
||||
snprintf(b, sizeof(b), "%.*g", prec, val);
|
||||
} else {
|
||||
int prec = _precision;
|
||||
snprintf(b, sizeof(b), "%.*f", prec, val);
|
||||
}
|
||||
(*this) << b;
|
||||
if(std::all_of(b + 1, b + strlen(b), isdigit)) (*this) << ".0";
|
||||
return *this;
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(i64 val){
|
||||
// str(-2**64).__len__() == 21
|
||||
buffer.reserve(buffer.size() + 24);
|
||||
if(val == 0){
|
||||
buffer.push_back('0');
|
||||
return *this;
|
||||
}
|
||||
if(val < 0){
|
||||
buffer.push_back('-');
|
||||
val = -val;
|
||||
}
|
||||
auto begin = buffer.end();
|
||||
while(val){
|
||||
buffer.push_back('0' + val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
std::reverse(begin, buffer.end());
|
||||
return *this;
|
||||
void SStream::write_hex(unsigned char c, bool non_zero) {
|
||||
unsigned char high = c >> 4;
|
||||
unsigned char low = c & 0xf;
|
||||
if(non_zero) {
|
||||
if(high) (*this) << PK_HEX_TABLE[high];
|
||||
if(high || low) (*this) << PK_HEX_TABLE[low];
|
||||
} else {
|
||||
(*this) << PK_HEX_TABLE[high];
|
||||
(*this) << PK_HEX_TABLE[low];
|
||||
}
|
||||
}
|
||||
|
||||
SStream& SStream::operator<<(f64 val){
|
||||
if(std::isinf(val)){
|
||||
return (*this) << (val > 0 ? "inf" : "-inf");
|
||||
}
|
||||
if(std::isnan(val)){
|
||||
return (*this) << "nan";
|
||||
}
|
||||
char b[32];
|
||||
if(_precision == -1){
|
||||
int prec = std::numeric_limits<f64>::max_digits10-1;
|
||||
snprintf(b, sizeof(b), "%.*g", prec, val);
|
||||
}else{
|
||||
int prec = _precision;
|
||||
snprintf(b, sizeof(b), "%.*f", prec, val);
|
||||
}
|
||||
(*this) << b;
|
||||
if(std::all_of(b+1, b+strlen(b), isdigit)) (*this) << ".0";
|
||||
return *this;
|
||||
void SStream::write_hex(void* p) {
|
||||
if(p == nullptr) {
|
||||
(*this) << "0x0";
|
||||
return;
|
||||
}
|
||||
(*this) << "0x";
|
||||
uintptr_t p_t = reinterpret_cast<uintptr_t>(p);
|
||||
bool non_zero = true;
|
||||
for(int i = sizeof(void*) - 1; i >= 0; i--) {
|
||||
unsigned char cpnt = (p_t >> (i * 8)) & 0xff;
|
||||
write_hex(cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SStream::write_hex(unsigned char c, bool non_zero){
|
||||
unsigned char high = c >> 4;
|
||||
unsigned char low = c & 0xf;
|
||||
if(non_zero){
|
||||
if(high) (*this) << PK_HEX_TABLE[high];
|
||||
if(high || low) (*this) << PK_HEX_TABLE[low];
|
||||
}else{
|
||||
(*this) << PK_HEX_TABLE[high];
|
||||
(*this) << PK_HEX_TABLE[low];
|
||||
}
|
||||
void SStream::write_hex(i64 val) {
|
||||
if(val == 0) {
|
||||
(*this) << "0x0";
|
||||
return;
|
||||
}
|
||||
|
||||
void SStream::write_hex(void* p){
|
||||
if(p == nullptr){
|
||||
(*this) << "0x0";
|
||||
return;
|
||||
}
|
||||
(*this) << "0x";
|
||||
uintptr_t p_t = reinterpret_cast<uintptr_t>(p);
|
||||
bool non_zero = true;
|
||||
for(int i=sizeof(void*)-1; i>=0; i--){
|
||||
unsigned char cpnt = (p_t >> (i * 8)) & 0xff;
|
||||
write_hex(cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
if(val < 0) {
|
||||
(*this) << "-";
|
||||
val = -val;
|
||||
}
|
||||
|
||||
void SStream::write_hex(i64 val){
|
||||
if(val == 0){
|
||||
(*this) << "0x0";
|
||||
return;
|
||||
}
|
||||
if(val < 0){
|
||||
(*this) << "-";
|
||||
val = -val;
|
||||
}
|
||||
(*this) << "0x";
|
||||
bool non_zero = true;
|
||||
for(int i=56; i>=0; i-=8){
|
||||
unsigned char cpnt = (val >> i) & 0xff;
|
||||
write_hex(cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
(*this) << "0x";
|
||||
bool non_zero = true;
|
||||
for(int i = 56; i >= 0; i -= 8) {
|
||||
unsigned char cpnt = (val >> i) & 0xff;
|
||||
write_hex(cpnt, non_zero);
|
||||
if(cpnt != 0) non_zero = false;
|
||||
}
|
||||
}
|
||||
|
||||
#undef PK_STR_ALLOCATE
|
||||
#undef PK_STR_COPY_INIT
|
||||
|
||||
|
||||
|
||||
// unary operators
|
||||
const StrName __repr__ = StrName::get("__repr__");
|
||||
const StrName __str__ = StrName::get("__str__");
|
||||
@ -601,5 +569,4 @@ const StrName pk_id_set = StrName::get("set");
|
||||
const StrName pk_id_long = StrName::get("long");
|
||||
const StrName pk_id_complex = StrName::get("complex");
|
||||
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,159 +1,173 @@
|
||||
#include "pocketpy/interpreter/cffi.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
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));
|
||||
});
|
||||
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__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(), ">");
|
||||
});
|
||||
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); \
|
||||
});
|
||||
#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__, >=)
|
||||
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);
|
||||
});
|
||||
|
||||
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());
|
||||
});
|
||||
|
||||
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){
|
||||
// @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){
|
||||
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], "'"));
|
||||
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;
|
||||
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);
|
||||
},
|
||||
{},
|
||||
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__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, "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, "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_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);
|
||||
});
|
||||
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")
|
||||
#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){
|
||||
void add_module_c(VM* vm) {
|
||||
PyObject* mod = vm->new_module("c");
|
||||
|
||||
vm->bind_func(mod, "malloc", 1, [](VM* vm, ArgsView args){
|
||||
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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]);
|
||||
@ -163,25 +177,23 @@ void add_module_c(VM* vm){
|
||||
|
||||
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){
|
||||
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");
|
||||
}
|
||||
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){
|
||||
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){
|
||||
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);
|
||||
@ -190,54 +202,54 @@ void add_module_c(VM* vm){
|
||||
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(), ">"); \
|
||||
}); \
|
||||
#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")
|
||||
@ -256,13 +268,13 @@ void add_module_c(VM* vm){
|
||||
#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){
|
||||
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){
|
||||
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;
|
||||
@ -272,8 +284,6 @@ void add_module_c(VM* vm){
|
||||
});
|
||||
}
|
||||
|
||||
PyVar from_void_p(VM* vm, void* p){
|
||||
return vm->new_user_object<VoidP>(p);
|
||||
}
|
||||
PyVar from_void_p(VM* vm, void* p) { return vm->new_user_object<VoidP>(p); }
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,126 +3,128 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace pkpy{
|
||||
PyVar* FastLocals::try_get_name(StrName name){
|
||||
int index = co->varnames_inv.try_get(name);
|
||||
if(index == -1) return nullptr;
|
||||
return &a[index];
|
||||
}
|
||||
namespace pkpy {
|
||||
PyVar* FastLocals::try_get_name(StrName name) {
|
||||
int index = co->varnames_inv.try_get(name);
|
||||
if(index == -1) return nullptr;
|
||||
return &a[index];
|
||||
}
|
||||
|
||||
NameDict_ FastLocals::to_namedict(){
|
||||
NameDict_ dict = std::make_shared<NameDict>();
|
||||
co->varnames_inv.apply([&](StrName name, int index){
|
||||
PyVar value = a[index];
|
||||
if(value) dict->set(name, value);
|
||||
});
|
||||
return dict;
|
||||
}
|
||||
NameDict_ FastLocals::to_namedict() {
|
||||
NameDict_ dict = std::make_shared<NameDict>();
|
||||
co->varnames_inv.apply([&](StrName name, int index) {
|
||||
PyVar value = a[index];
|
||||
if(value) dict->set(name, 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);
|
||||
}
|
||||
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 = co->lines[ip()].iblock;
|
||||
while(i >= 0){
|
||||
if(co->blocks[i].type == CodeBlockType::TRY_EXCEPT) break;
|
||||
i = co->blocks[i].parent;
|
||||
int Frame::prepare_jump_exception_handler(ValueStack* _s) {
|
||||
// try to find a parent try block
|
||||
int i = co->lines[ip()].iblock;
|
||||
while(i >= 0) {
|
||||
if(co->blocks[i].type == CodeBlockType::TRY_EXCEPT) break;
|
||||
i = co->blocks[i].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 co->blocks[i].end;
|
||||
}
|
||||
|
||||
int Frame::_exit_block(ValueStack* _s, int i) {
|
||||
auto type = co->blocks[i].type;
|
||||
if(type == CodeBlockType::FOR_LOOP) {
|
||||
_s->pop(); // pop the iterator
|
||||
// pop possible stack memory slots
|
||||
if(_s->top().type == kTpStackMemoryIndex) {
|
||||
int count = _s->top().as<StackMemory>().count;
|
||||
assert(count < 0);
|
||||
_s->_sp += count;
|
||||
_s->_sp -= 2; // pop header and tail
|
||||
}
|
||||
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 co->blocks[i].end;
|
||||
} else if(type == CodeBlockType::CONTEXT_MANAGER) {
|
||||
_s->pop();
|
||||
}
|
||||
return co->blocks[i].parent;
|
||||
}
|
||||
|
||||
int Frame::_exit_block(ValueStack* _s, int i){
|
||||
auto type = co->blocks[i].type;
|
||||
if(type == CodeBlockType::FOR_LOOP){
|
||||
_s->pop(); // pop the iterator
|
||||
// pop possible stack memory slots
|
||||
if(_s->top().type == kTpStackMemoryIndex){
|
||||
int count = _s->top().as<StackMemory>().count;
|
||||
assert(count < 0);
|
||||
_s->_sp += count;
|
||||
_s->_sp -= 2; // pop header and tail
|
||||
}
|
||||
}else if(type==CodeBlockType::CONTEXT_MANAGER){
|
||||
_s->pop();
|
||||
}
|
||||
return co->blocks[i].parent;
|
||||
void Frame::prepare_jump_break(ValueStack* _s, int target) {
|
||||
int i = co->lines[ip()].iblock;
|
||||
if(target >= co->codes.size()) {
|
||||
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 = co->lines[target].iblock;
|
||||
while(i >= 0 && i != next_block)
|
||||
i = _exit_block(_s, i);
|
||||
if(i != next_block) throw std::runtime_error("invalid jump");
|
||||
}
|
||||
}
|
||||
|
||||
void Frame::prepare_jump_break(ValueStack* _s, int target){
|
||||
int i = co->lines[ip()].iblock;
|
||||
if(target >= co->codes.size()){
|
||||
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 = co->lines[target].iblock;
|
||||
while(i>=0 && i!=next_block) i = _exit_block(_s, i);
|
||||
if(i!=next_block) throw std::runtime_error("invalid jump");
|
||||
}
|
||||
void Frame::set_unwind_target(PyVar* _sp) {
|
||||
int iblock = co->lines[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;
|
||||
}
|
||||
}
|
||||
|
||||
void Frame::set_unwind_target(PyVar* _sp){
|
||||
int iblock = co->lines[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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
void CallStack::pushx(LinkedFrame* p) {
|
||||
p->f_back = _tail;
|
||||
_tail = p;
|
||||
++_size;
|
||||
}
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,55 +1,56 @@
|
||||
#include "pocketpy/interpreter/gc.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
int ManagedHeap::sweep(){
|
||||
vector<PyObject*> alive;
|
||||
alive.reserve(gen.size() / 2);
|
||||
for(PyObject* obj: gen){
|
||||
if(obj->gc_marked){
|
||||
obj->gc_marked = false;
|
||||
alive.push_back(obj);
|
||||
}else{
|
||||
int ManagedHeap::sweep() {
|
||||
vector<PyObject*> alive;
|
||||
alive.reserve(gen.size() / 2);
|
||||
for(PyObject* obj: gen) {
|
||||
if(obj->gc_marked) {
|
||||
obj->gc_marked = false;
|
||||
alive.push_back(obj);
|
||||
} else {
|
||||
#if PK_DEBUG_GC_STATS
|
||||
deleted[obj->type] += 1;
|
||||
deleted[obj->type] += 1;
|
||||
#endif
|
||||
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
||||
_delete(obj);
|
||||
}
|
||||
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
||||
_delete(obj);
|
||||
}
|
||||
|
||||
// clear _no_gc marked flag
|
||||
for(PyObject* obj: _no_gc) obj->gc_marked = false;
|
||||
|
||||
int freed = gen.size() - alive.size();
|
||||
|
||||
#if PK_DEBUG_GC_STATS
|
||||
for(auto& [type, count]: deleted){
|
||||
std::cout << "GC: " << _type_name(vm, type).sv() << "=" << count << std::endl;
|
||||
}
|
||||
std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
|
||||
deleted.clear();
|
||||
#endif
|
||||
gen.clear();
|
||||
gen.swap(alive);
|
||||
PoolObject_shrink_to_fit();
|
||||
return freed;
|
||||
}
|
||||
|
||||
void ManagedHeap::_auto_collect(){
|
||||
// clear _no_gc marked flag
|
||||
for(PyObject* obj: _no_gc)
|
||||
obj->gc_marked = false;
|
||||
|
||||
int freed = gen.size() - alive.size();
|
||||
|
||||
#if PK_DEBUG_GC_STATS
|
||||
for(auto& [type, count]: deleted) {
|
||||
std::cout << "GC: " << _type_name(vm, type).sv() << "=" << count << std::endl;
|
||||
}
|
||||
std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
|
||||
deleted.clear();
|
||||
#endif
|
||||
gen.clear();
|
||||
gen.swap(alive);
|
||||
PoolObject_shrink_to_fit();
|
||||
return freed;
|
||||
}
|
||||
|
||||
void ManagedHeap::_auto_collect() {
|
||||
#if !PK_DEBUG_NO_AUTO_GC
|
||||
if(_gc_lock_counter > 0) return;
|
||||
gc_counter = 0;
|
||||
collect();
|
||||
gc_threshold = gen.size() * 2;
|
||||
if(gc_threshold < PK_GC_MIN_THRESHOLD) gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||
if(_gc_lock_counter > 0) return;
|
||||
gc_counter = 0;
|
||||
collect();
|
||||
gc_threshold = gen.size() * 2;
|
||||
if(gc_threshold < PK_GC_MIN_THRESHOLD) gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int ManagedHeap::collect(){
|
||||
assert(_gc_lock_counter == 0);
|
||||
mark();
|
||||
int freed = sweep();
|
||||
return freed;
|
||||
}
|
||||
} // namespace pkpy
|
||||
int ManagedHeap::collect() {
|
||||
assert(_gc_lock_counter == 0);
|
||||
mark();
|
||||
int freed = sweep();
|
||||
return freed;
|
||||
}
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,130 +1,144 @@
|
||||
#include "pocketpy/interpreter/iter.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
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 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 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 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 = utf8len(s.data[self.i]);
|
||||
self.i += len;
|
||||
vm->s_data.push(VAR(s.substr(start, len)));
|
||||
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 = utf8len(s.data[self.i]);
|
||||
self.i += len;
|
||||
vm->s_data.push(VAR(s.substr(start, len)));
|
||||
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);
|
||||
// relocate stack objects (their addresses become invalid)
|
||||
for(PyVar* p=lf->frame.actual_sp_base(); p!=vm->s_data.end(); p++){
|
||||
if(p->type == VM::tp_stack_memory){
|
||||
// TODO: refactor this
|
||||
int count = p->as<StackMemory>().count;
|
||||
if(count < 0){
|
||||
void* new_p = p + count;
|
||||
p[1]._1 = reinterpret_cast<i64>(new_p);
|
||||
}
|
||||
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);
|
||||
// relocate stack objects (their addresses become invalid)
|
||||
for(PyVar* p = lf->frame.actual_sp_base(); p != vm->s_data.end(); p++) {
|
||||
if(p->type == VM::tp_stack_memory) {
|
||||
// TODO: refactor this
|
||||
int count = p->as<StackMemory>().count;
|
||||
if(count < 0) {
|
||||
void* new_p = p + count;
|
||||
p[1]._1 = reinterpret_cast<i64>(new_p);
|
||||
}
|
||||
}
|
||||
s_backup.clear();
|
||||
vm->callstack.pushx(lf);
|
||||
lf = nullptr;
|
||||
}
|
||||
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 == PY_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;
|
||||
}
|
||||
PyVar ret;
|
||||
try {
|
||||
ret = vm->__run_top_frame();
|
||||
} catch(...) {
|
||||
state = 2; // end this generator immediately when an exception is thrown
|
||||
throw;
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
if(ret == PY_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 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);
|
||||
Dict& d = PK_OBJ_GET(Dict, self.ref);
|
||||
if(self.i == -1) return 0;
|
||||
vm->s_data.push(d._items[self.i].first);
|
||||
vm->s_data.push(d._items[self.i].second);
|
||||
self.i = d._items[self.i].next;
|
||||
return 2;
|
||||
});
|
||||
}
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
PyVar VM::__py_generator(LinkedFrame* frame, ArgsView buffer){
|
||||
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);
|
||||
Dict& d = PK_OBJ_GET(Dict, self.ref);
|
||||
if(self.i == -1) return 0;
|
||||
vm->s_data.push(d._items[self.i].first);
|
||||
vm->s_data.push(d._items[self.i].second);
|
||||
self.i = d._items[self.i].next;
|
||||
return 2;
|
||||
});
|
||||
}
|
||||
|
||||
PyVar VM::__py_generator(LinkedFrame* frame, ArgsView buffer) {
|
||||
return vm->new_user_object<Generator>(std::move(frame), buffer);
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,41 +1,39 @@
|
||||
#include "pocketpy/interpreter/profiler.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
static std::string left_pad(std::string s, int width){
|
||||
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){
|
||||
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::begin() { frames.clear(); }
|
||||
|
||||
void LineProfiler::_step(int callstack_size, Frame* frame){
|
||||
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()){
|
||||
if(frames.empty()) {
|
||||
frames.push({callstack_size, frame, clock(), nullptr});
|
||||
}else{
|
||||
} else {
|
||||
_step_end(callstack_size, frame, line);
|
||||
}
|
||||
|
||||
auto& file_records = records[filename];
|
||||
if(file_records.empty()){
|
||||
if(file_records.empty()) {
|
||||
// initialize file_records
|
||||
int total_lines = frame->co->src->line_starts.size();
|
||||
file_records.resize(total_lines + 1);
|
||||
for(int i=1; i<=total_lines; i++){
|
||||
for(int i = 1; i <= total_lines; i++) {
|
||||
file_records[i].line = i;
|
||||
}
|
||||
}
|
||||
@ -43,7 +41,7 @@ void LineProfiler::_step(int callstack_size, Frame* frame){
|
||||
frames.top().prev_record = &file_records[line];
|
||||
}
|
||||
|
||||
void LineProfiler::_step_end(int callstack_size, Frame* frame, int line){
|
||||
void LineProfiler::_step_end(int callstack_size, Frame* frame, int line) {
|
||||
clock_t now = clock();
|
||||
_FrameRecord& top_frame_record = frames.top();
|
||||
_LineRecord* prev_record = top_frame_record.prev_record;
|
||||
@ -52,21 +50,21 @@ void LineProfiler::_step_end(int callstack_size, Frame* frame, int line){
|
||||
assert(abs(id_delta) <= 1);
|
||||
|
||||
// current line is about to change
|
||||
if(prev_record->line != line){
|
||||
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){
|
||||
|
||||
if(id_delta == 1) {
|
||||
frames.push({callstack_size, frame, now, nullptr});
|
||||
}else{
|
||||
} else {
|
||||
if(id_delta == -1) frames.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void LineProfiler::end(){
|
||||
void LineProfiler::end() {
|
||||
clock_t now = clock();
|
||||
_FrameRecord& top_frame_record = frames.top();
|
||||
_LineRecord* prev_record = top_frame_record.prev_record;
|
||||
@ -80,9 +78,9 @@ void LineProfiler::end(){
|
||||
assert(frames.empty());
|
||||
}
|
||||
|
||||
Str LineProfiler::stats(){
|
||||
Str LineProfiler::stats() {
|
||||
SStream ss;
|
||||
for(FuncDecl* decl: functions){
|
||||
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;
|
||||
@ -90,7 +88,7 @@ Str LineProfiler::stats(){
|
||||
vector<_LineRecord>& file_records = records[filename];
|
||||
if(file_records.empty()) continue;
|
||||
clock_t total_time = 0;
|
||||
for(int line = start_line; line <= end_line; line++){
|
||||
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";
|
||||
@ -98,19 +96,19 @@ Str LineProfiler::stats(){
|
||||
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++){
|
||||
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){
|
||||
if(record.hits == 0) {
|
||||
ss << std::string(10 + 13 + 9 + 9, ' ');
|
||||
}else{
|
||||
} 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){
|
||||
if(total_time == 0) {
|
||||
ss << left_pad("0.0", 9);
|
||||
}else{
|
||||
} else {
|
||||
ss << left_pad(to_string_1f(record.time * (f64)100 / total_time), 9);
|
||||
}
|
||||
}
|
||||
@ -122,4 +120,4 @@ Str LineProfiler::stats(){
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,9 @@
|
||||
#include "pocketpy/modules/array2d.hpp"
|
||||
#include "pocketpy/interpreter/bindings.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct Array2d{
|
||||
struct Array2d {
|
||||
PK_ALWAYS_PASS_BY_POINTER(Array2d)
|
||||
|
||||
PyVar* data;
|
||||
@ -11,55 +11,49 @@ struct Array2d{
|
||||
int n_rows;
|
||||
int numel;
|
||||
|
||||
Array2d(){
|
||||
Array2d() {
|
||||
data = nullptr;
|
||||
n_cols = 0;
|
||||
n_rows = 0;
|
||||
numel = 0;
|
||||
}
|
||||
|
||||
void init(int n_cols, int n_rows){
|
||||
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;
|
||||
}
|
||||
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{
|
||||
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];
|
||||
}
|
||||
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;
|
||||
}
|
||||
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){
|
||||
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){
|
||||
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");
|
||||
}
|
||||
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];
|
||||
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;
|
||||
});
|
||||
@ -71,7 +65,7 @@ struct Array2d{
|
||||
PY_READONLY_FIELD(Array2d, "numel", numel);
|
||||
|
||||
// _get
|
||||
vm->bind_func(type, "_get", 3, [](VM* vm, ArgsView args){
|
||||
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]);
|
||||
@ -80,7 +74,7 @@ struct Array2d{
|
||||
});
|
||||
|
||||
// _set
|
||||
vm->bind_func(type, "_set", 4, [](VM* vm, ArgsView args){
|
||||
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]);
|
||||
@ -89,14 +83,14 @@ struct Array2d{
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind_func(type, "is_valid", 3, [](VM* vm, ArgsView args){
|
||||
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){
|
||||
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]);
|
||||
@ -104,34 +98,34 @@ struct Array2d{
|
||||
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");
|
||||
#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){
|
||||
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])){
|
||||
if(is_int(xy[0]) && is_int(xy[1])) {
|
||||
i64 col = xy[0].as<i64>();
|
||||
i64 row = xy[1].as<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)){
|
||||
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++){
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -140,10 +134,10 @@ struct Array2d{
|
||||
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){
|
||||
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])){
|
||||
if(is_int(xy[0]) && is_int(xy[1])) {
|
||||
i64 col = xy[0].as<i64>();
|
||||
i64 row = xy[1].as<i64>();
|
||||
self.check_valid(vm, col, row);
|
||||
@ -151,11 +145,11 @@ struct Array2d{
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)){
|
||||
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).index){
|
||||
switch(vm->_tp(_2).index) {
|
||||
case VM::tp_int.index: is_basic_type = true; break;
|
||||
case VM::tp_float.index: is_basic_type = true; break;
|
||||
case VM::tp_str.index: is_basic_type = true; break;
|
||||
@ -163,19 +157,19 @@ struct Array2d{
|
||||
default: is_basic_type = _2 == vm->None;
|
||||
}
|
||||
|
||||
if(is_basic_type){
|
||||
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)){
|
||||
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){
|
||||
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++)
|
||||
@ -186,161 +180,163 @@ struct Array2d{
|
||||
vm->TypeError("expected `tuple[int, int]` or `tuple[slice, slice]` as index");
|
||||
});
|
||||
|
||||
#undef HANDLE_SLICE
|
||||
#undef HANDLE_SLICE
|
||||
|
||||
vm->bind_func(type, "tolist", 1, [](VM* vm, ArgsView args){
|
||||
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++){
|
||||
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);
|
||||
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){
|
||||
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{
|
||||
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){
|
||||
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++){
|
||||
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){
|
||||
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++){
|
||||
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++){
|
||||
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){
|
||||
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++){
|
||||
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){
|
||||
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)){
|
||||
if(is_type(args[1], VM::tp_list)) {
|
||||
const List& list = PK_OBJ_GET(List, args[1]);
|
||||
if(list.size() != self.numel){
|
||||
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++){
|
||||
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){
|
||||
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++){
|
||||
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){
|
||||
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++){
|
||||
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){
|
||||
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++){
|
||||
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);
|
||||
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++){
|
||||
} 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);
|
||||
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{
|
||||
} else {
|
||||
vm->ValueError("neighborhood must be 'Moore' or 'von Neumann'");
|
||||
}
|
||||
return new_array_obj;
|
||||
return new_array_obj;
|
||||
});
|
||||
|
||||
vm->bind_func(type, "count", 2, [](VM* vm, ArgsView args){
|
||||
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);
|
||||
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){
|
||||
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)){
|
||||
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);
|
||||
@ -360,30 +356,30 @@ struct Array2d{
|
||||
});
|
||||
}
|
||||
|
||||
void _gc_mark(VM* vm) const{
|
||||
for(int i = 0; i < numel; i++) vm->obj_gc_mark(data[i]);
|
||||
void _gc_mark(VM* vm) const {
|
||||
for(int i = 0; i < numel; i++)
|
||||
vm->obj_gc_mark(data[i]);
|
||||
}
|
||||
|
||||
~Array2d(){
|
||||
delete[] data;
|
||||
}
|
||||
~Array2d() { delete[] data; }
|
||||
};
|
||||
|
||||
|
||||
struct Array2dIter{
|
||||
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); }
|
||||
Array2dIter(PyVar ref, Array2d* a) : ref(ref), a(a), i(0) {}
|
||||
|
||||
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{
|
||||
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);
|
||||
@ -395,20 +391,19 @@ struct Array2dIter{
|
||||
}
|
||||
};
|
||||
|
||||
void add_module_array2d(VM* vm){
|
||||
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){
|
||||
vm->bind__iter__(array2d_iter_t, [](VM* vm, PyVar _0) {
|
||||
return vm->new_user_object<Array2dIter>(_0, &_0.obj_get<Array2d>());
|
||||
});
|
||||
vm->_all_types[array2d_iter_t].op__iter__ = [](VM* vm, PyVar _0){
|
||||
vm->_all_types[array2d_iter_t].op__iter__ = [](VM* vm, PyVar _0) {
|
||||
vm->new_stack_object<Array2dIter>(vm->_tp_user<Array2dIter>(), _0, &_0.obj_get<Array2d>());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "pocketpy/modules/base64.hpp"
|
||||
#include "pocketpy/interpreter/bindings.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
// https://github.com/zhicheng/base64/blob/master/base64.c
|
||||
|
||||
@ -9,6 +9,7 @@ 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',
|
||||
@ -71,114 +72,99 @@ const unsigned char base64de[] = {
|
||||
/* '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;
|
||||
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];
|
||||
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 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;
|
||||
}
|
||||
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;
|
||||
out[j] = 0;
|
||||
|
||||
return j;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
return j;
|
||||
}
|
||||
|
||||
void add_module_base64(VM* vm){
|
||||
void add_module_base64(VM* vm) {
|
||||
PyObject* mod = vm->new_module("base64");
|
||||
|
||||
// b64encode
|
||||
vm->bind_func(mod, "b64encode", 1, [](VM* vm, ArgsView args){
|
||||
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);
|
||||
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){
|
||||
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);
|
||||
@ -186,4 +172,4 @@ void add_module_base64(VM* vm){
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,60 +1,57 @@
|
||||
#include "pocketpy/modules/csv.hpp"
|
||||
#include "pocketpy/interpreter/bindings.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
void add_module_csv(VM *vm){
|
||||
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){
|
||||
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++){
|
||||
for(int i = 0; i < csvfile.size(); i++) {
|
||||
std::string_view line = CAST(Str&, csvfile[i]).sv();
|
||||
if(i == 0){
|
||||
if(i == 0) {
|
||||
// Skip utf8 BOM if there is any.
|
||||
if (strncmp(line.data(), "\xEF\xBB\xBF", 3) == 0) line = line.substr(3);
|
||||
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:
|
||||
__NEXT_LINE:
|
||||
j = 0;
|
||||
while(j < line.size()){
|
||||
switch(line[j]){
|
||||
while(j < line.size()) {
|
||||
switch(line[j]) {
|
||||
case '"':
|
||||
if(in_quote){
|
||||
if(j+1 < line.size() && line[j+1] == '"'){
|
||||
if(in_quote) {
|
||||
if(j + 1 < line.size() && line[j + 1] == '"') {
|
||||
buffer += '"';
|
||||
j++;
|
||||
}else{
|
||||
} else {
|
||||
in_quote = false;
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
in_quote = true;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if(in_quote){
|
||||
if(in_quote) {
|
||||
buffer += line[j];
|
||||
}else{
|
||||
} else {
|
||||
row.push_back(VAR(buffer));
|
||||
buffer.clear();
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
break; // ignore
|
||||
default:
|
||||
buffer += line[j];
|
||||
break;
|
||||
case '\r': break; // ignore
|
||||
default: buffer += line[j]; break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
if(in_quote){
|
||||
if(i == csvfile.size()-1){
|
||||
if(in_quote) {
|
||||
if(i == csvfile.size() - 1) {
|
||||
vm->ValueError("unterminated quote");
|
||||
}else{
|
||||
} else {
|
||||
buffer += '\n';
|
||||
i++;
|
||||
line = CAST(Str&, csvfile[i]).sv();
|
||||
@ -67,22 +64,18 @@ __NEXT_LINE:
|
||||
return VAR(std::move(ret));
|
||||
});
|
||||
|
||||
vm->bind(mod, "DictReader(csvfile: list[str]) -> list[dict]", [](VM* vm, ArgsView args){
|
||||
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");
|
||||
}
|
||||
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++){
|
||||
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()");
|
||||
}
|
||||
if(row.size() != header.size()) { vm->ValueError("row.size() != header.size()"); }
|
||||
Dict row_dict;
|
||||
for(int j=0; j<header.size(); j++){
|
||||
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)));
|
||||
@ -91,4 +84,4 @@ __NEXT_LINE:
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
#include "pocketpy/modules/dataclasses.hpp"
|
||||
#include "pocketpy/interpreter/bindings.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
static void patch__init__(VM* vm, Type cls){
|
||||
vm->bind(vm->_t(cls), "__init__(self, *args, **kwargs)", [](VM* vm, ArgsView _view){
|
||||
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_.apply([&](PyVar k, PyVar v) {
|
||||
kwargs.set(CAST(Str&, k), v);
|
||||
});
|
||||
|
||||
@ -18,26 +18,27 @@ static void patch__init__(VM* vm, Type 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)){
|
||||
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()){
|
||||
} else {
|
||||
if(i < args.size()) {
|
||||
self->attr().set(field, args[i]);
|
||||
++i;
|
||||
}else if(cls_d.contains(field)){ // has default value
|
||||
} else if(cls_d.contains(field)) { // has default value
|
||||
self->attr().set(field, cls_d[field]);
|
||||
}else{
|
||||
} 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(args.size() > i) {
|
||||
vm->TypeError(
|
||||
_S(cls_info->name, " takes ", fields.size(), " positional arguments but ", args.size(), " were given"));
|
||||
}
|
||||
if(kwargs.size() > 0){
|
||||
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()));
|
||||
}
|
||||
@ -45,17 +46,19 @@ static void patch__init__(VM* vm, Type cls){
|
||||
});
|
||||
}
|
||||
|
||||
static void patch__repr__(VM* vm, Type cls){
|
||||
vm->bind__repr__(cls, [](VM* vm, PyVar _0) -> Str{
|
||||
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 << ", ";
|
||||
for(StrName field: fields) {
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
ss << field << "=" << vm->py_repr(obj_d[field]);
|
||||
}
|
||||
ss << ")";
|
||||
@ -63,12 +66,12 @@ static void patch__repr__(VM* vm, Type cls){
|
||||
});
|
||||
}
|
||||
|
||||
static void patch__eq__(VM* vm, Type cls){
|
||||
vm->bind__eq__(cls, [](VM* vm, PyVar _0, PyVar _1){
|
||||
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){
|
||||
for(StrName field: fields) {
|
||||
PyVar lhs = _0->attr(field);
|
||||
PyVar rhs = _1->attr(field);
|
||||
if(vm->py_ne(lhs, rhs)) return vm->False;
|
||||
@ -77,10 +80,10 @@ static void patch__eq__(VM* vm, Type cls){
|
||||
});
|
||||
}
|
||||
|
||||
void add_module_dataclasses(VM* vm){
|
||||
void add_module_dataclasses(VM* vm) {
|
||||
PyObject* mod = vm->new_module("dataclasses");
|
||||
|
||||
vm->bind_func(mod, "dataclass", 1, [](VM* vm, ArgsView args){
|
||||
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();
|
||||
@ -91,11 +94,11 @@ void add_module_dataclasses(VM* vm){
|
||||
|
||||
const auto& fields = vm->_all_types[cls].annotated_fields;
|
||||
bool has_default = false;
|
||||
for(StrName field: fields){
|
||||
if(cls_d.contains(field)){
|
||||
for(StrName field: fields) {
|
||||
if(cls_d.contains(field)) {
|
||||
has_default = true;
|
||||
}else{
|
||||
if(has_default){
|
||||
} else {
|
||||
if(has_default) {
|
||||
vm->TypeError(_S("non-default argument ", field.escape(), " follows default argument"));
|
||||
}
|
||||
}
|
||||
@ -103,15 +106,15 @@ void add_module_dataclasses(VM* vm){
|
||||
return args[0];
|
||||
});
|
||||
|
||||
vm->bind_func(mod, "asdict", 1, [](VM* vm, ArgsView args){
|
||||
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){
|
||||
for(StrName field: fields) {
|
||||
d.set(vm, VAR(field.sv()), obj_d[field]);
|
||||
}
|
||||
return VAR(std::move(d));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -3,193 +3,161 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
// https://easings.net/
|
||||
|
||||
const double kPi = 3.1415926545;
|
||||
|
||||
static double easeLinear( double x ) {
|
||||
return x;
|
||||
}
|
||||
static double easeLinear(double x) { return x; }
|
||||
|
||||
static double easeInSine( double x ) {
|
||||
return 1.0 - std::cos( x * kPi / 2 );
|
||||
}
|
||||
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 easeOutSine(double x) { return std::sin(x * kPi / 2); }
|
||||
|
||||
static double easeInOutSine( double x ) {
|
||||
return -( std::cos( kPi * x ) - 1 ) / 2;
|
||||
}
|
||||
static double easeInOutSine(double x) { return -(std::cos(kPi * x) - 1) / 2; }
|
||||
|
||||
static double easeInQuad( double x ) {
|
||||
return x * x;
|
||||
}
|
||||
static double easeInQuad(double x) { return x * x; }
|
||||
|
||||
static double easeOutQuad( double x ) {
|
||||
return 1 - std::pow( 1 - x, 2 );
|
||||
}
|
||||
static double easeOutQuad(double x) { return 1 - std::pow(1 - x, 2); }
|
||||
|
||||
static double easeInOutQuad( double x ) {
|
||||
if( x < 0.5 ) {
|
||||
static double easeInOutQuad(double x) {
|
||||
if(x < 0.5) {
|
||||
return 2 * x * x;
|
||||
} else {
|
||||
return 1 - std::pow( -2 * x + 2, 2 ) / 2;
|
||||
return 1 - std::pow(-2 * x + 2, 2) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static double easeInCubic( double x ) {
|
||||
return x * x * x;
|
||||
}
|
||||
static double easeInCubic(double x) { return x * x * x; }
|
||||
|
||||
static double easeOutCubic( double x ) {
|
||||
return 1 - std::pow( 1 - x, 3 );
|
||||
}
|
||||
static double easeOutCubic(double x) { return 1 - std::pow(1 - x, 3); }
|
||||
|
||||
static double easeInOutCubic( double x ) {
|
||||
if( x < 0.5 ) {
|
||||
static double easeInOutCubic(double x) {
|
||||
if(x < 0.5) {
|
||||
return 4 * x * x * x;
|
||||
} else {
|
||||
return 1 - std::pow( -2 * x + 2, 3 ) / 2;
|
||||
return 1 - std::pow(-2 * x + 2, 3) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static double easeInQuart( double x ) {
|
||||
return std::pow( x, 4 );
|
||||
}
|
||||
static double easeInQuart(double x) { return std::pow(x, 4); }
|
||||
|
||||
static double easeOutQuart( double x ) {
|
||||
return 1 - std::pow( 1 - 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 );
|
||||
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;
|
||||
return 1 - std::pow(-2 * x + 2, 4) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static double easeInQuint( double x ) {
|
||||
return std::pow( x, 5 );
|
||||
}
|
||||
static double easeInQuint(double x) { return std::pow(x, 5); }
|
||||
|
||||
static double easeOutQuint( double x ) {
|
||||
return 1 - std::pow( 1 - 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 );
|
||||
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;
|
||||
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 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 easeOutExpo(double x) { return x == 1 ? 1 : 1 - std::pow(2, -10 * x); }
|
||||
|
||||
static double easeInOutExpo( double x ) {
|
||||
if( x == 0 ) {
|
||||
static double easeInOutExpo(double x) {
|
||||
if(x == 0) {
|
||||
return 0;
|
||||
} else if( x == 1 ) {
|
||||
} else if(x == 1) {
|
||||
return 1;
|
||||
} else if( x < 0.5 ) {
|
||||
return std::pow( 2, 20 * x - 10 ) / 2;
|
||||
} else if(x < 0.5) {
|
||||
return std::pow(2, 20 * x - 10) / 2;
|
||||
} else {
|
||||
return (2 - std::pow( 2, -20 * x + 10 )) / 2;
|
||||
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 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 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;
|
||||
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;
|
||||
return (std::sqrt(1 - std::pow(-2 * x + 2, 2)) + 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static double easeInBack( double x ) {
|
||||
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 ) {
|
||||
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 );
|
||||
return 1 + c3 * std::pow(x - 1, 3) + c1 * std::pow(x - 1, 2);
|
||||
}
|
||||
|
||||
static double easeInOutBack( double x ) {
|
||||
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;
|
||||
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;
|
||||
return (std::pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static double easeInElastic( double x ) {
|
||||
static double easeInElastic(double x) {
|
||||
const double c4 = (2 * kPi) / 3;
|
||||
if( x == 0 ) {
|
||||
if(x == 0) {
|
||||
return 0;
|
||||
} else if( x == 1 ) {
|
||||
} else if(x == 1) {
|
||||
return 1;
|
||||
} else {
|
||||
return -std::pow( 2, 10 * x - 10 ) * std::sin( (x * 10 - 10.75) * c4 );
|
||||
return -std::pow(2, 10 * x - 10) * std::sin((x * 10 - 10.75) * c4);
|
||||
}
|
||||
}
|
||||
|
||||
static double easeOutElastic( double x ) {
|
||||
static double easeOutElastic(double x) {
|
||||
const double c4 = (2 * kPi) / 3;
|
||||
if( x == 0 ) {
|
||||
if(x == 0) {
|
||||
return 0;
|
||||
} else if( x == 1 ) {
|
||||
} else if(x == 1) {
|
||||
return 1;
|
||||
} else {
|
||||
return std::pow( 2, -10 * x ) * std::sin( (x * 10 - 0.75) * c4 ) + 1;
|
||||
return std::pow(2, -10 * x) * std::sin((x * 10 - 0.75) * c4) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static double easeInOutElastic( double x ) {
|
||||
static double easeInOutElastic(double x) {
|
||||
const double c5 = (2 * kPi) / 4.5;
|
||||
if( x == 0 ) {
|
||||
if(x == 0) {
|
||||
return 0;
|
||||
} else if( x == 1 ) {
|
||||
} 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 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;
|
||||
return (std::pow(2, -20 * x + 10) * std::sin((20 * x - 11.125) * c5)) / 2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static double easeOutBounce( double x ) {
|
||||
static double easeOutBounce(double x) {
|
||||
const double n1 = 7.5625;
|
||||
const double d1 = 2.75;
|
||||
if( x < 1 / d1 ) {
|
||||
if(x < 1 / d1) {
|
||||
return n1 * x * x;
|
||||
} else if( x < 2 / d1 ) {
|
||||
} else if(x < 2 / d1) {
|
||||
x -= 1.5 / d1;
|
||||
return n1 * x * x + 0.75;
|
||||
} else if( x < 2.5 / d1 ) {
|
||||
} else if(x < 2.5 / d1) {
|
||||
x -= 2.25 / d1;
|
||||
return n1 * x * x + 0.9375;
|
||||
} else {
|
||||
@ -198,23 +166,19 @@ static double easeOutBounce( double x ) {
|
||||
}
|
||||
}
|
||||
|
||||
static double easeInBounce( double x ) {
|
||||
return 1 - easeOutBounce(1 - x);
|
||||
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;
|
||||
}
|
||||
|
||||
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){
|
||||
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)); \
|
||||
#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)
|
||||
@ -251,4 +215,4 @@ void add_module_easing(VM* vm){
|
||||
|
||||
#undef EASE
|
||||
}
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
#if PK_ENABLE_OS
|
||||
|
||||
@ -19,7 +19,7 @@ struct FileIO {
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
};
|
||||
|
||||
static FILE* io_fopen(const char* name, const char* mode){
|
||||
static FILE* io_fopen(const char* name, const char* mode) {
|
||||
#if _MSC_VER
|
||||
FILE* fp;
|
||||
errno_t err = fopen_s(&fp, name, mode);
|
||||
@ -30,7 +30,7 @@ static FILE* io_fopen(const char* name, const char* mode){
|
||||
#endif
|
||||
}
|
||||
|
||||
static size_t io_fread(void* buffer, size_t size, size_t count, FILE* fp){
|
||||
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
|
||||
@ -38,7 +38,7 @@ static size_t io_fread(void* buffer, size_t size, size_t count, FILE* fp){
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned char* _default_import_handler(const char* name, int* out_size){
|
||||
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");
|
||||
@ -48,30 +48,28 @@ unsigned char* _default_import_handler(const char* name, int* out_size){
|
||||
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
|
||||
(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){
|
||||
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]));
|
||||
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){
|
||||
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){
|
||||
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{
|
||||
} else {
|
||||
buffer_size = size;
|
||||
}
|
||||
unsigned char* buffer = (unsigned char*)std::malloc(buffer_size);
|
||||
@ -79,32 +77,30 @@ void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
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()));
|
||||
}
|
||||
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){
|
||||
vm->bind_func(type, "write", 2, [](VM* vm, ArgsView args) {
|
||||
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
|
||||
if(io.is_text){
|
||||
if(io.is_text) {
|
||||
Str& s = CAST(Str&, args[1]);
|
||||
fwrite(s.data, 1, s.length(), io.fp);
|
||||
}else{
|
||||
} 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){
|
||||
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){
|
||||
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]);
|
||||
@ -113,13 +109,13 @@ void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind_func(type, "close", 1, [](VM* vm, ArgsView args){
|
||||
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){
|
||||
vm->bind_func(type, __exit__, 1, [](VM* vm, ArgsView args) {
|
||||
FileIO& io = PK_OBJ_GET(FileIO, args[0]);
|
||||
io.close();
|
||||
return vm->None;
|
||||
@ -128,19 +124,19 @@ void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
vm->bind_func(type, __enter__, 1, PK_LAMBDA(args[0]));
|
||||
}
|
||||
|
||||
FileIO::FileIO(VM* vm, const Str& file, const Str& mode){
|
||||
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(){
|
||||
void FileIO::close() {
|
||||
if(fp == nullptr) return;
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
|
||||
void add_module_io(VM* vm){
|
||||
void add_module_io(VM* vm) {
|
||||
PyObject* mod = vm->new_module("io");
|
||||
vm->register_user_class<FileIO>(mod, "FileIO");
|
||||
|
||||
@ -148,105 +144,104 @@ void add_module_io(VM* vm){
|
||||
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){
|
||||
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){
|
||||
void add_module_os(VM* vm) {
|
||||
PyObject* mod = vm->new_module("os");
|
||||
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(VM::tp_object);
|
||||
mod->attr().set("path", path_obj);
|
||||
|
||||
|
||||
// Working directory is shared by all VMs!!
|
||||
vm->bind_func(mod, "getcwd", 0, [](VM* vm, ArgsView args){
|
||||
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){
|
||||
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){
|
||||
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{
|
||||
try {
|
||||
di = std::filesystem::directory_iterator(path);
|
||||
}catch(std::filesystem::filesystem_error&){
|
||||
vm->IOError(path.string());
|
||||
}
|
||||
} catch(std::filesystem::filesystem_error&) { vm->IOError(path.string()); }
|
||||
List ret;
|
||||
for(auto& p: di) ret.push_back(VAR(p.path().filename().string()));
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
vm->bind_func(path_obj, "join", -1, [](VM* vm, ArgsView args) {
|
||||
std::filesystem::path path;
|
||||
for(int i=0; i<args.size(); i++){
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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;
|
||||
}
|
||||
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
|
||||
} // namespace pkpy
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,9 +8,9 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
struct PyStructTime{
|
||||
struct PyStructTime {
|
||||
int tm_year;
|
||||
int tm_mon;
|
||||
int tm_mday;
|
||||
@ -21,7 +21,7 @@ struct PyStructTime{
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
|
||||
PyStructTime(std::time_t t){
|
||||
PyStructTime(std::time_t t) {
|
||||
std::tm* tm = std::localtime(&t);
|
||||
tm_year = tm->tm_year + 1900;
|
||||
tm_mon = tm->tm_mon + 1;
|
||||
@ -34,7 +34,7 @@ struct PyStructTime{
|
||||
tm_isdst = tm->tm_isdst;
|
||||
}
|
||||
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type){
|
||||
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);
|
||||
@ -47,7 +47,7 @@ struct PyStructTime{
|
||||
}
|
||||
};
|
||||
|
||||
void add_module_time(VM* vm){
|
||||
void add_module_time(VM* vm) {
|
||||
PyObject* mod = vm->new_module("time");
|
||||
vm->register_user_class<PyStructTime>(mod, "struct_time");
|
||||
|
||||
@ -59,7 +59,7 @@ void add_module_time(VM* vm){
|
||||
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){
|
||||
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;
|
||||
@ -74,7 +74,7 @@ void add_module_time(VM* vm){
|
||||
});
|
||||
}
|
||||
|
||||
void add_module_sys(VM* vm){
|
||||
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]));
|
||||
@ -97,14 +97,14 @@ void add_module_sys(VM* vm){
|
||||
});
|
||||
}
|
||||
|
||||
void add_module_json(VM* vm){
|
||||
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)){
|
||||
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{
|
||||
} else {
|
||||
sv = CAST(Str&, args[0]).sv();
|
||||
}
|
||||
CodeObject_ code = vm->compile(sv, "<json>", JSON_MODE);
|
||||
@ -117,10 +117,10 @@ void add_module_json(VM* vm){
|
||||
}
|
||||
|
||||
// https://docs.python.org/3.5/library/math.html
|
||||
void add_module_math(VM* vm){
|
||||
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("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()));
|
||||
|
||||
@ -131,7 +131,7 @@ void add_module_math(VM* vm){
|
||||
List& list = CAST(List&, args[0]);
|
||||
double sum = 0;
|
||||
double c = 0;
|
||||
for(PyVar arg : list){
|
||||
for(PyVar arg: list) {
|
||||
double x = CAST_F(arg);
|
||||
double y = x - c;
|
||||
double t = sum + y;
|
||||
@ -145,7 +145,7 @@ void add_module_math(VM* vm){
|
||||
i64 b = CAST(i64, args[1]);
|
||||
if(a < 0) a = -a;
|
||||
if(b < 0) b = -b;
|
||||
while(b != 0){
|
||||
while(b != 0) {
|
||||
i64 t = b;
|
||||
b = a % b;
|
||||
a = t;
|
||||
@ -165,7 +165,7 @@ void add_module_math(VM* vm){
|
||||
|
||||
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){
|
||||
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));
|
||||
@ -185,7 +185,7 @@ void add_module_math(VM* vm){
|
||||
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)));
|
||||
|
||||
@ -199,12 +199,13 @@ void add_module_math(VM* vm){
|
||||
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;
|
||||
for(i64 i = 2; i <= n; i++)
|
||||
r *= i;
|
||||
return VAR(r);
|
||||
});
|
||||
}
|
||||
|
||||
void add_module_traceback(VM* vm){
|
||||
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");
|
||||
@ -220,13 +221,13 @@ void add_module_traceback(VM* vm){
|
||||
});
|
||||
}
|
||||
|
||||
void add_module_dis(VM* vm){
|
||||
void add_module_dis(VM* vm) {
|
||||
PyObject* mod = vm->new_module("dis");
|
||||
|
||||
vm->bind_func(mod, "dis", 1, [](VM* vm, ArgsView args) {
|
||||
CodeObject_ code;
|
||||
PyVar obj = args[0];
|
||||
if(is_type(obj, vm->tp_str)){
|
||||
if(is_type(obj, vm->tp_str)) {
|
||||
const Str& source = CAST(Str, obj);
|
||||
code = vm->compile(source, "<dis>", EXEC_MODE);
|
||||
}
|
||||
@ -238,37 +239,36 @@ void add_module_dis(VM* vm){
|
||||
});
|
||||
}
|
||||
|
||||
void add_module_gc(VM* vm){
|
||||
void add_module_gc(VM* vm) {
|
||||
PyObject* mod = vm->new_module("gc");
|
||||
vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(vm->heap.collect())));
|
||||
}
|
||||
|
||||
void add_module_enum(VM* vm){
|
||||
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);
|
||||
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));
|
||||
}
|
||||
};
|
||||
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){
|
||||
void add_module___builtins(VM* vm) {
|
||||
PyObject* mod = vm->new_module("__builtins");
|
||||
|
||||
vm->bind_func(mod, "next", 1, [](VM* vm, ArgsView args){
|
||||
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){
|
||||
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");
|
||||
@ -277,11 +277,11 @@ void add_module___builtins(VM* vm){
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/************************************************/
|
||||
#if PK_ENABLE_PROFILER
|
||||
struct LineProfilerW;
|
||||
struct _LpGuard{
|
||||
|
||||
struct _LpGuard {
|
||||
PK_ALWAYS_PASS_BY_POINTER(_LpGuard)
|
||||
LineProfilerW* lp;
|
||||
VM* vm;
|
||||
@ -290,16 +290,16 @@ struct _LpGuard{
|
||||
};
|
||||
|
||||
// line_profiler wrapper
|
||||
struct LineProfilerW{
|
||||
struct LineProfilerW {
|
||||
LineProfiler profiler;
|
||||
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type){
|
||||
vm->bind_func(type, __new__, 1, [](VM* vm, ArgsView args){
|
||||
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){
|
||||
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();
|
||||
@ -307,19 +307,20 @@ struct LineProfilerW{
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "runcall(self, func, *args)", [](VM* vm, ArgsView view){
|
||||
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);
|
||||
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){
|
||||
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;
|
||||
@ -327,28 +328,23 @@ struct LineProfilerW{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
_LpGuard::_LpGuard(LineProfilerW* lp, VM* vm): lp(lp), vm(vm) {
|
||||
if(vm->_profiler){
|
||||
vm->ValueError("only one profiler can be enabled at a time");
|
||||
}
|
||||
_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(){
|
||||
_LpGuard::~_LpGuard() {
|
||||
vm->_profiler = nullptr;
|
||||
lp->profiler.end();
|
||||
}
|
||||
|
||||
void add_module_line_profiler(VM *vm){
|
||||
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;
|
||||
}
|
||||
void add_module_line_profiler(VM* vm) { (void)vm; }
|
||||
#endif
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -36,23 +36,21 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
struct mt19937{
|
||||
static const int N = 624;
|
||||
static const int M = 397;
|
||||
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 */
|
||||
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);
|
||||
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[]. */
|
||||
@ -63,32 +61,31 @@ struct mt19937{
|
||||
}
|
||||
|
||||
/* generates a random number on [0,0xffffffff]-interval */
|
||||
uint32_t next_uint32(void)
|
||||
{
|
||||
uint32_t next_uint32(void) {
|
||||
uint32_t y;
|
||||
static uint32_t mag01[2]={0x0UL, MATRIX_A};
|
||||
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 */
|
||||
if(mti >= N) { /* generate N words at one time */
|
||||
int kk;
|
||||
|
||||
if (mti == N+1) /* if init_genrand() has not been called, */
|
||||
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 = 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];
|
||||
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];
|
||||
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 */
|
||||
@ -100,44 +97,36 @@ struct mt19937{
|
||||
return y;
|
||||
}
|
||||
|
||||
uint64_t next_uint64(void){
|
||||
return (uint64_t(next_uint32()) << 32) | next_uint32();
|
||||
}
|
||||
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 */
|
||||
}
|
||||
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){
|
||||
int64_t randint(int64_t a, int64_t b) {
|
||||
uint64_t delta = b - a + 1;
|
||||
if(delta < 0x80000000UL){
|
||||
if(delta < 0x80000000UL) {
|
||||
return a + next_uint32() % (uint32_t)delta;
|
||||
}else{
|
||||
} else {
|
||||
return a + next_uint64() % delta;
|
||||
}
|
||||
}
|
||||
|
||||
float uniform(float a, float b){
|
||||
return a + random() * (b - a);
|
||||
}
|
||||
float uniform(float a, float b) { return a + random() * (b - a); }
|
||||
};
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
struct Random{
|
||||
struct Random {
|
||||
mt19937 gen;
|
||||
|
||||
Random(){
|
||||
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){
|
||||
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);
|
||||
});
|
||||
@ -152,7 +141,7 @@ struct Random{
|
||||
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");
|
||||
if(a > b) vm->ValueError("randint(a, b): a must be less than or equal to b");
|
||||
return VAR(self.gen.randint(a, b));
|
||||
});
|
||||
|
||||
@ -165,14 +154,14 @@ struct Random{
|
||||
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);
|
||||
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--){
|
||||
for(int i = L.size() - 1; i > 0; i--) {
|
||||
int j = self.gen.randint(0, i);
|
||||
std::swap(L[i], L[j]);
|
||||
}
|
||||
@ -183,7 +172,7 @@ struct Random{
|
||||
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);
|
||||
int index = self.gen.randint(0, view.size() - 1);
|
||||
return view[index];
|
||||
});
|
||||
|
||||
@ -194,20 +183,21 @@ struct Random{
|
||||
int size = view.size();
|
||||
if(size == 0) vm->IndexError("cannot choose from an empty sequence");
|
||||
array<f64> cum_weights(size);
|
||||
if(args[2] == vm->None){
|
||||
for(int i = 0; i < size; i++) cum_weights[i] = i + 1;
|
||||
}else{
|
||||
if(args[2] == vm->None) {
|
||||
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++){
|
||||
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++){
|
||||
for(int i = 0; i < k; i++) {
|
||||
f64 r = self.gen.uniform(0.0, cum_weights[size - 1]);
|
||||
int idx = std::lower_bound(cum_weights.begin(), cum_weights.end(), r) - cum_weights.begin();
|
||||
result[i] = data[idx];
|
||||
@ -217,7 +207,7 @@ struct Random{
|
||||
}
|
||||
};
|
||||
|
||||
void add_module_random(VM* vm){
|
||||
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>();
|
||||
@ -230,4 +220,4 @@ void add_module_random(VM* vm){
|
||||
mod->attr().set("choices", vm->getattr(instance, "choices"));
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "pocketpy/objects/builtins.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
PyVar const PY_OP_CALL(Type(), new PyObject(Type()));
|
||||
PyVar const PY_OP_YIELD(Type(), new PyObject(Type()));
|
||||
} // namespace pkpy
|
||||
namespace pkpy {
|
||||
const PyVar PY_OP_CALL(Type(), new PyObject(Type()));
|
||||
const PyVar PY_OP_YIELD(Type(), new PyObject(Type()));
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#include "pocketpy/objects/codeobject.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
CodeObject::CodeObject(std::shared_ptr<SourceData> src, const Str& name):
|
||||
src(src), name(name), nlocals(0), start_line(-1), end_line(-1) {
|
||||
blocks.push_back(CodeBlock(CodeBlockType::NO_BLOCK, -1, 0));
|
||||
}
|
||||
} // namespace pkpy
|
||||
CodeObject::CodeObject(std::shared_ptr<SourceData> src, const Str& name) :
|
||||
src(src), name(name), nlocals(0), start_line(-1), end_line(-1) {
|
||||
blocks.push_back(CodeBlock(CodeBlockType::NO_BLOCK, -1, 0));
|
||||
}
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,174 +1,180 @@
|
||||
#include "pocketpy/objects/dict.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
namespace pkpy {
|
||||
|
||||
Dict::Dict(): _capacity(__Capacity),
|
||||
_mask(__Capacity-1),
|
||||
_size(0), _critical_size(__Capacity*__LoadFactor+0.5f), _head_idx(-1), _tail_idx(-1){
|
||||
__alloc_items();
|
||||
}
|
||||
Dict::Dict() :
|
||||
_capacity(__Capacity), _mask(__Capacity - 1), _size(0), _critical_size(__Capacity * __LoadFactor + 0.5f),
|
||||
_head_idx(-1), _tail_idx(-1) {
|
||||
__alloc_items();
|
||||
}
|
||||
|
||||
void Dict::__alloc_items(){
|
||||
_items = (Item*)std::malloc(_capacity * sizeof(Item));
|
||||
for(int i=0; i<_capacity; i++){
|
||||
_items[i].first = nullptr;
|
||||
_items[i].second = nullptr;
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Dict::Dict(Dict&& other){
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
_items = other._items;
|
||||
other._items = nullptr;
|
||||
}
|
||||
|
||||
Dict::Dict(const Dict& other){
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
// copy items
|
||||
_items = (Item*)std::malloc(_capacity * sizeof(Item));
|
||||
std::memcpy(_items, other._items, _capacity * sizeof(Item));
|
||||
}
|
||||
|
||||
void Dict::set(VM* vm, PyVar key, PyVar val){
|
||||
// do possible rehash
|
||||
if(_size+1 > _critical_size) _rehash(vm);
|
||||
bool ok; int i;
|
||||
_probe_1(vm, key, ok, i);
|
||||
if(!ok) {
|
||||
_size++;
|
||||
_items[i].first = key;
|
||||
|
||||
// append to tail
|
||||
if(_size == 0+1){
|
||||
_head_idx = i;
|
||||
_tail_idx = i;
|
||||
}else{
|
||||
_items[i].prev = _tail_idx;
|
||||
_items[_tail_idx].next = i;
|
||||
_tail_idx = i;
|
||||
}
|
||||
}
|
||||
_items[i].second = val;
|
||||
}
|
||||
|
||||
void Dict::_rehash(VM* vm){
|
||||
Item* old_items = _items;
|
||||
int old_head_idx = _head_idx;
|
||||
|
||||
_capacity *= 4;
|
||||
_mask = _capacity - 1;
|
||||
_size = 0;
|
||||
_critical_size = _capacity*__LoadFactor+0.5f;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
|
||||
__alloc_items();
|
||||
|
||||
// copy old items to new dict
|
||||
int i = old_head_idx;
|
||||
while(i != -1){
|
||||
set(vm, old_items[i].first, old_items[i].second);
|
||||
i = old_items[i].next;
|
||||
}
|
||||
|
||||
std::free(old_items);
|
||||
}
|
||||
|
||||
|
||||
PyVar Dict::try_get(VM* vm, PyVar key) const{
|
||||
bool ok; int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
if(!ok) return nullptr;
|
||||
return _items[i].second;
|
||||
}
|
||||
|
||||
bool Dict::contains(VM* vm, PyVar key) const{
|
||||
bool ok; int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Dict::del(VM* vm, PyVar key){
|
||||
bool ok; int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
if(!ok) return false;
|
||||
void Dict::__alloc_items() {
|
||||
_items = (Item*)std::malloc(_capacity * sizeof(Item));
|
||||
for(int i = 0; i < _capacity; i++) {
|
||||
_items[i].first = nullptr;
|
||||
// _items[i].second = PY_DELETED_SLOT; // do not change .second if it is not NULL, it means the slot is occupied by a deleted item
|
||||
_size--;
|
||||
|
||||
if(_size == 0){
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
}else{
|
||||
if(_head_idx == i){
|
||||
_head_idx = _items[i].next;
|
||||
_items[_head_idx].prev = -1;
|
||||
}else if(_tail_idx == i){
|
||||
_tail_idx = _items[i].prev;
|
||||
_items[_tail_idx].next = -1;
|
||||
}else{
|
||||
_items[_items[i].prev].next = _items[i].next;
|
||||
_items[_items[i].next].prev = _items[i].prev;
|
||||
}
|
||||
}
|
||||
_items[i].second = nullptr;
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Dict::update(VM* vm, const Dict& other){
|
||||
other.apply([&](PyVar k, PyVar v){ set(vm, k, v); });
|
||||
}
|
||||
Dict::Dict(Dict&& other) {
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
_items = other._items;
|
||||
other._items = nullptr;
|
||||
}
|
||||
|
||||
Tuple Dict::keys() const{
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1){
|
||||
t[j++] = _items[i].first;
|
||||
i = _items[i].next;
|
||||
Dict::Dict(const Dict& other) {
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
// copy items
|
||||
_items = (Item*)std::malloc(_capacity * sizeof(Item));
|
||||
std::memcpy(_items, other._items, _capacity * sizeof(Item));
|
||||
}
|
||||
|
||||
void Dict::set(VM* vm, PyVar key, PyVar val) {
|
||||
// do possible rehash
|
||||
if(_size + 1 > _critical_size) _rehash(vm);
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_1(vm, key, ok, i);
|
||||
if(!ok) {
|
||||
_size++;
|
||||
_items[i].first = key;
|
||||
|
||||
// append to tail
|
||||
if(_size == 0 + 1) {
|
||||
_head_idx = i;
|
||||
_tail_idx = i;
|
||||
} else {
|
||||
_items[i].prev = _tail_idx;
|
||||
_items[_tail_idx].next = i;
|
||||
_tail_idx = i;
|
||||
}
|
||||
assert(j == _size);
|
||||
return t;
|
||||
}
|
||||
_items[i].second = val;
|
||||
}
|
||||
|
||||
void Dict::_rehash(VM* vm) {
|
||||
Item* old_items = _items;
|
||||
int old_head_idx = _head_idx;
|
||||
|
||||
_capacity *= 4;
|
||||
_mask = _capacity - 1;
|
||||
_size = 0;
|
||||
_critical_size = _capacity * __LoadFactor + 0.5f;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
|
||||
__alloc_items();
|
||||
|
||||
// copy old items to new dict
|
||||
int i = old_head_idx;
|
||||
while(i != -1) {
|
||||
set(vm, old_items[i].first, old_items[i].second);
|
||||
i = old_items[i].next;
|
||||
}
|
||||
|
||||
Tuple Dict::values() const{
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1){
|
||||
t[j++] = _items[i].second;
|
||||
i = _items[i].next;
|
||||
}
|
||||
assert(j == _size);
|
||||
return t;
|
||||
}
|
||||
std::free(old_items);
|
||||
}
|
||||
|
||||
void Dict::clear(){
|
||||
_size = 0;
|
||||
PyVar Dict::try_get(VM* vm, PyVar key) const {
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
if(!ok) return nullptr;
|
||||
return _items[i].second;
|
||||
}
|
||||
|
||||
bool Dict::contains(VM* vm, PyVar key) const {
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Dict::del(VM* vm, PyVar key) {
|
||||
bool ok;
|
||||
int i;
|
||||
_probe_0(vm, key, ok, i);
|
||||
if(!ok) return false;
|
||||
_items[i].first = nullptr;
|
||||
// _items[i].second = PY_DELETED_SLOT; // do not change .second if it is not NULL, it means the slot is occupied by
|
||||
// a deleted item
|
||||
_size--;
|
||||
|
||||
if(_size == 0) {
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
for(int i=0; i<_capacity; i++){
|
||||
_items[i].first = nullptr;
|
||||
_items[i].second = nullptr;
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
} else {
|
||||
if(_head_idx == i) {
|
||||
_head_idx = _items[i].next;
|
||||
_items[_head_idx].prev = -1;
|
||||
} else if(_tail_idx == i) {
|
||||
_tail_idx = _items[i].prev;
|
||||
_items[_tail_idx].next = -1;
|
||||
} else {
|
||||
_items[_items[i].prev].next = _items[i].next;
|
||||
_items[_items[i].next].prev = _items[i].prev;
|
||||
}
|
||||
}
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
Dict::~Dict(){
|
||||
if(_items) std::free(_items);
|
||||
void Dict::update(VM* vm, const Dict& other) {
|
||||
other.apply([&](PyVar k, PyVar v) {
|
||||
set(vm, k, v);
|
||||
});
|
||||
}
|
||||
|
||||
Tuple Dict::keys() const {
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1) {
|
||||
t[j++] = _items[i].first;
|
||||
i = _items[i].next;
|
||||
}
|
||||
} // namespace pkpy
|
||||
assert(j == _size);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tuple Dict::values() const {
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1) {
|
||||
t[j++] = _items[i].second;
|
||||
i = _items[i].next;
|
||||
}
|
||||
assert(j == _size);
|
||||
return t;
|
||||
}
|
||||
|
||||
void Dict::clear() {
|
||||
_size = 0;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
for(int i = 0; i < _capacity; i++) {
|
||||
_items[i].first = nullptr;
|
||||
_items[i].second = nullptr;
|
||||
_items[i].prev = -1;
|
||||
_items[i].next = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Dict::~Dict() {
|
||||
if(_items) std::free(_items);
|
||||
}
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
#include "pocketpy/objects/error.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
Str Exception::summary() const {
|
||||
SStream ss;
|
||||
if(is_re) ss << "Traceback (most recent call last):\n";
|
||||
// while(!st.empty()) {
|
||||
// ss << st.top().snapshot() << '\n';
|
||||
// st.pop();
|
||||
// }
|
||||
const auto& container = stacktrace.container();
|
||||
for(int i=container.size()-1; i>=0; i--){
|
||||
ss << container[i].snapshot() << '\n';
|
||||
}
|
||||
if (!msg.empty()) ss << type.sv() << ": " << msg;
|
||||
else ss << type.sv();
|
||||
return ss.str();
|
||||
namespace pkpy {
|
||||
Str Exception::summary() const {
|
||||
SStream ss;
|
||||
if(is_re) ss << "Traceback (most recent call last):\n";
|
||||
// while(!st.empty()) {
|
||||
// ss << st.top().snapshot() << '\n';
|
||||
// st.pop();
|
||||
// }
|
||||
const auto& container = stacktrace.container();
|
||||
for(int i = container.size() - 1; i >= 0; i--) {
|
||||
ss << container[i].snapshot() << '\n';
|
||||
}
|
||||
if(!msg.empty())
|
||||
ss << type.sv() << ": " << msg;
|
||||
else
|
||||
ss << type.sv();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include "pocketpy/objects/object.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
PyVar::PyVar(PyObject* p): PyVar(p->type, p) {}
|
||||
} // namespace pkpy
|
||||
namespace pkpy {
|
||||
PyVar::PyVar(PyObject* p) : PyVar(p->type, p) {}
|
||||
} // namespace pkpy
|
||||
|
||||
@ -1,66 +1,68 @@
|
||||
#include "pocketpy/objects/sourcedata.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
SourceData::SourceData(std::string_view source, const Str& filename, CompileMode mode): filename(filename), mode(mode) {
|
||||
int index = 0;
|
||||
// Skip utf8 BOM if there is any.
|
||||
if (strncmp(source.data(), "\xEF\xBB\xBF", 3) == 0) index += 3;
|
||||
// Drop all '\r'
|
||||
SStream ss(source.size() + 1);
|
||||
while(index < source.size()){
|
||||
if(source[index] != '\r') ss << source[index];
|
||||
index++;
|
||||
namespace pkpy {
|
||||
SourceData::SourceData(std::string_view source, const Str& filename, CompileMode mode) :
|
||||
filename(filename), mode(mode) {
|
||||
int index = 0;
|
||||
// Skip utf8 BOM if there is any.
|
||||
if(strncmp(source.data(), "\xEF\xBB\xBF", 3) == 0) index += 3;
|
||||
// Drop all '\r'
|
||||
SStream ss(source.size() + 1);
|
||||
while(index < source.size()) {
|
||||
if(source[index] != '\r') ss << source[index];
|
||||
index++;
|
||||
}
|
||||
this->source = ss.str();
|
||||
if(this->source.size > 5 && this->source.sv().substr(0, 5) == "pkpy:") {
|
||||
this->is_precompiled = true;
|
||||
} else {
|
||||
this->is_precompiled = false;
|
||||
}
|
||||
line_starts.push_back(this->source.c_str());
|
||||
}
|
||||
|
||||
SourceData::SourceData(const Str& filename, CompileMode mode) : filename(filename), mode(mode) {
|
||||
line_starts.push_back(this->source.c_str());
|
||||
}
|
||||
|
||||
std::pair<const char*, const char*> SourceData::_get_line(int lineno) const {
|
||||
if(is_precompiled || lineno == -1) return {nullptr, nullptr};
|
||||
lineno -= 1;
|
||||
if(lineno < 0) lineno = 0;
|
||||
const char* _start = line_starts[lineno];
|
||||
const char* i = _start;
|
||||
// max 300 chars
|
||||
while(*i != '\n' && *i != '\0' && i - _start < 300)
|
||||
i++;
|
||||
return {_start, i};
|
||||
}
|
||||
|
||||
std::string_view SourceData::get_line(int lineno) const {
|
||||
auto [_0, _1] = _get_line(lineno);
|
||||
if(_0 && _1) return std::string_view(_0, _1 - _0);
|
||||
return "<?>";
|
||||
}
|
||||
|
||||
Str SourceData::snapshot(int lineno, const char* cursor, std::string_view name) const {
|
||||
SStream ss;
|
||||
ss << " " << "File \"" << filename << "\", line " << lineno;
|
||||
if(!name.empty()) ss << ", in " << name;
|
||||
if(!is_precompiled) {
|
||||
ss << '\n';
|
||||
std::pair<const char*, const char*> pair = _get_line(lineno);
|
||||
Str line = "<?>";
|
||||
int removed_spaces = 0;
|
||||
if(pair.first && pair.second) {
|
||||
line = Str(pair.first, pair.second - pair.first).lstrip();
|
||||
removed_spaces = pair.second - pair.first - line.length();
|
||||
if(line.empty()) line = "<?>";
|
||||
}
|
||||
this->source = ss.str();
|
||||
if(this->source.size>5 && this->source.sv().substr(0, 5)=="pkpy:"){
|
||||
this->is_precompiled = true;
|
||||
}else{
|
||||
this->is_precompiled = false;
|
||||
ss << " " << line;
|
||||
if(cursor && line != "<?>" && cursor >= pair.first && cursor <= pair.second) {
|
||||
auto column = cursor - pair.first - removed_spaces;
|
||||
if(column >= 0) ss << "\n " << std::string(column, ' ') << "^";
|
||||
}
|
||||
line_starts.push_back(this->source.c_str());
|
||||
}
|
||||
|
||||
SourceData::SourceData(const Str& filename, CompileMode mode): filename(filename), mode(mode) {
|
||||
line_starts.push_back(this->source.c_str());
|
||||
}
|
||||
|
||||
std::pair<const char*,const char*> SourceData::_get_line(int lineno) const {
|
||||
if(is_precompiled || lineno == -1) return {nullptr, nullptr};
|
||||
lineno -= 1;
|
||||
if(lineno < 0) lineno = 0;
|
||||
const char* _start = line_starts[lineno];
|
||||
const char* i = _start;
|
||||
// max 300 chars
|
||||
while(*i != '\n' && *i != '\0' && i-_start < 300) i++;
|
||||
return {_start, i};
|
||||
}
|
||||
|
||||
std::string_view SourceData::get_line(int lineno) const{
|
||||
auto [_0, _1] = _get_line(lineno);
|
||||
if(_0 && _1) return std::string_view(_0, _1-_0);
|
||||
return "<?>";
|
||||
}
|
||||
|
||||
Str SourceData::snapshot(int lineno, const char* cursor, std::string_view name) const{
|
||||
SStream ss;
|
||||
ss << " " << "File \"" << filename << "\", line " << lineno;
|
||||
if(!name.empty()) ss << ", in " << name;
|
||||
if(!is_precompiled){
|
||||
ss << '\n';
|
||||
std::pair<const char*,const char*> pair = _get_line(lineno);
|
||||
Str line = "<?>";
|
||||
int removed_spaces = 0;
|
||||
if(pair.first && pair.second){
|
||||
line = Str(pair.first, pair.second-pair.first).lstrip();
|
||||
removed_spaces = pair.second - pair.first - line.length();
|
||||
if(line.empty()) line = "<?>";
|
||||
}
|
||||
ss << " " << line;
|
||||
if(cursor && line != "<?>" && cursor >= pair.first && cursor <= pair.second){
|
||||
auto column = cursor - pair.first - removed_spaces;
|
||||
if(column >= 0) ss << "\n " << std::string(column, ' ') << "^";
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace pkpy
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace pkpy
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
Tuple::Tuple(int n){
|
||||
if(n <= INLINED_SIZE){
|
||||
Tuple::Tuple(int n) {
|
||||
if(n <= INLINED_SIZE) {
|
||||
this->_args = _inlined;
|
||||
}else{
|
||||
} else {
|
||||
this->_args = (PyVar*)std::malloc(n * sizeof(PyVar));
|
||||
}
|
||||
this->_size = n;
|
||||
@ -13,39 +13,44 @@ Tuple::Tuple(int n){
|
||||
|
||||
Tuple::Tuple(Tuple&& other) noexcept {
|
||||
_size = other._size;
|
||||
if(other.is_inlined()){
|
||||
if(other.is_inlined()) {
|
||||
_args = _inlined;
|
||||
for(int i=0; i<_size; i++) _args[i] = other._args[i];
|
||||
}else{
|
||||
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){
|
||||
Tuple::Tuple(PyVar _0, PyVar _1) : Tuple(2) {
|
||||
_args[0] = _0;
|
||||
_args[1] = _1;
|
||||
}
|
||||
|
||||
Tuple::Tuple(PyVar _0, PyVar _1, PyVar _2): Tuple(3){
|
||||
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); }
|
||||
Tuple::~Tuple() {
|
||||
if(!is_inlined()) std::free(_args);
|
||||
}
|
||||
|
||||
List ArgsView::to_list() const{
|
||||
List ArgsView::to_list() const {
|
||||
List ret(size());
|
||||
for(int i=0; i<size(); i++) ret[i] = _begin[i];
|
||||
for(int i = 0; i < size(); i++)
|
||||
ret[i] = _begin[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
Tuple ArgsView::to_tuple() const{
|
||||
Tuple ArgsView::to_tuple() const {
|
||||
Tuple ret(size());
|
||||
for(int i=0; i<size(); i++) ret[i] = _begin[i];
|
||||
for(int i = 0; i < size(); i++)
|
||||
ret[i] = _begin[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
} // namespace pkpy
|
||||
|
||||
556
src/pocketpy.cpp
556
src/pocketpy.cpp
File diff suppressed because it is too large
Load Diff
@ -7,32 +7,29 @@
|
||||
|
||||
using 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_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;
|
||||
#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();
|
||||
}
|
||||
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.top().end();
|
||||
}
|
||||
|
||||
static PyVar stack_item(VM* vm, int index){
|
||||
static PyVar stack_item(VM* vm, int index) {
|
||||
PyVar* begin;
|
||||
PyVar* end = vm->s_data.end();
|
||||
if(vm->callstack.empty()){
|
||||
if(vm->callstack.empty()) {
|
||||
begin = vm->s_data.begin();
|
||||
}else{
|
||||
} else {
|
||||
assert(!vm->__c.s_view.empty());
|
||||
begin = vm->__c.s_view.top().begin();
|
||||
}
|
||||
@ -42,27 +39,24 @@ static PyVar stack_item(VM* vm, int index){
|
||||
return begin[index];
|
||||
}
|
||||
|
||||
#define PK_PROTECTED(__B) \
|
||||
try{ __B } \
|
||||
catch(TopLevelException e) { \
|
||||
vm->__c.error = 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; \
|
||||
#define PK_PROTECTED(__B) \
|
||||
try { \
|
||||
__B \
|
||||
} catch(TopLevelException e) { \
|
||||
vm->__c.error = 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PyVar res;
|
||||
PK_PROTECTED(
|
||||
@ -72,16 +66,15 @@ bool pkpy_exec(pkpy_vm* vm_handle, const char* source) {
|
||||
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;
|
||||
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;
|
||||
if(module == nullptr){ mod = vm->_main;
|
||||
}else{
|
||||
mod = vm->_modules[module].get(); // may raise
|
||||
mod = vm->_modules[module].get(); // may raise
|
||||
}
|
||||
CodeObject_ code = vm->compile(source, filename, (CompileMode)mode);
|
||||
res = vm->_exec(code, mod);
|
||||
@ -89,13 +82,13 @@ bool pkpy_exec_2(pkpy_vm* vm_handle, const char* source, const char* filename, i
|
||||
return res != nullptr;
|
||||
}
|
||||
|
||||
void pkpy_set_main_argv(pkpy_vm* vm_handle, int argc, char** argv){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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;
|
||||
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);
|
||||
@ -104,15 +97,15 @@ bool pkpy_dup(pkpy_vm* vm_handle, int n){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkpy_pop(pkpy_vm* vm_handle, int n){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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){
|
||||
bool pkpy_pop_top(pkpy_vm* vm_handle) {
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||
@ -120,7 +113,7 @@ bool pkpy_pop_top(pkpy_vm* vm_handle){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkpy_dup_top(pkpy_vm* vm_handle){
|
||||
bool pkpy_dup_top(pkpy_vm* vm_handle) {
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||
@ -128,7 +121,7 @@ bool pkpy_dup_top(pkpy_vm* vm_handle){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkpy_rot_two(pkpy_vm* vm_handle){
|
||||
bool pkpy_rot_two(pkpy_vm* vm_handle) {
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(2)
|
||||
@ -136,19 +129,17 @@ bool pkpy_rot_two(pkpy_vm* vm_handle){
|
||||
return true;
|
||||
}
|
||||
|
||||
int pkpy_stack_size(pkpy_vm* vm_handle){
|
||||
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->callstack.empty()) { return vm->s_data.size(); }
|
||||
if(vm->__c.s_view.empty()) exit(127);
|
||||
return vm->s_data._sp - vm->__c.s_view.top().begin();
|
||||
}
|
||||
|
||||
// int
|
||||
bool pkpy_push_int(pkpy_vm* vm_handle, int value) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PyVar res;
|
||||
PK_PROTECTED(
|
||||
@ -159,16 +150,16 @@ bool pkpy_push_int(pkpy_vm* vm_handle, int value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkpy_is_int(pkpy_vm* vm_handle, int i){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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;
|
||||
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);
|
||||
@ -179,15 +170,15 @@ bool pkpy_to_int(pkpy_vm* vm_handle, int i, int* out){
|
||||
|
||||
// float
|
||||
bool pkpy_push_float(pkpy_vm* vm_handle, double value) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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;
|
||||
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);
|
||||
@ -195,8 +186,8 @@ bool pkpy_is_float(pkpy_vm* vm_handle, int i){
|
||||
)
|
||||
}
|
||||
|
||||
bool pkpy_to_float(pkpy_vm* vm_handle, int i, double* out){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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);
|
||||
@ -207,14 +198,14 @@ bool pkpy_to_float(pkpy_vm* vm_handle, int i, double* out){
|
||||
|
||||
// bool
|
||||
bool pkpy_push_bool(pkpy_vm* vm_handle, bool value) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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;
|
||||
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);
|
||||
@ -222,8 +213,8 @@ bool pkpy_is_bool(pkpy_vm* vm_handle, int i){
|
||||
)
|
||||
}
|
||||
|
||||
bool pkpy_to_bool(pkpy_vm* vm_handle, int i, bool* out){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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);
|
||||
@ -234,15 +225,15 @@ bool pkpy_to_bool(pkpy_vm* vm_handle, int i, bool* out){
|
||||
|
||||
// string
|
||||
bool pkpy_push_string(pkpy_vm* vm_handle, pkpy_CString value) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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;
|
||||
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);
|
||||
@ -250,8 +241,8 @@ bool pkpy_is_string(pkpy_vm* vm_handle, int i){
|
||||
)
|
||||
}
|
||||
|
||||
bool pkpy_to_string(pkpy_vm* vm_handle, int i, pkpy_CString* out){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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);
|
||||
@ -263,15 +254,15 @@ bool pkpy_to_string(pkpy_vm* vm_handle, int i, pkpy_CString* out){
|
||||
|
||||
// void_p
|
||||
bool pkpy_push_voidp(pkpy_vm* vm_handle, void* value) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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;
|
||||
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);
|
||||
@ -279,8 +270,8 @@ bool pkpy_is_voidp(pkpy_vm* vm_handle, int i){
|
||||
)
|
||||
}
|
||||
|
||||
bool pkpy_to_voidp(pkpy_vm* vm_handle, int i, void** out){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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);
|
||||
@ -292,14 +283,14 @@ bool pkpy_to_voidp(pkpy_vm* vm_handle, int i, void** out){
|
||||
|
||||
// none
|
||||
bool pkpy_push_none(pkpy_vm* vm_handle) {
|
||||
VM* vm = (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;
|
||||
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);
|
||||
@ -309,25 +300,25 @@ bool pkpy_is_none(pkpy_vm* vm_handle, int i){
|
||||
|
||||
// null
|
||||
bool pkpy_push_null(pkpy_vm* vm_handle) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
vm->s_data.push(PY_NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct TempViewPopper{
|
||||
struct TempViewPopper {
|
||||
VM* vm;
|
||||
bool used;
|
||||
|
||||
TempViewPopper(VM* vm): vm(vm), used(false) {}
|
||||
TempViewPopper(VM* vm) : vm(vm), used(false) {}
|
||||
|
||||
void restore() noexcept{
|
||||
void restore() noexcept {
|
||||
if(used) return;
|
||||
vm->__c.s_view.pop();
|
||||
used = true;
|
||||
}
|
||||
|
||||
~TempViewPopper(){ restore(); }
|
||||
~TempViewPopper() { restore(); }
|
||||
};
|
||||
|
||||
// function
|
||||
@ -337,25 +328,25 @@ static PyVar c_function_wrapper(VM* vm, ArgsView args) {
|
||||
|
||||
vm->__c.s_view.push(args);
|
||||
TempViewPopper _tvp(vm);
|
||||
int retc = f((pkpy_vm*)vm); // may raise, _tvp will handle this via RAII
|
||||
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;
|
||||
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);
|
||||
assert(retc == vm->s_data._sp - curr_sp);
|
||||
if(retc == 0) return vm->None;
|
||||
if (retc == 1) return vm->s_data.popx();
|
||||
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;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PyVar f_obj;
|
||||
PK_PROTECTED(
|
||||
@ -367,7 +358,7 @@ bool pkpy_push_function(pkpy_vm* vm_handle, const char* sig, pkpy_CFunction f) {
|
||||
|
||||
// special push
|
||||
bool pkpy_push_module(pkpy_vm* vm_handle, const char* name) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_PROTECTED(
|
||||
PyObject* module = vm->new_module(name);
|
||||
@ -378,7 +369,7 @@ bool pkpy_push_module(pkpy_vm* vm_handle, const char* name) {
|
||||
|
||||
// some opt
|
||||
bool pkpy_getattr(pkpy_vm* vm_handle, pkpy_CName name) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||
PyVar o = vm->s_data.top();
|
||||
@ -389,7 +380,7 @@ bool pkpy_getattr(pkpy_vm* vm_handle, pkpy_CName name) {
|
||||
}
|
||||
|
||||
bool pkpy_setattr(pkpy_vm* vm_handle, pkpy_CName name) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(2)
|
||||
PyVar a = vm->s_data.top();
|
||||
@ -401,21 +392,21 @@ bool pkpy_setattr(pkpy_vm* vm_handle, pkpy_CName name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//get global will also get bulitins
|
||||
// get global will also get bulitins
|
||||
bool pkpy_getglobal(pkpy_vm* vm_handle, pkpy_CName name) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PyVar o = vm->_main->attr().try_get(StrName(name));
|
||||
if (o == nullptr) {
|
||||
if(o == nullptr) {
|
||||
o = vm->builtins->attr().try_get(StrName(name));
|
||||
if (o == nullptr) return false;
|
||||
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;
|
||||
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());
|
||||
@ -423,7 +414,7 @@ bool pkpy_setglobal(pkpy_vm* vm_handle, pkpy_CName name) {
|
||||
}
|
||||
|
||||
bool pkpy_eval(pkpy_vm* vm_handle, const char* source) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_PROTECTED(
|
||||
CodeObject_ co = vm->compile(source, "<eval>", EVAL_MODE);
|
||||
@ -434,7 +425,7 @@ bool pkpy_eval(pkpy_vm* vm_handle, const char* source) {
|
||||
}
|
||||
|
||||
bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
@ -450,8 +441,8 @@ bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkpy_get_unbound_method(pkpy_vm* vm_handle, pkpy_CName name){
|
||||
VM* vm = (VM*) vm_handle;
|
||||
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();
|
||||
@ -466,7 +457,7 @@ bool pkpy_get_unbound_method(pkpy_vm* vm_handle, pkpy_CName name){
|
||||
}
|
||||
|
||||
bool pkpy_py_repr(pkpy_vm* vm_handle) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||
PyVar item = vm->s_data.top();
|
||||
@ -478,7 +469,7 @@ bool pkpy_py_repr(pkpy_vm* vm_handle) {
|
||||
}
|
||||
|
||||
bool pkpy_py_str(pkpy_vm* vm_handle) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||
PyVar item = vm->s_data.top();
|
||||
@ -490,7 +481,7 @@ bool pkpy_py_str(pkpy_vm* vm_handle) {
|
||||
}
|
||||
|
||||
bool pkpy_py_import(pkpy_vm* vm_handle, pkpy_CString name) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_PROTECTED(
|
||||
PyVar module = vm->py_import(name);
|
||||
@ -501,14 +492,15 @@ bool pkpy_py_import(pkpy_vm* vm_handle, pkpy_CString name) {
|
||||
|
||||
/* Error Handling */
|
||||
bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PyVar e_t = vm->_main->attr().try_get_likely_found(name);
|
||||
if(e_t == nullptr){
|
||||
if(e_t == nullptr) {
|
||||
e_t = vm->builtins->attr().try_get_likely_found(name);
|
||||
if(e_t == nullptr){
|
||||
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;
|
||||
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();
|
||||
@ -516,23 +508,23 @@ bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) {
|
||||
}
|
||||
|
||||
bool pkpy_check_error(pkpy_vm* vm_handle) {
|
||||
VM* vm = (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;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
// no error
|
||||
if (vm->__c.error == nullptr) return false;
|
||||
if(vm->__c.error == nullptr) return false;
|
||||
Exception& e = vm->__c.error->as<Exception>();
|
||||
if (message != nullptr)
|
||||
if(message != nullptr)
|
||||
*message = strdup(e.summary().c_str());
|
||||
else
|
||||
std::cout << e.summary() << std::endl;
|
||||
vm->__c.error = nullptr;
|
||||
if(vm->callstack.empty()){
|
||||
if(vm->callstack.empty()) {
|
||||
vm->s_data.clear();
|
||||
}else{
|
||||
} else {
|
||||
if(vm->__c.s_view.empty()) exit(127);
|
||||
vm->s_data.reset(vm->__c.s_view.top().end());
|
||||
}
|
||||
@ -540,7 +532,7 @@ bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
|
||||
}
|
||||
|
||||
bool pkpy_vectorcall(pkpy_vm* vm_handle, int argc) {
|
||||
VM* vm = (VM*) vm_handle;
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(argc + 2)
|
||||
PyVar res;
|
||||
@ -550,39 +542,28 @@ bool pkpy_vectorcall(pkpy_vm* vm_handle, int argc) {
|
||||
vm->s_data.push(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
void pkpy_free(void* p){
|
||||
std::free(p);
|
||||
}
|
||||
void pkpy_free(void* p) { std::free(p); }
|
||||
|
||||
pkpy_CName pkpy_name(const char* name){
|
||||
return StrName(name).index;
|
||||
}
|
||||
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();
|
||||
}
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
bool pkpy_repl_input(void* r, const char* line) { return ((REPL*)r)->input(line); }
|
||||
|
||||
void pkpy_delete_repl(void* repl){
|
||||
delete (REPL*)repl;
|
||||
}
|
||||
void pkpy_delete_repl(void* repl) { delete (REPL*)repl; }
|
||||
|
||||
#endif // PK_NO_EXPORT_C_API
|
||||
#endif // PK_NO_EXPORT_C_API
|
||||
|
||||
@ -4,42 +4,42 @@
|
||||
#include "pocketpy/common/export.h"
|
||||
|
||||
namespace pkpy {
|
||||
REPL::REPL(VM* vm) : vm(vm){
|
||||
vm->stdout_write("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
|
||||
vm->stdout_write(_S("[", sizeof(void*)*8, " bit] on ", kPlatformStrings[PK_SYS_PLATFORM], "\n"));
|
||||
vm->stdout_write("https://github.com/pocketpy/pocketpy" "\n");
|
||||
vm->stdout_write("Type \"exit()\" to exit." "\n");
|
||||
}
|
||||
REPL::REPL(VM* vm) : vm(vm) {
|
||||
vm->stdout_write("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
|
||||
vm->stdout_write(_S("[", sizeof(void*) * 8, " bit] on ", kPlatformStrings[PK_SYS_PLATFORM], "\n"));
|
||||
vm->stdout_write("https://github.com/pocketpy/pocketpy" "\n");
|
||||
vm->stdout_write("Type \"exit()\" to exit." "\n");
|
||||
}
|
||||
|
||||
bool REPL::input(std::string line){
|
||||
CompileMode mode = REPL_MODE;
|
||||
if(need_more_lines){
|
||||
buffer += line;
|
||||
buffer += '\n';
|
||||
int n = buffer.size();
|
||||
if(n>=need_more_lines){
|
||||
for(int i=buffer.size()-need_more_lines; i<buffer.size(); i++){
|
||||
// no enough lines
|
||||
if(buffer[i] != '\n') return true;
|
||||
}
|
||||
need_more_lines = 0;
|
||||
line = buffer;
|
||||
buffer.clear();
|
||||
mode = CELL_MODE;
|
||||
}else{
|
||||
return true;
|
||||
bool REPL::input(std::string line) {
|
||||
CompileMode mode = REPL_MODE;
|
||||
if(need_more_lines) {
|
||||
buffer += line;
|
||||
buffer += '\n';
|
||||
int n = buffer.size();
|
||||
if(n >= need_more_lines) {
|
||||
for(int i = buffer.size() - need_more_lines; i < buffer.size(); i++) {
|
||||
// no enough lines
|
||||
if(buffer[i] != '\n') return true;
|
||||
}
|
||||
need_more_lines = 0;
|
||||
line = buffer;
|
||||
buffer.clear();
|
||||
mode = CELL_MODE;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
try{
|
||||
vm->exec(line, "<stdin>", mode);
|
||||
}catch(NeedMoreLines ne){
|
||||
buffer += line;
|
||||
buffer += '\n';
|
||||
need_more_lines = ne.is_compiling_class ? 3 : 2;
|
||||
if (need_more_lines) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
try {
|
||||
vm->exec(line, "<stdin>", mode);
|
||||
} catch(NeedMoreLines ne) {
|
||||
buffer += line;
|
||||
buffer += '\n';
|
||||
need_more_lines = ne.is_compiling_class ? 3 : 2;
|
||||
if(need_more_lines) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
|
||||
@ -4,34 +4,31 @@
|
||||
#include <sstream>
|
||||
|
||||
#if __has_include("pocketpy_c.h")
|
||||
#include "pocketpy_c.h"
|
||||
#include "pocketpy_c.h"
|
||||
#else
|
||||
// for amalgamated build
|
||||
#include "pocketpy.h"
|
||||
// for amalgamated build
|
||||
#include "pocketpy.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <windows.h>
|
||||
|
||||
std::string pkpy_platform_getline(bool* eof) {
|
||||
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
std::wstringstream wss;
|
||||
WCHAR buf;
|
||||
DWORD read;
|
||||
while (ReadConsoleW(hStdin, &buf, 1, &read, NULL) && buf != L'\n') {
|
||||
if (eof && buf == L'\x1A') *eof = true; // Ctrl+Z
|
||||
while(ReadConsoleW(hStdin, &buf, 1, &read, NULL) && buf != L'\n') {
|
||||
if(eof && buf == L'\x1A') *eof = true; // Ctrl+Z
|
||||
wss << buf;
|
||||
}
|
||||
std::wstring wideInput = wss.str();
|
||||
int length =
|
||||
WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(),
|
||||
(int)wideInput.length(), NULL, 0, NULL, NULL);
|
||||
int length = WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), NULL, 0, NULL, NULL);
|
||||
std::string output;
|
||||
output.resize(length);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(),
|
||||
&output[0], length, NULL, NULL);
|
||||
if (!output.empty() && output.back() == '\r') output.pop_back();
|
||||
WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), &output[0], length, NULL, NULL);
|
||||
if(!output.empty() && output.back() == '\r') output.pop_back();
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -39,8 +36,8 @@ std::string pkpy_platform_getline(bool* eof) {
|
||||
|
||||
std::string pkpy_platform_getline(bool* eof) {
|
||||
std::string output;
|
||||
if (!std::getline(std::cin, output)) {
|
||||
if (eof) *eof = true;
|
||||
if(!std::getline(std::cin, output)) {
|
||||
if(eof) *eof = true;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@ -48,10 +45,10 @@ std::string pkpy_platform_getline(bool* eof) {
|
||||
#endif
|
||||
|
||||
static int f_input(pkpy_vm* vm) {
|
||||
if (!pkpy_is_none(vm, -1)) {
|
||||
if(!pkpy_is_none(vm, -1)) {
|
||||
pkpy_CString prompt;
|
||||
bool ok = pkpy_to_string(vm, -1, &prompt);
|
||||
if (!ok) return 0;
|
||||
if(!ok) return 0;
|
||||
std::cout << prompt << std::flush;
|
||||
}
|
||||
bool eof;
|
||||
@ -71,44 +68,42 @@ int main(int argc, char** argv) {
|
||||
pkpy_py_import(vm, "builtins");
|
||||
pkpy_setattr(vm, pkpy_name("input"));
|
||||
|
||||
if (argc == 1) {
|
||||
if(argc == 1) {
|
||||
void* repl = pkpy_new_repl(vm);
|
||||
bool need_more_lines = false;
|
||||
while (true) {
|
||||
while(true) {
|
||||
std::cout << (need_more_lines ? "... " : ">>> ");
|
||||
bool eof = false;
|
||||
std::string line = pkpy_platform_getline(&eof);
|
||||
if (eof) break;
|
||||
if(eof) break;
|
||||
need_more_lines = pkpy_repl_input(repl, line.c_str());
|
||||
}
|
||||
pkpy_delete_vm(vm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc == 2) {
|
||||
if(argc == 2) {
|
||||
std::string argv_1 = argv[1];
|
||||
if (argv_1 == "-h" || argv_1 == "--help") goto __HELP;
|
||||
if(argv_1 == "-h" || argv_1 == "--help") goto __HELP;
|
||||
|
||||
std::filesystem::path filepath(argv[1]);
|
||||
filepath = std::filesystem::absolute(filepath);
|
||||
if (!std::filesystem::exists(filepath)) {
|
||||
if(!std::filesystem::exists(filepath)) {
|
||||
std::cerr << "File not found: " << argv_1 << std::endl;
|
||||
return 2;
|
||||
}
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
if(!file.is_open()) {
|
||||
std::cerr << "Failed to open file: " << argv_1 << std::endl;
|
||||
return 3;
|
||||
}
|
||||
std::string src((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
std::string src((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
pkpy_set_main_argv(vm, argc, argv);
|
||||
|
||||
bool ok = pkpy_exec_2(vm, src.c_str(),
|
||||
filepath.filename().string().c_str(), 0, NULL);
|
||||
if (!ok) pkpy_clear_error(vm, NULL);
|
||||
bool ok = pkpy_exec_2(vm, src.c_str(), filepath.filename().string().c_str(), 0, NULL);
|
||||
if(!ok) pkpy_clear_error(vm, NULL);
|
||||
pkpy_delete_vm(vm);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user