From 1c82060dafde76ddc6fb466381f1bb928f583d16 Mon Sep 17 00:00:00 2001 From: ykiko Date: Tue, 4 Jun 2024 22:55:17 +0800 Subject: [PATCH] Format the world. (#259) * add some clang-format offs. * add formats. * format the world. * AllowShortIfStatementsOnASingleLine * off REGION. * Rollback vm.hpp * Disable insert. --- .clang-format | 106 +- include/pocketpy.h | 2 +- include/pocketpy.hpp | 2 +- include/pocketpy/common/any.hpp | 76 +- include/pocketpy/common/config.h | 1 + include/pocketpy/common/export.h | 1 + include/pocketpy/common/gil.hpp | 5 +- include/pocketpy/common/memorypool.hpp | 6 +- include/pocketpy/common/namedict.hpp | 103 +- include/pocketpy/common/str.hpp | 139 +- include/pocketpy/common/traits.hpp | 38 +- include/pocketpy/common/types.hpp | 16 +- include/pocketpy/common/utils.hpp | 42 +- include/pocketpy/common/vector.hpp | 231 +- include/pocketpy/common/version.h | 1 + include/pocketpy/compiler/compiler.hpp | 68 +- include/pocketpy/compiler/expr.hpp | 279 ++- include/pocketpy/compiler/lexer.hpp | 104 +- include/pocketpy/interpreter/bindings.hpp | 268 ++- include/pocketpy/interpreter/ceval.hpp | 2 +- include/pocketpy/interpreter/cffi.hpp | 77 +- include/pocketpy/interpreter/frame.hpp | 130 +- include/pocketpy/interpreter/gc.hpp | 42 +- include/pocketpy/interpreter/iter.hpp | 51 +- include/pocketpy/interpreter/profiler.hpp | 11 +- include/pocketpy/interpreter/vm.hpp | 398 ++-- include/pocketpy/modules/array2d.hpp | 2 +- include/pocketpy/modules/base64.hpp | 2 +- include/pocketpy/modules/csv.hpp | 2 +- include/pocketpy/modules/dataclasses.hpp | 4 +- include/pocketpy/modules/easing.hpp | 4 +- include/pocketpy/modules/io.hpp | 10 +- include/pocketpy/modules/linalg.hpp | 185 +- include/pocketpy/modules/modules.hpp | 4 +- include/pocketpy/modules/random.hpp | 4 +- include/pocketpy/objects/base.hpp | 75 +- include/pocketpy/objects/builtins.hpp | 57 +- include/pocketpy/objects/codeobject.hpp | 102 +- include/pocketpy/objects/dict.hpp | 24 +- include/pocketpy/objects/error.hpp | 37 +- include/pocketpy/objects/object.hpp | 28 +- include/pocketpy/objects/sourcedata.hpp | 16 +- include/pocketpy/objects/stackmemory.hpp | 13 +- include/pocketpy/objects/tuplelist.hpp | 33 +- include/pocketpy/opcodes.h | 2 +- include/pocketpy/pocketpy.hpp | 28 +- include/pocketpy/pocketpy_c.h | 151 +- include/pocketpy/tools/repl.hpp | 7 +- include/pocketpy_c.h | 2 +- include/pybind11/internal/builtins.h | 195 +- include/pybind11/internal/cast.h | 295 ++- include/pybind11/internal/class.h | 311 ++- include/pybind11/internal/cpp_function.h | 659 +++--- include/pybind11/internal/instance.h | 270 ++- include/pybind11/internal/kernel.h | 189 +- include/pybind11/internal/object.h | 440 ++-- include/pybind11/internal/type_traits.h | 226 +- include/pybind11/internal/types.h | 348 ++- include/pybind11/pybind11.h | 2 +- scripts/format.py | 4 +- src/common/any.cpp | 10 +- src/common/memorypool.cpp | 130 +- src/common/str.cpp | 901 ++++---- src/compiler/compiler.cpp | 2551 ++++++++++----------- src/compiler/expr.cpp | 1370 +++++------ src/compiler/lexer.cpp | 1017 ++++---- src/interpreter/ceval.cpp | 2087 +++++++++-------- src/interpreter/cffi.cpp | 364 +-- src/interpreter/frame.cpp | 216 +- src/interpreter/gc.cpp | 89 +- src/interpreter/iter.cpp | 238 +- src/interpreter/profiler.cpp | 50 +- src/interpreter/vm.cpp | 1742 +++++++------- src/modules/array2d.cpp | 229 +- src/modules/base64.cpp | 158 +- src/modules/csv.cpp | 59 +- src/modules/dataclasses.cpp | 65 +- src/modules/easing.cpp | 188 +- src/modules/io.cpp | 101 +- src/modules/linalg.cpp | 1099 ++++----- src/modules/modules.cpp | 108 +- src/modules/random.cpp | 100 +- src/objects/builtins.cpp | 8 +- src/objects/codeobject.cpp | 12 +- src/objects/dict.cpp | 314 +-- src/objects/error.cpp | 34 +- src/objects/object.cpp | 6 +- src/objects/sourcedata.cpp | 124 +- src/objects/tuplelist.cpp | 33 +- src/pocketpy.cpp | 556 ++--- src/pocketpy_c.cpp | 257 +-- src/tools/repl.cpp | 68 +- src2/main.cpp | 51 +- 93 files changed, 10429 insertions(+), 9836 deletions(-) diff --git a/.clang-format b/.clang-format index 05639fd7..e5ca3fc0 100644 --- a/.clang-format +++ b/.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"] diff --git a/include/pocketpy.h b/include/pocketpy.h index f56efe16..b200f148 100644 --- a/include/pocketpy.h +++ b/include/pocketpy.h @@ -1,3 +1,3 @@ #pragma once -#include "pocketpy/pocketpy.hpp" \ No newline at end of file +#include "pocketpy/pocketpy.hpp" diff --git a/include/pocketpy.hpp b/include/pocketpy.hpp index f56efe16..b200f148 100644 --- a/include/pocketpy.hpp +++ b/include/pocketpy.hpp @@ -1,3 +1,3 @@ #pragma once -#include "pocketpy/pocketpy.hpp" \ No newline at end of file +#include "pocketpy/pocketpy.hpp" diff --git a/include/pocketpy/common/any.hpp b/include/pocketpy/common/any.hpp index 1e9ad99d..6fa09ee1 100644 --- a/include/pocketpy/common/any.hpp +++ b/include/pocketpy/common/any.hpp @@ -10,22 +10,24 @@ namespace pkpy { -template -inline constexpr bool is_any_sso_v = is_pod_v && sizeof(T) <= sizeof(void*); +template +constexpr inline bool is_any_sso_v = is_pod_v && sizeof(T) <= sizeof(void*); -struct any{ - struct vtable{ +struct any { + struct vtable { const std::type_index type; void (*deleter)(void*); - template - inline static vtable* get(){ + template + inline static vtable* get() { static_assert(std::is_same_v>); - if constexpr (is_any_sso_v){ - static vtable vt{ typeid(T), nullptr }; + if constexpr(is_any_sso_v) { + static vtable vt{typeid(T), nullptr}; return &vt; - }else{ - static vtable vt{ typeid(T), [](void* ptr){ delete static_cast(ptr); } }; + } else { + static vtable vt{typeid(T), [](void* ptr) { + delete static_cast(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 - any(T&& value){ + template + any(T&& value) { using U = std::decay_t; static_assert(!std::is_same_v, "any(const any&) is deleted"); static_assert(sizeof(U) == sizeof(T)); - if constexpr (is_any_sso_v){ + if constexpr(is_any_sso_v) { std::memcpy(&data, &value, sizeof(U)); - }else{ + } else { data = new U(std::forward(value)); } _vt = vtable::get(); } 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 - T& _cast() const noexcept{ + template + T& _cast() const noexcept { static_assert(std::is_same_v>); - if constexpr (is_any_sso_v){ + if constexpr(is_any_sso_v) { return *((T*)(&data)); - }else{ + } else { return *(static_cast(data)); } } - template - T& cast() const{ + template + T& cast() const { static_assert(std::is_same_v>); if(type_id() != typeid(T)) __bad_any_cast(typeid(T), type_id()); return _cast(); @@ -83,29 +85,29 @@ struct any{ static void __bad_any_cast(const std::type_index expected, const std::type_index actual); }; -template +template struct function; -template -struct function{ +template +struct function { 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 - function(F&& f) : _impl(std::forward(f)){ - _wrapper = [](const any& impl, Params... params) -> Ret{ + template + function(F&& f) : _impl(std::forward(f)) { + _wrapper = [](const any& impl, Params... params) -> Ret { return impl._cast>()(std::forward(params)...); }; } - Ret operator()(Params... params) const{ + Ret operator() (Params... params) const { assert(_wrapper); return _wrapper(_impl, std::forward(params)...); } }; -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/common/config.h b/include/pocketpy/common/config.h index d3fbff03..c68c62e8 100644 --- a/include/pocketpy/common/config.h +++ b/include/pocketpy/common/config.h @@ -1,4 +1,5 @@ #pragma once +// clang-format off /*************** feature settings ***************/ diff --git a/include/pocketpy/common/export.h b/include/pocketpy/common/export.h index a2beb3d3..55b803e4 100644 --- a/include/pocketpy/common/export.h +++ b/include/pocketpy/common/export.h @@ -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) diff --git a/include/pocketpy/common/gil.hpp b/include/pocketpy/common/gil.hpp index d3970097..992cd2ca 100644 --- a/include/pocketpy/common/gil.hpp +++ b/include/pocketpy/common/gil.hpp @@ -5,10 +5,13 @@ #include 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 diff --git a/include/pocketpy/common/memorypool.hpp b/include/pocketpy/common/memorypool.hpp index adf12f46..2d5bdd54 100644 --- a/include/pocketpy/common/memorypool.hpp +++ b/include/pocketpy/common/memorypool.hpp @@ -6,10 +6,10 @@ #include #include -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; diff --git a/include/pocketpy/common/namedict.hpp b/include/pocketpy/common/namedict.hpp index 359b62c2..68746c40 100644 --- a/include/pocketpy/common/namedict.hpp +++ b/include/pocketpy/common/namedict.hpp @@ -6,20 +6,22 @@ #include -namespace pkpy{ +namespace pkpy { -template -constexpr T default_invalid_value(){ - if constexpr(std::is_same_v) return -1; - else return nullptr; +template +constexpr T default_invalid_value() { + if constexpr(std::is_same_v) + return -1; + else + return nullptr; } -template +template struct NameDictImpl { PK_ALWAYS_PASS_BY_POINTER(NameDictImpl) using Item = std::pair; - static constexpr uint16_t kInitialCapacity = 16; + constexpr static uint16_t kInitialCapacity = 16; static_assert(is_pod_v); 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(); 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 + template 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 keys() const { array 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 items() const{ + array items() const { array 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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/common/str.hpp b/include/pocketpy/common/str.hpp index 5b4808a9..7b16fe83 100644 --- a/include/pocketpy/common/str.hpp +++ b/include/pocketpy/common/str.hpp @@ -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); + Str(std::pair); 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()(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()(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 split(const Str& sep) const; vector 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& _interned(); @@ -128,32 +135,34 @@ struct StrName { static uint32_t _pesudo_random_index; }; -struct SStream{ +struct SStream { PK_ALWAYS_PASS_BY_POINTER(SStream) vector 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 +template 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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/common/traits.hpp b/include/pocketpy/common/traits.hpp index dd286aaa..35e90220 100644 --- a/include/pocketpy/common/traits.hpp +++ b/include/pocketpy/common/traits.hpp @@ -2,35 +2,39 @@ #include -namespace pkpy{ +namespace pkpy { // is_pod_v<> for c++17 and c++20 -template -inline constexpr bool is_pod_v = std::is_trivially_copyable_v && std::is_standard_layout_v; +template +constexpr inline bool is_pod_v = std::is_trivially_copyable_v && std::is_standard_layout_v; // https://en.cppreference.com/w/cpp/types/is_integral -template -inline constexpr bool is_integral_v = !std::is_same_v && std::is_integral_v; +template +constexpr inline bool is_integral_v = !std::is_same_v && std::is_integral_v; -template -inline constexpr bool is_floating_point_v = std::is_same_v || std::is_same_v; +template +constexpr inline bool is_floating_point_v = std::is_same_v || std::is_same_v; // 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 -inline constexpr bool is_sso_v = is_integral_v || is_floating_point_v; +template +constexpr inline bool is_sso_v = is_integral_v || is_floating_point_v; // if is_sso_v is true, return T, else return T& -template +template using obj_get_t = std::conditional_t, T, T&>; -template -constexpr inline bool is_trivially_relocatable_v = std::is_trivially_copyable_v && std::is_trivially_destructible_v; +template +constexpr inline bool is_trivially_relocatable_v = + std::is_trivially_copyable_v && std::is_trivially_destructible_v; -template struct has_gc_marker : std::false_type {}; -template struct has_gc_marker> : std::true_type {}; +template +struct has_gc_marker : std::false_type {}; -template -inline constexpr int py_sizeof = 16 + sizeof(T); -} // namespace pkpy \ No newline at end of file +template +struct has_gc_marker> : std::true_type {}; + +template +constexpr inline int py_sizeof = 16 + sizeof(T); +} // namespace pkpy diff --git a/include/pocketpy/common/types.hpp b/include/pocketpy/common/types.hpp index 45d712ad..44d65548 100644 --- a/include/pocketpy/common/types.hpp +++ b/include/pocketpy/common/types.hpp @@ -2,10 +2,10 @@ #include -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 \ No newline at end of file +}; // namespace pkpy diff --git a/include/pocketpy/common/utils.hpp b/include/pocketpy/common/utils.hpp index 708c9818..6b8ebfc0 100644 --- a/include/pocketpy/common/utils.hpp +++ b/include/pocketpy/common/utils.hpp @@ -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?istop; 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 diff --git a/include/pocketpy/common/vector.hpp b/include/pocketpy/common/vector.hpp index bbfd95d4..e46318e2 100644 --- a/include/pocketpy/common/vector.hpp +++ b/include/pocketpy/common/vector.hpp @@ -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 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) { + if constexpr(is_trivially_relocatable_v) { 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 void emplace_back(Args&&... args) { - if (_size == _capacity) reserve(_capacity * 2); + if(_size == _capacity) reserve(_capacity * 2); new (&_data[_size++]) T(std::forward(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) { - _data[_size].~T(); - } + if constexpr(!std::is_trivially_destructible_v) { _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 > 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 void emplace(Args&&... args) { vec.emplace_back(std::forward(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 > class stack_no_copy : public stack { - 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; using const_reverse_iterator = std::reverse_iterator; - [[nodiscard]] bool is_small() const { - return m_begin == reinterpret_cast(m_buffer); - } + [[nodiscard]] bool is_small() const { return m_begin == reinterpret_cast(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) { + if constexpr(std::is_trivially_copyable_v) { 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) { + if constexpr(is_trivially_relocatable_v) { 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(m_buffer)), - m_end(m_begin), - m_max(m_begin + N) {} +public: + small_vector() : m_begin(reinterpret_cast(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( - other.is_small() ? m_buffer : std::malloc(sizeof(T) * capacity)); + m_begin = reinterpret_cast(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(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 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) { - m_begin = (pointer)std::realloc(m_begin, - sizeof(T) * new_capacity); + if(!is_small()) { + if constexpr(is_trivially_relocatable_v) { + 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) { - m_end->~T(); - } + if constexpr(!std::is_trivially_destructible_v) { m_end->~T(); } } void clear() { @@ -435,11 +466,11 @@ class small_vector { template class small_vector_2 : public small_vector { - 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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/common/version.h b/include/pocketpy/common/version.h index 308598fb..b5ab8098 100644 --- a/include/pocketpy/common/version.h +++ b/include/pocketpy/common/version.h @@ -1,4 +1,5 @@ #pragma once +// clang-format off #define PK_VERSION "2.0.0" #define PK_VERSION_MAJOR 2 diff --git a/include/pocketpy/compiler/compiler.hpp b/include/pocketpy/compiler/compiler.hpp index 16f75a3b..bf432793 100644 --- a/include/pocketpy/compiler/compiler.hpp +++ b/include/pocketpy/compiler/compiler.hpp @@ -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 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 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 @@ -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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/compiler/expr.hpp b/include/pocketpy/compiler/expr.hpp index 1db694e0..2db2bc32 100644 --- a/include/pocketpy/compiler/expr.hpp +++ b/include/pocketpy/compiler/expr.hpp @@ -3,43 +3,64 @@ #include "pocketpy/objects/codeobject.hpp" #include "pocketpy/compiler/lexer.hpp" -namespace pkpy{ +namespace pkpy { struct CodeEmitContext; struct Expr; -template -class unique_ptr_128{ +template +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 - unique_ptr_128(unique_ptr_128&& other): ptr(other.detach()) {} + ~unique_ptr_128() { + if(ptr) { + ptr->~T(); + PoolExpr_dealloc(ptr); + } + } - operator bool() const { return ptr != nullptr; } + template + unique_ptr_128(unique_ptr_128&& other) : ptr(other.detach()) {} - template - unique_ptr_128& operator=(unique_ptr_128&& other) { - if(ptr) { ptr->~T(); PoolExpr_dealloc(ptr); }; + operator bool () const { return ptr != nullptr; } + + template + unique_ptr_128& operator= (unique_ptr_128&& 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_; typedef small_vector Expr_vector; -template<> +template <> constexpr inline bool is_trivially_relocatable_v = 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 s_expr; int level; vector 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&); 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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/compiler/lexer.hpp b/include/pocketpy/compiler/lexer.hpp index b3f2c2ba..3c18b5f5 100644 --- a/include/pocketpy/compiler/lexer.hpp +++ b/include/pocketpy/compiler/lexer.hpp @@ -5,10 +5,11 @@ #include -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; const int kTokenCount = sizeof(kTokens) / sizeof(kTokens[0]); constexpr TokenIndex TK(const char token[]) { - for(int k=0; k kTokenKwMap = [](){ +const std::map kTokenKwMap = []() { std::map map; - for(int k=TK("class"); k <= >= != ==, 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> 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 src); vector 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 diff --git a/include/pocketpy/interpreter/bindings.hpp b/include/pocketpy/interpreter/bindings.hpp index 8a044133..776d200c 100644 --- a/include/pocketpy/interpreter/bindings.hpp +++ b/include/pocketpy/interpreter/bindings.hpp @@ -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 -struct NativeProxyFuncC final: NativeProxyFuncCBase { - static constexpr int N = sizeof...(Params); - using _Fp = Ret(*)(Params...); +template +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(vm, args, std::make_index_sequence()); } - template - PyVar call(VM* vm, ArgsView args, std::index_sequence){ - if constexpr(std::is_void_v<__Ret>){ + template + PyVar call(VM* vm, ArgsView args, std::index_sequence) { + if constexpr(std::is_void_v<__Ret>) { func(py_cast(vm, args[Is])...); return vm->None; - }else{ + } else { __Ret ret = func(py_cast(vm, args[Is])...); return VAR(std::move(ret)); } } }; -template -struct NativeProxyMethodC final: NativeProxyFuncCBase { - static constexpr int N = sizeof...(Params); - using _Fp = Ret(T::*)(Params...); +template +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(vm, args, std::make_index_sequence()); } - template - PyVar call(VM* vm, ArgsView args, std::index_sequence){ - obj_get_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(vm, args[Is+1])...); + template + PyVar call(VM* vm, ArgsView args, std::index_sequence) { + obj_get_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(vm, args[Is + 1])...); return vm->None; - }else{ - __Ret ret = (self.*func)(py_cast(vm, args[Is+1])...); + } else { + __Ret ret = (self.*func)(py_cast(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(args.begin()); return (*pf)(vm, args); } -template -PyObject* VM::bind(PyObject* obj, const char* sig, Ret(*func)(Params...), BindType bt){ +template +PyObject* VM::bind(PyObject* obj, const char* sig, Ret (*func)(Params...), BindType bt) { NativeProxyFuncCBase* proxy = new NativeProxyFuncC(func); return vm->bind(obj, sig, __proxy_wrapper, proxy, bt); } -template -PyObject* VM::bind(PyObject* obj, const char* sig, Ret(T::*func)(Params...), BindType bt){ +template +PyObject* VM::bind(PyObject* obj, const char* sig, Ret (T::*func)(Params...), BindType bt) { NativeProxyFuncCBase* proxy = new NativeProxyMethodC(func); return vm->bind(obj, sig, __proxy_wrapper, proxy, bt); } -template -PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Ret(*func)(Params...), BindType bt){ +template +PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Ret (*func)(Params...), BindType bt) { NativeProxyFuncCBase* proxy = new NativeProxyFuncC(func); return vm->bind(obj, sig, docstring, __proxy_wrapper, proxy, bt); } -template -PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Ret(T::*func)(Params...), BindType bt){ +template +PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Ret (T::*func)(Params...), BindType bt) { NativeProxyFuncCBase* proxy = new NativeProxyMethodC(func); return vm->bind(obj, sig, docstring, __proxy_wrapper, proxy, bt); } -template -PyObject* VM::bind_field(PyObject* obj, const char* name, F T::*field){ +template +PyObject* VM::bind_field(PyObject* obj, const char* name, F T::*field) { static_assert(!std::is_reference_v); 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 self = PK_OBJ_GET(T, args[0]); F T::*field = lambda_get_userdata(args.begin()); return VAR(self.*field); }; PyVar _0 = new_object(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 self = PK_OBJ_GET(T, args[0]); F T::*field = lambda_get_userdata(args.begin()); self.*field = py_cast(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 self = PK_OBJ_GET(T, args[0]); \ - return VAR(self.EXPR); \ - }, \ - [](VM* vm, ArgsView args){ \ - obj_get_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 self = PK_OBJ_GET(T, args[0]); \ + return VAR(self.EXPR); \ + }, \ + [](VM* vm, ArgsView args) { \ + obj_get_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 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 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 self = PK_OBJ_GET(T, args[0]); \ - return VAR(self.FGET()); \ - }, \ - [](VM* vm, ArgsView args){ \ - obj_get_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 self = PK_OBJ_GET(T, args[0]); \ + return VAR(self.FGET()); \ + }, \ + [](VM* vm, ArgsView args) { \ + obj_get_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 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 self = PK_OBJ_GET(T, args[0]); \ + return VAR(self.FGET()); \ + }); /*****************************************************************/ -#define PY_STRUCT_LIKE(wT) \ - static_assert(std::is_trivially_copyable::value); \ - static_assert(!is_sso_v); \ - 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(); \ - 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(&self, sizeof(wT)); \ - }); \ - vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args){ \ - wT& self = _CAST(wT&, args[0]); \ - return vm->new_user_object(&self); \ - }); \ - vm->bind_func(type, "copy", 1, [](VM* vm, ArgsView args){ \ - wT& self = _CAST(wT&, args[0]); \ - return vm->new_user_object(self); \ - }); \ - vm->bind_func(type, "sizeof", 1, [](VM* vm, ArgsView args){ \ - return VAR(sizeof(wT)); \ - }); \ - vm->bind__eq__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ \ - wT& self = _CAST(wT&, _0); \ - if(!vm->isinstance(_1, vm->_tp_user())) return vm->NotImplemented; \ - wT& other = _CAST(wT&, _1); \ - return VAR(self == other); \ - }); \ +#define PY_STRUCT_LIKE(wT) \ + static_assert(std::is_trivially_copyable::value); \ + static_assert(!is_sso_v); \ + 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(); \ + 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(&self, sizeof(wT)); \ + }); \ + vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args) { \ + wT& self = _CAST(wT&, args[0]); \ + return vm->new_user_object(&self); \ + }); \ + vm->bind_func(type, "copy", 1, [](VM* vm, ArgsView args) { \ + wT& self = _CAST(wT&, args[0]); \ + return vm->new_user_object(self); \ + }); \ + vm->bind_func(type, "sizeof", 1, [](VM* vm, ArgsView args) { \ + return VAR(sizeof(wT)); \ + }); \ + vm->bind__eq__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { \ + wT& self = _CAST(wT&, _0); \ + if(!vm->isinstance(_1, vm->_tp_user())) return vm->NotImplemented; \ + wT& other = _CAST(wT&, _1); \ + return VAR(self == other); \ + }); -#define PY_POINTER_SETGETITEM(T) \ - vm->bind__getitem__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ \ - obj_get_t self = PK_OBJ_GET(VoidP, _0); \ - i64 i = CAST(i64, _1); \ - T* tgt = reinterpret_cast(self.ptr); \ - return VAR(tgt[i]); \ - }); \ - vm->bind__setitem__(type->as(), [](VM* vm, PyVar _0, PyVar _1, PyVar _2){ \ - obj_get_t self = PK_OBJ_GET(VoidP, _0); \ - i64 i = CAST(i64, _1); \ - T* tgt = reinterpret_cast(self.ptr); \ - tgt[i] = CAST(T, _2); \ - }); \ +#define PY_POINTER_SETGETITEM(T) \ + vm->bind__getitem__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { \ + obj_get_t self = PK_OBJ_GET(VoidP, _0); \ + i64 i = CAST(i64, _1); \ + T* tgt = reinterpret_cast(self.ptr); \ + return VAR(tgt[i]); \ + }); \ + vm->bind__setitem__(type->as(), [](VM* vm, PyVar _0, PyVar _1, PyVar _2) { \ + obj_get_t self = PK_OBJ_GET(VoidP, _0); \ + i64 i = CAST(i64, _1); \ + T* tgt = reinterpret_cast(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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/interpreter/ceval.hpp b/include/pocketpy/interpreter/ceval.hpp index a1dc7d1d..f7817d77 100644 --- a/include/pocketpy/interpreter/ceval.hpp +++ b/include/pocketpy/interpreter/ceval.hpp @@ -1,4 +1,4 @@ #pragma once #include "pocketpy/interpreter/vm.hpp" -// dummy header for ceval.cpp \ No newline at end of file +// dummy header for ceval.cpp diff --git a/include/pocketpy/interpreter/cffi.hpp b/include/pocketpy/interpreter/cffi.hpp index f1023321..0c4627f5 100644 --- a/include/pocketpy/interpreter/cffi.hpp +++ b/include/pocketpy/interpreter/cffi.hpp @@ -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(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(mod, #name, base); \ + } -struct VoidP{ +struct VoidP { void* ptr; - VoidP(const void* ptr): ptr(const_cast(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(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 P("c", NAME); \ - PyVar type = vm->_modules[P.first]->attr(P.second); \ - return vm->new_object(type->as(), val); \ +#define POINTER_VAR(Tp, NAME) \ + inline PyVar py_var(VM* vm, Tp val) { \ + const static std::pair P("c", NAME); \ + PyVar type = vm->_modules[P.first]->attr(P.second); \ + return vm->new_object(type->as(), 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 -Tp to_void_p(VM* vm, PyVar var){ +template +Tp to_void_p(VM* vm, PyVar var) { static_assert(std::is_pointer_v); - 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(p.ptr); } + /*****************************************************************/ void add_module_c(VM* vm); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/interpreter/frame.hpp b/include/pocketpy/interpreter/frame.hpp index d6a5adf2..188f6d81 100644 --- a/include/pocketpy/interpreter/frame.hpp +++ b/include/pocketpy/interpreter/frame.hpp @@ -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 - void emplace(Args&&... args){ - new(_sp) PyVar(std::forward(args)...); + template + void emplace(Args&&... args) { + new (_sp) PyVar(std::forward(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 + + template LinkedFrame(LinkedFrame* f_back, Args&&... args) : f_back(f_back), frame(std::forward(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 - void emplace(Args&&... args){ + bool empty() const { return _size == 0; } + + void clear() { + while(!empty()) + pop(); + } + + template + void emplace(Args&&... args) { static_assert(sizeof(LinkedFrame) <= kPoolFrameBlockSize); - _tail = new(PoolFrame_alloc()) LinkedFrame(_tail, std::forward(args)...); + _tail = new (PoolFrame_alloc()) LinkedFrame(_tail, std::forward(args)...); ++_size; } @@ -161,12 +198,13 @@ struct CallStack{ return _tail->frame; } - template - void apply(Func&& f){ - for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back) f(p->frame); + template + void apply(Func&& f) { + for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back) + f(p->frame); } - ~CallStack(){ clear(); } + ~CallStack() { clear(); } }; -}; // namespace pkpy \ No newline at end of file +}; // namespace pkpy diff --git a/include/pocketpy/interpreter/gc.hpp b/include/pocketpy/interpreter/gc.hpp index 9b704320..82cc7eb6 100644 --- a/include/pocketpy/interpreter/gc.hpp +++ b/include/pocketpy/interpreter/gc.hpp @@ -6,54 +6,52 @@ #include "pocketpy/objects/object.hpp" namespace pkpy { -struct ManagedHeap{ +struct ManagedHeap { vector _no_gc; vector 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 - PyObject* gcnew(Type type, Args&&... args){ + template + PyObject* gcnew(Type type, Args&&... args) { using __T = std::decay_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)...); gen.push_back(p); gc_counter++; return p; } - template - PyObject* _new(Type type, Args&&... args){ + template + PyObject* _new(Type type, Args&&... args) { using __T = std::decay_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)...); _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 diff --git a/include/pocketpy/interpreter/iter.hpp b/include/pocketpy/interpreter/iter.hpp index ff916fde..130c08f7 100644 --- a/include/pocketpy/interpreter/iter.hpp +++ b/include/pocketpy/interpreter/iter.hpp @@ -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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/interpreter/profiler.hpp b/include/pocketpy/interpreter/profiler.hpp index 2b1ead30..29d2f9f8 100644 --- a/include/pocketpy/interpreter/profiler.hpp +++ b/include/pocketpy/interpreter/profiler.hpp @@ -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> records; stack_no_copy<_FrameRecord> frames; @@ -35,4 +36,4 @@ struct LineProfiler{ Str stats(); }; -} // namespace pkpy +} // namespace pkpy diff --git a/include/pocketpy/interpreter/vm.hpp b/include/pocketpy/interpreter/vm.hpp index 6889c2a8..492fb05e 100644 --- a/include/pocketpy/interpreter/vm.hpp +++ b/include/pocketpy/interpreter/vm.hpp @@ -11,67 +11,76 @@ #include -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 - inline static Vt get(){ + template + inline static Vt get() { static_assert(std::is_same_v>); Vt vt; - if constexpr(!std::is_trivially_destructible_v){ - vt._dtor = [](void* p){ ((T*)p)->~T(); }; + if constexpr(!std::is_trivially_destructible_v) { + vt._dtor = [](void* p) { + ((T*)p)->~T(); + }; } - if constexpr(has_gc_marker::value){ - vt._gc_mark = [](void* p, VM* vm){ ((T*)p)->_gc_mark(vm); }; + if constexpr(has_gc_marker::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 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 pending; - vector pending_is_init; // a.k.a __init__.py + vector 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 _all_types; - - NameDict _modules; // loaded modules - std::map _lazy_modules; // lazy loaded modules - struct{ + NameDict _modules; // loaded modules + std::map _lazy_modules; // lazy loaded modules + + struct { PyObject* error; stack_no_copy 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 _stdout; // function _stderr; // function _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 - Type _find_type_in_cxx_typeid_map(){ + template + 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 +constexpr inline bool is_immutable_v = + is_integral_v || is_floating_point_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || + std::is_pointer_v || std::is_enum_v; -template -inline constexpr bool is_immutable_v = is_integral_v || is_floating_point_v - || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v - || std::is_same_v || std::is_same_v - || std::is_pointer_v || std::is_enum_v; +template +constexpr Type _find_type_in_const_cxx_typeid_map() { + return Type(); +} -template constexpr Type _find_type_in_const_cxx_typeid_map(){ return Type(); } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_str; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_list; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_tuple; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_function; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_native_func; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_bound_method; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_range; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_slice; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_exception; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_bytes; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_mappingproxy; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_dict; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_property; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_star_wrapper; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_staticmethod; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_classmethod; } -template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_stack_memory; } +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_str; +} -template -PyVar py_var(VM* vm, __T&& value){ +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_list; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_tuple; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_function; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_native_func; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_bound_method; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_range; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_slice; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_exception; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_bytes; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_mappingproxy; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_dict; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_property; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_star_wrapper; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_staticmethod; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_classmethod; +} + +template <> +constexpr Type _find_type_in_const_cxx_typeid_map() { + return VM::tp_stack_memory; +} + +template +PyVar py_var(VM* vm, __T&& value) { using T = std::decay_t<__T>; static_assert(!std::is_same_v, "py_var(VM*, PyVar) is not allowed"); - if constexpr(std::is_same_v || std::is_same_v || std::is_same_v){ + if constexpr(std::is_same_v || std::is_same_v || + std::is_same_v) { // str (shortcuts) return VAR(Str(std::forward<__T>(value))); - }else if constexpr(std::is_same_v){ + } else if constexpr(std::is_same_v) { // NoneType return vm->None; - }else if constexpr(std::is_same_v){ + } else if constexpr(std::is_same_v) { // bool return value ? vm->True : vm->False; - }else if constexpr(is_integral_v){ + } else if constexpr(is_integral_v) { // int return PyVar(VM::tp_int, static_cast(value)); - }else if constexpr(is_floating_point_v){ + } else if constexpr(is_floating_point_v) { // float return PyVar(VM::tp_float, static_cast(value)); - }else if constexpr(std::is_pointer_v){ + } else if constexpr(std::is_pointer_v) { return from_void_p(vm, (void*)value); - }else{ + } else { constexpr Type const_type = _find_type_in_const_cxx_typeid_map(); - if constexpr((bool)const_type){ - if constexpr(is_sso_v) return PyVar(const_type, value); - else return vm->heap.gcnew(const_type, std::forward<__T>(value)); - }else{ + if constexpr((bool)const_type) { + if constexpr(is_sso_v) + return PyVar(const_type, value); + else + return vm->heap.gcnew(const_type, std::forward<__T>(value)); + } else { Type type = vm->_find_type_in_cxx_typeid_map(); - if constexpr(is_sso_v) return PyVar(type, value); - else return vm->heap.gcnew(type, std::forward<__T>(value)); + if constexpr(is_sso_v) + return PyVar(type, value); + else + return vm->heap.gcnew(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 +template __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 && std::is_reference_v<__T>), "SSO types cannot be reference"); - if constexpr(std::is_same_v || std::is_same_v){ + if constexpr(std::is_same_v || std::is_same_v) { 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){ + } else if constexpr(std::is_same_v) { 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){ + } else if constexpr(is_integral_v) { static_assert(!std::is_reference_v<__T>); // int - if constexpr(with_check){ + if constexpr(with_check) { if(is_int(obj)) return (T)obj.as(); vm->TypeError("expected 'int', got " + _type_name(vm, vm->_tp(obj)).escape()); } return (T)obj.as(); - }else if constexpr(is_floating_point_v){ + } else if constexpr(is_floating_point_v) { static_assert(!std::is_reference_v<__T>); if(is_float(obj)) return (T)obj.as(); if(is_int(obj)) return (T)obj.as(); vm->TypeError("expected 'int' or 'float', got " + _type_name(vm, vm->_tp(obj)).escape()); return 0.0f; - }else if constexpr(std::is_enum_v){ + } else if constexpr(std::is_enum_v) { static_assert(!std::is_reference_v<__T>); return (__T)_py_cast__internal(vm, obj); - }else if constexpr(std::is_pointer_v){ + } else if constexpr(std::is_pointer_v) { static_assert(!std::is_reference_v<__T>); return to_void_p(vm, obj); - }else{ + } else { constexpr Type const_type = _find_type_in_const_cxx_typeid_map(); - if constexpr((bool)const_type){ - if constexpr(with_check){ - if constexpr(std::is_same_v){ + if constexpr((bool)const_type) { + if constexpr(with_check) { + if constexpr(std::is_same_v) { // 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(); vm->check_compatible_type(obj, type); } @@ -634,25 +738,31 @@ __T _py_cast__internal(VM* vm, PyVar obj) { } } -template -__T py_cast(VM* vm, PyVar obj) { return _py_cast__internal<__T, true>(vm, obj); } -template -__T _py_cast(VM* vm, PyVar obj) { return _py_cast__internal<__T, false>(vm, obj); } +template +__T py_cast(VM* vm, PyVar obj) { + return _py_cast__internal<__T, true>(vm, obj); +} -template -PyObject* VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _register, Type base, bool subclass_enabled){ +template +__T _py_cast(VM* vm, PyVar obj) { + return _py_cast__internal<__T, false>(vm, obj); +} + +template +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()); mod->attr().set(name, type); _cxx_typeid_map[typeid(T)] = type->as(); _register(this, mod, type); - if(!type->attr().contains(__new__)){ + if(!type->attr().contains(__new__)) { if constexpr(std::is_default_constructible_v) { - bind_func(type, __new__, -1, [](VM* vm, ArgsView args){ + bind_func(type, __new__, -1, [](VM* vm, ArgsView args) { Type cls_t = args[0]->as(); return vm->new_object(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 -PyObject* VM::register_user_class(PyObject* mod, StrName name, Type base, bool subclass_enabled){ +template +PyObject* VM::register_user_class(PyObject* mod, StrName name, Type base, bool subclass_enabled) { return register_user_class(mod, name, &T::_register, base, subclass_enabled); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/array2d.hpp b/include/pocketpy/modules/array2d.hpp index b121251d..eaa4340d 100644 --- a/include/pocketpy/modules/array2d.hpp +++ b/include/pocketpy/modules/array2d.hpp @@ -6,4 +6,4 @@ namespace pkpy { void add_module_array2d(VM* vm); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/base64.hpp b/include/pocketpy/modules/base64.hpp index 6db26650..aca8f52d 100644 --- a/include/pocketpy/modules/base64.hpp +++ b/include/pocketpy/modules/base64.hpp @@ -6,4 +6,4 @@ namespace pkpy { void add_module_base64(VM* vm); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/csv.hpp b/include/pocketpy/modules/csv.hpp index 80107efa..07422744 100644 --- a/include/pocketpy/modules/csv.hpp +++ b/include/pocketpy/modules/csv.hpp @@ -6,4 +6,4 @@ namespace pkpy { void add_module_csv(VM* vm); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/dataclasses.hpp b/include/pocketpy/modules/dataclasses.hpp index 6f5fd03a..87d2d27f 100644 --- a/include/pocketpy/modules/dataclasses.hpp +++ b/include/pocketpy/modules/dataclasses.hpp @@ -2,8 +2,8 @@ #include "pocketpy/common/types.hpp" -namespace pkpy{ +namespace pkpy { void add_module_dataclasses(VM* vm); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/easing.hpp b/include/pocketpy/modules/easing.hpp index 7cd72b38..4f31e6dc 100644 --- a/include/pocketpy/modules/easing.hpp +++ b/include/pocketpy/modules/easing.hpp @@ -2,8 +2,8 @@ #include "pocketpy/common/types.hpp" -namespace pkpy{ +namespace pkpy { void add_module_easing(VM* vm); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/io.hpp b/include/pocketpy/modules/io.hpp index ed954b26..c566b8ce 100644 --- a/include/pocketpy/modules/io.hpp +++ b/include/pocketpy/modules/io.hpp @@ -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 diff --git a/include/pocketpy/modules/linalg.hpp b/include/pocketpy/modules/linalg.hpp index ee04cddf..21c386ed 100644 --- a/include/pocketpy/modules/linalg.hpp +++ b/include/pocketpy/modules/linalg.hpp @@ -5,90 +5,167 @@ #include -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); static_assert(is_pod_v); static_assert(is_pod_v); -template<> -inline constexpr bool is_sso_v = true; -template<> -inline constexpr bool is_sso_v = true; +template <> +constexpr inline bool is_sso_v = true; +template <> +constexpr inline bool is_sso_v = true; -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/modules.hpp b/include/pocketpy/modules/modules.hpp index aa9f7ed2..942f96e3 100644 --- a/include/pocketpy/modules/modules.hpp +++ b/include/pocketpy/modules/modules.hpp @@ -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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/modules/random.hpp b/include/pocketpy/modules/random.hpp index a811b08a..cd892c99 100644 --- a/include/pocketpy/modules/random.hpp +++ b/include/pocketpy/modules/random.hpp @@ -2,8 +2,8 @@ #include "pocketpy/common/types.hpp" -namespace pkpy{ +namespace pkpy { void add_module_random(VM* vm); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/base.hpp b/include/pocketpy/objects/base.hpp index 1dc457ba..063d9e8c 100644 --- a/include/pocketpy/objects/base.hpp +++ b/include/pocketpy/objects/base.hpp @@ -8,25 +8,31 @@ #include #include -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(p)) {} + PyVar(Type type, PyObject* p) : type(type), is_ptr(true), flags(0), _0(0), _1(reinterpret_cast(p)) {} + // SSO initialized (is_sso = true) - template - PyVar(Type type, T value): type(type), is_ptr(false), flags(0), _0(0), _1(0) { + template + 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() = value; } - template - T& as(){ + template + T& as() { static_assert(!std::is_reference_v); - if constexpr(sizeof(T) <= 8){ + if constexpr(sizeof(T) <= 8) { return reinterpret_cast(_1); - }else{ + } else { return reinterpret_cast(_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 + template obj_get_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); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/builtins.hpp b/include/pocketpy/objects/builtins.hpp index 39f3fc22..49cfc8a1 100644 --- a/include/pocketpy/objects/builtins.hpp +++ b/include/pocketpy/objects/builtins.hpp @@ -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; -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 T to_void_p(VM*, PyVar); +template +T to_void_p(VM*, PyVar); PyVar from_void_p(VM*, void*); - -template -obj_get_t PyVar::obj_get(){ - if constexpr(is_sso_v){ +template +obj_get_t PyVar::obj_get() { + if constexpr(is_sso_v) { return as(); - }else{ + } else { assert(is_ptr); void* v = ((PyObject*)_1)->_value_ptr(); return *reinterpret_cast(v); @@ -119,6 +136,6 @@ obj_get_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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/codeobject.hpp b/include/pocketpy/objects/codeobject.hpp index 8e0a107d..698fe63e 100644 --- a/include/pocketpy/objects/codeobject.hpp +++ b/include/pocketpy/objects/codeobject.hpp @@ -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; @@ -13,7 +13,7 @@ using NativeFuncC = function; 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; using FuncDecl_ = std::shared_ptr; 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 src; @@ -85,10 +84,10 @@ struct CodeObject { vector codes; vector lines; - - small_vector_2 consts; // constants - small_vector_2 varnames; // local variables - int nlocals; // varnames.size() + + small_vector_2 consts; // constants + small_vector_2 varnames; // local variables + int nlocals; // varnames.size() NameDictInt varnames_inv; vector 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 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 args; // indices in co->varnames small_vector_2 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 -T& lambda_get_userdata(PyVar* p){ +template +T& lambda_get_userdata(PyVar* p) { static_assert(std::is_same_v>); int offset = p[-1] != nullptr ? -1 : -2; return p[offset].obj_get()._userdata.cast(); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/dict.hpp b/include/pocketpy/objects/dict.hpp index 4a1b0548..853f0d25 100644 --- a/include/pocketpy/objects/dict.hpp +++ b/include/pocketpy/objects/dict.hpp @@ -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 + template 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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/error.hpp b/include/pocketpy/objects/error.hpp index 17e2fd9c..0bdf2736 100644 --- a/include/pocketpy/objects/error.hpp +++ b/include/pocketpy/objects/error.hpp @@ -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 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 src, int lineno, const char* cursor, std::string_view name): + Frame(std::shared_ptr src, int lineno, const char* cursor, std::string_view name) : src(src), lineno(lineno), cursor(cursor), name(name) {} }; stack 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 - void st_push(Args&&... args){ + template + void st_push(Args&&... args) { if(stacktrace.size() >= 7) return; stacktrace.emplace(std::forward(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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/object.hpp b/include/pocketpy/objects/object.hpp index ba33343d..9392dd3f 100644 --- a/include/pocketpy/objects/object.hpp +++ b/include/pocketpy/objects/object.hpp @@ -3,22 +3,24 @@ #include "pocketpy/common/namedict.hpp" #include "pocketpy/objects/base.hpp" -namespace pkpy{ +namespace pkpy { using NameDict = NameDictImpl; using NameDict_ = std::shared_ptr; using NameDictInt = NameDictImpl; 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 T& as() noexcept { + template + T& as() noexcept { static_assert(std::is_same_v>); return *reinterpret_cast(_value_ptr()); } @@ -35,17 +37,17 @@ struct PyObject final{ PyObject(Type type) : gc_marked(false), type(type), _attr(nullptr) {} - template - void placement_new(Args&&... args){ + template + void placement_new(Args&&... args) { static_assert(std::is_same_v>); - new(_value_ptr()) T(std::forward(args)...); + new (_value_ptr()) T(std::forward(args)...); // backdoor for important builtin types - if constexpr(std::is_same_v){ + if constexpr(std::is_same_v) { _attr = new NameDict(); - }else if constexpr(std::is_same_v){ + } else if constexpr(std::is_same_v) { _attr = new NameDict(PK_TYPE_ATTR_LOAD_FACTOR); - }else if constexpr(std::is_same_v){ + } else if constexpr(std::is_same_v) { _attr = new NameDict(PK_TYPE_ATTR_LOAD_FACTOR); } } @@ -53,4 +55,4 @@ struct PyObject final{ static_assert(sizeof(PyObject) <= 16); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/sourcedata.hpp b/include/pocketpy/objects/sourcedata.hpp index 3d9fe3c4..2123246a 100644 --- a/include/pocketpy/objects/sourcedata.hpp +++ b/include/pocketpy/objects/sourcedata.hpp @@ -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 _precompiled_tokens; - + SourceData(std::string_view source, const Str& filename, CompileMode mode); SourceData(const Str& filename, CompileMode mode); - std::pair _get_line(int lineno) const; + std::pair _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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/stackmemory.hpp b/include/pocketpy/objects/stackmemory.hpp index 7cf01292..c68c1991 100644 --- a/include/pocketpy/objects/stackmemory.hpp +++ b/include/pocketpy/objects/stackmemory.hpp @@ -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 = true; +template <> +constexpr inline bool is_sso_v = true; -inline const int kTpStackMemoryIndex = 27; +const inline int kTpStackMemoryIndex = 27; -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy/objects/tuplelist.hpp b/include/pocketpy/objects/tuplelist.hpp index 716945db..e344cb3c 100644 --- a/include/pocketpy/objects/tuplelist.hpp +++ b/include/pocketpy/objects/tuplelist.hpp @@ -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{ +struct List : public vector { using vector::vector; void _gc_mark(VM*) const; - Tuple to_tuple() const{ + Tuple to_tuple() const { Tuple ret(size()); - for(int i=0; i <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 80); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); - static_assert(py_sizeof <= 64); -} // namespace pkpy \ No newline at end of file +namespace pkpy { +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 80); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +static_assert(py_sizeof <= 64); +} // namespace pkpy diff --git a/include/pocketpy/pocketpy_c.h b/include/pocketpy/pocketpy_c.h index f894b5d3..700a0311 100644 --- a/include/pocketpy/pocketpy_c.h +++ b/include/pocketpy/pocketpy_c.h @@ -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 diff --git a/include/pocketpy/tools/repl.hpp b/include/pocketpy/tools/repl.hpp index 51014ab3..eb4f047a 100644 --- a/include/pocketpy/tools/repl.hpp +++ b/include/pocketpy/tools/repl.hpp @@ -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 \ No newline at end of file +} // namespace pkpy diff --git a/include/pocketpy_c.h b/include/pocketpy_c.h index 9e662f8e..517bcb0d 100644 --- a/include/pocketpy_c.h +++ b/include/pocketpy_c.h @@ -1,3 +1,3 @@ #pragma once -#include "pocketpy/pocketpy_c.h" \ No newline at end of file +#include "pocketpy/pocketpy_c.h" diff --git a/include/pybind11/internal/builtins.h b/include/pybind11/internal/builtins.h index 2dadf0b1..a48f5e63 100644 --- a/include/pybind11/internal/builtins.h +++ b/include/pybind11/internal/builtins.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(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(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(name); - vm->delattr(obj.ptr(), key); - } +inline void delattr(const handle& obj, const handle& name) { + auto& key = _builtin_cast(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(name); - return reinterpret_borrow(vm->getattr(obj.ptr(), key)); - } +inline object getattr(const handle& obj, const handle& name) { + auto& key = _builtin_cast(name); + return reinterpret_borrow(vm->getattr(obj.ptr(), key)); +} - inline object getattr(const handle& obj, const char* name) { - return reinterpret_borrow(vm->getattr(obj.ptr(), name)); - } +inline object getattr(const handle& obj, const char* name) { + return reinterpret_borrow(vm->getattr(obj.ptr(), name)); +} - inline object getattr(const handle& obj, const handle& name, const handle& default_) { - if(!hasattr(obj, name)) { - return reinterpret_borrow(default_); +inline object getattr(const handle& obj, const handle& name, const handle& default_) { + if(!hasattr(obj, name)) { return reinterpret_borrow(default_); } + return getattr(obj, name); +} + +inline object getattr(const handle& obj, const char* name, const handle& default_) { + if(!hasattr(obj, name)) { return reinterpret_borrow(default_); } + return getattr(obj, name); +} + +inline void setattr(const handle& obj, const handle& name, const handle& value) { + auto& key = _builtin_cast(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 +inline bool isinstance(const handle& obj) { + pkpy::Type cls = _builtin_cast(type::handle_of().ptr()); + return vm->isinstance(obj.ptr(), cls); +} + +template <> +inline bool isinstance(const handle&) = delete; + +template <> +inline bool isinstance(const handle& obj) { + return hasattr(obj, "__iter__"); +} + +template <> +inline bool isinstance(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(type)); +} + +inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); } + +template +struct type_caster; + +template +handle + _cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) { + using U = std::remove_pointer_t>>; + return type_caster::cast(std::forward(value), policy, parent); +} + +template +object + cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) { + return reinterpret_borrow(_cast(std::forward(value), policy, parent)); +} + +template +T cast(handle obj, bool convert = false) { + using Caster = type_caster>>>; + Caster caster; + + if(caster.load(obj, convert)) { + if constexpr(std::is_rvalue_reference_v) { + 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(default_); - } - return getattr(obj, name); - } - - inline void setattr(const handle& obj, const handle& name, const handle& value) { - auto& key = _builtin_cast(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 - inline bool isinstance(const handle& obj) { - pkpy::Type cls = _builtin_cast(type::handle_of().ptr()); - return vm->isinstance(obj.ptr(), cls); - } - - template <> - inline bool isinstance(const handle&) = delete; - - template <> - inline bool isinstance(const handle& obj) { - return hasattr(obj, "__iter__"); - } - - template <> - inline bool isinstance(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(type)); - } - - inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); } - - template - struct type_caster; - - template - handle _cast(T&& value, - return_value_policy policy = return_value_policy::automatic_reference, - handle parent = handle()) { - using U = std::remove_pointer_t>>; - return type_caster::cast(std::forward(value), policy, parent); - } - - template - object cast(T&& value, - return_value_policy policy = return_value_policy::automatic_reference, - handle parent = handle()) { - return reinterpret_borrow(_cast(std::forward(value), policy, parent)); - } - - template - T cast(handle obj, bool convert = false) { - using Caster = - type_caster>>>; - Caster caster; - - if(caster.load(obj, convert)) { - if constexpr(std::is_rvalue_reference_v) { - 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 diff --git a/include/pybind11/internal/cast.h b/include/pybind11/internal/cast.h index f6cdc320..385230b5 100644 --- a/include/pybind11/internal/cast.h +++ b/include/pybind11/internal/cast.h @@ -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 - constexpr inline bool is_string_v = - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v; +template +constexpr inline bool is_string_v = std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; - template - constexpr bool is_pyobject_v = std::is_base_of_v; +template +constexpr bool is_pyobject_v = std::is_base_of_v; - template - struct type_caster; +template +struct type_caster; - template <> - struct type_caster { - bool value; +template <> +struct type_caster { + bool value; - bool load(const handle& src, bool) { - if(isinstance(src)) { - value = pkpy::_py_cast(vm, src.ptr()); - return true; - } - - return false; + bool load(const handle& src, bool) { + if(isinstance(src)) { + value = pkpy::_py_cast(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 +struct type_caster>> { + T value; + + bool load(const handle& src, bool convert) { + if(isinstance(src)) { + value = pkpy::_py_cast(vm, src.ptr()); + return true; + } + + return false; + } + + static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); } +}; + +template +struct type_caster>> { + T value; + + bool load(const handle& src, bool convert) { + if(isinstance(src)) { + value = pkpy::_py_cast(vm, src.ptr()); + return true; + } + + if(convert && isinstance(src)) { + value = pkpy::_py_cast(vm, src.ptr()); + return true; + } + + return false; + } + + static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); } +}; + +template +struct type_caster>> { + T value; + + bool load(const handle& src, bool) { + if(isinstance(src)) { + // FIXME: support other kinds of string + value = pkpy::_py_cast(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 +struct type_caster>> { + T value; + + bool load(const handle& src, bool) { + if(isinstance(src)) { + value = reinterpret_borrow(src); + return true; + } + + return false; + } + + template + static handle cast(U&& src, return_value_policy, handle) { + return std::forward(src); + } +}; + +template +struct type_caster { + value_wrapper value; + + using underlying_type = std::remove_pointer_t; + + bool load(handle src, bool convert) { + if(isinstance(src)) { + auto& i = _builtin_cast(src); + value.pointer = &i.cast(); + return true; + } + + return false; + } + + template + 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(value), type, policy, parent.ptr()); + } + vm->TypeError("type not registered"); + } +}; + +template +struct type_caster || std::is_reference_v>> { + using underlying = std::conditional_t, std::remove_pointer_t, std::remove_reference_t>; + + struct wrapper { + type_caster caster; + + operator T () { + if constexpr(std::is_pointer_v) { + return caster.value.pointer; + } else { + return caster.value; + } } }; - template - struct type_caster>> { - T value; + wrapper value; - bool load(const handle& src, bool convert) { - if(isinstance(src)) { - value = pkpy::_py_cast(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 - struct type_caster>> { - T value; - - bool load(const handle& src, bool convert) { - if(isinstance(src)) { - value = pkpy::_py_cast(vm, src.ptr()); - return true; - } - - if(convert && isinstance(src)) { - value = pkpy::_py_cast(vm, src.ptr()); - return true; - } - - return false; - } - - static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); } - }; - - template - struct type_caster>> { - T value; - - bool load(const handle& src, bool) { - if(isinstance(src)) { - // FIXME: support other kinds of string - value = pkpy::_py_cast(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 - struct type_caster>> { - T value; - - bool load(const handle& src, bool) { - if(isinstance(src)) { - value = reinterpret_borrow(src); - return true; - } - - return false; - } - - template - static handle cast(U&& src, return_value_policy, handle) { - return std::forward(src); - } - }; - - template - struct type_caster { - value_wrapper value; - - using underlying_type = std::remove_pointer_t; - - bool load(handle src, bool convert) { - if(isinstance(src)) { - auto& i = _builtin_cast(src); - value.pointer = &i.cast(); - return true; - } - - return false; - } - - template - 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(value), type, policy, parent.ptr()); - } - vm->TypeError("type not registered"); - } - }; - - template - struct type_caster || std::is_reference_v>> { - using underlying = std::conditional_t, - std::remove_pointer_t, - std::remove_reference_t>; - - struct wrapper { - type_caster caster; - - operator T () { - if constexpr(std::is_pointer_v) { - return caster.value.pointer; - } else { - return caster.value; - } - } - }; - - wrapper value; - - bool load(const handle& src, bool convert) { return value.caster.load(src, convert); } - - template - static handle cast(U&& value, return_value_policy policy, const handle& parent) { - return type_caster::cast(std::forward(value), policy, parent); - } - }; + template + static handle cast(U&& value, return_value_policy policy, const handle& parent) { + return type_caster::cast(std::forward(value), policy, parent); + } +}; } // namespace pybind11 diff --git a/include/pybind11/internal/class.h b/include/pybind11/internal/class.h index 6adb892c..94a2e650 100644 --- a/include/pybind11/internal/class.h +++ b/include/pybind11/internal/class.h @@ -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 - class class_ : public type { - public: - using type::type; +template +class class_ : public type { +public: + using type::type; - template - 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()), - true) { - pkpy::PyVar mod = scope.ptr(); - mod->attr().set(name, m_ptr); - vm->_cxx_typeid_map[typeid(T)] = _builtin_cast(m_ptr); - vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) { - auto cls = _builtin_cast(args[0]); - return instance::create(cls); - }); - } - - /// bind constructor - template - class_& def(init, const Extra&... extra) { - if constexpr(!std::is_constructible_v) { - static_assert(std::is_constructible_v, "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 - class_& def(const char* name, Fn&& f, const Extra&... extra) { - using first = std::tuple_element_t<0, callable_args_t>>; - constexpr bool is_first_base_of_v = - std::is_reference_v && std::is_base_of_v>; - - 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(f), pkpy::BindType::DEFAULT, extra...); - } + template + 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()), + true) { + pkpy::PyVar mod = scope.ptr(); + mod->attr().set(name, m_ptr); + vm->_cxx_typeid_map[typeid(T)] = _builtin_cast(m_ptr); + vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) { + auto cls = _builtin_cast(args[0]); + return instance::create(cls); + }); + } + /// bind constructor + template + class_& def(init, const Extra&... extra) { + if constexpr(!std::is_constructible_v) { + static_assert(std::is_constructible_v, "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 - class_& def(Operator op, const Extras&... extras) { - op.execute(*this, extras...); - return *this; + /// bind member function + template + class_& def(const char* name, Fn&& f, const Extra&... extra) { + using first = std::tuple_element_t<0, callable_args_t>>; + constexpr bool is_first_base_of_v = std::is_reference_v && std::is_base_of_v>; + + 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(f), pkpy::BindType::DEFAULT, extra...); } - // TODO: factory function + return *this; + } - /// bind static function - template - class_& def_static(const char* name, Fn&& f, const Extra&... extra) { - bind_function(*this, name, std::forward(f), pkpy::BindType::STATICMETHOD, extra...); - return *this; + /// bind operators + template + class_& def(Operator op, const Extras&... extras) { + op.execute(*this, extras...); + return *this; + } + + // TODO: factory function + + /// bind static function + template + class_& def_static(const char* name, Fn&& f, const Extra&... extra) { + bind_function(*this, name, std::forward(f), pkpy::BindType::STATICMETHOD, extra...); + return *this; + } + + template + class_& def_readwrite(const char* name, MP mp, const Extras&... extras) { + if constexpr(!std::is_member_object_pointer_v) { + static_assert(std::is_member_object_pointer_v, "def_readwrite only supports pointer to data member"); + } else { + bind_property(*this, name, mp, mp, extras...); } + return *this; + } - template - class_& def_readwrite(const char* name, MP mp, const Extras&... extras) { - if constexpr(!std::is_member_object_pointer_v) { - static_assert(std::is_member_object_pointer_v, - "def_readwrite only supports pointer to data member"); - } else { - bind_property(*this, name, mp, mp, extras...); - } - return *this; + template + class_& def_readonly(const char* name, MP mp, const Extras&... extras) { + if constexpr(!std::is_member_object_pointer_v) { + static_assert(std::is_member_object_pointer_v, "def_readonly only supports pointer to data member"); + } else { + bind_property(*this, name, mp, nullptr, extras...); } + return *this; + } - template - class_& def_readonly(const char* name, MP mp, const Extras&... extras) { - if constexpr(!std::is_member_object_pointer_v) { - static_assert(std::is_member_object_pointer_v, - "def_readonly only supports pointer to data member"); - } else { - bind_property(*this, name, mp, nullptr, extras...); - } - return *this; + template + class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) { + bind_property(*this, name, std::forward(g), std::forward(s), extras...); + return *this; + } + + template + class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) { + bind_property(*this, name, std::forward(mp), nullptr, extras...); + return *this; + } + + template + class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) { + static_assert( + dependent_false, + "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); + return *this; + } + + template + class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) { + static_assert( + dependent_false, + "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); + return *this; + } + + template + class_& def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) { + static_assert( + dependent_false, + "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); + return *this; + } +}; + +template +class enum_ : public class_ { + std::map m_values; + +public: + using class_::class_; + + template + enum_(const handle& scope, const char* name, Args&&... args) : + class_(scope, name, std::forward(args)...) {} + + enum_& value(const char* name, T value) { + handle var = type_caster::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 - class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) { - bind_property(*this, name, std::forward(g), std::forward(s), extras...); - return *this; - } - - template - class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) { - bind_property(*this, name, std::forward(mp), nullptr, extras...); - return *this; - } - - template - class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) { - static_assert( - dependent_false, - "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); - return *this; - } - - template - class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) { - static_assert( - dependent_false, - "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); - return *this; - } - - template - class_& - def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) { - static_assert( - dependent_false, - "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); - return *this; - } - }; - - template - class enum_ : public class_ { - std::map m_values; - - public: - using class_::class_; - - template - enum_(const handle& scope, const char* name, Args&&... args) : - class_(scope, name, std::forward(args)...) {} - - enum_& value(const char* name, T value) { - handle var = type_caster::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 diff --git a/include/pybind11/internal/cpp_function.h b/include/pybind11/internal/cpp_function.h index 2f073c42..3eb31d56 100644 --- a/include/pybind11/internal/cpp_function.h +++ b/include/pybind11/internal/cpp_function.h @@ -5,379 +5,354 @@ namespace pybind11 { - template - struct keep_alive {}; +template +struct keep_alive {}; - template - struct call_guard { - static_assert(std::is_default_constructible_v, - "call_guard must be default constructible"); +template +struct call_guard { + static_assert(std::is_default_constructible_v, "call_guard must be default constructible"); +}; + +// append the overload to the beginning of the overload list +struct prepend {}; + +template +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 IndexSequence = std::make_index_sequence>> +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 - struct init {}; + template + 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 + function_record(Fn&& f, const char* name, const Extras&... extras) : name(name), next(nullptr) { - template >, - typename IndexSequence = std::make_index_sequence>> - 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 - friend struct generator; - - public: - template - 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(f)); - destructor = [](function_record* self) { - reinterpret_cast(self->buffer)->~Fn(); - }; - } else { - data = new auto(std::forward(f)); - destructor = [](function_record* self) { delete static_cast(self->data); }; - } - - using Generator = generator, std::tuple>; - Generator::initialize(*this, extras...); - wrapper = Generator::generate(); - } - - ~function_record() { destructor(this); } - - template - auto& cast() { - if constexpr(sizeof(Fn) <= sizeof(buffer)) { - return *reinterpret_cast(buffer); - } else { - return *static_cast(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 - handle invoke(Fn&& fn, - std::index_sequence, - std::tuple...>& casters, - return_value_policy policy, - handle parent) { - using underlying_type = std::decay_t; - using ret = callable_return_t; - - // if the return type is void, return None - if constexpr(std::is_void_v) { - // resolve the member function pointer - if constexpr(std::is_member_function_pointer_v) { - [&](class_type_t& self, auto&... args) { - (self.*fn)(args...); - }(std::get(casters).value...); - } else { - fn(std::get(casters).value...); - } - return vm->None; + if constexpr(sizeof(f) <= sizeof(buffer)) { + new (buffer) auto(std::forward(f)); + destructor = [](function_record* self) { + reinterpret_cast(self->buffer)->~Fn(); + }; } else { - // resolve the member function pointer - if constexpr(std::is_member_function_pointer_v>) { - return type_caster::cast( - [&](class_type_t& self, auto&... args) { - return (self.*fn)(args...); - }(std::get(casters).value...), - policy, - parent); - } else { - return type_caster::cast(fn(std::get(casters).value...), policy, parent); - } - } - } - - template - struct generator, std::tuple, std::index_sequence> { - 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...> != 0; - constexpr bool has_kwargs = types_count_v...> != 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(vm->tp_tuple, n); - auto& tuple = var.obj_get(); - 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(vm->tp_dict); - auto& dict = var.obj_get(); - - for(std::size_t i = 0; i < n; i += 2) { - pkpy::i64 index = pkpy::_py_cast(vm, view[count + i]); - pkpy::PyVar str = - vm->new_object(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...> casters; - - // check type compatibility - if(((std::get(casters).load(stack[Is], convert)) && ...)) { - return invoke(self.cast(), - std::index_sequence{}, - casters, - self.policy, - parent); - } - - return handle(); + data = new auto(std::forward(f)); + destructor = [](function_record* self) { + delete static_cast(self->data); }; } - }; - constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) { - auto& record = pkpy::lambda_get_userdata(view.begin()); - return record(view).ptr(); - }; - - class cpp_function : public function { - public: - template - cpp_function(Fn&& f, const Extras&... extras) { - pkpy::any userdata = function_record(std::forward(f), "anonymous", extras...); - m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata)); - inc_ref(); - } - }; - - template - 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), name, extras...); - callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata)); - } else { - auto& userdata = callable.obj_get()._userdata; - function_record* record = new function_record(std::forward(fn), name, extras...); - - constexpr bool is_prepend = (types_count_v != 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::tuple>; + Generator::initialize(*this, extras...); + wrapper = Generator::generate(); } - template - 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; - using Setter = std::decay_t; + ~function_record() { destructor(this); } - getter = vm->new_object( - vm->tp_native_func, - [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar { - auto& getter = pkpy::lambda_get_userdata(view.begin()); + template + auto& cast() { + if constexpr(sizeof(Fn) <= sizeof(buffer)) { + return *reinterpret_cast(buffer); + } else { + return *static_cast(data); + } + } - if constexpr(std::is_member_pointer_v) { - using Self = class_type_t; - auto& self = _builtin_cast(view[0]).cast(); + 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) { - return type_caster>::cast( - self.*getter, - return_value_policy::reference_internal, - view[0]) - .ptr(); - } else { - return type_caster>::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 +handle invoke(Fn&& fn, + std::index_sequence, + std::tuple...>& casters, + return_value_policy policy, + handle parent) { + using underlying_type = std::decay_t; + using ret = callable_return_t; + + // if the return type is void, return None + if constexpr(std::is_void_v) { + // resolve the member function pointer + if constexpr(std::is_member_function_pointer_v) { + [&](class_type_t& self, auto&... args) { + (self.*fn)(args...); + }(std::get(casters).value...); + } else { + fn(std::get(casters).value...); + } + return vm->None; + } else { + // resolve the member function pointer + if constexpr(std::is_member_function_pointer_v>) { + return type_caster::cast( + [&](class_type_t& self, auto&... args) { + return (self.*fn)(args...); + }(std::get(casters).value...), + policy, + parent); + } else { + return type_caster::cast(fn(std::get(casters).value...), policy, parent); + } + } +} + +template +struct generator, std::tuple, std::index_sequence> { + 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...> != 0; + constexpr bool has_kwargs = types_count_v...> != 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(vm->tp_tuple, n); + auto& tuple = var.obj_get(); + 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(vm->tp_dict); + auto& dict = var.obj_get(); + + for(std::size_t i = 0; i < n; i += 2) { + pkpy::i64 index = pkpy::_py_cast(vm, view[count + i]); + pkpy::PyVar str = vm->new_object(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...> casters; + + // check type compatibility + if(((std::get(casters).load(stack[Is], convert)) && ...)) { + return invoke(self.cast(), std::index_sequence{}, casters, self.policy, parent); + } + + return handle(); + }; + } +}; + +constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) { + auto& record = pkpy::lambda_get_userdata(view.begin()); + return record(view).ptr(); +}; + +class cpp_function : public function { +public: + template + cpp_function(Fn&& f, const Extras&... extras) { + pkpy::any userdata = function_record(std::forward(f), "anonymous", extras...); + m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata)); + inc_ref(); + } +}; + +template +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), name, extras...); + callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata)); + } else { + auto& userdata = callable.obj_get()._userdata; + function_record* record = new function_record(std::forward(fn), name, extras...); + + constexpr bool is_prepend = (types_count_v != 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 +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; + using Setter = std::decay_t; + + getter = vm->new_object( + vm->tp_native_func, + [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar { + auto& getter = pkpy::lambda_get_userdata(view.begin()); + + if constexpr(std::is_member_pointer_v) { + using Self = class_type_t; + auto& self = _builtin_cast(view[0]).cast(); + + if constexpr(std::is_member_object_pointer_v) { + return type_caster>::cast(self.*getter, + return_value_policy::reference_internal, + view[0]) + .ptr(); } else { - using Self = std::tuple_element_t<0, callable_args_t>; - auto& self = _builtin_cast(view[0]).cast(); - - return type_caster>::cast( - getter(self), - return_value_policy::reference_internal, - view[0]) + return type_caster>::cast((self.*getter)(), + return_value_policy::reference_internal, + view[0]) .ptr(); } - }, - 1, - std::forward(getter_)); - if constexpr(!std::is_same_v) { - setter = vm->new_object( - vm->tp_native_func, - [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar { - auto& setter = pkpy::lambda_get_userdata(view.begin()); + } else { + using Self = std::tuple_element_t<0, callable_args_t>; + auto& self = _builtin_cast(view[0]).cast(); - if constexpr(std::is_member_pointer_v) { - using Self = class_type_t; - auto& self = _builtin_cast(view[0]).cast(); + return type_caster>::cast(getter(self), + return_value_policy::reference_internal, + view[0]) + .ptr(); + } + }, + 1, + std::forward(getter_)); - if constexpr(std::is_member_object_pointer_v) { - type_caster> caster; - if(caster.load(view[1], true)) { - self.*setter = caster.value; - return vm->None; - } - } else { - type_caster>> caster; - if(caster.load(view[1], true)) { - (self.*setter)(caster.value); - return vm->None; - } + if constexpr(!std::is_same_v) { + setter = vm->new_object( + vm->tp_native_func, + [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar { + auto& setter = pkpy::lambda_get_userdata(view.begin()); + + if constexpr(std::is_member_pointer_v) { + using Self = class_type_t; + auto& self = _builtin_cast(view[0]).cast(); + + if constexpr(std::is_member_object_pointer_v) { + type_caster> 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>; - auto& self = _builtin_cast(view[0]).cast(); - type_caster>> 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>; + auto& self = _builtin_cast(view[0]).cast(); - vm->TypeError("invalid argument"); - }, - 2, - std::forward(setter_)); - } + type_caster>> caster; + if(caster.load(view[1], true)) { + setter(self, caster.value); + return vm->None; + } + } - pkpy::PyVar property = vm->new_object(vm->tp_property, getter, setter); - var->attr().set(name, property); - return property; + vm->TypeError("invalid argument"); + }, + 2, + std::forward(setter_)); } + pkpy::PyVar property = vm->new_object(vm->tp_property, getter, setter); + var->attr().set(name, property); + return property; +} + } // namespace pybind11 diff --git a/include/pybind11/internal/instance.h b/include/pybind11/internal/instance.h index dae3baec..d80e36bf 100644 --- a/include/pybind11/internal/instance.h +++ b/include/pybind11/internal/instance.h @@ -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 - static type_info& of() { - static_assert(!std::is_reference_v && !std::is_const_v>, - "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 + static type_info& of() { + static_assert(!std::is_reference_v && !std::is_const_v>, + "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 - static pkpy::PyVar create(pkpy::Type type) { - instance instance; - instance.type = &type_info::of(); - instance.data = operator new (sizeof(T)); - instance.flag = Flag::Own; - return vm->new_object(type, std::move(instance)); - } - - template - 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>; - - // resolve for automatic policy. - if(policy == return_value_policy::automatic) { - policy = std::is_pointer_v ? return_value_policy::take_ownership - : std::is_lvalue_reference_v ? return_value_policy::copy - : return_value_policy::move; - } else if(policy == return_value_policy::automatic_reference) { - policy = std::is_pointer_v ? return_value_policy::reference - : std::is_lvalue_reference_v ? 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) { - return *reinterpret_cast(value); - } else { - return const_cast(value); - } - }(); - - instance instance; - instance.type = &type_info::of(); - - 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(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 - T& cast() noexcept { - return *static_cast(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 + static pkpy::PyVar create(pkpy::Type type) { + instance instance; + instance.type = &type_info::of(); + instance.data = operator new (sizeof(T)); + instance.flag = Flag::Own; + return vm->new_object(type, std::move(instance)); + } + + template + 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>; + + // resolve for automatic policy. + if(policy == return_value_policy::automatic) { + policy = std::is_pointer_v ? return_value_policy::take_ownership + : std::is_lvalue_reference_v ? return_value_policy::copy + : return_value_policy::move; + } else if(policy == return_value_policy::automatic_reference) { + policy = std::is_pointer_v ? return_value_policy::reference + : std::is_lvalue_reference_v ? 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) { + return *reinterpret_cast(value); + } else { + return const_cast(value); + } + }(); + + instance instance; + instance.type = &type_info::of(); + + 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(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 + T& cast() noexcept { + return *static_cast(data); + } +}; } // namespace pybind11 diff --git a/include/pybind11/internal/kernel.h b/include/pybind11/internal/kernel.h index b84fc9c3..1331fdff 100644 --- a/include/pybind11/internal/kernel.h +++ b/include/pybind11/internal/kernel.h @@ -2,107 +2,98 @@ #include -namespace pybind11 -{ - inline pkpy::VM* vm = nullptr; - inline std::map* _ref_counts_map = nullptr; +namespace pybind11 { +inline pkpy::VM* vm = nullptr; +inline std::map* _ref_counts_map = nullptr; - inline void initialize(bool enable_os = true) - { - vm = new pkpy::VM(enable_os); - _ref_counts_map = new std::map(); +inline void initialize(bool enable_os = true) { + vm = new pkpy::VM(enable_os); + _ref_counts_map = new std::map(); - // 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 diff --git a/include/pybind11/internal/object.h b/include/pybind11/internal/object.h index 80008292..b6b1514e 100644 --- a/include/pybind11/internal/object.h +++ b/include/pybind11/internal/object.h @@ -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 - T& _builtin_cast(const handle& obj); +template +T& _builtin_cast(const handle& obj); - template - T reinterpret_borrow(const handle& h); +template +T reinterpret_borrow(const handle& h); - template - T reinterpret_steal(const handle& h); +template +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 + 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(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr)); + } + + bool contains(const handle& other) const { + return pkpy::py_cast(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 + 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 + friend T& _builtin_cast(const handle& obj) { + // FIXME: 2.0 does not use Py_ anymore + static_assert(!std::is_reference_v, "T must not be a reference type."); + return obj.ptr().obj_get(); + } +}; + +static_assert(std::is_trivially_copyable_v); + +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 - 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(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr)); - } - - bool contains(const handle& other) const { - return pkpy::py_cast(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 - 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 - friend T& _builtin_cast(const handle& obj) { - // FIXME: 2.0 does not use Py_ anymore - static_assert(!std::is_reference_v, "T must not be a reference type."); - return obj.ptr().obj_get(); - } - }; - - static_assert(std::is_trivially_copyable_v); - - 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 - friend T reinterpret_borrow(const handle& h) { - return {h, true}; - } - - template - 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(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 + friend T reinterpret_borrow(const handle& h) { + return {h, true}; + } + + template + 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(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 diff --git a/include/pybind11/internal/type_traits.h b/include/pybind11/internal/type_traits.h index 124d539d..ea9dee70 100644 --- a/include/pybind11/internal/type_traits.h +++ b/include/pybind11/internal/type_traits.h @@ -4,154 +4,154 @@ #include namespace pybind11 { - template - constexpr bool dependent_false = false; +template +constexpr bool dependent_false = false; - template - struct tuple_push_front; +template +struct tuple_push_front; - template - struct tuple_push_front> { - using type = std::tuple; +template +struct tuple_push_front> { + using type = std::tuple; +}; + +template +using tuple_push_front_t = typename tuple_push_front::type; + +// traits for function types +template +struct function_traits { + static_assert(dependent_false, "unsupported function type"); +}; + +#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \ + template \ + struct function_traits { \ + using return_type = R; \ + using args_type = std::tuple; \ + constexpr static std::size_t args_count = sizeof...(Args); \ }; - template - using tuple_push_front_t = typename tuple_push_front::type; - - // traits for function types - template - struct function_traits { - static_assert(dependent_false, "unsupported function type"); - }; - -#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \ - template \ - struct function_traits { \ - using return_type = R; \ - using args_type = std::tuple; \ - 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 - using function_return_t = typename function_traits::return_type; +template +using function_return_t = typename function_traits::return_type; - template - using function_args_t = typename function_traits::args_type; +template +using function_args_t = typename function_traits::args_type; - template - constexpr std::size_t function_args_count = function_traits::args_count; +template +constexpr std::size_t function_args_count = function_traits::args_count; - // traits for member pointers - template - struct member_traits; +// traits for member pointers +template +struct member_traits; - template - struct member_traits { - using member_type = M; - using class_type = C; - }; +template +struct member_traits { + using member_type = M; + using class_type = C; +}; - template - using member_type_t = typename member_traits::member_type; +template +using member_type_t = typename member_traits::member_type; - template - using class_type_t = typename member_traits::class_type; +template +using class_type_t = typename member_traits::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 - constexpr inline bool is_function_pointer_v = std::is_function_v>; +template +constexpr inline bool is_function_pointer_v = std::is_function_v>; - template - constexpr bool is_functor_v = false; +template +constexpr bool is_functor_v = false; - template - constexpr inline bool is_functor_v> = true; +template +constexpr inline bool is_functor_v> = true; - template - struct callable_traits; +template +struct callable_traits; - template - struct callable_traits>> { - using args_type = tuple_push_front_t&, function_args_t>>; - using return_type = function_return_t>; - }; +template +struct callable_traits>> { + using args_type = tuple_push_front_t&, function_args_t>>; + using return_type = function_return_t>; +}; - template - struct callable_traits>> { - using args_type = function_args_t>; - using return_type = function_return_t>; - }; +template +struct callable_traits>> { + using args_type = function_args_t>; + using return_type = function_return_t>; +}; - template - struct callable_traits>> { - using args_type = function_args_t>; - using return_type = function_return_t>; - }; +template +struct callable_traits>> { + using args_type = function_args_t>; + using return_type = function_return_t>; +}; - template - using callable_args_t = typename callable_traits::args_type; +template +using callable_args_t = typename callable_traits::args_type; - template - using callable_return_t = typename callable_traits::return_type; +template +using callable_return_t = typename callable_traits::return_type; - template - constexpr std::size_t callable_args_count_v = std::tuple_size_v>; +template +constexpr std::size_t callable_args_count_v = std::tuple_size_v>; - template - struct type_identity { - using type = T; - }; +template +struct type_identity { + using type = T; +}; - template - using remove_cvref_t = std::remove_cv_t>; +template +using remove_cvref_t = std::remove_cv_t>; - template - constexpr inline std::size_t types_count_v = (std::is_same_v + ...); +template +constexpr inline std::size_t types_count_v = (std::is_same_v + ...); - template - constexpr inline std::size_t types_count_v = 0; +template +constexpr inline std::size_t types_count_v = 0; - template - struct value_wrapper { - T* pointer; +template +struct value_wrapper { + T* pointer; - operator T& () { return *pointer; } - }; + operator T& () { return *pointer; } +}; - template - struct value_wrapper { - T* pointer; +template +struct value_wrapper { + T* pointer; - operator T* () { return pointer; } - }; + operator T* () { return pointer; } +}; - template - struct value_wrapper { - T* pointer; +template +struct value_wrapper { + T* pointer; - operator T& () { return *pointer; } - }; + operator T& () { return *pointer; } +}; - template - struct value_wrapper { - T* pointer; +template +struct value_wrapper { + T* pointer; - operator T&& () { return std::move(*pointer); } - }; + operator T&& () { return std::move(*pointer); } +}; } // namespace pybind11 diff --git a/include/pybind11/internal/types.h b/include/pybind11/internal/types.h index d223ad8f..e1cd1512 100644 --- a/include/pybind11/internal/types.h +++ b/include/pybind11/internal/types.h @@ -3,216 +3,206 @@ #include "object.h" namespace pybind11 { - class type : public object { - public: - using object::object; - template - static handle handle_of(); - }; +class type : public object { +public: + using object::object; + template + 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::VM::tp_list), true) {} - }; + list() : object(vm->new_object(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::VM::tp_tuple, n), true) {} + tuple(int n) : object(vm->new_object(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::VM::tp_set), true) {} - }; +class set : public object { +public: + using object::object; + // set() : object(vm->new_object(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::VM::tp_dict), true) {} - }; + dict() : object(vm->new_object(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::VM::tp_str, c, len), true) { +public: + using object::object; + str(const char* c, int len) : + object(vm->new_object(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 - str format(Args&&... args) const; - }; + template + 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 - attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward(key)){}; - - template - attr_accessor& operator= (T&& value) & { - static_assert(std::is_base_of_v>, - "T must be derived from object"); - m_ptr = std::forward(value); - return *this; - } - - template - attr_accessor& operator= (T&& value) && { - static_assert(std::is_base_of_v>, - "T must be derived from object"); - setattr(*this, key, std::forward(value)); - return *this; - } - }; - - inline attr_accessor handle::attr(const char* name) const { - return attr_accessor(reinterpret_borrow(*this), str(name)); - } - - inline attr_accessor handle::attr(const handle& name) const { - return attr_accessor(reinterpret_borrow(*this), reinterpret_borrow(name)); - } - - inline attr_accessor handle::attr(object&& name) const { - return attr_accessor(reinterpret_borrow(*this), std::move(name)); - } - - class item_accessor : public object { - public: - object key; - - public: - template - item_accessor(const object& obj, T&& key) : object(obj), key(std::forward(key)){}; - - template - item_accessor& operator= (T&& value) & { - static_assert(std::is_base_of_v>, - "T must be derived from object"); - m_ptr = std::forward(value); - } - - template - item_accessor& operator= (object&& value) && { - static_assert(std::is_base_of_v>, - "T must be derived from object"); - setitem(*this, key, std::forward(value)); - } - }; - - inline item_accessor handle::operator[] (int64_t key) const { - return item_accessor(reinterpret_borrow(*this), int_(key)); - } - - inline item_accessor handle::operator[] (const char* key) const { - return item_accessor(reinterpret_borrow(*this), str(key)); - } - - inline item_accessor handle::operator[] (const handle& key) const { - return item_accessor(reinterpret_borrow(*this), reinterpret_borrow(key)); - } - - inline item_accessor handle::operator[] (object&& key) const { - return item_accessor(reinterpret_borrow(*this), std::move(key)); - } - - class args : public tuple { - using tuple::tuple; - }; - - class kwargs : public dict { - using dict::dict; - }; +public: + template + attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward(key)){}; template - handle type::handle_of() { - if constexpr(std::is_same_v) { - return vm->_t(vm->tp_object); - } -#define PYBIND11_TYPE_MAPPER(type, tp) \ - else if constexpr(std::is_same_v) { \ - return vm->_t(vm->tp); \ + attr_accessor& operator= (T&& value) & { + static_assert(std::is_base_of_v>, "T must be derived from object"); + m_ptr = std::forward(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 + attr_accessor& operator= (T&& value) && { + static_assert(std::is_base_of_v>, "T must be derived from object"); + setattr(*this, key, std::forward(value)); + return *this; } +}; + +inline attr_accessor handle::attr(const char* name) const { + return attr_accessor(reinterpret_borrow(*this), str(name)); +} + +inline attr_accessor handle::attr(const handle& name) const { + return attr_accessor(reinterpret_borrow(*this), reinterpret_borrow(name)); +} + +inline attr_accessor handle::attr(object&& name) const { + return attr_accessor(reinterpret_borrow(*this), std::move(name)); +} + +class item_accessor : public object { +public: + object key; + +public: + template + item_accessor(const object& obj, T&& key) : object(obj), key(std::forward(key)){}; + + template + item_accessor& operator= (T&& value) & { + static_assert(std::is_base_of_v>, "T must be derived from object"); + m_ptr = std::forward(value); + } + + template + item_accessor& operator= (object&& value) && { + static_assert(std::is_base_of_v>, "T must be derived from object"); + setitem(*this, key, std::forward(value)); + } +}; + +inline item_accessor handle::operator[] (int64_t key) const { + return item_accessor(reinterpret_borrow(*this), int_(key)); +} + +inline item_accessor handle::operator[] (const char* key) const { + return item_accessor(reinterpret_borrow(*this), str(key)); +} + +inline item_accessor handle::operator[] (const handle& key) const { + return item_accessor(reinterpret_borrow(*this), reinterpret_borrow(key)); +} + +inline item_accessor handle::operator[] (object&& key) const { + return item_accessor(reinterpret_borrow(*this), std::move(key)); +} + +class args : public tuple { + using tuple::tuple; +}; + +class kwargs : public dict { + using dict::dict; +}; + +template +handle type::handle_of() { + if constexpr(std::is_same_v) { return vm->_t(vm->tp_object); } +#define PYBIND11_TYPE_MAPPER(type, tp) \ + else if constexpr(std::is_same_v) { 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 diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 3f35334d..e33f169b 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1,3 +1,3 @@ #pragma once -#include "internal/class.h" \ No newline at end of file +#include "internal/class.h" diff --git a/scripts/format.py b/scripts/format.py index fd07c5c7..b77b18eb 100644 --- a/scripts/format.py +++ b/scripts/format.py @@ -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) diff --git a/src/common/any.cpp b/src/common/any.cpp index 0a687e5c..d3c76954 100644 --- a/src/common/any.cpp +++ b/src/common/any.cpp @@ -3,20 +3,20 @@ #include #include -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 \ No newline at end of file +} // namespace pkpy diff --git a/src/common/memorypool.cpp b/src/common/memorypool.cpp index 7be76055..bf3d91e2 100644 --- a/src/common/memorypool.cpp +++ b/src/common/memorypool.cpp @@ -6,28 +6,28 @@ #include #include -namespace pkpy{ +namespace pkpy { -struct LinkedListNode{ +struct LinkedListNode { LinkedListNode* prev; LinkedListNode* next; }; -template -struct DoubleLinkedList{ +template +struct DoubleLinkedList { static_assert(std::is_base_of_v); 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(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 - void apply(Func func){ + template + void apply(Func func) { LinkedListNode* p = head.next; - while(p != &tail){ + while(p != &tail) { LinkedListNode* next = p->next; func(static_cast(p)); p = next; @@ -90,42 +88,41 @@ struct DoubleLinkedList{ } }; -template -struct MemoryPool{ - static const int __MaxBlocks = 256*1024 / __BlockSize; - static const int __MinArenaCount = PK_GC_MIN_THRESHOLD*100 / (256*1024); +template +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 _arenas; DoubleLinkedList _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 -struct FixedMemoryPool{ - struct Block{ +template +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(p); ++_free_list_end; - }else{ + } else { std::free(p); } } @@ -246,12 +243,17 @@ static FixedMemoryPool 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 \ No newline at end of file +} // namespace pkpy diff --git a/src/common/str.cpp b/src/common/str.cpp index a08ec777..ea7b71df 100644 --- a/src/common/str.cpp +++ b/src/common/str.cpp @@ -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 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; isize; 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 detached): size(detached.second), is_ascii(true) { - this->data = detached.first; - for(int i=0; isv() < 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; ioperator[](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 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 Str::split(const Str& sep) const{ - vector 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 Str::split(const Str& sep) const { + vector 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 Str::split(char sep) const{ - vector 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 Str::split(char sep) const { + vector 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& StrName::_interned(){ - static std::map interned; - return interned; - } +std::map& StrName::_interned() { + static std::map interned; + return interned; +} - std::map& StrName::_r_interned(){ - static std::map r_interned; - return r_interned; - } +std::map& StrName::_r_interned() { + static std::map 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(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(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::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::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(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(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 \ No newline at end of file +} // namespace pkpy diff --git a/src/compiler/compiler.cpp b/src/compiler/compiler.cpp index bf4b2ee8..5e5fecdc 100644 --- a/src/compiler/compiler.cpp +++ b/src/compiler/compiler.cpp @@ -4,112 +4,106 @@ #include -namespace pkpy{ - PrattRule Compiler::rules[kTokenCount]; +namespace pkpy { +PrattRule Compiler::rules[kTokenCount]; - NameScope Compiler::name_scope() const { - auto s = contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; - if(unknown_global_scope && s == NAME_GLOBAL) s = NAME_GLOBAL_UNKNOWN; - return s; +NameScope Compiler::name_scope() const { + auto s = contexts.size() > 1 ? NAME_LOCAL : NAME_GLOBAL; + if(unknown_global_scope && s == NAME_GLOBAL) s = NAME_GLOBAL_UNKNOWN; + return s; +} + +CodeObject_ Compiler::push_global_context() { + CodeObject_ co = std::make_shared(lexer.src, lexer.src->filename); + co->start_line = i == 0 ? 1 : prev().line; + contexts.push(CodeEmitContext(vm, co, contexts.size())); + return co; +} + +FuncDecl_ Compiler::push_f_context(Str name) { + FuncDecl_ decl = std::make_shared(); + decl->code = std::make_shared(lexer.src, name); + decl->code->start_line = i == 0 ? 1 : prev().line; + decl->nested = name_scope() == NAME_LOCAL; + contexts.push(CodeEmitContext(vm, decl->code, contexts.size())); + contexts.top().func = decl; + return decl; +} + +void Compiler::pop_context() { + if(!ctx()->s_expr.empty()) { throw std::runtime_error("!ctx()->s_expr.empty()"); } + // add a `return None` in the end as a guard + // previously, we only do this if the last opcode is not a return + // however, this is buggy...since there may be a jump to the end (out of bound) even if the last opcode is a return + ctx()->emit_(OP_RETURN_VALUE, 1, BC_KEEPLINE, true); + // find the last valid token + int j = i - 1; + while(tokens[j].type == TK("@eol") || tokens[j].type == TK("@dedent") || tokens[j].type == TK("@eof")) + j--; + ctx()->co->end_line = tokens[j].line; + + // some check here + auto& codes = ctx()->co->codes; + if(ctx()->co->nlocals > PK_MAX_CO_VARNAMES) { SyntaxError("maximum number of local variables exceeded"); } + if(ctx()->co->consts.size() > 65530) { SyntaxError("maximum number of constants exceeded"); } + // pre-compute LOOP_BREAK and LOOP_CONTINUE + for(int i = 0; i < codes.size(); i++) { + Bytecode& bc = codes[i]; + if(bc.op == OP_LOOP_CONTINUE) { + bc.set_signed_arg(ctx()->co->blocks[bc.arg].start - i); + } else if(bc.op == OP_LOOP_BREAK) { + bc.set_signed_arg(ctx()->co->blocks[bc.arg].get_break_end() - i); + } } - - CodeObject_ Compiler::push_global_context(){ - CodeObject_ co = std::make_shared(lexer.src, lexer.src->filename); - co->start_line = i==0 ? 1 : prev().line; - contexts.push(CodeEmitContext(vm, co, contexts.size())); - return co; - } - - FuncDecl_ Compiler::push_f_context(Str name){ - FuncDecl_ decl = std::make_shared(); - decl->code = std::make_shared(lexer.src, name); - decl->code->start_line = i==0 ? 1 : prev().line; - decl->nested = name_scope() == NAME_LOCAL; - contexts.push(CodeEmitContext(vm, decl->code, contexts.size())); - contexts.top().func = decl; - return decl; - } - - void Compiler::pop_context(){ - if(!ctx()->s_expr.empty()){ - throw std::runtime_error("!ctx()->s_expr.empty()"); - } - // add a `return None` in the end as a guard - // previously, we only do this if the last opcode is not a return - // however, this is buggy...since there may be a jump to the end (out of bound) even if the last opcode is a return - ctx()->emit_(OP_RETURN_VALUE, 1, BC_KEEPLINE, true); - // find the last valid token - int j = i-1; - while(tokens[j].type == TK("@eol") || tokens[j].type == TK("@dedent") || tokens[j].type == TK("@eof")) j--; - ctx()->co->end_line = tokens[j].line; - - // some check here - auto& codes = ctx()->co->codes; - if(ctx()->co->nlocals > PK_MAX_CO_VARNAMES){ - SyntaxError("maximum number of local variables exceeded"); - } - if(ctx()->co->consts.size() > 65530){ - SyntaxError("maximum number of constants exceeded"); - } - // pre-compute LOOP_BREAK and LOOP_CONTINUE - for(int i=0; ico->blocks[bc.arg].start - i); - }else if(bc.op == OP_LOOP_BREAK){ - bc.set_signed_arg(ctx()->co->blocks[bc.arg].get_break_end() - i); - } - } - // pre-compute func->is_simple - FuncDecl_ func = contexts.top().func; - if(func){ - // check generator - for(Bytecode bc: func->code->codes){ - if(bc.op == OP_YIELD_VALUE || bc.op == OP_FOR_ITER_YIELD_VALUE){ - func->type = FuncType::GENERATOR; - for(Bytecode bc: func->code->codes){ - if(bc.op == OP_RETURN_VALUE && bc.arg == BC_NOARG){ - SyntaxError("'return' with argument inside generator function"); - } + // pre-compute func->is_simple + FuncDecl_ func = contexts.top().func; + if(func) { + // check generator + for(Bytecode bc: func->code->codes) { + if(bc.op == OP_YIELD_VALUE || bc.op == OP_FOR_ITER_YIELD_VALUE) { + func->type = FuncType::GENERATOR; + for(Bytecode bc: func->code->codes) { + if(bc.op == OP_RETURN_VALUE && bc.arg == BC_NOARG) { + SyntaxError("'return' with argument inside generator function"); } - break; } + break; } - if(func->type == FuncType::UNSET){ - bool is_simple = true; - if(func->kwargs.size() > 0) is_simple = false; - if(func->starred_arg >= 0) is_simple = false; - if(func->starred_kwarg >= 0) is_simple = false; - - if(is_simple){ - func->type = FuncType::SIMPLE; - - bool is_empty = false; - if(func->code->codes.size() == 1){ - Bytecode bc = func->code->codes[0]; - if(bc.op == OP_RETURN_VALUE && bc.arg == 1){ - is_empty = true; - } - } - if(is_empty) func->type = FuncType::EMPTY; - } - else func->type = FuncType::NORMAL; - } - - assert(func->type != FuncType::UNSET); } - contexts.pop(); + if(func->type == FuncType::UNSET) { + bool is_simple = true; + if(func->kwargs.size() > 0) is_simple = false; + if(func->starred_arg >= 0) is_simple = false; + if(func->starred_kwarg >= 0) is_simple = false; + + if(is_simple) { + func->type = FuncType::SIMPLE; + + bool is_empty = false; + if(func->code->codes.size() == 1) { + Bytecode bc = func->code->codes[0]; + if(bc.op == OP_RETURN_VALUE && bc.arg == 1) { is_empty = true; } + } + if(is_empty) func->type = FuncType::EMPTY; + } else + func->type = FuncType::NORMAL; + } + + assert(func->type != FuncType::UNSET); } + contexts.pop(); +} - void Compiler::init_pratt_rules(){ - static bool initialized = false; - if(initialized) return; - initialized = true; +void Compiler::init_pratt_rules() { + static bool initialized = false; + if(initialized) return; + initialized = true; + // clang-format off // http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ #define PK_METHOD(name) &Compiler::name #define PK_NO_INFIX nullptr, PREC_LOWEST - for(TokenIndex i=0; iis_compiling_class); } + return consumed; +} + +bool Compiler::match_end_stmt() { + if(match(TK(";"))) { + match_newlines(); return true; } + if(match_newlines() || curr().type == TK("@eof")) return true; + if(curr().type == TK("@dedent")) return true; + return false; +} - void Compiler::consume(TokenIndex expected) { - if (!match(expected)){ - SyntaxError( - _S("expected '", TK_STR(expected), "', got '", TK_STR(curr().type), "'") - ); - } - } +void Compiler::consume_end_stmt() { + if(!match_end_stmt()) SyntaxError("expected statement end"); +} - bool Compiler::match_newlines_repl(){ - return match_newlines(mode()==REPL_MODE); - } +void Compiler::EXPR() { parse_expression(PREC_LOWEST + 1); } - bool Compiler::match_newlines(bool repl_throw) { - bool consumed = false; - if (curr().type == TK("@eol")) { - while (curr().type == TK("@eol")) advance(); - consumed = true; - } - if (repl_throw && curr().type == TK("@eof")){ - throw NeedMoreLines(ctx()->is_compiling_class); - } - return consumed; - } - - bool Compiler::match_end_stmt() { - if (match(TK(";"))) { match_newlines(); return true; } - if (match_newlines() || curr().type == TK("@eof")) return true; - if (curr().type == TK("@dedent")) return true; - return false; - } - - void Compiler::consume_end_stmt() { - if (!match_end_stmt()) SyntaxError("expected statement end"); - } - - void Compiler::EXPR() { - parse_expression(PREC_LOWEST+1); - } - - void Compiler::EXPR_TUPLE(bool allow_slice) { - parse_expression(PREC_LOWEST+1, allow_slice); - if(!match(TK(","))) return; - // tuple expression - Expr_vector items; +void Compiler::EXPR_TUPLE(bool allow_slice) { + parse_expression(PREC_LOWEST + 1, allow_slice); + if(!match(TK(","))) return; + // tuple expression + Expr_vector items; + items.push_back(ctx()->s_expr.popx()); + do { + if(curr().brackets_level) match_newlines_repl(); + if(!is_expression(allow_slice)) break; + parse_expression(PREC_LOWEST + 1, allow_slice); items.push_back(ctx()->s_expr.popx()); - do { - if(curr().brackets_level) match_newlines_repl(); - if(!is_expression(allow_slice)) break; - parse_expression(PREC_LOWEST+1, allow_slice); - items.push_back(ctx()->s_expr.popx()); - if(curr().brackets_level) match_newlines_repl(); - } while(match(TK(","))); - ctx()->s_expr.push(make_expr(std::move(items))); - } + if(curr().brackets_level) match_newlines_repl(); + } while(match(TK(","))); + ctx()->s_expr.push(make_expr(std::move(items))); +} - // special case for `for loop` and `comp` - Expr_ Compiler::EXPR_VARS(){ - Expr_vector items; - do { - consume(TK("@id")); - items.push_back(make_expr(prev().str(), name_scope())); - } while(match(TK(","))); - if(items.size()==1) return std::move(items[0]); - return make_expr(std::move(items)); - } - - void Compiler::exprLiteral(){ - ctx()->s_expr.push(make_expr(prev().value)); - } - - void Compiler::exprLong(){ - ctx()->s_expr.push(make_expr(prev().str())); - } - - void Compiler::exprImag(){ - ctx()->s_expr.push(make_expr(std::get(prev().value))); - } - - void Compiler::exprBytes(){ - ctx()->s_expr.push(make_expr(std::get(prev().value))); - } - - void Compiler::exprFString(){ - ctx()->s_expr.push(make_expr(std::get(prev().value))); - } - - void Compiler::exprLambda(){ - FuncDecl_ decl = push_f_context(""); - auto e = make_expr(decl); - if(!match(TK(":"))){ - _compile_f_args(e->decl, false); - consume(TK(":")); - } - // https://github.com/pocketpy/pocketpy/issues/37 - parse_expression(PREC_LAMBDA + 1); - ctx()->emit_expr(); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); - pop_context(); - ctx()->s_expr.push(std::move(e)); - } - - void Compiler::exprOr(){ - auto e = make_expr(); - e->lhs = ctx()->s_expr.popx(); - parse_expression(PREC_LOGICAL_OR + 1); - e->rhs = ctx()->s_expr.popx(); - ctx()->s_expr.push(std::move(e)); - } - - void Compiler::exprAnd(){ - auto e = make_expr(); - e->lhs = ctx()->s_expr.popx(); - parse_expression(PREC_LOGICAL_AND + 1); - e->rhs = ctx()->s_expr.popx(); - ctx()->s_expr.push(std::move(e)); - } - - void Compiler::exprTernary(){ - auto e = make_expr(); - e->true_expr = ctx()->s_expr.popx(); - // cond - parse_expression(PREC_TERNARY + 1); - e->cond = ctx()->s_expr.popx(); - consume(TK("else")); - // if false - parse_expression(PREC_TERNARY + 1); - e->false_expr = ctx()->s_expr.popx(); - ctx()->s_expr.push(std::move(e)); - } - - void Compiler::exprBinaryOp(){ - auto e = make_expr(); - e->op = prev().type; - e->lhs = ctx()->s_expr.popx(); - parse_expression(rules[e->op].precedence + 1); - e->rhs = ctx()->s_expr.popx(); - ctx()->s_expr.push(std::move(e)); - } - - void Compiler::exprNot() { - parse_expression(PREC_LOGICAL_NOT + 1); - ctx()->s_expr.push(make_expr(ctx()->s_expr.popx())); - } - - void Compiler::exprUnaryOp(){ - TokenIndex op = prev().type; - parse_expression(PREC_UNARY + 1); - switch(op){ - case TK("-"): - ctx()->s_expr.push(make_expr(ctx()->s_expr.popx())); - break; - case TK("~"): - ctx()->s_expr.push(make_expr(ctx()->s_expr.popx())); - break; - case TK("*"): - ctx()->s_expr.push(make_expr(1, ctx()->s_expr.popx())); - break; - case TK("**"): - ctx()->s_expr.push(make_expr(2, ctx()->s_expr.popx())); - break; - default: assert(false); - } - } - - void Compiler::exprGroup(){ - match_newlines_repl(); - EXPR_TUPLE(); // () is just for change precedence - match_newlines_repl(); - consume(TK(")")); - if(ctx()->s_expr.top()->is_tuple()) return; - Expr_ g = make_expr(ctx()->s_expr.popx()); - ctx()->s_expr.push(std::move(g)); - } - - void Compiler::consume_comp(unique_ptr_128 ce, Expr_ expr){ - ce->expr = std::move(expr); - ce->vars = EXPR_VARS(); - consume(TK("in")); - parse_expression(PREC_TERNARY + 1); - ce->iter = ctx()->s_expr.popx(); - match_newlines_repl(); - if(match(TK("if"))){ - parse_expression(PREC_TERNARY + 1); - ce->cond = ctx()->s_expr.popx(); - } - ctx()->s_expr.push(std::move(ce)); - match_newlines_repl(); - } - - void Compiler::exprList() { - int line = prev().line; - Expr_vector items; - do { - match_newlines_repl(); - if (curr().type == TK("]")) break; - EXPR(); - items.push_back(ctx()->s_expr.popx()); - match_newlines_repl(); - if(items.size()==1 && match(TK("for"))){ - consume_comp(make_expr(), std::move(items[0])); - consume(TK("]")); - return; - } - match_newlines_repl(); - } while (match(TK(","))); - consume(TK("]")); - auto e = make_expr(std::move(items)); - e->line = line; // override line - ctx()->s_expr.push(std::move(e)); - } - - void Compiler::exprMap() { - bool parsing_dict = false; // {...} may be dict or set - Expr_vector items; - do { - match_newlines_repl(); - if (curr().type == TK("}")) break; - EXPR(); - int star_level = ctx()->s_expr.top()->star_level(); - if(star_level==2 || curr().type == TK(":")){ - parsing_dict = true; - } - if(parsing_dict){ - auto dict_item = make_expr(); - if(star_level == 2){ - dict_item->key = nullptr; - dict_item->value = ctx()->s_expr.popx(); - }else{ - consume(TK(":")); - EXPR(); - dict_item->key = ctx()->s_expr.popx(); - dict_item->value = ctx()->s_expr.popx(); - } - items.push_back(std::move(dict_item)); - }else{ - items.push_back(ctx()->s_expr.popx()); - } - match_newlines_repl(); - if(items.size()==1 && match(TK("for"))){ - if(parsing_dict) consume_comp(make_expr(), std::move(items[0])); - else consume_comp(make_expr(), std::move(items[0])); - consume(TK("}")); - return; - } - match_newlines_repl(); - } while (match(TK(","))); - consume(TK("}")); - if(items.size()==0 || parsing_dict){ - auto e = make_expr(std::move(items)); - ctx()->s_expr.push(std::move(e)); - }else{ - auto e = make_expr(std::move(items)); - ctx()->s_expr.push(std::move(e)); - } - } - - void Compiler::exprCall() { - auto e = make_expr(); - e->callable = ctx()->s_expr.popx(); - do { - match_newlines_repl(); - if (curr().type==TK(")")) break; - if(curr().type==TK("@id") && next().type==TK("=")) { - consume(TK("@id")); - Str key = prev().str(); - consume(TK("=")); - EXPR(); - e->kwargs.push_back({key, ctx()->s_expr.popx()}); - } else{ - EXPR(); - if(ctx()->s_expr.top()->star_level() == 2){ - // **kwargs - e->kwargs.push_back({"**", ctx()->s_expr.popx()}); - }else{ - // positional argument - if(!e->kwargs.empty()) SyntaxError("positional argument follows keyword argument"); - e->args.push_back(ctx()->s_expr.popx()); - } - } - match_newlines_repl(); - } while (match(TK(","))); - consume(TK(")")); - if(e->args.size() > 32767) SyntaxError("too many positional arguments"); - if(e->kwargs.size() > 32767) SyntaxError("too many keyword arguments"); - ctx()->s_expr.push(std::move(e)); - } - - void Compiler::exprName(){ - Str name = prev().str(); - NameScope scope = name_scope(); - if(ctx()->global_names.contains(name)){ - scope = NAME_GLOBAL; - } - ctx()->s_expr.push(make_expr(name, scope)); - } - - void Compiler::exprAttrib() { +// special case for `for loop` and `comp` +Expr_ Compiler::EXPR_VARS() { + Expr_vector items; + do { consume(TK("@id")); - ctx()->s_expr.push( - make_expr(ctx()->s_expr.popx(), StrName::get(prev().sv())) - ); - } + items.push_back(make_expr(prev().str(), name_scope())); + } while(match(TK(","))); + if(items.size() == 1) return std::move(items[0]); + return make_expr(std::move(items)); +} - void Compiler::exprSlice0() { - auto slice = make_expr(); - if(is_expression()){ // : - EXPR(); - slice->stop = ctx()->s_expr.popx(); - // try optional step - if(match(TK(":"))){ // :: - EXPR(); - slice->step = ctx()->s_expr.popx(); - } - }else if(match(TK(":"))){ - if(is_expression()){ // :: - EXPR(); - slice->step = ctx()->s_expr.popx(); - } // else :: - } // else : - ctx()->s_expr.push(std::move(slice)); - } +void Compiler::exprLiteral() { ctx()->s_expr.push(make_expr(prev().value)); } - void Compiler::exprSlice1() { - auto slice = make_expr(); - slice->start = ctx()->s_expr.popx(); - if(is_expression()){ // : - EXPR(); - slice->stop = ctx()->s_expr.popx(); - // try optional step - if(match(TK(":"))){ // :: - EXPR(); - slice->step = ctx()->s_expr.popx(); - } - }else if(match(TK(":"))){ // :: - EXPR(); - slice->step = ctx()->s_expr.popx(); - } // else : - ctx()->s_expr.push(std::move(slice)); - } - - void Compiler::exprSubscr() { - auto e = make_expr(); - match_newlines_repl(); - e->a = ctx()->s_expr.popx(); // a - EXPR_TUPLE(true); - e->b = ctx()->s_expr.popx(); // a[] - match_newlines_repl(); - consume(TK("]")); - ctx()->s_expr.push(std::move(e)); - } +void Compiler::exprLong() { ctx()->s_expr.push(make_expr(prev().str())); } - void Compiler::exprLiteral0() { - ctx()->s_expr.push(make_expr(prev().type)); - } +void Compiler::exprImag() { ctx()->s_expr.push(make_expr(std::get(prev().value))); } - void Compiler::compile_block_body(void (Compiler::*callback)()) { - if(callback == nullptr) callback = &Compiler::compile_stmt; +void Compiler::exprBytes() { ctx()->s_expr.push(make_expr(std::get(prev().value))); } + +void Compiler::exprFString() { ctx()->s_expr.push(make_expr(std::get(prev().value))); } + +void Compiler::exprLambda() { + FuncDecl_ decl = push_f_context(""); + auto e = make_expr(decl); + if(!match(TK(":"))) { + _compile_f_args(e->decl, false); consume(TK(":")); - if(curr().type!=TK("@eol") && curr().type!=TK("@eof")){ - while(true){ - compile_stmt(); - bool possible = curr().type!=TK("@eol") && curr().type!=TK("@eof"); - if(prev().type != TK(";") || !possible) break; - } + } + // https://github.com/pocketpy/pocketpy/issues/37 + parse_expression(PREC_LAMBDA + 1); + ctx()->emit_expr(); + ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); + pop_context(); + ctx()->s_expr.push(std::move(e)); +} + +void Compiler::exprOr() { + auto e = make_expr(); + e->lhs = ctx()->s_expr.popx(); + parse_expression(PREC_LOGICAL_OR + 1); + e->rhs = ctx()->s_expr.popx(); + ctx()->s_expr.push(std::move(e)); +} + +void Compiler::exprAnd() { + auto e = make_expr(); + e->lhs = ctx()->s_expr.popx(); + parse_expression(PREC_LOGICAL_AND + 1); + e->rhs = ctx()->s_expr.popx(); + ctx()->s_expr.push(std::move(e)); +} + +void Compiler::exprTernary() { + auto e = make_expr(); + e->true_expr = ctx()->s_expr.popx(); + // cond + parse_expression(PREC_TERNARY + 1); + e->cond = ctx()->s_expr.popx(); + consume(TK("else")); + // if false + parse_expression(PREC_TERNARY + 1); + e->false_expr = ctx()->s_expr.popx(); + ctx()->s_expr.push(std::move(e)); +} + +void Compiler::exprBinaryOp() { + auto e = make_expr(); + e->op = prev().type; + e->lhs = ctx()->s_expr.popx(); + parse_expression(rules[e->op].precedence + 1); + e->rhs = ctx()->s_expr.popx(); + ctx()->s_expr.push(std::move(e)); +} + +void Compiler::exprNot() { + parse_expression(PREC_LOGICAL_NOT + 1); + ctx()->s_expr.push(make_expr(ctx()->s_expr.popx())); +} + +void Compiler::exprUnaryOp() { + TokenIndex op = prev().type; + parse_expression(PREC_UNARY + 1); + switch(op) { + case TK("-"): ctx()->s_expr.push(make_expr(ctx()->s_expr.popx())); break; + case TK("~"): ctx()->s_expr.push(make_expr(ctx()->s_expr.popx())); break; + case TK("*"): ctx()->s_expr.push(make_expr(1, ctx()->s_expr.popx())); break; + case TK("**"): ctx()->s_expr.push(make_expr(2, ctx()->s_expr.popx())); break; + default: assert(false); + } +} + +void Compiler::exprGroup() { + match_newlines_repl(); + EXPR_TUPLE(); // () is just for change precedence + match_newlines_repl(); + consume(TK(")")); + if(ctx()->s_expr.top()->is_tuple()) return; + Expr_ g = make_expr(ctx()->s_expr.popx()); + ctx()->s_expr.push(std::move(g)); +} + +void Compiler::consume_comp(unique_ptr_128 ce, Expr_ expr) { + ce->expr = std::move(expr); + ce->vars = EXPR_VARS(); + consume(TK("in")); + parse_expression(PREC_TERNARY + 1); + ce->iter = ctx()->s_expr.popx(); + match_newlines_repl(); + if(match(TK("if"))) { + parse_expression(PREC_TERNARY + 1); + ce->cond = ctx()->s_expr.popx(); + } + ctx()->s_expr.push(std::move(ce)); + match_newlines_repl(); +} + +void Compiler::exprList() { + int line = prev().line; + Expr_vector items; + do { + match_newlines_repl(); + if(curr().type == TK("]")) break; + EXPR(); + items.push_back(ctx()->s_expr.popx()); + match_newlines_repl(); + if(items.size() == 1 && match(TK("for"))) { + consume_comp(make_expr(), std::move(items[0])); + consume(TK("]")); return; } - if(!match_newlines(mode()==REPL_MODE)){ - SyntaxError("expected a new line after ':'"); - } - consume(TK("@indent")); - while (curr().type != TK("@dedent")) { - match_newlines(); - (this->*callback)(); - match_newlines(); - } - consume(TK("@dedent")); - } + match_newlines_repl(); + } while(match(TK(","))); + consume(TK("]")); + auto e = make_expr(std::move(items)); + e->line = line; // override line + ctx()->s_expr.push(std::move(e)); +} - // import a [as b] - // import a [as b], c [as d] - void Compiler::compile_normal_import() { - do { +void Compiler::exprMap() { + bool parsing_dict = false; // {...} may be dict or set + Expr_vector items; + do { + match_newlines_repl(); + if(curr().type == TK("}")) break; + EXPR(); + int star_level = ctx()->s_expr.top()->star_level(); + if(star_level == 2 || curr().type == TK(":")) { parsing_dict = true; } + if(parsing_dict) { + auto dict_item = make_expr(); + if(star_level == 2) { + dict_item->key = nullptr; + dict_item->value = ctx()->s_expr.popx(); + } else { + consume(TK(":")); + EXPR(); + dict_item->key = ctx()->s_expr.popx(); + dict_item->value = ctx()->s_expr.popx(); + } + items.push_back(std::move(dict_item)); + } else { + items.push_back(ctx()->s_expr.popx()); + } + match_newlines_repl(); + if(items.size() == 1 && match(TK("for"))) { + if(parsing_dict) + consume_comp(make_expr(), std::move(items[0])); + else + consume_comp(make_expr(), std::move(items[0])); + consume(TK("}")); + return; + } + match_newlines_repl(); + } while(match(TK(","))); + consume(TK("}")); + if(items.size() == 0 || parsing_dict) { + auto e = make_expr(std::move(items)); + ctx()->s_expr.push(std::move(e)); + } else { + auto e = make_expr(std::move(items)); + ctx()->s_expr.push(std::move(e)); + } +} + +void Compiler::exprCall() { + auto e = make_expr(); + e->callable = ctx()->s_expr.popx(); + do { + match_newlines_repl(); + if(curr().type == TK(")")) break; + if(curr().type == TK("@id") && next().type == TK("=")) { consume(TK("@id")); - Str name = prev().str(); - ctx()->emit_(OP_IMPORT_PATH, ctx()->add_const_string(name.sv()), prev().line); - if (match(TK("as"))) { - consume(TK("@id")); - name = prev().str(); + Str key = prev().str(); + consume(TK("=")); + EXPR(); + e->kwargs.push_back({key, ctx()->s_expr.popx()}); + } else { + EXPR(); + if(ctx()->s_expr.top()->star_level() == 2) { + // **kwargs + e->kwargs.push_back({"**", ctx()->s_expr.popx()}); + } else { + // positional argument + if(!e->kwargs.empty()) SyntaxError("positional argument follows keyword argument"); + e->args.push_back(ctx()->s_expr.popx()); } - ctx()->emit_store_name(name_scope(), StrName(name), prev().line); - } while (match(TK(","))); - consume_end_stmt(); - } - - // from a import b [as c], d [as e] - // from a.b import c [as d] - // from . import a [as b] - // from .a import b [as c] - // from ..a import b [as c] - // from .a.b import c [as d] - // from xxx import * - void Compiler::compile_from_import() { - int dots = 0; - - while(true){ - switch(curr().type){ - case TK("."): dots+=1; break; - case TK(".."): dots+=2; break; - case TK("..."): dots+=3; break; - default: goto __EAT_DOTS_END; - } - advance(); } + match_newlines_repl(); + } while(match(TK(","))); + consume(TK(")")); + if(e->args.size() > 32767) SyntaxError("too many positional arguments"); + if(e->kwargs.size() > 32767) SyntaxError("too many keyword arguments"); + ctx()->s_expr.push(std::move(e)); +} + +void Compiler::exprName() { + Str name = prev().str(); + NameScope scope = name_scope(); + if(ctx()->global_names.contains(name)) { scope = NAME_GLOBAL; } + ctx()->s_expr.push(make_expr(name, scope)); +} + +void Compiler::exprAttrib() { + consume(TK("@id")); + ctx()->s_expr.push(make_expr(ctx()->s_expr.popx(), StrName::get(prev().sv()))); +} + +void Compiler::exprSlice0() { + auto slice = make_expr(); + if(is_expression()) { // : + EXPR(); + slice->stop = ctx()->s_expr.popx(); + // try optional step + if(match(TK(":"))) { // :: + EXPR(); + slice->step = ctx()->s_expr.popx(); + } + } else if(match(TK(":"))) { + if(is_expression()) { // :: + EXPR(); + slice->step = ctx()->s_expr.popx(); + } // else :: + } // else : + ctx()->s_expr.push(std::move(slice)); +} + +void Compiler::exprSlice1() { + auto slice = make_expr(); + slice->start = ctx()->s_expr.popx(); + if(is_expression()) { // : + EXPR(); + slice->stop = ctx()->s_expr.popx(); + // try optional step + if(match(TK(":"))) { // :: + EXPR(); + slice->step = ctx()->s_expr.popx(); + } + } else if(match(TK(":"))) { // :: + EXPR(); + slice->step = ctx()->s_expr.popx(); + } // else : + ctx()->s_expr.push(std::move(slice)); +} + +void Compiler::exprSubscr() { + auto e = make_expr(); + match_newlines_repl(); + e->a = ctx()->s_expr.popx(); // a + EXPR_TUPLE(true); + e->b = ctx()->s_expr.popx(); // a[] + match_newlines_repl(); + consume(TK("]")); + ctx()->s_expr.push(std::move(e)); +} + +void Compiler::exprLiteral0() { ctx()->s_expr.push(make_expr(prev().type)); } + +void Compiler::compile_block_body(void (Compiler::*callback)()) { + if(callback == nullptr) callback = &Compiler::compile_stmt; + consume(TK(":")); + if(curr().type != TK("@eol") && curr().type != TK("@eof")) { + while(true) { + compile_stmt(); + bool possible = curr().type != TK("@eol") && curr().type != TK("@eof"); + if(prev().type != TK(";") || !possible) break; + } + return; + } + if(!match_newlines(mode() == REPL_MODE)) { SyntaxError("expected a new line after ':'"); } + consume(TK("@indent")); + while(curr().type != TK("@dedent")) { + match_newlines(); + (this->*callback)(); + match_newlines(); + } + consume(TK("@dedent")); +} + +// import a [as b] +// import a [as b], c [as d] +void Compiler::compile_normal_import() { + do { + consume(TK("@id")); + Str name = prev().str(); + ctx()->emit_(OP_IMPORT_PATH, ctx()->add_const_string(name.sv()), prev().line); + if(match(TK("as"))) { + consume(TK("@id")); + name = prev().str(); + } + ctx()->emit_store_name(name_scope(), StrName(name), prev().line); + } while(match(TK(","))); + consume_end_stmt(); +} + +// from a import b [as c], d [as e] +// from a.b import c [as d] +// from . import a [as b] +// from .a import b [as c] +// from ..a import b [as c] +// from .a.b import c [as d] +// from xxx import * +void Compiler::compile_from_import() { + int dots = 0; + + while(true) { + switch(curr().type) { + case TK("."): dots += 1; break; + case TK(".."): dots += 2; break; + case TK("..."): dots += 3; break; + default: goto __EAT_DOTS_END; + } + advance(); + } __EAT_DOTS_END: - SStream ss; - for(int i=0; i 0){ - // @id is optional if dots > 0 - if(match(TK("@id"))){ - ss << prev().sv(); - while (match(TK("."))) { - consume(TK("@id")); - ss << "." << prev().sv(); - } - } - }else{ - // @id is required if dots == 0 - consume(TK("@id")); + if(dots > 0) { + // @id is optional if dots > 0 + if(match(TK("@id"))) { ss << prev().sv(); - while (match(TK("."))) { + while(match(TK("."))) { consume(TK("@id")); ss << "." << prev().sv(); } } - - ctx()->emit_(OP_IMPORT_PATH, ctx()->add_const_string(ss.str().sv()), prev().line); - consume(TK("import")); - - if (match(TK("*"))) { - if(name_scope() != NAME_GLOBAL) SyntaxError("from import * can only be used in global scope"); - // pop the module and import __all__ - ctx()->emit_(OP_POP_IMPORT_STAR, BC_NOARG, prev().line); - consume_end_stmt(); - return; - } - - do { - ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); + } else { + // @id is required if dots == 0 + consume(TK("@id")); + ss << prev().sv(); + while(match(TK("."))) { consume(TK("@id")); - Str name = prev().str(); - ctx()->emit_(OP_LOAD_ATTR, StrName(name).index, prev().line); - if (match(TK("as"))) { - consume(TK("@id")); - name = prev().str(); - } - ctx()->emit_store_name(name_scope(), StrName(name), prev().line); - } while (match(TK(","))); - ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); + ss << "." << prev().sv(); + } + } + + ctx()->emit_(OP_IMPORT_PATH, ctx()->add_const_string(ss.str().sv()), prev().line); + consume(TK("import")); + + if(match(TK("*"))) { + if(name_scope() != NAME_GLOBAL) SyntaxError("from import * can only be used in global scope"); + // pop the module and import __all__ + ctx()->emit_(OP_POP_IMPORT_STAR, BC_NOARG, prev().line); consume_end_stmt(); + return; } - bool Compiler::is_expression(bool allow_slice){ - PrattCallback prefix = rules[curr().type].prefix; - return prefix != nullptr && (allow_slice || curr().type!=TK(":")); - } - - void Compiler::parse_expression(int precedence, bool allow_slice) { - PrattCallback prefix = rules[curr().type].prefix; - if (prefix==nullptr || (curr().type==TK(":") && !allow_slice)){ - SyntaxError(Str("expected an expression, got ") + TK_STR(curr().type)); - } - advance(); - (this->*prefix)(); - while (rules[curr().type].precedence >= precedence && (allow_slice || curr().type!=TK(":"))) { - TokenIndex op = curr().type; - advance(); - PrattCallback infix = rules[op].infix; - assert(infix != nullptr); - (this->*infix)(); - } - } - - void Compiler::compile_if_stmt() { - EXPR(); // condition - ctx()->emit_expr(); - int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); - compile_block_body(); - if (match(TK("elif"))) { - int exit_patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, prev().line); - ctx()->patch_jump(patch); - compile_if_stmt(); - ctx()->patch_jump(exit_patch); - } else if (match(TK("else"))) { - int exit_patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, prev().line); - ctx()->patch_jump(patch); - compile_block_body(); - ctx()->patch_jump(exit_patch); - } else { - ctx()->patch_jump(patch); - } - } - - void Compiler::compile_while_loop() { - CodeBlock* block = ctx()->enter_block(CodeBlockType::WHILE_LOOP); - EXPR(); // condition - ctx()->emit_expr(); - int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); - compile_block_body(); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); - ctx()->patch_jump(patch); - ctx()->exit_block(); - // optional else clause - if (match(TK("else"))) { - compile_block_body(); - block->end2 = ctx()->co->codes.size(); - } - } - - void Compiler::compile_for_loop() { - Expr_ vars = EXPR_VARS(); - consume(TK("in")); - EXPR_TUPLE(); ctx()->emit_expr(); - ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, BC_KEEPLINE); - CodeBlock* block = ctx()->enter_block(CodeBlockType::FOR_LOOP); - int for_codei = ctx()->emit_(OP_FOR_ITER, ctx()->curr_iblock, BC_KEEPLINE); - bool ok = vars->emit_store(ctx()); - if(!ok) SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind - ctx()->try_merge_for_iter_store(for_codei); - compile_block_body(); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); - ctx()->exit_block(); - // optional else clause - if (match(TK("else"))) { - compile_block_body(); - block->end2 = ctx()->co->codes.size(); - } - } - - void Compiler::compile_try_except() { - ctx()->enter_block(CodeBlockType::TRY_EXCEPT); - ctx()->emit_(OP_TRY_ENTER, BC_NOARG, prev().line); - compile_block_body(); - small_vector_2 patches; - patches.push_back( - ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE) - ); - ctx()->exit_block(); - - int finally_entry = -1; - if(curr().type != TK("finally")){ - do { - StrName as_name; - consume(TK("except")); - if(is_expression()){ - EXPR(); // push assumed type on to the stack - ctx()->emit_expr(); - ctx()->emit_(OP_EXCEPTION_MATCH, BC_NOARG, prev().line); - if(match(TK("as"))){ - consume(TK("@id")); - as_name = StrName(prev().sv()); - } - }else{ - ctx()->emit_(OP_LOAD_TRUE, BC_NOARG, BC_KEEPLINE); - } - int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); - // on match - if(!as_name.empty()){ - ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); - ctx()->emit_store_name(name_scope(), as_name, BC_KEEPLINE); - } - // pop the exception - ctx()->emit_(OP_POP_EXCEPTION, BC_NOARG, BC_KEEPLINE); - compile_block_body(); - patches.push_back(ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE)); - ctx()->patch_jump(patch); - }while(curr().type == TK("except")); - } - - if(match(TK("finally"))){ - int patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); - finally_entry = ctx()->co->codes.size(); - compile_block_body(); - ctx()->emit_(OP_JUMP_ABSOLUTE_TOP, BC_NOARG, BC_KEEPLINE); - ctx()->patch_jump(patch); - } - // no match, re-raise - if(finally_entry != -1){ - i64 target = ctx()->co->codes.size()+2; - ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE); - int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); - ctx()->co->codes[i].set_signed_arg(finally_entry - i); - } - ctx()->emit_(OP_RE_RAISE, BC_NOARG, BC_KEEPLINE); - - // no exception or no match, jump to the end - for (int patch : patches) ctx()->patch_jump(patch); - if(finally_entry != -1){ - i64 target = ctx()->co->codes.size()+2; - ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE); - int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); - ctx()->co->codes[i].set_signed_arg(finally_entry - i); - } - } - - void Compiler::compile_decorated(){ - Expr_vector decorators; - do{ - EXPR(); - decorators.push_back(ctx()->s_expr.popx()); - if(!match_newlines_repl()) SyntaxError(); - }while(match(TK("@"))); - - if(match(TK("class"))){ - compile_class(decorators); - }else{ - consume(TK("def")); - compile_function(decorators); - } - } - - bool Compiler::try_compile_assignment(){ - switch (curr().type) { - case TK("+="): case TK("-="): case TK("*="): case TK("/="): case TK("//="): case TK("%="): - case TK("<<="): case TK(">>="): case TK("&="): case TK("|="): case TK("^="): { - Expr* lhs_p = ctx()->s_expr.top().get(); - if(lhs_p->is_starred()) SyntaxError(); - if(ctx()->is_compiling_class) SyntaxError("can't use inplace operator in class definition"); - advance(); - // a[x] += 1; a and x should be evaluated only once - // a.x += 1; a should be evaluated only once - auto e = make_expr(true); // inplace=true - e->op = prev().type - 1; // -1 to remove = - e->lhs = ctx()->s_expr.popx(); - EXPR_TUPLE(); - e->rhs = ctx()->s_expr.popx(); - if(e->rhs->is_starred()) SyntaxError(); - e->emit_(ctx()); - bool ok = lhs_p->emit_store_inplace(ctx()); - if(!ok) SyntaxError(); - } return true; - case TK("="): { - int n = 0; - while(match(TK("="))){ - EXPR_TUPLE(); - n += 1; - } - // stack size is n+1 - Expr_ val = ctx()->s_expr.popx(); - val->emit_(ctx()); - for(int j=1; jemit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); - for(int j=0; js_expr.popx(); - if(e->is_starred()) SyntaxError(); - bool ok = e->emit_store(ctx()); - if(!ok) SyntaxError(); - } - } return true; - default: return false; - } - } - - void Compiler::compile_stmt() { - if(match(TK("class"))){ - compile_class(); - return; - } - advance(); - int kw_line = prev().line; // backup line number - int curr_loop_block = ctx()->get_loop(); - switch(prev().type){ - case TK("break"): - if (curr_loop_block < 0) SyntaxError("'break' outside loop"); - ctx()->emit_(OP_LOOP_BREAK, curr_loop_block, kw_line); - consume_end_stmt(); - break; - case TK("continue"): - if (curr_loop_block < 0) SyntaxError("'continue' not properly in loop"); - ctx()->emit_(OP_LOOP_CONTINUE, curr_loop_block, kw_line); - consume_end_stmt(); - break; - case TK("yield"): - if (contexts.size() <= 1) SyntaxError("'yield' outside function"); - EXPR_TUPLE(); ctx()->emit_expr(); - ctx()->emit_(OP_YIELD_VALUE, BC_NOARG, kw_line); - consume_end_stmt(); - break; - case TK("yield from"): - if (contexts.size() <= 1) SyntaxError("'yield from' outside function"); - EXPR_TUPLE(); ctx()->emit_expr(); - - ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, kw_line); - ctx()->enter_block(CodeBlockType::FOR_LOOP); - ctx()->emit_(OP_FOR_ITER_YIELD_VALUE, BC_NOARG, kw_line); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), kw_line); - ctx()->exit_block(); - consume_end_stmt(); - break; - case TK("return"): - if (contexts.size() <= 1) SyntaxError("'return' outside function"); - if(match_end_stmt()){ - ctx()->emit_(OP_RETURN_VALUE, 1, kw_line); - }else{ - EXPR_TUPLE(); ctx()->emit_expr(); - consume_end_stmt(); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, kw_line); - } - break; - /*************************************************/ - case TK("if"): compile_if_stmt(); break; - case TK("while"): compile_while_loop(); break; - case TK("for"): compile_for_loop(); break; - case TK("import"): compile_normal_import(); break; - case TK("from"): compile_from_import(); break; - case TK("def"): compile_function(); break; - case TK("@"): compile_decorated(); break; - case TK("try"): compile_try_except(); break; - case TK("pass"): consume_end_stmt(); break; - /*************************************************/ - case TK("++"):{ - consume(TK("@id")); - StrName name(prev().sv()); - NameScope scope = name_scope(); - bool is_global = ctx()->global_names.contains(name.sv()); - if(is_global) scope = NAME_GLOBAL; - switch(scope){ - case NAME_LOCAL: - ctx()->emit_(OP_INC_FAST, ctx()->add_varname(name), prev().line); - break; - case NAME_GLOBAL: - ctx()->emit_(OP_INC_GLOBAL, name.index, prev().line); - break; - default: SyntaxError(); break; - } - consume_end_stmt(); - break; - } - case TK("--"):{ - consume(TK("@id")); - StrName name(prev().sv()); - switch(name_scope()){ - case NAME_LOCAL: - ctx()->emit_(OP_DEC_FAST, ctx()->add_varname(name), prev().line); - break; - case NAME_GLOBAL: - ctx()->emit_(OP_DEC_GLOBAL, name.index, prev().line); - break; - default: SyntaxError(); break; - } - consume_end_stmt(); - break; - } - case TK("assert"):{ - EXPR(); // condition - ctx()->emit_expr(); - int index = ctx()->emit_(OP_POP_JUMP_IF_TRUE, BC_NOARG, kw_line); - int has_msg = 0; - if(match(TK(","))){ - EXPR(); // message - ctx()->emit_expr(); - has_msg = 1; - } - ctx()->emit_(OP_RAISE_ASSERT, has_msg, kw_line); - ctx()->patch_jump(index); - consume_end_stmt(); - break; - } - case TK("global"): - do { - consume(TK("@id")); - ctx()->global_names.push_back(prev().sv()); - } while (match(TK(","))); - consume_end_stmt(); - break; - case TK("raise"): { - EXPR(); ctx()->emit_expr(); - ctx()->emit_(OP_RAISE, BC_NOARG, kw_line); - consume_end_stmt(); - } break; - case TK("del"): { - EXPR_TUPLE(); - Expr_ e = ctx()->s_expr.popx(); - bool ok = e->emit_del(ctx()); - if(!ok) SyntaxError(); - consume_end_stmt(); - } break; - case TK("with"): { - EXPR(); // [ ] - ctx()->emit_expr(); - ctx()->enter_block(CodeBlockType::CONTEXT_MANAGER); - Expr_ as_name; - if(match(TK("as"))){ - consume(TK("@id")); - as_name = make_expr(prev().str(), name_scope()); - } - ctx()->emit_(OP_WITH_ENTER, BC_NOARG, prev().line); - // [ .__enter__() ] - if(as_name != nullptr){ - bool ok = as_name->emit_store(ctx()); - if(!ok) SyntaxError(); - }else{ - ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); - } - compile_block_body(); - ctx()->emit_(OP_WITH_EXIT, BC_NOARG, prev().line); - ctx()->exit_block(); - } break; - /*************************************************/ - case TK("=="): { - consume(TK("@id")); - if(mode()!=EXEC_MODE) SyntaxError("'label' is only available in EXEC_MODE"); - bool ok = ctx()->add_label(prev().str()); - consume(TK("==")); - if(!ok) SyntaxError("label " + prev().str().escape() + " already exists"); - consume_end_stmt(); - } break; - case TK("->"): - consume(TK("@id")); - if(mode()!=EXEC_MODE) SyntaxError("'goto' is only available in EXEC_MODE"); - ctx()->emit_(OP_GOTO, StrName(prev().sv()).index, prev().line); - consume_end_stmt(); - break; - /*************************************************/ - // handle dangling expression or assignment - default: { - advance(-1); // do revert since we have pre-called advance() at the beginning - EXPR_TUPLE(); - - bool is_typed_name = false; // e.g. x: int - // eat variable's type hint if it is a single name - if(ctx()->s_expr.top()->is_name()){ - if(match(TK(":"))){ - consume_type_hints(); - is_typed_name = true; - - if(ctx()->is_compiling_class){ - NameExpr* ne = static_cast(ctx()->s_expr.top().get()); - ctx()->emit_(OP_ADD_CLASS_ANNOTATION, ne->name.index, BC_KEEPLINE); - } - } - } - if(!try_compile_assignment()){ - if(!ctx()->s_expr.empty() && ctx()->s_expr.top()->is_starred()){ - SyntaxError(); - } - if(!is_typed_name){ - ctx()->emit_expr(); - if((mode()==CELL_MODE || mode()==REPL_MODE) && name_scope()==NAME_GLOBAL){ - ctx()->emit_(OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE); - }else{ - ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); - } - }else{ - assert(ctx()->s_expr.size() == 1); - ctx()->s_expr.pop(); - } - } - consume_end_stmt(); - } - } - } - - void Compiler::consume_type_hints(){ - EXPR(); - ctx()->s_expr.pop(); - } - - void Compiler::_add_decorators(const Expr_vector& decorators){ - // [obj] - for(auto it=decorators.rbegin(); it!=decorators.rend(); ++it){ - (*it)->emit_(ctx()); // [obj, f] - ctx()->emit_(OP_ROT_TWO, BC_NOARG, (*it)->line); // [f, obj] - ctx()->emit_(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); // [f, obj, NULL] - ctx()->emit_(OP_ROT_TWO, BC_NOARG, BC_KEEPLINE); // [obj, NULL, f] - ctx()->emit_(OP_CALL, 1, (*it)->line); // [obj] - } - } - - void Compiler::compile_class(const Expr_vector& decorators){ + do { + ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); consume(TK("@id")); - int namei = StrName(prev().sv()).index; - Expr_ base = nullptr; - if(match(TK("("))){ - if(is_expression()){ - EXPR(); - base = ctx()->s_expr.popx(); - } - consume(TK(")")); - } - if(base == nullptr){ - ctx()->emit_(OP_LOAD_NONE, BC_NOARG, prev().line); - }else { - base->emit_(ctx()); - } - ctx()->emit_(OP_BEGIN_CLASS, namei, BC_KEEPLINE); - - for(auto& c: this->contexts.container()){ - if(c.is_compiling_class){ - SyntaxError("nested class is not allowed"); - } - } - ctx()->is_compiling_class = true; - compile_block_body(); - ctx()->is_compiling_class = false; - - if(!decorators.empty()){ - ctx()->emit_(OP_BEGIN_CLASS_DECORATION, BC_NOARG, BC_KEEPLINE); - _add_decorators(decorators); - ctx()->emit_(OP_END_CLASS_DECORATION, BC_NOARG, BC_KEEPLINE); - } - - ctx()->emit_(OP_END_CLASS, namei, BC_KEEPLINE); - } - - void Compiler::_compile_f_args(FuncDecl_ decl, bool enable_type_hints){ - int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs - do { - if(state > 3) SyntaxError(); - if(state == 3) SyntaxError("**kwargs should be the last argument"); - match_newlines(); - if(match(TK("*"))){ - if(state < 1) state = 1; - else SyntaxError("*args should be placed before **kwargs"); - } - else if(match(TK("**"))){ - state = 3; - } + Str name = prev().str(); + ctx()->emit_(OP_LOAD_ATTR, StrName(name).index, prev().line); + if(match(TK("as"))) { consume(TK("@id")); - StrName name = prev().str(); + name = prev().str(); + } + ctx()->emit_store_name(name_scope(), StrName(name), prev().line); + } while(match(TK(","))); + ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); + consume_end_stmt(); +} - // check duplicate argument name - for(int j: decl->args){ - if(decl->code->varnames[j] == name) { - SyntaxError("duplicate argument name"); - } - } - for(auto& kv: decl->kwargs){ - if(decl->code->varnames[kv.index] == name){ - SyntaxError("duplicate argument name"); - } - } - if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){ - SyntaxError("duplicate argument name"); - } - if(decl->starred_kwarg!=-1 && decl->code->varnames[decl->starred_kwarg] == name){ - SyntaxError("duplicate argument name"); - } +bool Compiler::is_expression(bool allow_slice) { + PrattCallback prefix = rules[curr().type].prefix; + return prefix != nullptr && (allow_slice || curr().type != TK(":")); +} - // eat type hints - if(enable_type_hints && match(TK(":"))) consume_type_hints(); - if(state == 0 && curr().type == TK("=")) state = 2; - int index = ctx()->add_varname(name); - switch (state) - { - case 0: - decl->args.push_back(index); - break; - case 1: - decl->starred_arg = index; - state+=1; - break; - case 2: { - consume(TK("=")); - PyVar value = read_literal(); - if(value == nullptr){ - SyntaxError(Str("default argument must be a literal")); - } - decl->add_kwarg(index, name, value); - } break; - case 3: - decl->starred_kwarg = index; - state+=1; - break; - } - } while (match(TK(","))); +void Compiler::parse_expression(int precedence, bool allow_slice) { + PrattCallback prefix = rules[curr().type].prefix; + if(prefix == nullptr || (curr().type == TK(":") && !allow_slice)) { + SyntaxError(Str("expected an expression, got ") + TK_STR(curr().type)); } - - void Compiler::compile_function(const Expr_vector& decorators){ - consume(TK("@id")); - Str decl_name = prev().str(); - FuncDecl_ decl = push_f_context(decl_name); - consume(TK("(")); - if (!match(TK(")"))) { - _compile_f_args(decl, true); - consume(TK(")")); - } - if(match(TK("->"))) consume_type_hints(); - compile_block_body(); - pop_context(); - - decl->docstring = nullptr; - if(decl->code->codes.size()>=2 && decl->code->codes[0].op == OP_LOAD_CONST && decl->code->codes[1].op == OP_POP_TOP){ - PyVar c = decl->code->consts[decl->code->codes[0].arg]; - if(is_type(c, vm->tp_str)){ - decl->code->codes[0].op = OP_NO_OP; - decl->code->codes[1].op = OP_NO_OP; - decl->docstring = PK_OBJ_GET(Str, c).c_str(); - } - } - ctx()->emit_(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line); - - _add_decorators(decorators); - - if(!ctx()->is_compiling_class){ - auto e = make_expr(decl_name, name_scope()); - e->emit_store(ctx()); - }else{ - int index = StrName(decl_name).index; - ctx()->emit_(OP_STORE_CLASS_ATTR, index, prev().line); - } - } - - PyVar Compiler::to_object(const TokenValue& value){ - PyVar obj = nullptr; - if(std::holds_alternative(value)){ - obj = VAR(std::get(value)); - } - if(std::holds_alternative(value)){ - obj = VAR(std::get(value)); - } - if(std::holds_alternative(value)){ - obj = VAR(std::get(value)); - } - assert(obj != nullptr); - return obj; - } - - PyVar Compiler::read_literal(){ + advance(); + (this->*prefix)(); + while(rules[curr().type].precedence >= precedence && (allow_slice || curr().type != TK(":"))) { + TokenIndex op = curr().type; advance(); - switch(prev().type){ - case TK("-"): { - consume(TK("@num")); - PyVar val = to_object(prev().value); - return vm->py_negate(val); - } - case TK("@num"): return to_object(prev().value); - case TK("@str"): return to_object(prev().value); - case TK("True"): return VAR(true); - case TK("False"): return VAR(false); - case TK("None"): return vm->None; - case TK("..."): return vm->Ellipsis; - case TK("("): { - List cpnts; - while(true) { - cpnts.push_back(read_literal()); - if(curr().type == TK(")")) break; - consume(TK(",")); - if(curr().type == TK(")")) break; + PrattCallback infix = rules[op].infix; + assert(infix != nullptr); + (this->*infix)(); + } +} + +void Compiler::compile_if_stmt() { + EXPR(); // condition + ctx()->emit_expr(); + int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); + compile_block_body(); + if(match(TK("elif"))) { + int exit_patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, prev().line); + ctx()->patch_jump(patch); + compile_if_stmt(); + ctx()->patch_jump(exit_patch); + } else if(match(TK("else"))) { + int exit_patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, prev().line); + ctx()->patch_jump(patch); + compile_block_body(); + ctx()->patch_jump(exit_patch); + } else { + ctx()->patch_jump(patch); + } +} + +void Compiler::compile_while_loop() { + CodeBlock* block = ctx()->enter_block(CodeBlockType::WHILE_LOOP); + EXPR(); // condition + ctx()->emit_expr(); + int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); + compile_block_body(); + ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); + ctx()->patch_jump(patch); + ctx()->exit_block(); + // optional else clause + if(match(TK("else"))) { + compile_block_body(); + block->end2 = ctx()->co->codes.size(); + } +} + +void Compiler::compile_for_loop() { + Expr_ vars = EXPR_VARS(); + consume(TK("in")); + EXPR_TUPLE(); + ctx()->emit_expr(); + ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, BC_KEEPLINE); + CodeBlock* block = ctx()->enter_block(CodeBlockType::FOR_LOOP); + int for_codei = ctx()->emit_(OP_FOR_ITER, ctx()->curr_iblock, BC_KEEPLINE); + bool ok = vars->emit_store(ctx()); + if(!ok) SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind + ctx()->try_merge_for_iter_store(for_codei); + compile_block_body(); + ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); + ctx()->exit_block(); + // optional else clause + if(match(TK("else"))) { + compile_block_body(); + block->end2 = ctx()->co->codes.size(); + } +} + +void Compiler::compile_try_except() { + ctx()->enter_block(CodeBlockType::TRY_EXCEPT); + ctx()->emit_(OP_TRY_ENTER, BC_NOARG, prev().line); + compile_block_body(); + small_vector_2 patches; + patches.push_back(ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE)); + ctx()->exit_block(); + + int finally_entry = -1; + if(curr().type != TK("finally")) { + do { + StrName as_name; + consume(TK("except")); + if(is_expression()) { + EXPR(); // push assumed type on to the stack + ctx()->emit_expr(); + ctx()->emit_(OP_EXCEPTION_MATCH, BC_NOARG, prev().line); + if(match(TK("as"))) { + consume(TK("@id")); + as_name = StrName(prev().sv()); } - consume(TK(")")); - return VAR(cpnts.to_tuple()); + } else { + ctx()->emit_(OP_LOAD_TRUE, BC_NOARG, BC_KEEPLINE); } - default: break; - } - return nullptr; + int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); + // on match + if(!as_name.empty()) { + ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); + ctx()->emit_store_name(name_scope(), as_name, BC_KEEPLINE); + } + // pop the exception + ctx()->emit_(OP_POP_EXCEPTION, BC_NOARG, BC_KEEPLINE); + compile_block_body(); + patches.push_back(ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE)); + ctx()->patch_jump(patch); + } while(curr().type == TK("except")); } - Compiler::Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope) - :lexer(vm, std::make_shared(source, filename, mode)){ - this->vm = vm; - this->unknown_global_scope = unknown_global_scope; - init_pratt_rules(); + if(match(TK("finally"))) { + int patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); + finally_entry = ctx()->co->codes.size(); + compile_block_body(); + ctx()->emit_(OP_JUMP_ABSOLUTE_TOP, BC_NOARG, BC_KEEPLINE); + ctx()->patch_jump(patch); } + // no match, re-raise + if(finally_entry != -1) { + i64 target = ctx()->co->codes.size() + 2; + ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE); + int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); + ctx()->co->codes[i].set_signed_arg(finally_entry - i); + } + ctx()->emit_(OP_RE_RAISE, BC_NOARG, BC_KEEPLINE); - Str Compiler::precompile(){ - auto tokens = lexer.run(); - SStream ss; - ss << "pkpy:" PK_VERSION << '\n'; // L1: version string - ss << (int)mode() << '\n'; // L2: mode + // no exception or no match, jump to the end + for(int patch: patches) + ctx()->patch_jump(patch); + if(finally_entry != -1) { + i64 target = ctx()->co->codes.size() + 2; + ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE); + int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); + ctx()->co->codes[i].set_signed_arg(finally_entry - i); + } +} - std::map token_indices; - for(auto token: tokens){ - if(is_raw_string_used(token.type)){ - auto it = token_indices.find(token.sv()); - if(it == token_indices.end()){ - token_indices[token.sv()] = 0; - // assert no '\n' in token.sv() - for(char c: token.sv()) assert(c!='\n'); +void Compiler::compile_decorated() { + Expr_vector decorators; + do { + EXPR(); + decorators.push_back(ctx()->s_expr.popx()); + if(!match_newlines_repl()) SyntaxError(); + } while(match(TK("@"))); + + if(match(TK("class"))) { + compile_class(decorators); + } else { + consume(TK("def")); + compile_function(decorators); + } +} + +bool Compiler::try_compile_assignment() { + switch(curr().type) { + case TK("+="): + case TK("-="): + case TK("*="): + case TK("/="): + case TK("//="): + case TK("%="): + case TK("<<="): + case TK(">>="): + case TK("&="): + case TK("|="): + case TK("^="): { + Expr* lhs_p = ctx()->s_expr.top().get(); + if(lhs_p->is_starred()) SyntaxError(); + if(ctx()->is_compiling_class) SyntaxError("can't use inplace operator in class definition"); + advance(); + // a[x] += 1; a and x should be evaluated only once + // a.x += 1; a should be evaluated only once + auto e = make_expr(true); // inplace=true + e->op = prev().type - 1; // -1 to remove = + e->lhs = ctx()->s_expr.popx(); + EXPR_TUPLE(); + e->rhs = ctx()->s_expr.popx(); + if(e->rhs->is_starred()) SyntaxError(); + e->emit_(ctx()); + bool ok = lhs_p->emit_store_inplace(ctx()); + if(!ok) SyntaxError(); + } + return true; + case TK("="): { + int n = 0; + while(match(TK("="))) { + EXPR_TUPLE(); + n += 1; + } + // stack size is n+1 + Expr_ val = ctx()->s_expr.popx(); + val->emit_(ctx()); + for(int j = 1; j < n; j++) + ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); + for(int j = 0; j < n; j++) { + auto e = ctx()->s_expr.popx(); + if(e->is_starred()) SyntaxError(); + bool ok = e->emit_store(ctx()); + if(!ok) SyntaxError(); + } + } + return true; + default: return false; + } +} + +void Compiler::compile_stmt() { + if(match(TK("class"))) { + compile_class(); + return; + } + advance(); + int kw_line = prev().line; // backup line number + int curr_loop_block = ctx()->get_loop(); + switch(prev().type) { + case TK("break"): + if(curr_loop_block < 0) SyntaxError("'break' outside loop"); + ctx()->emit_(OP_LOOP_BREAK, curr_loop_block, kw_line); + consume_end_stmt(); + break; + case TK("continue"): + if(curr_loop_block < 0) SyntaxError("'continue' not properly in loop"); + ctx()->emit_(OP_LOOP_CONTINUE, curr_loop_block, kw_line); + consume_end_stmt(); + break; + case TK("yield"): + if(contexts.size() <= 1) SyntaxError("'yield' outside function"); + EXPR_TUPLE(); + ctx()->emit_expr(); + ctx()->emit_(OP_YIELD_VALUE, BC_NOARG, kw_line); + consume_end_stmt(); + break; + case TK("yield from"): + if(contexts.size() <= 1) SyntaxError("'yield from' outside function"); + EXPR_TUPLE(); + ctx()->emit_expr(); + + ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, kw_line); + ctx()->enter_block(CodeBlockType::FOR_LOOP); + ctx()->emit_(OP_FOR_ITER_YIELD_VALUE, BC_NOARG, kw_line); + ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), kw_line); + ctx()->exit_block(); + consume_end_stmt(); + break; + case TK("return"): + if(contexts.size() <= 1) SyntaxError("'return' outside function"); + if(match_end_stmt()) { + ctx()->emit_(OP_RETURN_VALUE, 1, kw_line); + } else { + EXPR_TUPLE(); + ctx()->emit_expr(); + consume_end_stmt(); + ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, kw_line); + } + break; + /*************************************************/ + case TK("if"): compile_if_stmt(); break; + case TK("while"): compile_while_loop(); break; + case TK("for"): compile_for_loop(); break; + case TK("import"): compile_normal_import(); break; + case TK("from"): compile_from_import(); break; + case TK("def"): compile_function(); break; + case TK("@"): compile_decorated(); break; + case TK("try"): compile_try_except(); break; + case TK("pass"): consume_end_stmt(); break; + /*************************************************/ + case TK("++"): { + consume(TK("@id")); + StrName name(prev().sv()); + NameScope scope = name_scope(); + bool is_global = ctx()->global_names.contains(name.sv()); + if(is_global) scope = NAME_GLOBAL; + switch(scope) { + case NAME_LOCAL: ctx()->emit_(OP_INC_FAST, ctx()->add_varname(name), prev().line); break; + case NAME_GLOBAL: ctx()->emit_(OP_INC_GLOBAL, name.index, prev().line); break; + default: SyntaxError(); break; + } + consume_end_stmt(); + break; + } + case TK("--"): { + consume(TK("@id")); + StrName name(prev().sv()); + switch(name_scope()) { + case NAME_LOCAL: ctx()->emit_(OP_DEC_FAST, ctx()->add_varname(name), prev().line); break; + case NAME_GLOBAL: ctx()->emit_(OP_DEC_GLOBAL, name.index, prev().line); break; + default: SyntaxError(); break; + } + consume_end_stmt(); + break; + } + case TK("assert"): { + EXPR(); // condition + ctx()->emit_expr(); + int index = ctx()->emit_(OP_POP_JUMP_IF_TRUE, BC_NOARG, kw_line); + int has_msg = 0; + if(match(TK(","))) { + EXPR(); // message + ctx()->emit_expr(); + has_msg = 1; + } + ctx()->emit_(OP_RAISE_ASSERT, has_msg, kw_line); + ctx()->patch_jump(index); + consume_end_stmt(); + break; + } + case TK("global"): + do { + consume(TK("@id")); + ctx()->global_names.push_back(prev().sv()); + } while(match(TK(","))); + consume_end_stmt(); + break; + case TK("raise"): { + EXPR(); + ctx()->emit_expr(); + ctx()->emit_(OP_RAISE, BC_NOARG, kw_line); + consume_end_stmt(); + } break; + case TK("del"): { + EXPR_TUPLE(); + Expr_ e = ctx()->s_expr.popx(); + bool ok = e->emit_del(ctx()); + if(!ok) SyntaxError(); + consume_end_stmt(); + } break; + case TK("with"): { + EXPR(); // [ ] + ctx()->emit_expr(); + ctx()->enter_block(CodeBlockType::CONTEXT_MANAGER); + Expr_ as_name; + if(match(TK("as"))) { + consume(TK("@id")); + as_name = make_expr(prev().str(), name_scope()); + } + ctx()->emit_(OP_WITH_ENTER, BC_NOARG, prev().line); + // [ .__enter__() ] + if(as_name != nullptr) { + bool ok = as_name->emit_store(ctx()); + if(!ok) SyntaxError(); + } else { + ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); + } + compile_block_body(); + ctx()->emit_(OP_WITH_EXIT, BC_NOARG, prev().line); + ctx()->exit_block(); + } break; + /*************************************************/ + case TK("=="): { + consume(TK("@id")); + if(mode() != EXEC_MODE) SyntaxError("'label' is only available in EXEC_MODE"); + bool ok = ctx()->add_label(prev().str()); + consume(TK("==")); + if(!ok) SyntaxError("label " + prev().str().escape() + " already exists"); + consume_end_stmt(); + } break; + case TK("->"): + consume(TK("@id")); + if(mode() != EXEC_MODE) SyntaxError("'goto' is only available in EXEC_MODE"); + ctx()->emit_(OP_GOTO, StrName(prev().sv()).index, prev().line); + consume_end_stmt(); + break; + /*************************************************/ + // handle dangling expression or assignment + default: { + advance(-1); // do revert since we have pre-called advance() at the beginning + EXPR_TUPLE(); + + bool is_typed_name = false; // e.g. x: int + // eat variable's type hint if it is a single name + if(ctx()->s_expr.top()->is_name()) { + if(match(TK(":"))) { + consume_type_hints(); + is_typed_name = true; + + if(ctx()->is_compiling_class) { + NameExpr* ne = static_cast(ctx()->s_expr.top().get()); + ctx()->emit_(OP_ADD_CLASS_ANNOTATION, ne->name.index, BC_KEEPLINE); + } } } - } - ss << "=" << (int)token_indices.size() << '\n'; // L3: raw string count - int index = 0; - for(auto& kv: token_indices){ - ss << kv.first << '\n'; // L4: raw strings - kv.second = index++; - } - - ss << "=" << (int)tokens.size() << '\n'; // L5: token count - for(int i=0; is_expr.empty() && ctx()->s_expr.top()->is_starred()) { SyntaxError(); } + if(!is_typed_name) { + ctx()->emit_expr(); + if((mode() == CELL_MODE || mode() == REPL_MODE) && name_scope() == NAME_GLOBAL) { + ctx()->emit_(OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE); + } else { + ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); + } + } else { + assert(ctx()->s_expr.size() == 1); + ctx()->s_expr.pop(); + } } - if(i>0 && tokens[i-1].line == token.line) ss << ','; - else ss << token.line << ','; - if(i>0 && tokens[i-1].brackets_level == token.brackets_level) ss << ','; - else ss << token.brackets_level << ','; - // visit token value - std::visit([&ss](auto&& arg){ + consume_end_stmt(); + } + } +} + +void Compiler::consume_type_hints() { + EXPR(); + ctx()->s_expr.pop(); +} + +void Compiler::_add_decorators(const Expr_vector& decorators) { + // [obj] + for(auto it = decorators.rbegin(); it != decorators.rend(); ++it) { + (*it)->emit_(ctx()); // [obj, f] + ctx()->emit_(OP_ROT_TWO, BC_NOARG, (*it)->line); // [f, obj] + ctx()->emit_(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); // [f, obj, NULL] + ctx()->emit_(OP_ROT_TWO, BC_NOARG, BC_KEEPLINE); // [obj, NULL, f] + ctx()->emit_(OP_CALL, 1, (*it)->line); // [obj] + } +} + +void Compiler::compile_class(const Expr_vector& decorators) { + consume(TK("@id")); + int namei = StrName(prev().sv()).index; + Expr_ base = nullptr; + if(match(TK("("))) { + if(is_expression()) { + EXPR(); + base = ctx()->s_expr.popx(); + } + consume(TK(")")); + } + if(base == nullptr) { + ctx()->emit_(OP_LOAD_NONE, BC_NOARG, prev().line); + } else { + base->emit_(ctx()); + } + ctx()->emit_(OP_BEGIN_CLASS, namei, BC_KEEPLINE); + + for(auto& c: this->contexts.container()) { + if(c.is_compiling_class) { SyntaxError("nested class is not allowed"); } + } + ctx()->is_compiling_class = true; + compile_block_body(); + ctx()->is_compiling_class = false; + + if(!decorators.empty()) { + ctx()->emit_(OP_BEGIN_CLASS_DECORATION, BC_NOARG, BC_KEEPLINE); + _add_decorators(decorators); + ctx()->emit_(OP_END_CLASS_DECORATION, BC_NOARG, BC_KEEPLINE); + } + + ctx()->emit_(OP_END_CLASS, namei, BC_KEEPLINE); +} + +void Compiler::_compile_f_args(FuncDecl_ decl, bool enable_type_hints) { + int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs + do { + if(state > 3) SyntaxError(); + if(state == 3) SyntaxError("**kwargs should be the last argument"); + match_newlines(); + if(match(TK("*"))) { + if(state < 1) + state = 1; + else + SyntaxError("*args should be placed before **kwargs"); + } else if(match(TK("**"))) { + state = 3; + } + consume(TK("@id")); + StrName name = prev().str(); + + // check duplicate argument name + for(int j: decl->args) { + if(decl->code->varnames[j] == name) { SyntaxError("duplicate argument name"); } + } + for(auto& kv: decl->kwargs) { + if(decl->code->varnames[kv.index] == name) { SyntaxError("duplicate argument name"); } + } + if(decl->starred_arg != -1 && decl->code->varnames[decl->starred_arg] == name) { + SyntaxError("duplicate argument name"); + } + if(decl->starred_kwarg != -1 && decl->code->varnames[decl->starred_kwarg] == name) { + SyntaxError("duplicate argument name"); + } + + // eat type hints + if(enable_type_hints && match(TK(":"))) consume_type_hints(); + if(state == 0 && curr().type == TK("=")) state = 2; + int index = ctx()->add_varname(name); + switch(state) { + case 0: decl->args.push_back(index); break; + case 1: + decl->starred_arg = index; + state += 1; + break; + case 2: { + consume(TK("=")); + PyVar value = read_literal(); + if(value == nullptr) { SyntaxError(Str("default argument must be a literal")); } + decl->add_kwarg(index, name, value); + } break; + case 3: + decl->starred_kwarg = index; + state += 1; + break; + } + } while(match(TK(","))); +} + +void Compiler::compile_function(const Expr_vector& decorators) { + consume(TK("@id")); + Str decl_name = prev().str(); + FuncDecl_ decl = push_f_context(decl_name); + consume(TK("(")); + if(!match(TK(")"))) { + _compile_f_args(decl, true); + consume(TK(")")); + } + if(match(TK("->"))) consume_type_hints(); + compile_block_body(); + pop_context(); + + decl->docstring = nullptr; + if(decl->code->codes.size() >= 2 && decl->code->codes[0].op == OP_LOAD_CONST && + decl->code->codes[1].op == OP_POP_TOP) { + PyVar c = decl->code->consts[decl->code->codes[0].arg]; + if(is_type(c, vm->tp_str)) { + decl->code->codes[0].op = OP_NO_OP; + decl->code->codes[1].op = OP_NO_OP; + decl->docstring = PK_OBJ_GET(Str, c).c_str(); + } + } + ctx()->emit_(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line); + + _add_decorators(decorators); + + if(!ctx()->is_compiling_class) { + auto e = make_expr(decl_name, name_scope()); + e->emit_store(ctx()); + } else { + int index = StrName(decl_name).index; + ctx()->emit_(OP_STORE_CLASS_ATTR, index, prev().line); + } +} + +PyVar Compiler::to_object(const TokenValue& value) { + PyVar obj = nullptr; + if(std::holds_alternative(value)) { obj = VAR(std::get(value)); } + if(std::holds_alternative(value)) { obj = VAR(std::get(value)); } + if(std::holds_alternative(value)) { obj = VAR(std::get(value)); } + assert(obj != nullptr); + return obj; +} + +PyVar Compiler::read_literal() { + advance(); + switch(prev().type) { + case TK("-"): { + consume(TK("@num")); + PyVar val = to_object(prev().value); + return vm->py_negate(val); + } + case TK("@num"): return to_object(prev().value); + case TK("@str"): return to_object(prev().value); + case TK("True"): return VAR(true); + case TK("False"): return VAR(false); + case TK("None"): return vm->None; + case TK("..."): return vm->Ellipsis; + case TK("("): { + List cpnts; + while(true) { + cpnts.push_back(read_literal()); + if(curr().type == TK(")")) break; + consume(TK(",")); + if(curr().type == TK(")")) break; + } + consume(TK(")")); + return VAR(cpnts.to_tuple()); + } + default: break; + } + return nullptr; +} + +Compiler::Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope) : + lexer(vm, std::make_shared(source, filename, mode)) { + this->vm = vm; + this->unknown_global_scope = unknown_global_scope; + init_pratt_rules(); +} + +Str Compiler::precompile() { + auto tokens = lexer.run(); + SStream ss; + ss << "pkpy:" PK_VERSION << '\n'; // L1: version string + ss << (int)mode() << '\n'; // L2: mode + + std::map token_indices; + for(auto token: tokens) { + if(is_raw_string_used(token.type)) { + auto it = token_indices.find(token.sv()); + if(it == token_indices.end()) { + token_indices[token.sv()] = 0; + // assert no '\n' in token.sv() + for(char c: token.sv()) + assert(c != '\n'); + } + } + } + ss << "=" << (int)token_indices.size() << '\n'; // L3: raw string count + int index = 0; + for(auto& kv: token_indices) { + ss << kv.first << '\n'; // L4: raw strings + kv.second = index++; + } + + ss << "=" << (int)tokens.size() << '\n'; // L5: token count + for(int i = 0; i < tokens.size(); i++) { + const Token& token = tokens[i]; + ss << (int)token.type << ','; + if(is_raw_string_used(token.type)) { ss << token_indices[token.sv()] << ','; } + if(i > 0 && tokens[i - 1].line == token.line) + ss << ','; + else + ss << token.line << ','; + if(i > 0 && tokens[i - 1].brackets_level == token.brackets_level) + ss << ','; + else + ss << token.brackets_level << ','; + // visit token value + std::visit( + [&ss](auto&& arg) { using T = std::decay_t; - if constexpr(std::is_same_v){ + if constexpr(std::is_same_v) { ss << 'I' << arg; - }else if constexpr(std::is_same_v){ + } else if constexpr(std::is_same_v) { ss << 'F' << arg; - }else if constexpr(std::is_same_v){ + } else if constexpr(std::is_same_v) { ss << 'S'; - for(char c: arg) ss.write_hex((unsigned char)c); + for(char c: arg) + ss.write_hex((unsigned char)c); } ss << '\n'; - }, token.value); - } - return ss.str(); + }, + token.value); + } + return ss.str(); +} + +void Compiler::from_precompiled(const char* source) { + TokenDeserializer deserializer(source); + deserializer.curr += 5; // skip "pkpy:" + std::string_view version = deserializer.read_string('\n'); + + if(version != PK_VERSION) { + Str error = _S("precompiled version mismatch: ", version, "!=" PK_VERSION); + throw std::runtime_error(error.c_str()); + } + if(deserializer.read_uint('\n') != (i64)mode()) { throw std::runtime_error("precompiled mode mismatch"); } + + int count = deserializer.read_count(); + vector& precompiled_tokens = lexer.src->_precompiled_tokens; + for(int i = 0; i < count; i++) { + precompiled_tokens.push_back(deserializer.read_string('\n')); } - void Compiler::from_precompiled(const char* source){ - TokenDeserializer deserializer(source); - deserializer.curr += 5; // skip "pkpy:" - std::string_view version = deserializer.read_string('\n'); - - if(version != PK_VERSION){ - Str error = _S("precompiled version mismatch: ", version, "!=" PK_VERSION); - throw std::runtime_error(error.c_str()); - } - if(deserializer.read_uint('\n') != (i64)mode()){ - throw std::runtime_error("precompiled mode mismatch"); + count = deserializer.read_count(); + for(int i = 0; i < count; i++) { + Token t; + t.type = (unsigned char)deserializer.read_uint(','); + if(is_raw_string_used(t.type)) { + i64 index = deserializer.read_uint(','); + t.start = precompiled_tokens[index].c_str(); + t.length = precompiled_tokens[index].size; + } else { + t.start = nullptr; + t.length = 0; } - int count = deserializer.read_count(); - vector& precompiled_tokens = lexer.src->_precompiled_tokens; - for(int i=0; iis_precompiled) { + from_precompiled(lexer.src->source.c_str()); + } else { + this->tokens = lexer.run(); } - CodeObject_ Compiler::compile(){ - assert(i == 0); // make sure it is the first time to compile + CodeObject_ code = push_global_context(); - if(lexer.src->is_precompiled){ - from_precompiled(lexer.src->source.c_str()); - }else{ - this->tokens = lexer.run(); - } + advance(); // skip @sof, so prev() is always valid + match_newlines(); // skip possible leading '\n' - CodeObject_ code = push_global_context(); - - advance(); // skip @sof, so prev() is always valid - match_newlines(); // skip possible leading '\n' - - if(mode()==EVAL_MODE) { - EXPR_TUPLE(); ctx()->emit_expr(); - consume(TK("@eof")); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); - pop_context(); - return code; - }else if(mode()==JSON_MODE){ - EXPR(); - Expr_ e = ctx()->s_expr.popx(); - if(!e->is_json_object()) SyntaxError("expect a JSON object, literal or array"); - consume(TK("@eof")); - e->emit_(ctx()); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); - pop_context(); - return code; - } - - while (!match(TK("@eof"))) { - compile_stmt(); - match_newlines(); - } + if(mode() == EVAL_MODE) { + EXPR_TUPLE(); + ctx()->emit_expr(); + consume(TK("@eof")); + ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); + pop_context(); + return code; + } else if(mode() == JSON_MODE) { + EXPR(); + Expr_ e = ctx()->s_expr.popx(); + if(!e->is_json_object()) SyntaxError("expect a JSON object, literal or array"); + consume(TK("@eof")); + e->emit_(ctx()); + ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); pop_context(); return code; } - // TODO: refactor this - void Lexer::throw_err(StrName type, Str msg, int lineno, const char* cursor){ - vm->__last_exception = vm->call(vm->builtins->attr(type), VAR(msg)).get(); - Exception& e = vm->__last_exception->as(); - e.st_push(src, lineno, cursor, ""); - throw TopLevelException(vm, &e); + while(!match(TK("@eof"))) { + compile_stmt(); + match_newlines(); } + pop_context(); + return code; +} - std::string_view TokenDeserializer::read_string(char c){ - const char* start = curr; - while(*curr != c) curr++; - std::string_view retval(start, curr-start); - curr++; // skip the delimiter - return retval; - } +// TODO: refactor this +void Lexer::throw_err(StrName type, Str msg, int lineno, const char* cursor) { + vm->__last_exception = vm->call(vm->builtins->attr(type), VAR(msg)).get(); + Exception& e = vm->__last_exception->as(); + e.st_push(src, lineno, cursor, ""); + throw TopLevelException(vm, &e); +} - Str TokenDeserializer::read_string_from_hex(char c){ - std::string_view s = read_string(c); - char* buffer = (char*)std::malloc(s.size()/2 + 1); - for(int i=0; i='0' && s[i]<='9') c += s[i]-'0'; - else if(s[i]>='a' && s[i]<='f') c += s[i]-'a'+10; - else assert(false); - 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 assert(false); - buffer[i/2] = c; - } - buffer[s.size()/2] = 0; - return std::pair(buffer, s.size()/2); - } - - int TokenDeserializer::read_count(){ - assert(*curr == '='); +std::string_view TokenDeserializer::read_string(char c) { + const char* start = curr; + while(*curr != c) curr++; - return read_uint('\n'); - } + std::string_view retval(start, curr - start); + curr++; // skip the delimiter + return retval; +} - i64 TokenDeserializer::read_uint(char c){ - i64 out = 0; - while(*curr != c){ - out = out*10 + (*curr-'0'); - curr++; - } - curr++; // skip the delimiter - return out; +Str TokenDeserializer::read_string_from_hex(char c) { + std::string_view s = read_string(c); + char* buffer = (char*)std::malloc(s.size() / 2 + 1); + 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 + assert(false); + 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 + assert(false); + buffer[i / 2] = c; } + buffer[s.size() / 2] = 0; + return std::pair(buffer, s.size() / 2); +} - f64 TokenDeserializer::read_float(char c){ - std::string_view sv = read_string(c); - return std::stod(std::string(sv)); +int TokenDeserializer::read_count() { + assert(*curr == '='); + curr++; + return read_uint('\n'); +} + +i64 TokenDeserializer::read_uint(char c) { + i64 out = 0; + while(*curr != c) { + out = out * 10 + (*curr - '0'); + curr++; } -} // namespace pkpy \ No newline at end of file + curr++; // skip the delimiter + return out; +} + +f64 TokenDeserializer::read_float(char c) { + std::string_view sv = read_string(c); + return std::stod(std::string(sv)); +} +} // namespace pkpy diff --git a/src/compiler/expr.cpp b/src/compiler/expr.cpp index 2c9ae3fb..14540797 100644 --- a/src/compiler/expr.cpp +++ b/src/compiler/expr.cpp @@ -1,771 +1,777 @@ #include "pocketpy/compiler/expr.hpp" #include "pocketpy/interpreter/vm.hpp" -namespace pkpy{ +namespace pkpy { - inline bool is_identifier(std::string_view s){ - if(s.empty()) return false; - if(!isalpha(s[0]) && s[0] != '_') return false; - for(char c: s) if(!isalnum(c) && c != '_') return false; - return true; +inline bool is_identifier(std::string_view s) { + if(s.empty()) return false; + if(!isalpha(s[0]) && s[0] != '_') return false; + for(char c: s) + if(!isalnum(c) && c != '_') return false; + return true; +} + +inline bool is_small_int(i64 value) { return value >= INT16_MIN && value <= INT16_MAX; } + +int CodeEmitContext::get_loop() const { + int index = curr_iblock; + while(index >= 0) { + if(co->blocks[index].type == CodeBlockType::FOR_LOOP) break; + if(co->blocks[index].type == CodeBlockType::WHILE_LOOP) break; + index = co->blocks[index].parent; } + return index; +} - inline bool is_small_int(i64 value){ - return value >= INT16_MIN && value <= INT16_MAX; +CodeBlock* CodeEmitContext::enter_block(CodeBlockType type) { + co->blocks.push_back(CodeBlock(type, curr_iblock, (int)co->codes.size())); + curr_iblock = co->blocks.size() - 1; + return &co->blocks[curr_iblock]; +} + +void CodeEmitContext::exit_block() { + auto curr_type = co->blocks[curr_iblock].type; + co->blocks[curr_iblock].end = co->codes.size(); + curr_iblock = co->blocks[curr_iblock].parent; + assert(curr_iblock >= 0); + if(curr_type == CodeBlockType::FOR_LOOP) { + // add a no op here to make block check work + emit_(OP_NO_OP, BC_NOARG, BC_KEEPLINE, true); } +} - int CodeEmitContext::get_loop() const { - int index = curr_iblock; - while(index >= 0){ - if(co->blocks[index].type == CodeBlockType::FOR_LOOP) break; - if(co->blocks[index].type == CodeBlockType::WHILE_LOOP) break; - index = co->blocks[index].parent; - } +// clear the expression stack and generate bytecode +void CodeEmitContext::emit_expr() { + assert(s_expr.size() == 1); + Expr_ expr = s_expr.popx(); + expr->emit_(this); +} + +int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual) { + co->codes.push_back(Bytecode{(uint8_t)opcode, arg}); + co->lines.push_back(CodeObject::LineInfo{line, is_virtual, curr_iblock}); + int i = co->codes.size() - 1; + if(line == BC_KEEPLINE) { + if(i >= 1) + co->lines[i].lineno = co->lines[i - 1].lineno; + else + co->lines[i].lineno = 1; + } + return i; +} + +void CodeEmitContext::revert_last_emit_() { + co->codes.pop_back(); + co->lines.pop_back(); +} + +void CodeEmitContext::try_merge_for_iter_store(int i) { + // [FOR_ITER, STORE_?, ] + if(co->codes[i].op != OP_FOR_ITER) return; + if(co->codes.size() - i != 2) return; + uint16_t arg = co->codes[i + 1].arg; + if(co->codes[i + 1].op == OP_STORE_FAST) { + revert_last_emit_(); + co->codes[i].op = OP_FOR_ITER_STORE_FAST; + co->codes[i].arg = arg; + return; + } + if(co->codes[i + 1].op == OP_STORE_GLOBAL) { + revert_last_emit_(); + co->codes[i].op = OP_FOR_ITER_STORE_GLOBAL; + co->codes[i].arg = arg; + return; + } +} + +int CodeEmitContext::emit_int(i64 value, int line) { + if(is_small_int(value)) { + return emit_(OP_LOAD_SMALL_INT, (uint16_t)value, line); + } else { + return emit_(OP_LOAD_CONST, add_const(VAR(value)), line); + } +} + +void CodeEmitContext::patch_jump(int index) { + int target = co->codes.size(); + co->codes[index].set_signed_arg(target - index); +} + +bool CodeEmitContext::add_label(StrName name) { + if(co->labels.contains(name)) return false; + co->labels.set(name, co->codes.size()); + return true; +} + +int CodeEmitContext::add_varname(StrName name) { + // PK_MAX_CO_VARNAMES will be checked when pop_context(), not here + int index = co->varnames_inv.try_get(name); + if(index >= 0) return index; + co->varnames.push_back(name); + co->nlocals++; + index = co->varnames.size() - 1; + co->varnames_inv.set(name, index); + return index; +} + +int CodeEmitContext::add_const_string(std::string_view key) { + auto it = _co_consts_string_dedup_map.find(key); + if(it != _co_consts_string_dedup_map.end()) { + return it->second; + } else { + co->consts.push_back(VAR(key)); + int index = co->consts.size() - 1; + key = co->consts.back().obj_get().sv(); + _co_consts_string_dedup_map[key] = index; return index; } +} - CodeBlock* CodeEmitContext::enter_block(CodeBlockType type){ - co->blocks.push_back(CodeBlock(type, curr_iblock, (int)co->codes.size())); - curr_iblock = co->blocks.size()-1; - return &co->blocks[curr_iblock]; - } - - void CodeEmitContext::exit_block(){ - auto curr_type = co->blocks[curr_iblock].type; - co->blocks[curr_iblock].end = co->codes.size(); - curr_iblock = co->blocks[curr_iblock].parent; - assert(curr_iblock >= 0); - if(curr_type == CodeBlockType::FOR_LOOP){ - // add a no op here to make block check work - emit_(OP_NO_OP, BC_NOARG, BC_KEEPLINE, true); - } - } - - // clear the expression stack and generate bytecode - void CodeEmitContext::emit_expr(){ - assert(s_expr.size() == 1); - Expr_ expr = s_expr.popx(); - expr->emit_(this); - } - - int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual) { - co->codes.push_back(Bytecode{(uint8_t)opcode, arg}); - co->lines.push_back(CodeObject::LineInfo{line, is_virtual, curr_iblock}); - int i = co->codes.size() - 1; - if(line == BC_KEEPLINE){ - if(i >= 1) co->lines[i].lineno = co->lines[i-1].lineno; - else co->lines[i].lineno = 1; - } - return i; - } - - void CodeEmitContext::revert_last_emit_(){ - co->codes.pop_back(); - co->lines.pop_back(); - } - - void CodeEmitContext::try_merge_for_iter_store(int i){ - // [FOR_ITER, STORE_?, ] - if(co->codes[i].op != OP_FOR_ITER) return; - if(co->codes.size() - i != 2) return; - uint16_t arg = co->codes[i+1].arg; - if(co->codes[i+1].op == OP_STORE_FAST){ - revert_last_emit_(); - co->codes[i].op = OP_FOR_ITER_STORE_FAST; - co->codes[i].arg = arg; - return; - } - if(co->codes[i+1].op == OP_STORE_GLOBAL){ - revert_last_emit_(); - co->codes[i].op = OP_FOR_ITER_STORE_GLOBAL; - co->codes[i].arg = arg; - return; - } - } - - int CodeEmitContext::emit_int(i64 value, int line){ - if(is_small_int(value)){ - return emit_(OP_LOAD_SMALL_INT, (uint16_t)value, line); - }else{ - return emit_(OP_LOAD_CONST, add_const(VAR(value)), line); - } - } - - void CodeEmitContext::patch_jump(int index) { - int target = co->codes.size(); - co->codes[index].set_signed_arg(target-index); - } - - bool CodeEmitContext::add_label(StrName name){ - if(co->labels.contains(name)) return false; - co->labels.set(name, co->codes.size()); - return true; - } - - int CodeEmitContext::add_varname(StrName name){ - // PK_MAX_CO_VARNAMES will be checked when pop_context(), not here - int index = co->varnames_inv.try_get(name); - if(index >= 0) return index; - co->varnames.push_back(name); - co->nlocals++; - index = co->varnames.size() - 1; - co->varnames_inv.set(name, index); +int CodeEmitContext::add_const(PyVar v) { + assert(!is_type(v, VM::tp_str)); + // non-string deduplication + auto it = _co_consts_nonstring_dedup_map.find(v); + if(it != _co_consts_nonstring_dedup_map.end()) { + return it->second; + } else { + co->consts.push_back(v); + int index = co->consts.size() - 1; + _co_consts_nonstring_dedup_map[v] = index; return index; } +} - int CodeEmitContext::add_const_string(std::string_view key){ - auto it = _co_consts_string_dedup_map.find(key); - if(it != _co_consts_string_dedup_map.end()){ - return it->second; - }else{ - co->consts.push_back(VAR(key)); - int index = co->consts.size() - 1; - key = co->consts.back().obj_get().sv(); - _co_consts_string_dedup_map[key] = index; - return index; - } +int CodeEmitContext::add_func_decl(FuncDecl_ decl) { + co->func_decls.push_back(decl); + return co->func_decls.size() - 1; +} + +void CodeEmitContext::emit_store_name(NameScope scope, StrName name, int line) { + switch(scope) { + case NAME_LOCAL: emit_(OP_STORE_FAST, add_varname(name), line); break; + case NAME_GLOBAL: emit_(OP_STORE_GLOBAL, StrName(name).index, line); break; + case NAME_GLOBAL_UNKNOWN: emit_(OP_STORE_NAME, StrName(name).index, line); break; + default: assert(false); break; } +} - int CodeEmitContext::add_const(PyVar v){ - assert(!is_type(v, VM::tp_str)); - // non-string deduplication - auto it = _co_consts_nonstring_dedup_map.find(v); - if(it != _co_consts_nonstring_dedup_map.end()){ - return it->second; - }else{ - co->consts.push_back(v); - int index = co->consts.size() - 1; - _co_consts_nonstring_dedup_map[v] = index; - return index; +void NameExpr::emit_(CodeEmitContext* ctx) { + int index = ctx->co->varnames_inv.try_get(name); + if(scope == NAME_LOCAL && index >= 0) { + ctx->emit_(OP_LOAD_FAST, index, line); + } else { + Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL; + if(ctx->is_compiling_class && scope == NAME_GLOBAL) { + // if we are compiling a class, we should use OP_LOAD_ATTR_GLOBAL instead of OP_LOAD_GLOBAL + // this supports @property.setter + op = OP_LOAD_CLASS_GLOBAL; + // exec()/eval() won't work with OP_LOAD_ATTR_GLOBAL in class body + } else { + // we cannot determine the scope when calling exec()/eval() + if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME; } + ctx->emit_(op, StrName(name).index, line); } +} - int CodeEmitContext::add_func_decl(FuncDecl_ decl){ - co->func_decls.push_back(decl); - return co->func_decls.size() - 1; +bool NameExpr::emit_del(CodeEmitContext* ctx) { + switch(scope) { + case NAME_LOCAL: ctx->emit_(OP_DELETE_FAST, ctx->add_varname(name), line); break; + case NAME_GLOBAL: ctx->emit_(OP_DELETE_GLOBAL, StrName(name).index, line); break; + case NAME_GLOBAL_UNKNOWN: ctx->emit_(OP_DELETE_NAME, StrName(name).index, line); break; + default: assert(false); break; } + return true; +} - void CodeEmitContext::emit_store_name(NameScope scope, StrName name, int line){ - switch(scope){ - case NAME_LOCAL: - emit_(OP_STORE_FAST, add_varname(name), line); - break; - case NAME_GLOBAL: - emit_(OP_STORE_GLOBAL, StrName(name).index, line); - break; - case NAME_GLOBAL_UNKNOWN: - emit_(OP_STORE_NAME, StrName(name).index, line); - break; - default: assert(false); break; - } - } - - - void NameExpr::emit_(CodeEmitContext* ctx) { - int index = ctx->co->varnames_inv.try_get(name); - if(scope == NAME_LOCAL && index >= 0){ - ctx->emit_(OP_LOAD_FAST, index, line); - }else{ - Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL; - if(ctx->is_compiling_class && scope == NAME_GLOBAL){ - // if we are compiling a class, we should use OP_LOAD_ATTR_GLOBAL instead of OP_LOAD_GLOBAL - // this supports @property.setter - op = OP_LOAD_CLASS_GLOBAL; - // exec()/eval() won't work with OP_LOAD_ATTR_GLOBAL in class body - }else{ - // we cannot determine the scope when calling exec()/eval() - if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME; - } - ctx->emit_(op, StrName(name).index, line); - } - } - - bool NameExpr::emit_del(CodeEmitContext* ctx) { - switch(scope){ - case NAME_LOCAL: - ctx->emit_(OP_DELETE_FAST, ctx->add_varname(name), line); - break; - case NAME_GLOBAL: - ctx->emit_(OP_DELETE_GLOBAL, StrName(name).index, line); - break; - case NAME_GLOBAL_UNKNOWN: - ctx->emit_(OP_DELETE_NAME, StrName(name).index, line); - break; - default: assert(false); break; - } +bool NameExpr::emit_store(CodeEmitContext* ctx) { + if(ctx->is_compiling_class) { + ctx->emit_(OP_STORE_CLASS_ATTR, name.index, line); return true; } + ctx->emit_store_name(scope, name, line); + return true; +} - bool NameExpr::emit_store(CodeEmitContext* ctx) { - if(ctx->is_compiling_class){ - ctx->emit_(OP_STORE_CLASS_ATTR, name.index, line); - return true; - } - ctx->emit_store_name(scope, name, line); - return true; +void InvertExpr::emit_(CodeEmitContext* ctx) { + child->emit_(ctx); + ctx->emit_(OP_UNARY_INVERT, BC_NOARG, line); +} + +void StarredExpr::emit_(CodeEmitContext* ctx) { + child->emit_(ctx); + ctx->emit_(OP_UNARY_STAR, level, line); +} + +bool StarredExpr::emit_store(CodeEmitContext* ctx) { + if(level != 1) return false; + // simply proxy to child + return child->emit_store(ctx); +} + +void NotExpr::emit_(CodeEmitContext* ctx) { + child->emit_(ctx); + ctx->emit_(OP_UNARY_NOT, BC_NOARG, line); +} + +void AndExpr::emit_(CodeEmitContext* ctx) { + lhs->emit_(ctx); + int patch = ctx->emit_(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line); + rhs->emit_(ctx); + ctx->patch_jump(patch); +} + +void OrExpr::emit_(CodeEmitContext* ctx) { + lhs->emit_(ctx); + int patch = ctx->emit_(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line); + rhs->emit_(ctx); + ctx->patch_jump(patch); +} + +void Literal0Expr::emit_(CodeEmitContext* ctx) { + switch(token) { + case TK("None"): ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); break; + case TK("True"): ctx->emit_(OP_LOAD_TRUE, BC_NOARG, line); break; + case TK("False"): ctx->emit_(OP_LOAD_FALSE, BC_NOARG, line); break; + case TK("..."): ctx->emit_(OP_LOAD_ELLIPSIS, BC_NOARG, line); break; + default: assert(false); } +} - void InvertExpr::emit_(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_UNARY_INVERT, BC_NOARG, line); +void LongExpr::emit_(CodeEmitContext* ctx) { + ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(s.sv()), line); + ctx->emit_(OP_BUILD_LONG, BC_NOARG, line); +} + +void ImagExpr::emit_(CodeEmitContext* ctx) { + VM* vm = ctx->vm; + ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(value)), line); + ctx->emit_(OP_BUILD_IMAG, BC_NOARG, line); +} + +void BytesExpr::emit_(CodeEmitContext* ctx) { + ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(s.sv()), line); + ctx->emit_(OP_BUILD_BYTES, BC_NOARG, line); +} + +void LiteralExpr::emit_(CodeEmitContext* ctx) { + VM* vm = ctx->vm; + if(std::holds_alternative(value)) { + i64 _val = std::get(value); + ctx->emit_int(_val, line); + return; } - - void StarredExpr::emit_(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_UNARY_STAR, level, line); + if(std::holds_alternative(value)) { + f64 _val = std::get(value); + ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line); + return; } - - bool StarredExpr::emit_store(CodeEmitContext* ctx) { - if(level != 1) return false; - // simply proxy to child - return child->emit_store(ctx); + if(std::holds_alternative(value)) { + std::string_view key = std::get(value).sv(); + ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(key), line); + return; } +} - void NotExpr::emit_(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_UNARY_NOT, BC_NOARG, line); - } - - void AndExpr::emit_(CodeEmitContext* ctx) { - lhs->emit_(ctx); - int patch = ctx->emit_(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line); - rhs->emit_(ctx); - ctx->patch_jump(patch); - } - - void OrExpr::emit_(CodeEmitContext* ctx) { - lhs->emit_(ctx); - int patch = ctx->emit_(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line); - rhs->emit_(ctx); - ctx->patch_jump(patch); - } - - void Literal0Expr::emit_(CodeEmitContext* ctx){ - switch (token) { - case TK("None"): ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); break; - case TK("True"): ctx->emit_(OP_LOAD_TRUE, BC_NOARG, line); break; - case TK("False"): ctx->emit_(OP_LOAD_FALSE, BC_NOARG, line); break; - case TK("..."): ctx->emit_(OP_LOAD_ELLIPSIS, BC_NOARG, line); break; - default: assert(false); - } - } - - void LongExpr::emit_(CodeEmitContext* ctx) { - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(s.sv()), line); - ctx->emit_(OP_BUILD_LONG, BC_NOARG, line); - } - - void ImagExpr::emit_(CodeEmitContext* ctx) { - VM* vm = ctx->vm; - ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(value)), line); - ctx->emit_(OP_BUILD_IMAG, BC_NOARG, line); - } - - void BytesExpr::emit_(CodeEmitContext* ctx) { - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(s.sv()), line); - ctx->emit_(OP_BUILD_BYTES, BC_NOARG, line); - } - - void LiteralExpr::emit_(CodeEmitContext* ctx) { - VM* vm = ctx->vm; - if(std::holds_alternative(value)){ - i64 _val = std::get(value); +void NegatedExpr::emit_(CodeEmitContext* ctx) { + VM* vm = ctx->vm; + // if child is a int of float, do constant folding + if(child->is_literal()) { + LiteralExpr* lit = static_cast(child.get()); + if(std::holds_alternative(lit->value)) { + i64 _val = -std::get(lit->value); ctx->emit_int(_val, line); return; } - if(std::holds_alternative(value)){ - f64 _val = std::get(value); + if(std::holds_alternative(lit->value)) { + f64 _val = -std::get(lit->value); ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line); return; } - if(std::holds_alternative(value)){ - std::string_view key = std::get(value).sv(); - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(key), line); - return; - } + } + child->emit_(ctx); + ctx->emit_(OP_UNARY_NEGATIVE, BC_NOARG, line); +} + +void SliceExpr::emit_(CodeEmitContext* ctx) { + if(start) { + start->emit_(ctx); + } else { + ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); } - void NegatedExpr::emit_(CodeEmitContext* ctx){ - VM* vm = ctx->vm; - // if child is a int of float, do constant folding - if(child->is_literal()){ - LiteralExpr* lit = static_cast(child.get()); - if(std::holds_alternative(lit->value)){ - i64 _val = -std::get(lit->value); - ctx->emit_int(_val, line); - return; - } - if(std::holds_alternative(lit->value)){ - f64 _val = -std::get(lit->value); - ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line); - return; + if(stop) { + stop->emit_(ctx); + } else { + ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); + } + + if(step) { + step->emit_(ctx); + } else { + ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); + } + + ctx->emit_(OP_BUILD_SLICE, BC_NOARG, line); +} + +void DictItemExpr::emit_(CodeEmitContext* ctx) { + if(is_starred()) { + assert(key == nullptr); + value->emit_(ctx); + } else { + value->emit_(ctx); + key->emit_(ctx); // reverse order + ctx->emit_(OP_BUILD_TUPLE, 2, line); + } +} + +bool TupleExpr::emit_store(CodeEmitContext* ctx) { + // TOS is an iterable + // items may contain StarredExpr, we should check it + int starred_i = -1; + for(int i = 0; i < items.size(); i++) { + if(!items[i]->is_starred()) continue; + if(starred_i == -1) + starred_i = i; + else + return false; // multiple StarredExpr not allowed + } + + if(starred_i == -1) { + Bytecode& prev = ctx->co->codes.back(); + if(prev.op == OP_BUILD_TUPLE && prev.arg == items.size()) { + // build tuple and unpack it is meaningless + ctx->revert_last_emit_(); + } else { + if(prev.op == OP_FOR_ITER) { + prev.op = OP_FOR_ITER_UNPACK; + prev.arg = items.size(); + } else { + ctx->emit_(OP_UNPACK_SEQUENCE, items.size(), line); } } - child->emit_(ctx); - ctx->emit_(OP_UNARY_NEGATIVE, BC_NOARG, line); + } else { + // starred assignment target must be in a tuple + if(items.size() == 1) return false; + // starred assignment target must be the last one (differ from cpython) + if(starred_i != items.size() - 1) return false; + // a,*b = [1,2,3] + // stack is [1,2,3] -> [1,[2,3]] + ctx->emit_(OP_UNPACK_EX, items.size() - 1, line); } - - - void SliceExpr::emit_(CodeEmitContext* ctx){ - if(start){ - start->emit_(ctx); - }else{ - ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); - } - - if(stop){ - stop->emit_(ctx); - }else{ - ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); - } - - if(step){ - step->emit_(ctx); - }else{ - ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); - } - - ctx->emit_(OP_BUILD_SLICE, BC_NOARG, line); + // do reverse emit + for(int i = items.size() - 1; i >= 0; i--) { + bool ok = items[i]->emit_store(ctx); + if(!ok) return false; } + return true; +} - void DictItemExpr::emit_(CodeEmitContext* ctx) { - if(is_starred()){ - assert(key == nullptr); - value->emit_(ctx); - }else{ - value->emit_(ctx); - key->emit_(ctx); // reverse order - ctx->emit_(OP_BUILD_TUPLE, 2, line); +bool TupleExpr::emit_del(CodeEmitContext* ctx) { + for(auto& e: items) { + bool ok = e->emit_del(ctx); + if(!ok) return false; + } + return true; +} + +void CompExpr::emit_(CodeEmitContext* ctx) { + ctx->emit_(op0(), 0, line); + iter->emit_(ctx); + ctx->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE); + ctx->enter_block(CodeBlockType::FOR_LOOP); + int curr_iblock = ctx->curr_iblock; + int for_codei = ctx->emit_(OP_FOR_ITER, curr_iblock, BC_KEEPLINE); + bool ok = vars->emit_store(ctx); + // this error occurs in `vars` instead of this line, but...nevermind + assert(ok); // this should raise a SyntaxError, but we just assert it + ctx->try_merge_for_iter_store(for_codei); + if(cond) { + cond->emit_(ctx); + int patch = ctx->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); + expr->emit_(ctx); + ctx->emit_(op1(), BC_NOARG, BC_KEEPLINE); + ctx->patch_jump(patch); + } else { + expr->emit_(ctx); + ctx->emit_(op1(), BC_NOARG, BC_KEEPLINE); + } + ctx->emit_(OP_LOOP_CONTINUE, curr_iblock, BC_KEEPLINE); + ctx->exit_block(); +} + +void FStringExpr::_load_simple_expr(CodeEmitContext* ctx, Str expr) { + bool repr = false; + if(expr.size >= 2 && expr.end()[-2] == '!') { + switch(expr.end()[-1]) { + case 'r': + repr = true; + expr = expr.substr(0, expr.size - 2); + break; + case 's': + repr = false; + expr = expr.substr(0, expr.size - 2); + break; + default: break; // nothing happens } } - - bool TupleExpr::emit_store(CodeEmitContext* ctx) { - // TOS is an iterable - // items may contain StarredExpr, we should check it - int starred_i = -1; - for(int i=0; iis_starred()) continue; - if(starred_i == -1) starred_i = i; - else return false; // multiple StarredExpr not allowed - } - - if(starred_i == -1){ - Bytecode& prev = ctx->co->codes.back(); - if(prev.op == OP_BUILD_TUPLE && prev.arg == items.size()){ - // build tuple and unpack it is meaningless - ctx->revert_last_emit_(); - }else{ - if(prev.op == OP_FOR_ITER){ - prev.op = OP_FOR_ITER_UNPACK; - prev.arg = items.size(); - }else{ - ctx->emit_(OP_UNPACK_SEQUENCE, items.size(), line); - } - } - }else{ - // starred assignment target must be in a tuple - if(items.size() == 1) return false; - // starred assignment target must be the last one (differ from cpython) - if(starred_i != items.size()-1) return false; - // a,*b = [1,2,3] - // stack is [1,2,3] -> [1,[2,3]] - ctx->emit_(OP_UNPACK_EX, items.size()-1, line); - } - // do reverse emit - for(int i=items.size()-1; i>=0; i--){ - bool ok = items[i]->emit_store(ctx); - if(!ok) return false; - } - return true; - } - - bool TupleExpr::emit_del(CodeEmitContext* ctx){ - for(auto& e: items){ - bool ok = e->emit_del(ctx); - if(!ok) return false; - } - return true; - } - - void CompExpr::emit_(CodeEmitContext* ctx){ - ctx->emit_(op0(), 0, line); - iter->emit_(ctx); - ctx->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE); - ctx->enter_block(CodeBlockType::FOR_LOOP); - int curr_iblock = ctx->curr_iblock; - int for_codei = ctx->emit_(OP_FOR_ITER, curr_iblock, BC_KEEPLINE); - bool ok = vars->emit_store(ctx); - // this error occurs in `vars` instead of this line, but...nevermind - assert(ok); // this should raise a SyntaxError, but we just assert it - ctx->try_merge_for_iter_store(for_codei); - if(cond){ - cond->emit_(ctx); - int patch = ctx->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); - expr->emit_(ctx); - ctx->emit_(op1(), BC_NOARG, BC_KEEPLINE); - ctx->patch_jump(patch); - }else{ - expr->emit_(ctx); - ctx->emit_(op1(), BC_NOARG, BC_KEEPLINE); - } - ctx->emit_(OP_LOOP_CONTINUE, curr_iblock, BC_KEEPLINE); - ctx->exit_block(); - } - - - void FStringExpr::_load_simple_expr(CodeEmitContext* ctx, Str expr){ - bool repr = false; - if(expr.size>=2 && expr.end()[-2]=='!'){ - switch(expr.end()[-1]){ - case 'r': repr = true; expr = expr.substr(0, expr.size-2); break; - case 's': repr = false; expr = expr.substr(0, expr.size-2); break; - default: break; // nothing happens + // name or name.name + bool is_fastpath = false; + if(is_identifier(expr.sv())) { + ctx->emit_(OP_LOAD_NAME, StrName(expr.sv()).index, line); + is_fastpath = true; + } else { + int dot = expr.index("."); + if(dot > 0) { + std::string_view a = expr.sv().substr(0, dot); + std::string_view b = expr.sv().substr(dot + 1); + if(is_identifier(a) && is_identifier(b)) { + ctx->emit_(OP_LOAD_NAME, StrName(a).index, line); + ctx->emit_(OP_LOAD_ATTR, StrName(b).index, line); + is_fastpath = true; } } - // name or name.name - bool is_fastpath = false; - if(is_identifier(expr.sv())){ - ctx->emit_(OP_LOAD_NAME, StrName(expr.sv()).index, line); - is_fastpath = true; - }else{ - int dot = expr.index("."); - if(dot > 0){ - std::string_view a = expr.sv().substr(0, dot); - std::string_view b = expr.sv().substr(dot+1); - if(is_identifier(a) && is_identifier(b)){ - ctx->emit_(OP_LOAD_NAME, StrName(a).index, line); - ctx->emit_(OP_LOAD_ATTR, StrName(b).index, line); - is_fastpath = true; - } - } - } - - if(!is_fastpath){ - int index = ctx->add_const_string(expr.sv()); - ctx->emit_(OP_FSTRING_EVAL, index, line); - } - - if(repr){ - ctx->emit_(OP_REPR, BC_NOARG, line); - } } - static bool is_fmt_valid_char(char c){ - switch(c){ + if(!is_fastpath) { + int index = ctx->add_const_string(expr.sv()); + ctx->emit_(OP_FSTRING_EVAL, index, line); + } + + if(repr) { ctx->emit_(OP_REPR, BC_NOARG, line); } +} + +static bool is_fmt_valid_char(char c) { + switch(c) { + // clang-format off case '-': case '=': case '*': case '#': case '@': case '!': case '~': case '<': case '>': case '^': case '.': case 'f': case 'd': case 's': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return true; default: return false; - } + // clang-format on } +} - void FStringExpr::emit_(CodeEmitContext* ctx){ - int i = 0; // left index - int j = 0; // right index - int count = 0; // how many string parts - bool flag = false; // true if we are in a expression +void FStringExpr::emit_(CodeEmitContext* ctx) { + int i = 0; // left index + int j = 0; // right index + int count = 0; // how many string parts + bool flag = false; // true if we are in a expression - while(j < src.size){ - if(flag){ - if(src[j] == '}'){ - // add expression - Str expr = src.substr(i, j-i); - // BUG: ':' is not a format specifier in f"{stack[2:]}" - int conon = expr.index(":"); - if(conon >= 0){ - Str spec = expr.substr(conon+1); - // filter some invalid spec - bool ok = true; - for(char c: spec) if(!is_fmt_valid_char(c)){ ok = false; break; } - if(ok){ - _load_simple_expr(ctx, expr.substr(0, conon)); - ctx->emit_(OP_FORMAT_STRING, ctx->add_const_string(spec.sv()), line); - }else{ - // ':' is not a spec indicator - _load_simple_expr(ctx, expr); + while(j < src.size) { + if(flag) { + if(src[j] == '}') { + // add expression + Str expr = src.substr(i, j - i); + // BUG: ':' is not a format specifier in f"{stack[2:]}" + int conon = expr.index(":"); + if(conon >= 0) { + Str spec = expr.substr(conon + 1); + // filter some invalid spec + bool ok = true; + for(char c: spec) + if(!is_fmt_valid_char(c)) { + ok = false; + break; } - }else{ + if(ok) { + _load_simple_expr(ctx, expr.substr(0, conon)); + ctx->emit_(OP_FORMAT_STRING, ctx->add_const_string(spec.sv()), line); + } else { + // ':' is not a spec indicator _load_simple_expr(ctx, expr); } - flag = false; - count++; - } - }else{ - if(src[j] == '{'){ - // look at next char - if(j+1 < src.size && src[j+1] == '{'){ - // {{ -> { - j++; - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string("{"), line); - count++; - }else{ - // { -> } - flag = true; - i = j+1; - } - }else if(src[j] == '}'){ - // look at next char - if(j+1 < src.size && src[j+1] == '}'){ - // }} -> } - j++; - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string("}"), line); - count++; - }else{ - // } -> error - // throw std::runtime_error("f-string: unexpected }"); - // just ignore - } - }else{ - // literal - i = j; - while(j < src.size && src[j] != '{' && src[j] != '}') j++; - Str literal = src.substr(i, j-i); - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line); - count++; - continue; // skip j++ + } else { + _load_simple_expr(ctx, expr); } + flag = false; + count++; + } + } else { + if(src[j] == '{') { + // look at next char + if(j + 1 < src.size && src[j + 1] == '{') { + // {{ -> { + j++; + ctx->emit_(OP_LOAD_CONST, ctx->add_const_string("{"), line); + count++; + } else { + // { -> } + flag = true; + i = j + 1; + } + } else if(src[j] == '}') { + // look at next char + if(j + 1 < src.size && src[j + 1] == '}') { + // }} -> } + j++; + ctx->emit_(OP_LOAD_CONST, ctx->add_const_string("}"), line); + count++; + } else { + // } -> error + // throw std::runtime_error("f-string: unexpected }"); + // just ignore + } + } else { + // literal + i = j; + while(j < src.size && src[j] != '{' && src[j] != '}') + j++; + Str literal = src.substr(i, j - i); + ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line); + count++; + continue; // skip j++ } - j++; } - - if(flag){ - // literal - Str literal = src.substr(i, src.size-i); - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line); - count++; - } - - ctx->emit_(OP_BUILD_STRING, count, line); + j++; } - - void SubscrExpr::emit_(CodeEmitContext* ctx){ - a->emit_(ctx); - b->emit_(ctx); - Bytecode last_bc = ctx->co->codes.back(); - if(b->is_name() && last_bc.op == OP_LOAD_FAST){ - ctx->revert_last_emit_(); - ctx->emit_(OP_LOAD_SUBSCR_FAST, last_bc.arg, line); - }else if(b->is_literal() && last_bc.op == OP_LOAD_SMALL_INT){ - ctx->revert_last_emit_(); - ctx->emit_(OP_LOAD_SUBSCR_SMALL_INT, last_bc.arg, line); - }else{ - ctx->emit_(OP_LOAD_SUBSCR, BC_NOARG, line); - } + if(flag) { + // literal + Str literal = src.substr(i, src.size - i); + ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line); + count++; } - bool SubscrExpr::emit_store(CodeEmitContext* ctx){ - a->emit_(ctx); - b->emit_(ctx); - Bytecode last_bc = ctx->co->codes.back(); - if(b->is_name() && last_bc.op == OP_LOAD_FAST){ - ctx->revert_last_emit_(); - ctx->emit_(OP_STORE_SUBSCR_FAST, last_bc.arg, line); - }else{ - ctx->emit_(OP_STORE_SUBSCR, BC_NOARG, line); - } - return true; - } + ctx->emit_(OP_BUILD_STRING, count, line); +} - void SubscrExpr::emit_inplace(CodeEmitContext* ctx){ - a->emit_(ctx); - b->emit_(ctx); - ctx->emit_(OP_DUP_TOP_TWO, BC_NOARG, line); +void SubscrExpr::emit_(CodeEmitContext* ctx) { + a->emit_(ctx); + b->emit_(ctx); + Bytecode last_bc = ctx->co->codes.back(); + if(b->is_name() && last_bc.op == OP_LOAD_FAST) { + ctx->revert_last_emit_(); + ctx->emit_(OP_LOAD_SUBSCR_FAST, last_bc.arg, line); + } else if(b->is_literal() && last_bc.op == OP_LOAD_SMALL_INT) { + ctx->revert_last_emit_(); + ctx->emit_(OP_LOAD_SUBSCR_SMALL_INT, last_bc.arg, line); + } else { ctx->emit_(OP_LOAD_SUBSCR, BC_NOARG, line); } +} - bool SubscrExpr::emit_store_inplace(CodeEmitContext* ctx){ - // [a, b, val] -> [val, a, b] - ctx->emit_(OP_ROT_THREE, BC_NOARG, line); +bool SubscrExpr::emit_store(CodeEmitContext* ctx) { + a->emit_(ctx); + b->emit_(ctx); + Bytecode last_bc = ctx->co->codes.back(); + if(b->is_name() && last_bc.op == OP_LOAD_FAST) { + ctx->revert_last_emit_(); + ctx->emit_(OP_STORE_SUBSCR_FAST, last_bc.arg, line); + } else { ctx->emit_(OP_STORE_SUBSCR, BC_NOARG, line); - return true; + } + return true; +} + +void SubscrExpr::emit_inplace(CodeEmitContext* ctx) { + a->emit_(ctx); + b->emit_(ctx); + ctx->emit_(OP_DUP_TOP_TWO, BC_NOARG, line); + ctx->emit_(OP_LOAD_SUBSCR, BC_NOARG, line); +} + +bool SubscrExpr::emit_store_inplace(CodeEmitContext* ctx) { + // [a, b, val] -> [val, a, b] + ctx->emit_(OP_ROT_THREE, BC_NOARG, line); + ctx->emit_(OP_STORE_SUBSCR, BC_NOARG, line); + return true; +} + +bool SubscrExpr::emit_del(CodeEmitContext* ctx) { + a->emit_(ctx); + b->emit_(ctx); + ctx->emit_(OP_DELETE_SUBSCR, BC_NOARG, line); + return true; +} + +void AttribExpr::emit_(CodeEmitContext* ctx) { + a->emit_(ctx); + ctx->emit_(OP_LOAD_ATTR, b.index, line); +} + +bool AttribExpr::emit_del(CodeEmitContext* ctx) { + a->emit_(ctx); + ctx->emit_(OP_DELETE_ATTR, b.index, line); + return true; +} + +bool AttribExpr::emit_store(CodeEmitContext* ctx) { + a->emit_(ctx); + ctx->emit_(OP_STORE_ATTR, b.index, line); + return true; +} + +void AttribExpr::emit_method(CodeEmitContext* ctx) { + a->emit_(ctx); + ctx->emit_(OP_LOAD_METHOD, b.index, line); +} + +void AttribExpr::emit_inplace(CodeEmitContext* ctx) { + a->emit_(ctx); + ctx->emit_(OP_DUP_TOP, BC_NOARG, line); + ctx->emit_(OP_LOAD_ATTR, b.index, line); +} + +bool AttribExpr::emit_store_inplace(CodeEmitContext* ctx) { + // [a, val] -> [val, a] + ctx->emit_(OP_ROT_TWO, BC_NOARG, line); + ctx->emit_(OP_STORE_ATTR, b.index, line); + return true; +} + +void CallExpr::emit_(CodeEmitContext* ctx) { + bool vargs = false; + bool vkwargs = false; + for(auto& arg: args) + if(arg->is_starred()) vargs = true; + for(auto& item: kwargs) + if(item.second->is_starred()) vkwargs = true; + + // if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy + if(callable->is_attrib()) { + auto p = static_cast(callable.get()); + p->emit_method(ctx); // OP_LOAD_METHOD + } else { + callable->emit_(ctx); + ctx->emit_(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); } - bool SubscrExpr::emit_del(CodeEmitContext* ctx){ - a->emit_(ctx); - b->emit_(ctx); - ctx->emit_(OP_DELETE_SUBSCR, BC_NOARG, line); - return true; - } + if(vargs || vkwargs) { + for(auto& item: args) + item->emit_(ctx); + ctx->emit_(OP_BUILD_TUPLE_UNPACK, (uint16_t)args.size(), line); - void AttribExpr::emit_(CodeEmitContext* ctx){ - a->emit_(ctx); - ctx->emit_(OP_LOAD_ATTR, b.index, line); - } - - bool AttribExpr::emit_del(CodeEmitContext* ctx) { - a->emit_(ctx); - ctx->emit_(OP_DELETE_ATTR, b.index, line); - return true; - } - - bool AttribExpr::emit_store(CodeEmitContext* ctx){ - a->emit_(ctx); - ctx->emit_(OP_STORE_ATTR, b.index, line); - return true; - } - - void AttribExpr::emit_method(CodeEmitContext* ctx) { - a->emit_(ctx); - ctx->emit_(OP_LOAD_METHOD, b.index, line); - } - - void AttribExpr::emit_inplace(CodeEmitContext* ctx) { - a->emit_(ctx); - ctx->emit_(OP_DUP_TOP, BC_NOARG, line); - ctx->emit_(OP_LOAD_ATTR, b.index, line); - } - - bool AttribExpr::emit_store_inplace(CodeEmitContext* ctx) { - // [a, val] -> [val, a] - ctx->emit_(OP_ROT_TWO, BC_NOARG, line); - ctx->emit_(OP_STORE_ATTR, b.index, line); - return true; - } - - void CallExpr::emit_(CodeEmitContext* ctx) { - bool vargs = false; - bool vkwargs = false; - for(auto& arg: args) if(arg->is_starred()) vargs = true; - for(auto& item: kwargs) if(item.second->is_starred()) vkwargs = true; - - // if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy - if(callable->is_attrib()){ - auto p = static_cast(callable.get()); - p->emit_method(ctx); // OP_LOAD_METHOD - }else{ - callable->emit_(ctx); - ctx->emit_(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); - } - - if(vargs || vkwargs){ - for(auto& item: args) item->emit_(ctx); - ctx->emit_(OP_BUILD_TUPLE_UNPACK, (uint16_t)args.size(), line); - - if(!kwargs.empty()){ - for(auto& item: kwargs){ - if(item.second->is_starred()){ - assert(item.second->star_level() == 2); - item.second->emit_(ctx); - }else{ - // k=v - int index = ctx->add_const_string(item.first.sv()); - ctx->emit_(OP_LOAD_CONST, index, line); - item.second->emit_(ctx); - ctx->emit_(OP_BUILD_TUPLE, 2, line); - } + if(!kwargs.empty()) { + for(auto& item: kwargs) { + if(item.second->is_starred()) { + assert(item.second->star_level() == 2); + item.second->emit_(ctx); + } else { + // k=v + int index = ctx->add_const_string(item.first.sv()); + ctx->emit_(OP_LOAD_CONST, index, line); + item.second->emit_(ctx); + ctx->emit_(OP_BUILD_TUPLE, 2, line); } - ctx->emit_(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line); - ctx->emit_(OP_CALL_TP, 1, line); - }else{ - ctx->emit_(OP_CALL_TP, 0, line); } - }else{ - // vectorcall protocol - for(auto& item: args) item->emit_(ctx); - for(auto& item: kwargs){ - i64 _val = StrName(item.first.sv()).index; - ctx->emit_int(_val, line); - item.second->emit_(ctx); - } - int KWARGC = kwargs.size(); - int ARGC = args.size(); - ctx->emit_(OP_CALL, (KWARGC<<8)|ARGC, line); + ctx->emit_(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line); + ctx->emit_(OP_CALL_TP, 1, line); + } else { + ctx->emit_(OP_CALL_TP, 0, line); } + } else { + // vectorcall protocol + for(auto& item: args) + item->emit_(ctx); + for(auto& item: kwargs) { + i64 _val = StrName(item.first.sv()).index; + ctx->emit_int(_val, line); + item.second->emit_(ctx); + } + int KWARGC = kwargs.size(); + int ARGC = args.size(); + ctx->emit_(OP_CALL, (KWARGC << 8) | ARGC, line); } +} - - bool BinaryExpr::is_compare() const { - switch(op){ - case TK("<"): case TK("<="): case TK("=="): - case TK("!="): case TK(">"): case TK(">="): return true; - default: return false; - } +bool BinaryExpr::is_compare() const { + switch(op) { + case TK("<"): + case TK("<="): + case TK("=="): + case TK("!="): + case TK(">"): + case TK(">="): return true; + default: return false; } +} - void BinaryExpr::_emit_compare(CodeEmitContext* ctx, small_vector_2& jmps){ - if(lhs->is_compare()){ - static_cast(lhs.get())->_emit_compare(ctx, jmps); - }else{ - lhs->emit_(ctx); // [a] - } - rhs->emit_(ctx); // [a, b] - ctx->emit_(OP_DUP_TOP, BC_NOARG, line); // [a, b, b] - ctx->emit_(OP_ROT_THREE, BC_NOARG, line); // [b, a, b] - switch(op){ - case TK("<"): ctx->emit_(OP_COMPARE_LT, BC_NOARG, line); break; - case TK("<="): ctx->emit_(OP_COMPARE_LE, BC_NOARG, line); break; - case TK("=="): ctx->emit_(OP_COMPARE_EQ, BC_NOARG, line); break; - case TK("!="): ctx->emit_(OP_COMPARE_NE, BC_NOARG, line); break; - case TK(">"): ctx->emit_(OP_COMPARE_GT, BC_NOARG, line); break; - case TK(">="): ctx->emit_(OP_COMPARE_GE, BC_NOARG, line); break; - default: PK_UNREACHABLE() - } +void BinaryExpr::_emit_compare(CodeEmitContext* ctx, small_vector_2& jmps) { + if(lhs->is_compare()) { + static_cast(lhs.get())->_emit_compare(ctx, jmps); + } else { + lhs->emit_(ctx); // [a] + } + rhs->emit_(ctx); // [a, b] + ctx->emit_(OP_DUP_TOP, BC_NOARG, line); // [a, b, b] + ctx->emit_(OP_ROT_THREE, BC_NOARG, line); // [b, a, b] + switch(op) { + case TK("<"): ctx->emit_(OP_COMPARE_LT, BC_NOARG, line); break; + case TK("<="): ctx->emit_(OP_COMPARE_LE, BC_NOARG, line); break; + case TK("=="): ctx->emit_(OP_COMPARE_EQ, BC_NOARG, line); break; + case TK("!="): ctx->emit_(OP_COMPARE_NE, BC_NOARG, line); break; + case TK(">"): ctx->emit_(OP_COMPARE_GT, BC_NOARG, line); break; + case TK(">="): ctx->emit_(OP_COMPARE_GE, BC_NOARG, line); break; + default: PK_UNREACHABLE() + } + // [b, RES] + int index = ctx->emit_(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line); + jmps.push_back(index); +} + +void BinaryExpr::emit_(CodeEmitContext* ctx) { + small_vector_2 jmps; + if(is_compare() && lhs->is_compare()) { + // (a < b) < c + static_cast(lhs.get())->_emit_compare(ctx, jmps); // [b, RES] - int index = ctx->emit_(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line); - jmps.push_back(index); - } - - void BinaryExpr::emit_(CodeEmitContext* ctx) { - small_vector_2 jmps; - if(is_compare() && lhs->is_compare()){ - // (a < b) < c - static_cast(lhs.get())->_emit_compare(ctx, jmps); - // [b, RES] - }else{ - // (1 + 2) < c - if(inplace){ - lhs->emit_inplace(ctx); - }else{ - lhs->emit_(ctx); - } + } else { + // (1 + 2) < c + if(inplace) { + lhs->emit_inplace(ctx); + } else { + lhs->emit_(ctx); } - - rhs->emit_(ctx); - switch (op) { - case TK("+"): ctx->emit_(OP_BINARY_ADD, BC_NOARG, line); break; - case TK("-"): ctx->emit_(OP_BINARY_SUB, BC_NOARG, line); break; - case TK("*"): ctx->emit_(OP_BINARY_MUL, BC_NOARG, line); break; - case TK("/"): ctx->emit_(OP_BINARY_TRUEDIV, BC_NOARG, line); break; - case TK("//"): ctx->emit_(OP_BINARY_FLOORDIV, BC_NOARG, line); break; - case TK("%"): ctx->emit_(OP_BINARY_MOD, BC_NOARG, line); break; - case TK("**"): ctx->emit_(OP_BINARY_POW, BC_NOARG, line); break; - - case TK("<"): ctx->emit_(OP_COMPARE_LT, BC_NOARG, line); break; - case TK("<="): ctx->emit_(OP_COMPARE_LE, BC_NOARG, line); break; - case TK("=="): ctx->emit_(OP_COMPARE_EQ, BC_NOARG, line); break; - case TK("!="): ctx->emit_(OP_COMPARE_NE, BC_NOARG, line); break; - case TK(">"): ctx->emit_(OP_COMPARE_GT, BC_NOARG, line); break; - case TK(">="): ctx->emit_(OP_COMPARE_GE, BC_NOARG, line); break; - - case TK("in"): ctx->emit_(OP_CONTAINS_OP, 0, line); break; - case TK("not in"): ctx->emit_(OP_CONTAINS_OP, 1, line); break; - case TK("is"): ctx->emit_(OP_IS_OP, BC_NOARG, line); break; - case TK("is not"): ctx->emit_(OP_IS_NOT_OP, BC_NOARG, line); break; - - case TK("<<"): ctx->emit_(OP_BITWISE_LSHIFT, BC_NOARG, line); break; - case TK(">>"): ctx->emit_(OP_BITWISE_RSHIFT, BC_NOARG, line); break; - case TK("&"): ctx->emit_(OP_BITWISE_AND, BC_NOARG, line); break; - case TK("|"): ctx->emit_(OP_BITWISE_OR, BC_NOARG, line); break; - case TK("^"): ctx->emit_(OP_BITWISE_XOR, BC_NOARG, line); break; - - case TK("@"): ctx->emit_(OP_BINARY_MATMUL, BC_NOARG, line); break; - default: assert(false); - } - - for(int i: jmps) ctx->patch_jump(i); } - void TernaryExpr::emit_(CodeEmitContext* ctx){ - cond->emit_(ctx); - int patch = ctx->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line); - true_expr->emit_(ctx); - int patch_2 = ctx->emit_(OP_JUMP_FORWARD, BC_NOARG, true_expr->line); - ctx->patch_jump(patch); - false_expr->emit_(ctx); - ctx->patch_jump(patch_2); + rhs->emit_(ctx); + switch(op) { + case TK("+"): ctx->emit_(OP_BINARY_ADD, BC_NOARG, line); break; + case TK("-"): ctx->emit_(OP_BINARY_SUB, BC_NOARG, line); break; + case TK("*"): ctx->emit_(OP_BINARY_MUL, BC_NOARG, line); break; + case TK("/"): ctx->emit_(OP_BINARY_TRUEDIV, BC_NOARG, line); break; + case TK("//"): ctx->emit_(OP_BINARY_FLOORDIV, BC_NOARG, line); break; + case TK("%"): ctx->emit_(OP_BINARY_MOD, BC_NOARG, line); break; + case TK("**"): ctx->emit_(OP_BINARY_POW, BC_NOARG, line); break; + + case TK("<"): ctx->emit_(OP_COMPARE_LT, BC_NOARG, line); break; + case TK("<="): ctx->emit_(OP_COMPARE_LE, BC_NOARG, line); break; + case TK("=="): ctx->emit_(OP_COMPARE_EQ, BC_NOARG, line); break; + case TK("!="): ctx->emit_(OP_COMPARE_NE, BC_NOARG, line); break; + case TK(">"): ctx->emit_(OP_COMPARE_GT, BC_NOARG, line); break; + case TK(">="): ctx->emit_(OP_COMPARE_GE, BC_NOARG, line); break; + + case TK("in"): ctx->emit_(OP_CONTAINS_OP, 0, line); break; + case TK("not in"): ctx->emit_(OP_CONTAINS_OP, 1, line); break; + case TK("is"): ctx->emit_(OP_IS_OP, BC_NOARG, line); break; + case TK("is not"): ctx->emit_(OP_IS_NOT_OP, BC_NOARG, line); break; + + case TK("<<"): ctx->emit_(OP_BITWISE_LSHIFT, BC_NOARG, line); break; + case TK(">>"): ctx->emit_(OP_BITWISE_RSHIFT, BC_NOARG, line); break; + case TK("&"): ctx->emit_(OP_BITWISE_AND, BC_NOARG, line); break; + case TK("|"): ctx->emit_(OP_BITWISE_OR, BC_NOARG, line); break; + case TK("^"): ctx->emit_(OP_BITWISE_XOR, BC_NOARG, line); break; + + case TK("@"): ctx->emit_(OP_BINARY_MATMUL, BC_NOARG, line); break; + default: assert(false); } -} // namespace pkpy \ No newline at end of file + for(int i: jmps) + ctx->patch_jump(i); +} + +void TernaryExpr::emit_(CodeEmitContext* ctx) { + cond->emit_(ctx); + int patch = ctx->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line); + true_expr->emit_(ctx); + int patch_2 = ctx->emit_(OP_JUMP_FORWARD, BC_NOARG, true_expr->line); + ctx->patch_jump(patch); + false_expr->emit_(ctx); + ctx->patch_jump(patch_2); +} + +} // namespace pkpy diff --git a/src/compiler/lexer.cpp b/src/compiler/lexer.cpp index 5cef480d..f80ad491 100644 --- a/src/compiler/lexer.cpp +++ b/src/compiler/lexer.cpp @@ -1,18 +1,22 @@ #include "pocketpy/compiler/lexer.hpp" -namespace pkpy{ +namespace pkpy { +// clang-format off static const uint32_t kLoRangeA[] = {170,186,443,448,660,1488,1519,1568,1601,1646,1649,1749,1774,1786,1791,1808,1810,1869,1969,1994,2048,2112,2144,2208,2230,2308,2365,2384,2392,2418,2437,2447,2451,2474,2482,2486,2493,2510,2524,2527,2544,2556,2565,2575,2579,2602,2610,2613,2616,2649,2654,2674,2693,2703,2707,2730,2738,2741,2749,2768,2784,2809,2821,2831,2835,2858,2866,2869,2877,2908,2911,2929,2947,2949,2958,2962,2969,2972,2974,2979,2984,2990,3024,3077,3086,3090,3114,3133,3160,3168,3200,3205,3214,3218,3242,3253,3261,3294,3296,3313,3333,3342,3346,3389,3406,3412,3423,3450,3461,3482,3507,3517,3520,3585,3634,3648,3713,3716,3718,3724,3749,3751,3762,3773,3776,3804,3840,3904,3913,3976,4096,4159,4176,4186,4193,4197,4206,4213,4238,4352,4682,4688,4696,4698,4704,4746,4752,4786,4792,4800,4802,4808,4824,4882,4888,4992,5121,5743,5761,5792,5873,5888,5902,5920,5952,5984,5998,6016,6108,6176,6212,6272,6279,6314,6320,6400,6480,6512,6528,6576,6656,6688,6917,6981,7043,7086,7098,7168,7245,7258,7401,7406,7413,7418,8501,11568,11648,11680,11688,11696,11704,11712,11720,11728,11736,12294,12348,12353,12447,12449,12543,12549,12593,12704,12784,13312,19968,40960,40982,42192,42240,42512,42538,42606,42656,42895,42999,43003,43011,43015,43020,43072,43138,43250,43259,43261,43274,43312,43360,43396,43488,43495,43514,43520,43584,43588,43616,43633,43642,43646,43697,43701,43705,43712,43714,43739,43744,43762,43777,43785,43793,43808,43816,43968,44032,55216,55243,63744,64112,64285,64287,64298,64312,64318,64320,64323,64326,64467,64848,64914,65008,65136,65142,65382,65393,65440,65474,65482,65490,65498,65536,65549,65576,65596,65599,65616,65664,66176,66208,66304,66349,66370,66384,66432,66464,66504,66640,66816,66864,67072,67392,67424,67584,67592,67594,67639,67644,67647,67680,67712,67808,67828,67840,67872,67968,68030,68096,68112,68117,68121,68192,68224,68288,68297,68352,68416,68448,68480,68608,68864,69376,69415,69424,69600,69635,69763,69840,69891,69956,69968,70006,70019,70081,70106,70108,70144,70163,70272,70280,70282,70287,70303,70320,70405,70415,70419,70442,70450,70453,70461,70480,70493,70656,70727,70751,70784,70852,70855,71040,71128,71168,71236,71296,71352,71424,71680,71935,72096,72106,72161,72163,72192,72203,72250,72272,72284,72349,72384,72704,72714,72768,72818,72960,72968,72971,73030,73056,73063,73066,73112,73440,73728,74880,77824,82944,92160,92736,92880,92928,93027,93053,93952,94032,94208,100352,110592,110928,110948,110960,113664,113776,113792,113808,123136,123214,123584,124928,126464,126469,126497,126500,126503,126505,126516,126521,126523,126530,126535,126537,126539,126541,126545,126548,126551,126553,126555,126557,126559,126561,126564,126567,126572,126580,126585,126590,126592,126603,126625,126629,126635,131072,173824,177984,178208,183984,194560}; static const uint32_t kLoRangeB[] = {170,186,443,451,660,1514,1522,1599,1610,1647,1747,1749,1775,1788,1791,1808,1839,1957,1969,2026,2069,2136,2154,2228,2237,2361,2365,2384,2401,2432,2444,2448,2472,2480,2482,2489,2493,2510,2525,2529,2545,2556,2570,2576,2600,2608,2611,2614,2617,2652,2654,2676,2701,2705,2728,2736,2739,2745,2749,2768,2785,2809,2828,2832,2856,2864,2867,2873,2877,2909,2913,2929,2947,2954,2960,2965,2970,2972,2975,2980,2986,3001,3024,3084,3088,3112,3129,3133,3162,3169,3200,3212,3216,3240,3251,3257,3261,3294,3297,3314,3340,3344,3386,3389,3406,3414,3425,3455,3478,3505,3515,3517,3526,3632,3635,3653,3714,3716,3722,3747,3749,3760,3763,3773,3780,3807,3840,3911,3948,3980,4138,4159,4181,4189,4193,4198,4208,4225,4238,4680,4685,4694,4696,4701,4744,4749,4784,4789,4798,4800,4805,4822,4880,4885,4954,5007,5740,5759,5786,5866,5880,5900,5905,5937,5969,5996,6000,6067,6108,6210,6264,6276,6312,6314,6389,6430,6509,6516,6571,6601,6678,6740,6963,6987,7072,7087,7141,7203,7247,7287,7404,7411,7414,7418,8504,11623,11670,11686,11694,11702,11710,11718,11726,11734,11742,12294,12348,12438,12447,12538,12543,12591,12686,12730,12799,19893,40943,40980,42124,42231,42507,42527,42539,42606,42725,42895,42999,43009,43013,43018,43042,43123,43187,43255,43259,43262,43301,43334,43388,43442,43492,43503,43518,43560,43586,43595,43631,43638,43642,43695,43697,43702,43709,43712,43714,43740,43754,43762,43782,43790,43798,43814,43822,44002,55203,55238,55291,64109,64217,64285,64296,64310,64316,64318,64321,64324,64433,64829,64911,64967,65019,65140,65276,65391,65437,65470,65479,65487,65495,65500,65547,65574,65594,65597,65613,65629,65786,66204,66256,66335,66368,66377,66421,66461,66499,66511,66717,66855,66915,67382,67413,67431,67589,67592,67637,67640,67644,67669,67702,67742,67826,67829,67861,67897,68023,68031,68096,68115,68119,68149,68220,68252,68295,68324,68405,68437,68466,68497,68680,68899,69404,69415,69445,69622,69687,69807,69864,69926,69956,70002,70006,70066,70084,70106,70108,70161,70187,70278,70280,70285,70301,70312,70366,70412,70416,70440,70448,70451,70457,70461,70480,70497,70708,70730,70751,70831,70853,70855,71086,71131,71215,71236,71338,71352,71450,71723,71935,72103,72144,72161,72163,72192,72242,72250,72272,72329,72349,72440,72712,72750,72768,72847,72966,72969,73008,73030,73061,73064,73097,73112,73458,74649,75075,78894,83526,92728,92766,92909,92975,93047,93071,94026,94032,100343,101106,110878,110930,110951,111355,113770,113788,113800,113817,123180,123214,123627,125124,126467,126495,126498,126500,126503,126514,126519,126521,126523,126530,126535,126537,126539,126543,126546,126548,126551,126553,126555,126557,126559,126562,126564,126570,126578,126583,126588,126590,126601,126619,126627,126633,126651,173782,177972,178205,183969,191456,195101}; +// clang-format on -static bool is_possible_number_char(char c){ - switch(c){ +static bool is_possible_number_char(char c) { + switch(c) { + // clang-format off case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case '.': case 'L': case 'x': case 'o': case 'j': return true; default: return false; + // clang-format on } } @@ -26,549 +30,586 @@ static bool is_unicode_Lo_char(uint32_t c) { return c >= kLoRangeA[index] && c <= kLoRangeB[index]; } - bool Lexer::match_n_chars(int n, char c0){ - const char* c = curr_char; - for(int i=0; i 0) return true; + int spaces = eat_spaces(); + if(peekchar() == '#') skip_line_comment(); + if(peekchar() == '\0' || peekchar() == '\n') return true; + // https://docs.python.org/3/reference/lexical_analysis.html#indentation + if(spaces > indents.top()) { + indents.push(spaces); + nexts.push_back(Token{TK("@indent"), token_start, 0, current_line, brackets_level, {}}); + } else if(spaces < indents.top()) { + while(spaces < indents.top()) { + indents.pop(); + nexts.push_back(Token{TK("@dedent"), token_start, 0, current_line, brackets_level, {}}); } + if(spaces != indents.top()) { return false; } } + return true; +} - bool Lexer::eat_indentation(){ - if(brackets_level > 0) return true; - int spaces = eat_spaces(); - if(peekchar() == '#') skip_line_comment(); - if(peekchar() == '\0' || peekchar() == '\n') return true; - // https://docs.python.org/3/reference/lexical_analysis.html#indentation - if(spaces > indents.top()){ - indents.push(spaces); - nexts.push_back(Token{TK("@indent"), token_start, 0, current_line, brackets_level, {}}); - } else if(spaces < indents.top()){ - while(spaces < indents.top()){ - indents.pop(); - nexts.push_back(Token{TK("@dedent"), token_start, 0, current_line, brackets_level, {}}); - } - if(spaces != indents.top()){ - return false; - } - } - return true; +char Lexer::eatchar() { + char c = peekchar(); + assert(c != '\n'); // eatchar() cannot consume a newline + curr_char++; + return c; +} + +char Lexer::eatchar_include_newline() { + char c = peekchar(); + curr_char++; + if(c == '\n') { + current_line++; + src->line_starts.push_back(curr_char); } + return c; +} - char Lexer::eatchar() { - char c = peekchar(); - assert(c != '\n'); // eatchar() cannot consume a newline - curr_char++; - return c; - } - - char Lexer::eatchar_include_newline() { - char c = peekchar(); - curr_char++; - if (c == '\n'){ - current_line++; - src->line_starts.push_back(curr_char); - } - return c; - } - - int Lexer::eat_name() { - curr_char--; - while(true){ - unsigned char c = peekchar(); - int u8bytes = utf8len(c, true); - if(u8bytes == 0) return 1; - if(u8bytes == 1){ - if(isalpha(c) || c=='_' || isdigit(c)) { - curr_char++; - continue; - }else{ - break; - } - } - // handle multibyte char - Str u8str(curr_char, u8bytes); - if(u8str.size != u8bytes) return 2; - uint32_t value = 0; - for(int k=0; k < u8bytes; k++){ - uint8_t b = u8str[k]; - if(k==0){ - if(u8bytes == 2) value = (b & 0b00011111) << 6; - else if(u8bytes == 3) value = (b & 0b00001111) << 12; - else if(u8bytes == 4) value = (b & 0b00000111) << 18; - }else{ - value |= (b & 0b00111111) << (6*(u8bytes-k-1)); - } - } - if(is_unicode_Lo_char(value)) curr_char += u8bytes; - else break; - } - - int length = (int)(curr_char - token_start); - if(length == 0) return 3; - std::string_view name(token_start, length); - - if(src->mode == JSON_MODE){ - if(name == "true"){ - add_token(TK("True")); - } else if(name == "false"){ - add_token(TK("False")); - } else if(name == "null"){ - add_token(TK("None")); +int Lexer::eat_name() { + curr_char--; + while(true) { + unsigned char c = peekchar(); + int u8bytes = utf8len(c, true); + if(u8bytes == 0) return 1; + if(u8bytes == 1) { + if(isalpha(c) || c == '_' || isdigit(c)) { + curr_char++; + continue; } else { - return 4; + break; } - return 0; } + // handle multibyte char + Str u8str(curr_char, u8bytes); + if(u8str.size != u8bytes) return 2; + uint32_t value = 0; + for(int k = 0; k < u8bytes; k++) { + uint8_t b = u8str[k]; + if(k == 0) { + if(u8bytes == 2) + value = (b & 0b00011111) << 6; + else if(u8bytes == 3) + value = (b & 0b00001111) << 12; + else if(u8bytes == 4) + value = (b & 0b00000111) << 18; + } else { + value |= (b & 0b00111111) << (6 * (u8bytes - k - 1)); + } + } + if(is_unicode_Lo_char(value)) + curr_char += u8bytes; + else + break; + } - if(kTokenKwMap.count(name)){ - add_token(kTokenKwMap.at(name)); + int length = (int)(curr_char - token_start); + if(length == 0) return 3; + std::string_view name(token_start, length); + + if(src->mode == JSON_MODE) { + if(name == "true") { + add_token(TK("True")); + } else if(name == "false") { + add_token(TK("False")); + } else if(name == "null") { + add_token(TK("None")); } else { - add_token(TK("@id")); + return 4; } return 0; } - void Lexer::skip_line_comment() { - char c; - while ((c = peekchar()) != '\0') { - if (c == '\n') return; - eatchar(); - } - } - - bool Lexer::matchchar(char c) { - if (peekchar() != c) return false; - eatchar_include_newline(); - return true; + if(kTokenKwMap.count(name)) { + add_token(kTokenKwMap.at(name)); + } else { + add_token(TK("@id")); } + return 0; +} - void Lexer::add_token(TokenIndex type, TokenValue value) { - switch(type){ - case TK("{"): case TK("["): case TK("("): brackets_level++; break; - case TK(")"): case TK("]"): case TK("}"): brackets_level--; break; - } - auto token = Token{ - type, - token_start, - (int)(curr_char - token_start), - current_line - ((type == TK("@eol")) ? 1 : 0), - brackets_level, - value - }; - // handle "not in", "is not", "yield from" - if(!nexts.empty()){ - auto& back = nexts.back(); - if(back.type == TK("not") && type == TK("in")){ - back.type = TK("not in"); - return; - } - if(back.type == TK("is") && type == TK("not")){ - back.type = TK("is not"); - return; - } - if(back.type == TK("yield") && type == TK("from")){ - back.type = TK("yield from"); - return; - } - nexts.push_back(token); - } +void Lexer::skip_line_comment() { + char c; + while((c = peekchar()) != '\0') { + if(c == '\n') return; + eatchar(); } +} - void Lexer::add_token_2(char c, TokenIndex one, TokenIndex two) { - if (matchchar(c)) add_token(two); - else add_token(one); +bool Lexer::matchchar(char c) { + if(peekchar() != c) return false; + eatchar_include_newline(); + return true; +} + +void Lexer::add_token(TokenIndex type, TokenValue value) { + switch(type) { + case TK("{"): + case TK("["): + case TK("("): brackets_level++; break; + case TK(")"): + case TK("]"): + case TK("}"): brackets_level--; break; } + auto token = Token{type, + token_start, + (int)(curr_char - token_start), + current_line - ((type == TK("@eol")) ? 1 : 0), + brackets_level, + value}; + // handle "not in", "is not", "yield from" + if(!nexts.empty()) { + auto& back = nexts.back(); + if(back.type == TK("not") && type == TK("in")) { + back.type = TK("not in"); + return; + } + if(back.type == TK("is") && type == TK("not")) { + back.type = TK("is not"); + return; + } + if(back.type == TK("yield") && type == TK("from")) { + back.type = TK("yield from"); + return; + } + nexts.push_back(token); + } +} - Str Lexer::eat_string_until(char quote, bool raw) { - bool quote3 = match_n_chars(2, quote); - small_vector_2 buff; - while (true) { - char c = eatchar_include_newline(); - if (c == quote){ - if(quote3 && !match_n_chars(2, quote)){ - buff.push_back(c); - continue; - } - break; - } - if (c == '\0'){ - if(quote3 && src->mode == REPL_MODE){ - throw NeedMoreLines(false); - } - SyntaxError("EOL while scanning string literal"); - } - if (c == '\n'){ - if(!quote3) SyntaxError("EOL while scanning string literal"); - else{ - buff.push_back(c); - continue; - } - } - if (!raw && c == '\\') { - switch (eatchar_include_newline()) { - case '"': buff.push_back('"'); break; - case '\'': buff.push_back('\''); break; - case '\\': buff.push_back('\\'); break; - case 'n': buff.push_back('\n'); break; - case 'r': buff.push_back('\r'); break; - case 't': buff.push_back('\t'); break; - case 'b': buff.push_back('\b'); break; - case 'x': { - char hex[3] = {eatchar(), eatchar(), '\0'}; - size_t parsed; - char code; - try{ - code = (char)std::stoi(hex, &parsed, 16); - }catch(...){ - SyntaxError("invalid hex char"); - } - if (parsed != 2) SyntaxError("invalid hex char"); - buff.push_back(code); - } break; - default: SyntaxError("invalid escape char"); - } - } else { +void Lexer::add_token_2(char c, TokenIndex one, TokenIndex two) { + if(matchchar(c)) + add_token(two); + else + add_token(one); +} + +Str Lexer::eat_string_until(char quote, bool raw) { + bool quote3 = match_n_chars(2, quote); + small_vector_2 buff; + while(true) { + char c = eatchar_include_newline(); + if(c == quote) { + if(quote3 && !match_n_chars(2, quote)) { buff.push_back(c); + continue; + } + break; + } + if(c == '\0') { + if(quote3 && src->mode == REPL_MODE) { throw NeedMoreLines(false); } + SyntaxError("EOL while scanning string literal"); + } + if(c == '\n') { + if(!quote3) + SyntaxError("EOL while scanning string literal"); + else { + buff.push_back(c); + continue; } } - return Str(buff.data(), buff.size()); - } - - void Lexer::eat_string(char quote, StringType type) { - Str s = eat_string_until(quote, type == RAW_STRING); - if(type == F_STRING){ - add_token(TK("@fstr"), s); - return; + if(!raw && c == '\\') { + switch(eatchar_include_newline()) { + case '"': buff.push_back('"'); break; + case '\'': buff.push_back('\''); break; + case '\\': buff.push_back('\\'); break; + case 'n': buff.push_back('\n'); break; + case 'r': buff.push_back('\r'); break; + case 't': buff.push_back('\t'); break; + case 'b': buff.push_back('\b'); break; + case 'x': { + char hex[3] = {eatchar(), eatchar(), '\0'}; + size_t parsed; + char code; + try { + code = (char)std::stoi(hex, &parsed, 16); + } catch(...) { SyntaxError("invalid hex char"); } + if(parsed != 2) SyntaxError("invalid hex char"); + buff.push_back(code); + } break; + default: SyntaxError("invalid escape char"); + } + } else { + buff.push_back(c); } - if(type == NORMAL_BYTES){ - add_token(TK("@bytes"), s); - return; - } - add_token(TK("@str"), s); } + return Str(buff.data(), buff.size()); +} - void Lexer::eat_number() { - const char* i = token_start; - while(is_possible_number_char(*i)) i++; +void Lexer::eat_string(char quote, StringType type) { + Str s = eat_string_until(quote, type == RAW_STRING); + if(type == F_STRING) { + add_token(TK("@fstr"), s); + return; + } + if(type == NORMAL_BYTES) { + add_token(TK("@bytes"), s); + return; + } + add_token(TK("@str"), s); +} - bool is_scientific_notation = false; - if(*(i-1) == 'e' && (*i == '+' || *i == '-')){ +void Lexer::eat_number() { + const char* i = token_start; + while(is_possible_number_char(*i)) + i++; + + bool is_scientific_notation = false; + if(*(i - 1) == 'e' && (*i == '+' || *i == '-')) { + i++; + while(isdigit(*i) || *i == 'j') i++; - while(isdigit(*i) || *i=='j') i++; - is_scientific_notation = true; - } - - std::string_view text(token_start, i - token_start); - this->curr_char = i; - - if(text[0] != '.' && !is_scientific_notation){ - // try long - if(i[-1] == 'L'){ - add_token(TK("@long")); - return; - } - // try integer - i64 int_out; - switch(parse_uint(text, &int_out, -1)){ - case IntParsingResult::Success: - add_token(TK("@num"), int_out); - return; - case IntParsingResult::Overflow: - SyntaxError("int literal is too large"); - return; - case IntParsingResult::Failure: - break; // do nothing - } - } - - // try float - double float_out; - char* p_end; - try{ - float_out = std::strtod(text.data(), &p_end); - }catch(...){ - SyntaxError("invalid number literal"); - } - - if(p_end == text.data() + text.size()){ - add_token(TK("@num"), (f64)float_out); - return; - } - - if(i[-1] == 'j' && p_end == text.data() + text.size() - 1){ - add_token(TK("@imag"), (f64)float_out); - return; - } - - SyntaxError("invalid number literal"); + is_scientific_notation = true; } - bool Lexer::lex_one_token() { - while (peekchar() != '\0') { - token_start = curr_char; - char c = eatchar_include_newline(); - switch (c) { - case '\'': case '"': eat_string(c, NORMAL_STRING); return true; - case '#': skip_line_comment(); break; - case '~': add_token(TK("~")); return true; - case '{': add_token(TK("{")); return true; - case '}': add_token(TK("}")); return true; - case ',': add_token(TK(",")); return true; - case ':': add_token(TK(":")); return true; - case ';': add_token(TK(";")); return true; - case '(': add_token(TK("(")); return true; - case ')': add_token(TK(")")); return true; - case '[': add_token(TK("[")); return true; - case ']': add_token(TK("]")); return true; - case '@': add_token(TK("@")); return true; - case '\\': { - // line continuation character - char c = eatchar_include_newline(); - if (c != '\n'){ - if(src->mode == REPL_MODE && c == '\0') throw NeedMoreLines(false); - SyntaxError("expected newline after line continuation character"); - } - eat_spaces(); - return true; + std::string_view text(token_start, i - token_start); + this->curr_char = i; + + if(text[0] != '.' && !is_scientific_notation) { + // try long + if(i[-1] == 'L') { + add_token(TK("@long")); + return; + } + // try integer + i64 int_out; + switch(parse_uint(text, &int_out, -1)) { + case IntParsingResult::Success: add_token(TK("@num"), int_out); return; + case IntParsingResult::Overflow: SyntaxError("int literal is too large"); return; + case IntParsingResult::Failure: break; // do nothing + } + } + + // try float + double float_out; + char* p_end; + try { + float_out = std::strtod(text.data(), &p_end); + } catch(...) { SyntaxError("invalid number literal"); } + + if(p_end == text.data() + text.size()) { + add_token(TK("@num"), (f64)float_out); + return; + } + + if(i[-1] == 'j' && p_end == text.data() + text.size() - 1) { + add_token(TK("@imag"), (f64)float_out); + return; + } + + SyntaxError("invalid number literal"); +} + +bool Lexer::lex_one_token() { + while(peekchar() != '\0') { + token_start = curr_char; + char c = eatchar_include_newline(); + switch(c) { + case '\'': + case '"': eat_string(c, NORMAL_STRING); return true; + case '#': skip_line_comment(); break; + case '~': add_token(TK("~")); return true; + case '{': add_token(TK("{")); return true; + case '}': add_token(TK("}")); return true; + case ',': add_token(TK(",")); return true; + case ':': add_token(TK(":")); return true; + case ';': add_token(TK(";")); return true; + case '(': add_token(TK("(")); return true; + case ')': add_token(TK(")")); return true; + case '[': add_token(TK("[")); return true; + case ']': add_token(TK("]")); return true; + case '@': add_token(TK("@")); return true; + case '\\': { + // line continuation character + char c = eatchar_include_newline(); + if(c != '\n') { + if(src->mode == REPL_MODE && c == '\0') throw NeedMoreLines(false); + SyntaxError("expected newline after line continuation character"); } - case '%': add_token_2('=', TK("%"), TK("%=")); return true; - case '&': add_token_2('=', TK("&"), TK("&=")); return true; - case '|': add_token_2('=', TK("|"), TK("|=")); return true; - case '^': add_token_2('=', TK("^"), TK("^=")); return true; - case '.': { + eat_spaces(); + return true; + } + case '%': add_token_2('=', TK("%"), TK("%=")); return true; + case '&': add_token_2('=', TK("&"), TK("&=")); return true; + case '|': add_token_2('=', TK("|"), TK("|=")); return true; + case '^': add_token_2('=', TK("^"), TK("^=")); return true; + case '.': { + if(matchchar('.')) { if(matchchar('.')) { - if(matchchar('.')) { - add_token(TK("...")); - } else { - add_token(TK("..")); - } + add_token(TK("...")); } else { - char next_char = peekchar(); - if(next_char >= '0' && next_char <= '9'){ - eat_number(); - }else{ - add_token(TK(".")); - } + add_token(TK("..")); } - return true; - } - case '=': add_token_2('=', TK("="), TK("==")); return true; - case '+': - if(matchchar('+')){ - add_token(TK("++")); - }else{ - add_token_2('=', TK("+"), TK("+=")); - } - return true; - case '>': { - if(matchchar('=')) add_token(TK(">=")); - else if(matchchar('>')) add_token_2('=', TK(">>"), TK(">>=")); - else add_token(TK(">")); - return true; - } - case '<': { - if(matchchar('=')) add_token(TK("<=")); - else if(matchchar('<')) add_token_2('=', TK("<<"), TK("<<=")); - else add_token(TK("<")); - return true; - } - case '-': { - if(matchchar('-')){ - add_token(TK("--")); - }else{ - if(matchchar('=')) add_token(TK("-=")); - else if(matchchar('>')) add_token(TK("->")); - else add_token(TK("-")); - } - return true; - } - case '!': - if(matchchar('=')) add_token(TK("!=")); - else SyntaxError("expected '=' after '!'"); - break; - case '*': - if (matchchar('*')) { - add_token(TK("**")); // '**' - } else { - add_token_2('=', TK("*"), TK("*=")); - } - return true; - case '/': - if(matchchar('/')) { - add_token_2('=', TK("//"), TK("//=")); - } else { - add_token_2('=', TK("/"), TK("/=")); - } - return true; - case ' ': case '\t': eat_spaces(); break; - case '\n': { - add_token(TK("@eol")); - if(!eat_indentation()) IndentationError("unindent does not match any outer indentation level"); - return true; - } - default: { - if(c == 'f'){ - if(matchchar('\'')) {eat_string('\'', F_STRING); return true;} - if(matchchar('"')) {eat_string('"', F_STRING); return true;} - }else if(c == 'r'){ - if(matchchar('\'')) {eat_string('\'', RAW_STRING); return true;} - if(matchchar('"')) {eat_string('"', RAW_STRING); return true;} - }else if(c == 'b'){ - if(matchchar('\'')) {eat_string('\'', NORMAL_BYTES); return true;} - if(matchchar('"')) {eat_string('"', NORMAL_BYTES); return true;} - } - if (c >= '0' && c <= '9') { + } else { + char next_char = peekchar(); + if(next_char >= '0' && next_char <= '9') { eat_number(); + } else { + add_token(TK(".")); + } + } + return true; + } + case '=': add_token_2('=', TK("="), TK("==")); return true; + case '+': + if(matchchar('+')) { + add_token(TK("++")); + } else { + add_token_2('=', TK("+"), TK("+=")); + } + return true; + case '>': { + if(matchchar('=')) + add_token(TK(">=")); + else if(matchchar('>')) + add_token_2('=', TK(">>"), TK(">>=")); + else + add_token(TK(">")); + return true; + } + case '<': { + if(matchchar('=')) + add_token(TK("<=")); + else if(matchchar('<')) + add_token_2('=', TK("<<"), TK("<<=")); + else + add_token(TK("<")); + return true; + } + case '-': { + if(matchchar('-')) { + add_token(TK("--")); + } else { + if(matchchar('=')) + add_token(TK("-=")); + else if(matchchar('>')) + add_token(TK("->")); + else + add_token(TK("-")); + } + return true; + } + case '!': + if(matchchar('=')) + add_token(TK("!=")); + else + SyntaxError("expected '=' after '!'"); + break; + case '*': + if(matchchar('*')) { + add_token(TK("**")); // '**' + } else { + add_token_2('=', TK("*"), TK("*=")); + } + return true; + case '/': + if(matchchar('/')) { + add_token_2('=', TK("//"), TK("//=")); + } else { + add_token_2('=', TK("/"), TK("/=")); + } + return true; + case ' ': + case '\t': eat_spaces(); break; + case '\n': { + add_token(TK("@eol")); + if(!eat_indentation()) IndentationError("unindent does not match any outer indentation level"); + return true; + } + default: { + if(c == 'f') { + if(matchchar('\'')) { + eat_string('\'', F_STRING); return true; } - switch (eat_name()) - { - case 0: break; - case 1: SyntaxError("invalid char: " + std::string(1, c)); break; - case 2: SyntaxError("invalid utf8 sequence: " + std::string(1, c)); break; - case 3: SyntaxError("@id contains invalid char"); break; - case 4: SyntaxError("invalid JSON token"); break; - default: assert(false); + if(matchchar('"')) { + eat_string('"', F_STRING); + return true; } + } else if(c == 'r') { + if(matchchar('\'')) { + eat_string('\'', RAW_STRING); + return true; + } + if(matchchar('"')) { + eat_string('"', RAW_STRING); + return true; + } + } else if(c == 'b') { + if(matchchar('\'')) { + eat_string('\'', NORMAL_BYTES); + return true; + } + if(matchchar('"')) { + eat_string('"', NORMAL_BYTES); + return true; + } + } + if(c >= '0' && c <= '9') { + eat_number(); return true; } + switch(eat_name()) { + case 0: break; + case 1: SyntaxError("invalid char: " + std::string(1, c)); break; + case 2: SyntaxError("invalid utf8 sequence: " + std::string(1, c)); break; + case 3: SyntaxError("@id contains invalid char"); break; + case 4: SyntaxError("invalid JSON token"); break; + default: assert(false); + } + return true; } } - - token_start = curr_char; - while(indents.size() > 1){ - indents.pop(); - add_token(TK("@dedent")); - return true; - } - add_token(TK("@eof")); - return false; } - void Lexer::throw_err(StrName type, Str msg){ - int lineno = current_line; - const char* cursor = curr_char; - if(peekchar() == '\n'){ - lineno--; - cursor--; - } - throw_err(type, msg, lineno, cursor); + token_start = curr_char; + while(indents.size() > 1) { + indents.pop(); + add_token(TK("@dedent")); + return true; } + add_token(TK("@eof")); + return false; +} - Lexer::Lexer(VM* vm, std::shared_ptr src) : vm(vm), src(src) { - this->token_start = src->source.c_str(); - this->curr_char = src->source.c_str(); - this->nexts.push_back(Token{TK("@sof"), token_start, 0, current_line, brackets_level, {}}); - this->indents.push(0); +void Lexer::throw_err(StrName type, Str msg) { + int lineno = current_line; + const char* cursor = curr_char; + if(peekchar() == '\n') { + lineno--; + cursor--; } + throw_err(type, msg, lineno, cursor); +} - vector Lexer::run() { - assert(curr_char == src->source.c_str()); - while (lex_one_token()); - return std::move(nexts); - } +Lexer::Lexer(VM* vm, std::shared_ptr src) : vm(vm), src(src) { + this->token_start = src->source.c_str(); + this->curr_char = src->source.c_str(); + this->nexts.push_back(Token{TK("@sof"), token_start, 0, current_line, brackets_level, {}}); + this->indents.push(0); +} -inline constexpr bool f_startswith_2(std::string_view t, const char* prefix){ +vector Lexer::run() { + assert(curr_char == src->source.c_str()); + while(lex_one_token()) + ; + return std::move(nexts); +} + +constexpr inline bool f_startswith_2(std::string_view t, const char* prefix) { if(t.length() < 2) return false; return t[0] == prefix[0] && t[1] == prefix[1]; } -IntParsingResult parse_uint(std::string_view text, i64* out, int base){ - *out = 0; +IntParsingResult parse_uint(std::string_view text, i64* out, int base) { + *out = 0; - if(base == -1){ - if(f_startswith_2(text, "0b")) base = 2; - else if(f_startswith_2(text, "0o")) base = 8; - else if(f_startswith_2(text, "0x")) base = 16; - else base = 10; - } + if(base == -1) { + if(f_startswith_2(text, "0b")) + base = 2; + else if(f_startswith_2(text, "0o")) + base = 8; + else if(f_startswith_2(text, "0x")) + base = 16; + else + base = 10; + } - if(base == 10){ - // 10-base 12334 - if(text.length() == 0) return IntParsingResult::Failure; - for(char c : text){ - if(c >= '0' && c <= '9'){ - i64 prev_out = *out; - *out = (*out * 10) + (c - '0'); - if(*out < prev_out) return IntParsingResult::Overflow; - }else{ - return IntParsingResult::Failure; - } + if(base == 10) { + // 10-base 12334 + if(text.length() == 0) return IntParsingResult::Failure; + for(char c: text) { + if(c >= '0' && c <= '9') { + i64 prev_out = *out; + *out = (*out * 10) + (c - '0'); + if(*out < prev_out) return IntParsingResult::Overflow; + } else { + return IntParsingResult::Failure; + } + } + return IntParsingResult::Success; + } else if(base == 2) { + // 2-base 0b101010 + if(f_startswith_2(text, "0b")) text.remove_prefix(2); + if(text.length() == 0) return IntParsingResult::Failure; + for(char c: text) { + if(c == '0' || c == '1') { + i64 prev_out = *out; + *out = (*out << 1) | (c - '0'); + if(*out < prev_out) return IntParsingResult::Overflow; + } else { + return IntParsingResult::Failure; + } + } + return IntParsingResult::Success; + } else if(base == 8) { + // 8-base 0o123 + if(f_startswith_2(text, "0o")) text.remove_prefix(2); + if(text.length() == 0) return IntParsingResult::Failure; + for(char c: text) { + if(c >= '0' && c <= '7') { + i64 prev_out = *out; + *out = (*out << 3) | (c - '0'); + if(*out < prev_out) return IntParsingResult::Overflow; + } else { + return IntParsingResult::Failure; + } + } + return IntParsingResult::Success; + } else if(base == 16) { + // 16-base 0x123 + if(f_startswith_2(text, "0x")) text.remove_prefix(2); + if(text.length() == 0) return IntParsingResult::Failure; + for(char c: text) { + i64 prev_out = *out; + if(c >= '0' && c <= '9') { + *out = (*out << 4) | (c - '0'); + if(*out < prev_out) return IntParsingResult::Overflow; + } else if(c >= 'a' && c <= 'f') { + *out = (*out << 4) | (c - 'a' + 10); + if(*out < prev_out) return IntParsingResult::Overflow; + } else if(c >= 'A' && c <= 'F') { + *out = (*out << 4) | (c - 'A' + 10); + if(*out < prev_out) return IntParsingResult::Overflow; + } else { + return IntParsingResult::Failure; + } + } + return IntParsingResult::Success; } - return IntParsingResult::Success; - }else if(base == 2){ - // 2-base 0b101010 - if(f_startswith_2(text, "0b")) text.remove_prefix(2); - if(text.length() == 0) return IntParsingResult::Failure; - for(char c : text){ - if(c == '0' || c == '1'){ - i64 prev_out = *out; - *out = (*out << 1) | (c - '0'); - if(*out < prev_out) return IntParsingResult::Overflow; - }else{ - return IntParsingResult::Failure; - } - } - return IntParsingResult::Success; - }else if(base == 8){ - // 8-base 0o123 - if(f_startswith_2(text, "0o")) text.remove_prefix(2); - if(text.length() == 0) return IntParsingResult::Failure; - for(char c : text){ - if(c >= '0' && c <= '7'){ - i64 prev_out = *out; - *out = (*out << 3) | (c - '0'); - if(*out < prev_out) return IntParsingResult::Overflow; - }else{ - return IntParsingResult::Failure; - } - } - return IntParsingResult::Success; - }else if(base == 16){ - // 16-base 0x123 - if(f_startswith_2(text, "0x")) text.remove_prefix(2); - if(text.length() == 0) return IntParsingResult::Failure; - for(char c : text){ - i64 prev_out = *out; - if(c >= '0' && c <= '9'){ - *out = (*out << 4) | (c - '0'); - if(*out < prev_out) return IntParsingResult::Overflow; - }else if(c >= 'a' && c <= 'f'){ - *out = (*out << 4) | (c - 'a' + 10); - if(*out < prev_out) return IntParsingResult::Overflow; - }else if(c >= 'A' && c <= 'F'){ - *out = (*out << 4) | (c - 'A' + 10); - if(*out < prev_out) return IntParsingResult::Overflow; - }else{ - return IntParsingResult::Failure; - } - } - return IntParsingResult::Success; - } - return IntParsingResult::Failure; + return IntParsingResult::Failure; } -} // namespace pkpy +} // namespace pkpy diff --git a/src/interpreter/ceval.cpp b/src/interpreter/ceval.cpp index 61f35f0c..344220ab 100644 --- a/src/interpreter/ceval.cpp +++ b/src/interpreter/ceval.cpp @@ -1,56 +1,60 @@ #include "pocketpy/interpreter/ceval.hpp" -namespace pkpy{ +namespace pkpy { -#define PREDICT_INT_OP(op) \ - if(is_int(_0) && is_int(_1)){ \ - TOP() = VAR(_0.as() op _1.as()); \ - DISPATCH() \ +#define PREDICT_INT_OP(op) \ + if(is_int(_0) && is_int(_1)) { \ + TOP() = VAR(_0.as() op _1.as()); \ + DISPATCH() \ } -#define PREDICT_INT_DIV_OP(op) \ - if(is_int(_0) && is_int(_1)){ \ - i64 divisor = _1.as(); \ - if(divisor == 0) ZeroDivisionError(); \ - TOP() = VAR(_0.as() op divisor); \ - DISPATCH() \ +#define PREDICT_INT_DIV_OP(op) \ + if(is_int(_0) && is_int(_1)) { \ + i64 divisor = _1.as(); \ + if(divisor == 0) ZeroDivisionError(); \ + TOP() = VAR(_0.as() op divisor); \ + DISPATCH() \ } -#define BINARY_F_COMPARE(func, op, rfunc) \ - PyVar ret; \ - const PyTypeInfo* _ti = _tp_info(_0); \ - if(_ti->m##func){ \ - ret = _ti->m##func(this, _0, _1); \ - }else{ \ - PyVar self; \ - PyVar _2 = get_unbound_method(_0, func, &self, false); \ - if(_2 != nullptr) ret = call_method(self, _2, _1); \ - else ret = NotImplemented; \ - } \ - if(is_not_implemented(ret)){ \ - PyVar self; \ - PyVar _2 = get_unbound_method(_1, rfunc, &self, false); \ - if(_2 != nullptr) ret = call_method(self, _2, _0); \ - else BinaryOptError(op, _0, _1); \ - if(is_not_implemented(ret)) BinaryOptError(op, _0, _1); \ - } +#define BINARY_F_COMPARE(func, op, rfunc) \ + PyVar ret; \ + const PyTypeInfo* _ti = _tp_info(_0); \ + if(_ti->m##func) { \ + ret = _ti->m##func(this, _0, _1); \ + } else { \ + PyVar self; \ + PyVar _2 = get_unbound_method(_0, func, &self, false); \ + if(_2 != nullptr) \ + ret = call_method(self, _2, _1); \ + else \ + ret = NotImplemented; \ + } \ + if(is_not_implemented(ret)) { \ + PyVar self; \ + PyVar _2 = get_unbound_method(_1, rfunc, &self, false); \ + if(_2 != nullptr) \ + ret = call_method(self, _2, _0); \ + else \ + BinaryOptError(op, _0, _1); \ + if(is_not_implemented(ret)) BinaryOptError(op, _0, _1); \ + } - -void VM::__op_unpack_sequence(uint16_t arg){ +void VM::__op_unpack_sequence(uint16_t arg) { PyVar _0 = POPX(); - if(is_type(_0, VM::tp_tuple)){ + if(is_type(_0, VM::tp_tuple)) { // fast path for tuple Tuple& tuple = PK_OBJ_GET(Tuple, _0); - if(tuple.size() == arg){ - for(PyVar obj: tuple) PUSH(obj); - }else{ + if(tuple.size() == arg) { + for(PyVar obj: tuple) + PUSH(obj); + } else { ValueError(_S("expected ", (int)arg, " values to unpack, got ", (int)tuple.size())); } - }else{ + } else { auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!! _0 = py_iter(_0); const PyTypeInfo* ti = _tp_info(_0); - for(int i=0; i", __lt__); return ret == True; } -bool VM::py_ge(PyVar _0, PyVar _1){ +bool VM::py_ge(PyVar _0, PyVar _1) { BINARY_F_COMPARE(__ge__, ">=", __le__); return ret == True; } @@ -82,988 +86,1093 @@ bool VM::py_ge(PyVar _0, PyVar _1){ #undef BINARY_F_COMPARE #if PK_ENABLE_PROFILER -#define CEVAL_STEP_CALLBACK() \ - if(_ceval_on_step) _ceval_on_step(this, frame, byte); \ - if(_profiler) _profiler->_step(callstack.size(), frame); \ +#define CEVAL_STEP_CALLBACK() \ + if(_ceval_on_step) _ceval_on_step(this, frame, byte); \ + if(_profiler) _profiler->_step(callstack.size(), frame); \ if(!_next_breakpoint.empty()) { _next_breakpoint._step(this); } #else -#define CEVAL_STEP_CALLBACK() \ - if(_ceval_on_step && _ceval_on_step){ \ - if(_ceval_on_step) \ - if(_ceval_on_step) _ceval_on_step(this, frame, byte); \ +#define CEVAL_STEP_CALLBACK() \ + if(_ceval_on_step && _ceval_on_step) { \ + if(_ceval_on_step) \ + if(_ceval_on_step) _ceval_on_step(this, frame, byte); \ } #endif -#define DISPATCH() { frame->_ip++; goto __NEXT_STEP; } -#define DISPATCH_JUMP(__offset) { frame->_ip+=__offset; goto __NEXT_STEP; } -#define DISPATCH_JUMP_ABSOLUTE(__target) { frame->_ip=&frame->co->codes[__target]; goto __NEXT_STEP; } +#define DISPATCH() \ + { \ + frame->_ip++; \ + goto __NEXT_STEP; \ + } +#define DISPATCH_JUMP(__offset) \ + { \ + frame->_ip += __offset; \ + goto __NEXT_STEP; \ + } +#define DISPATCH_JUMP_ABSOLUTE(__target) \ + { \ + frame->_ip = &frame->co->codes[__target]; \ + goto __NEXT_STEP; \ + } -PyVar VM::__run_top_frame(){ +PyVar VM::__run_top_frame() { Frame* frame = &callstack.top(); const Frame* base_frame = frame; InternalException __internal_exception; - while(true){ - try{ -/**********************************************************************/ -{ -__NEXT_FRAME: - Bytecode byte; + while(true) { + try { + /**********************************************************************/ + { + __NEXT_FRAME: + Bytecode byte; - if(__internal_exception.type == InternalExceptionType::Null){ - // None - frame->_ip++; - }else if(__internal_exception.type == InternalExceptionType::Handled){ - // HandledException + continue - frame->_ip = &frame->co->codes[__internal_exception.arg]; - __internal_exception = {}; - }else{ - // UnhandledException + continue (need_raise = true) - // ToBeRaisedException + continue (need_raise = true) - __internal_exception = {}; - __raise_exc(); // no return - } + if(__internal_exception.type == InternalExceptionType::Null) { + // None + frame->_ip++; + } else if(__internal_exception.type == InternalExceptionType::Handled) { + // HandledException + continue + frame->_ip = &frame->co->codes[__internal_exception.arg]; + __internal_exception = {}; + } else { + // UnhandledException + continue (need_raise = true) + // ToBeRaisedException + continue (need_raise = true) + __internal_exception = {}; + __raise_exc(); // no return + } -__NEXT_STEP: - byte = *frame->_ip; - CEVAL_STEP_CALLBACK() + __NEXT_STEP: + byte = *frame->_ip; + CEVAL_STEP_CALLBACK() #if PK_DEBUG_CEVAL_STEP - __log_s_data(); + __log_s_data(); #endif - switch ((Opcode)byte.op) - { - case OP_NO_OP: DISPATCH() - /*****************************************/ - case OP_POP_TOP: POP(); DISPATCH() - case OP_DUP_TOP: PUSH(TOP()); DISPATCH() - case OP_DUP_TOP_TWO: - // [a, b] - PUSH(SECOND()); // [a, b, a] - PUSH(SECOND()); // [a, b, a, b] - DISPATCH() - case OP_ROT_TWO: std::swap(TOP(), SECOND()); DISPATCH() - case OP_ROT_THREE:{ - // [a, b, c] -> [c, a, b] - PyVar _0 = TOP(); - TOP() = SECOND(); - SECOND() = THIRD(); - THIRD() = _0; - } DISPATCH() - case OP_PRINT_EXPR: - if(TOP() != None) stdout_write(py_repr(TOP()) + "\n"); - POP(); - DISPATCH() - /*****************************************/ - case OP_LOAD_CONST: - PUSH(frame->co->consts[byte.arg]); - DISPATCH() - case OP_LOAD_NONE: PUSH(None); DISPATCH() - case OP_LOAD_TRUE: PUSH(True); DISPATCH() - case OP_LOAD_FALSE: PUSH(False); DISPATCH() - /*****************************************/ - case OP_LOAD_SMALL_INT: - s_data.emplace(tp_int, (i64)(int16_t)byte.arg); - DISPATCH() - /*****************************************/ - case OP_LOAD_ELLIPSIS: PUSH(Ellipsis); DISPATCH() - case OP_LOAD_FUNCTION: { - const FuncDecl_& decl = frame->co->func_decls[byte.arg]; - PyVar obj; - if(decl->nested){ - NameDict_ captured = frame->_locals.to_namedict(); - obj = VAR(Function(decl, frame->_module, nullptr, captured)); - captured->set(decl->code->name, obj); - }else{ - obj = VAR(Function(decl, frame->_module, nullptr, nullptr)); - } - PUSH(obj); - } DISPATCH() - case OP_LOAD_NULL: PUSH(PY_NULL); DISPATCH() - /*****************************************/ - case OP_LOAD_FAST: { - PyVar _0 = frame->_locals[byte.arg]; - if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); - PUSH(_0); - } DISPATCH() - case OP_LOAD_NAME: { - StrName _name(byte.arg); - PyVar* slot = frame->_locals.try_get_name(_name); - if(slot != nullptr) { - if(*slot == PY_NULL) vm->UnboundLocalError(_name); - PUSH(*slot); - DISPATCH() - } - PyVar* _0 = frame->f_closure_try_get(_name); - if(_0 != nullptr) { PUSH(*_0); DISPATCH() } - _0 = frame->f_globals().try_get_2_likely_found(_name); - if(_0 != nullptr) { PUSH(*_0); DISPATCH() } - _0 = vm->builtins->attr().try_get_2_likely_found(_name); - if(_0 != nullptr) { PUSH(*_0); DISPATCH() } - vm->NameError(_name); - } DISPATCH() - case OP_LOAD_NONLOCAL: { - StrName _name(byte.arg); - PyVar* _0 = frame->f_closure_try_get(_name); - if(_0 != nullptr) { PUSH(*_0); DISPATCH() } - _0 = frame->f_globals().try_get_2_likely_found(_name); - if(_0 != nullptr) { PUSH(*_0); DISPATCH() } - _0 = vm->builtins->attr().try_get_2_likely_found(_name); - if(_0 != nullptr) { PUSH(*_0); DISPATCH() } - vm->NameError(_name); - } DISPATCH() - case OP_LOAD_GLOBAL:{ - StrName _name(byte.arg); - PyVar _0 = frame->f_globals().try_get_likely_found(_name); - if(_0 != nullptr) { PUSH(_0); DISPATCH() } - _0 = vm->builtins->attr().try_get_likely_found(_name); - if(_0 != nullptr) { PUSH(_0); DISPATCH() } - vm->NameError(_name); - } DISPATCH() - case OP_LOAD_ATTR:{ - TOP() = getattr(TOP(), StrName(byte.arg)); - } DISPATCH() - case OP_LOAD_CLASS_GLOBAL:{ - assert(__curr_class != nullptr); - StrName _name(byte.arg); - PyVar _0 = getattr(__curr_class, _name, false); - if(_0 != nullptr) { PUSH(_0); DISPATCH() } - // load global if attribute not found - _0 = frame->f_globals().try_get_likely_found(_name); - if(_0 != nullptr) { PUSH(_0); DISPATCH() } - _0 = vm->builtins->attr().try_get_likely_found(_name); - if(_0 != nullptr) { PUSH(_0); DISPATCH() } - vm->NameError(_name); - } DISPATCH() - case OP_LOAD_METHOD:{ - PyVar _0; - TOP() = get_unbound_method(TOP(), StrName(byte.arg), &_0, true, true); - PUSH(_0); - }DISPATCH() - case OP_LOAD_SUBSCR:{ - PyVar _1 = POPX(); // b - PyVar _0 = TOP(); // a - auto _ti = _tp_info(_0); - if(_ti->m__getitem__){ - TOP() = _ti->m__getitem__(this, _0, _1); - }else{ - TOP() = call_method(_0, __getitem__, _1); - } - } DISPATCH() - case OP_LOAD_SUBSCR_FAST:{ - PyVar _1 = frame->_locals[byte.arg]; - if(_1 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); - PyVar _0 = TOP(); // a - auto _ti = _tp_info(_0); - if(_ti->m__getitem__){ - TOP() = _ti->m__getitem__(this, _0, _1); - }else{ - TOP() = call_method(_0, __getitem__, _1); - } - } DISPATCH() - case OP_LOAD_SUBSCR_SMALL_INT:{ - PyVar _1 = VAR((int16_t)byte.arg); - PyVar _0 = TOP(); // a - auto _ti = _tp_info(_0); - if(_ti->m__getitem__){ - TOP() = _ti->m__getitem__(this, _0, _1); - }else{ - TOP() = call_method(_0, __getitem__, _1); - } - } DISPATCH() - case OP_STORE_FAST: - frame->_locals[byte.arg] = POPX(); - DISPATCH() - case OP_STORE_NAME:{ - StrName _name(byte.arg); - PyVar _0 = POPX(); - if(frame->_callable != nullptr){ - PyVar* slot = frame->_locals.try_get_name(_name); - if(slot != nullptr){ - *slot = _0; // store in locals if possible - }else{ - Function& func = frame->_callable->as(); - if(func.decl == __dynamic_func_decl){ - assert(func._closure != nullptr); - func._closure->set(_name, _0); - }else{ - vm->NameError(_name); - } - } - }else{ - frame->f_globals().set(_name, _0); - } - } DISPATCH() - case OP_STORE_GLOBAL: - frame->f_globals().set(StrName(byte.arg), POPX()); - DISPATCH() - case OP_STORE_ATTR: { - PyVar _0 = TOP(); // a - PyVar _1 = SECOND(); // val - setattr(_0, StrName(byte.arg), _1); - STACK_SHRINK(2); - } DISPATCH() - case OP_STORE_SUBSCR:{ - PyVar _2 = POPX(); // b - PyVar _1 = POPX(); // a - PyVar _0 = POPX(); // val - auto _ti = _tp_info(_1); - if(_ti->m__setitem__){ - _ti->m__setitem__(this, _1, _2, _0); - }else{ - call_method(_1, __setitem__, _2, _0); - } - }DISPATCH() - case OP_STORE_SUBSCR_FAST:{ - PyVar _2 = frame->_locals[byte.arg]; // b - if(_2 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); - PyVar _1 = POPX(); // a - PyVar _0 = POPX(); // val - auto _ti = _tp_info(_1); - if(_ti->m__setitem__){ - _ti->m__setitem__(this, _1, _2, _0); - }else{ - call_method(_1, __setitem__, _2, _0); - } - }DISPATCH() - case OP_DELETE_FAST:{ - PyVar _0 = frame->_locals[byte.arg]; - if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); - frame->_locals[byte.arg].set_null(); - }DISPATCH() - case OP_DELETE_NAME:{ - StrName _name(byte.arg); - if(frame->_callable != nullptr){ - PyVar* slot = frame->_locals.try_get_name(_name); - if(slot != nullptr){ - slot->set_null(); - }else{ - Function& func = frame->_callable->as(); - if(func.decl == __dynamic_func_decl){ - assert(func._closure != nullptr); - bool ok = func._closure->del(_name); - if(!ok) vm->NameError(_name); - }else{ - vm->NameError(_name); - } - } - }else{ - if(!frame->f_globals().del(_name)) vm->NameError(_name); - } - } DISPATCH() - case OP_DELETE_GLOBAL:{ - StrName _name(byte.arg); - if(!frame->f_globals().del(_name)) vm->NameError(_name); - }DISPATCH() - case OP_DELETE_ATTR:{ - PyVar _0 = POPX(); - delattr(_0, StrName(byte.arg)); - } DISPATCH() - case OP_DELETE_SUBSCR:{ - PyVar _1 = POPX(); - PyVar _0 = POPX(); - auto _ti = _tp_info(_0); - if(_ti->m__delitem__){ - _ti->m__delitem__(this, _0, _1); - }else{ - call_method(_0, __delitem__, _1); - } - }DISPATCH() - /*****************************************/ - case OP_BUILD_LONG: { - PyVar _0 = builtins->attr().try_get_likely_found(pk_id_long); - if(_0 == nullptr) AttributeError(builtins, pk_id_long); - TOP() = call(_0, TOP()); - } DISPATCH() - case OP_BUILD_IMAG: { - PyVar _0 = builtins->attr().try_get_likely_found(pk_id_complex); - if(_0 == nullptr) AttributeError(builtins, pk_id_long); - TOP() = call(_0, VAR(0), TOP()); - } DISPATCH() - case OP_BUILD_BYTES: { - const Str& s = CAST(Str&, TOP()); - unsigned char* p = (unsigned char*)std::malloc(s.size); - std::memcpy(p, s.data, s.size); - TOP() = VAR(Bytes(p, s.size)); - } DISPATCH() - case OP_BUILD_TUPLE:{ - PyVar _0 = VAR(STACK_VIEW(byte.arg).to_tuple()); - STACK_SHRINK(byte.arg); - PUSH(_0); - } DISPATCH() - case OP_BUILD_LIST:{ - PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); - STACK_SHRINK(byte.arg); - PUSH(_0); - } DISPATCH() - case OP_BUILD_DICT:{ - if(byte.arg == 0){ - PUSH(VAR(Dict())); - DISPATCH() - } - PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); - _0 = call(_t(tp_dict), _0); - STACK_SHRINK(byte.arg); - PUSH(_0); - } DISPATCH() - case OP_BUILD_SET:{ - PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); - _0 = call(builtins->attr(pk_id_set), _0); - STACK_SHRINK(byte.arg); - PUSH(_0); - } DISPATCH() - case OP_BUILD_SLICE:{ - PyVar _2 = POPX(); // step - PyVar _1 = POPX(); // stop - PyVar _0 = POPX(); // start - PUSH(VAR(Slice(_0, _1, _2))); - } DISPATCH() - case OP_BUILD_STRING: { - SStream ss; - ArgsView view = STACK_VIEW(byte.arg); - for(PyVar obj : view) ss << py_str(obj); - STACK_SHRINK(byte.arg); - PUSH(VAR(ss.str())); - } DISPATCH() - /*****************************************/ - case OP_BUILD_TUPLE_UNPACK: { - List list; - __unpack_as_list(STACK_VIEW(byte.arg), list); - STACK_SHRINK(byte.arg); - PyVar _0 = VAR(list.to_tuple()); - PUSH(_0); - } DISPATCH() - case OP_BUILD_LIST_UNPACK: { - List list; - __unpack_as_list(STACK_VIEW(byte.arg), list); - STACK_SHRINK(byte.arg); - PyVar _0 = VAR(std::move(list)); - PUSH(_0); - } DISPATCH() - case OP_BUILD_DICT_UNPACK: { - Dict dict; - __unpack_as_dict(STACK_VIEW(byte.arg), dict); - STACK_SHRINK(byte.arg); - PyVar _0 = VAR(std::move(dict)); - PUSH(_0); - } DISPATCH() - case OP_BUILD_SET_UNPACK: { - List list; - __unpack_as_list(STACK_VIEW(byte.arg), list); - STACK_SHRINK(byte.arg); - PyVar _0 = VAR(std::move(list)); - _0 = call(builtins->attr(pk_id_set), _0); - PUSH(_0); - } DISPATCH() - /*****************************************/ -#define BINARY_OP_SPECIAL(func) \ - _ti = _tp_info(_0); \ - if(_ti->m##func){ \ - TOP() = _ti->m##func(this, _0, _1); \ - }else{ \ - PyVar self; \ - PyVar _2 = get_unbound_method(_0, func, &self, false); \ - if(_2 != nullptr) TOP() = call_method(self, _2, _1); \ - else TOP() = NotImplemented; \ - } + switch((Opcode)byte.op) { + case OP_NO_OP: DISPATCH() + /*****************************************/ + case OP_POP_TOP: POP(); DISPATCH() + case OP_DUP_TOP: PUSH(TOP()); DISPATCH() + case OP_DUP_TOP_TWO: + // [a, b] + PUSH(SECOND()); // [a, b, a] + PUSH(SECOND()); // [a, b, a, b] + DISPATCH() + case OP_ROT_TWO: std::swap(TOP(), SECOND()); DISPATCH() + case OP_ROT_THREE: { + // [a, b, c] -> [c, a, b] + PyVar _0 = TOP(); + TOP() = SECOND(); + SECOND() = THIRD(); + THIRD() = _0; + } + DISPATCH() + case OP_PRINT_EXPR: + if(TOP() != None) stdout_write(py_repr(TOP()) + "\n"); + POP(); + DISPATCH() + /*****************************************/ + case OP_LOAD_CONST: PUSH(frame->co->consts[byte.arg]); DISPATCH() + case OP_LOAD_NONE: PUSH(None); DISPATCH() + case OP_LOAD_TRUE: PUSH(True); DISPATCH() + case OP_LOAD_FALSE: PUSH(False); DISPATCH() + /*****************************************/ + case OP_LOAD_SMALL_INT: s_data.emplace(tp_int, (i64)(int16_t)byte.arg); DISPATCH() + /*****************************************/ + case OP_LOAD_ELLIPSIS: PUSH(Ellipsis); DISPATCH() + case OP_LOAD_FUNCTION: { + const FuncDecl_& decl = frame->co->func_decls[byte.arg]; + PyVar obj; + if(decl->nested) { + NameDict_ captured = frame->_locals.to_namedict(); + obj = VAR(Function(decl, frame->_module, nullptr, captured)); + captured->set(decl->code->name, obj); + } else { + obj = VAR(Function(decl, frame->_module, nullptr, nullptr)); + } + PUSH(obj); + } + DISPATCH() + case OP_LOAD_NULL: PUSH(PY_NULL); DISPATCH() + /*****************************************/ + case OP_LOAD_FAST: { + PyVar _0 = frame->_locals[byte.arg]; + if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); + PUSH(_0); + } + DISPATCH() + case OP_LOAD_NAME: { + StrName _name(byte.arg); + PyVar* slot = frame->_locals.try_get_name(_name); + if(slot != nullptr) { + if(*slot == PY_NULL) vm->UnboundLocalError(_name); + PUSH(*slot); + DISPATCH() + } + PyVar* _0 = frame->f_closure_try_get(_name); + if(_0 != nullptr) { + PUSH(*_0); + DISPATCH() + } + _0 = frame->f_globals().try_get_2_likely_found(_name); + if(_0 != nullptr) { + PUSH(*_0); + DISPATCH() + } + _0 = vm->builtins->attr().try_get_2_likely_found(_name); + if(_0 != nullptr) { + PUSH(*_0); + DISPATCH() + } + vm->NameError(_name); + } + DISPATCH() + case OP_LOAD_NONLOCAL: { + StrName _name(byte.arg); + PyVar* _0 = frame->f_closure_try_get(_name); + if(_0 != nullptr) { + PUSH(*_0); + DISPATCH() + } + _0 = frame->f_globals().try_get_2_likely_found(_name); + if(_0 != nullptr) { + PUSH(*_0); + DISPATCH() + } + _0 = vm->builtins->attr().try_get_2_likely_found(_name); + if(_0 != nullptr) { + PUSH(*_0); + DISPATCH() + } + vm->NameError(_name); + } + DISPATCH() + case OP_LOAD_GLOBAL: { + StrName _name(byte.arg); + PyVar _0 = frame->f_globals().try_get_likely_found(_name); + if(_0 != nullptr) { + PUSH(_0); + DISPATCH() + } + _0 = vm->builtins->attr().try_get_likely_found(_name); + if(_0 != nullptr) { + PUSH(_0); + DISPATCH() + } + vm->NameError(_name); + } + DISPATCH() + case OP_LOAD_ATTR: { + TOP() = getattr(TOP(), StrName(byte.arg)); + } + DISPATCH() + case OP_LOAD_CLASS_GLOBAL: { + assert(__curr_class != nullptr); + StrName _name(byte.arg); + PyVar _0 = getattr(__curr_class, _name, false); + if(_0 != nullptr) { + PUSH(_0); + DISPATCH() + } + // load global if attribute not found + _0 = frame->f_globals().try_get_likely_found(_name); + if(_0 != nullptr) { + PUSH(_0); + DISPATCH() + } + _0 = vm->builtins->attr().try_get_likely_found(_name); + if(_0 != nullptr) { + PUSH(_0); + DISPATCH() + } + vm->NameError(_name); + } + DISPATCH() + case OP_LOAD_METHOD: { + PyVar _0; + TOP() = get_unbound_method(TOP(), StrName(byte.arg), &_0, true, true); + PUSH(_0); + } + DISPATCH() + case OP_LOAD_SUBSCR: { + PyVar _1 = POPX(); // b + PyVar _0 = TOP(); // a + auto _ti = _tp_info(_0); + if(_ti->m__getitem__) { + TOP() = _ti->m__getitem__(this, _0, _1); + } else { + TOP() = call_method(_0, __getitem__, _1); + } + } + DISPATCH() + case OP_LOAD_SUBSCR_FAST: { + PyVar _1 = frame->_locals[byte.arg]; + if(_1 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); + PyVar _0 = TOP(); // a + auto _ti = _tp_info(_0); + if(_ti->m__getitem__) { + TOP() = _ti->m__getitem__(this, _0, _1); + } else { + TOP() = call_method(_0, __getitem__, _1); + } + } + DISPATCH() + case OP_LOAD_SUBSCR_SMALL_INT: { + PyVar _1 = VAR((int16_t)byte.arg); + PyVar _0 = TOP(); // a + auto _ti = _tp_info(_0); + if(_ti->m__getitem__) { + TOP() = _ti->m__getitem__(this, _0, _1); + } else { + TOP() = call_method(_0, __getitem__, _1); + } + } + DISPATCH() + case OP_STORE_FAST: frame->_locals[byte.arg] = POPX(); DISPATCH() + case OP_STORE_NAME: { + StrName _name(byte.arg); + PyVar _0 = POPX(); + if(frame->_callable != nullptr) { + PyVar* slot = frame->_locals.try_get_name(_name); + if(slot != nullptr) { + *slot = _0; // store in locals if possible + } else { + Function& func = frame->_callable->as(); + if(func.decl == __dynamic_func_decl) { + assert(func._closure != nullptr); + func._closure->set(_name, _0); + } else { + vm->NameError(_name); + } + } + } else { + frame->f_globals().set(_name, _0); + } + } + DISPATCH() + case OP_STORE_GLOBAL: frame->f_globals().set(StrName(byte.arg), POPX()); DISPATCH() + case OP_STORE_ATTR: { + PyVar _0 = TOP(); // a + PyVar _1 = SECOND(); // val + setattr(_0, StrName(byte.arg), _1); + STACK_SHRINK(2); + } + DISPATCH() + case OP_STORE_SUBSCR: { + PyVar _2 = POPX(); // b + PyVar _1 = POPX(); // a + PyVar _0 = POPX(); // val + auto _ti = _tp_info(_1); + if(_ti->m__setitem__) { + _ti->m__setitem__(this, _1, _2, _0); + } else { + call_method(_1, __setitem__, _2, _0); + } + } + DISPATCH() + case OP_STORE_SUBSCR_FAST: { + PyVar _2 = frame->_locals[byte.arg]; // b + if(_2 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); + PyVar _1 = POPX(); // a + PyVar _0 = POPX(); // val + auto _ti = _tp_info(_1); + if(_ti->m__setitem__) { + _ti->m__setitem__(this, _1, _2, _0); + } else { + call_method(_1, __setitem__, _2, _0); + } + } + DISPATCH() + case OP_DELETE_FAST: { + PyVar _0 = frame->_locals[byte.arg]; + if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); + frame->_locals[byte.arg].set_null(); + } + DISPATCH() + case OP_DELETE_NAME: { + StrName _name(byte.arg); + if(frame->_callable != nullptr) { + PyVar* slot = frame->_locals.try_get_name(_name); + if(slot != nullptr) { + slot->set_null(); + } else { + Function& func = frame->_callable->as(); + if(func.decl == __dynamic_func_decl) { + assert(func._closure != nullptr); + bool ok = func._closure->del(_name); + if(!ok) vm->NameError(_name); + } else { + vm->NameError(_name); + } + } + } else { + if(!frame->f_globals().del(_name)) vm->NameError(_name); + } + } + DISPATCH() + case OP_DELETE_GLOBAL: { + StrName _name(byte.arg); + if(!frame->f_globals().del(_name)) vm->NameError(_name); + } + DISPATCH() + case OP_DELETE_ATTR: { + PyVar _0 = POPX(); + delattr(_0, StrName(byte.arg)); + } + DISPATCH() + case OP_DELETE_SUBSCR: { + PyVar _1 = POPX(); + PyVar _0 = POPX(); + auto _ti = _tp_info(_0); + if(_ti->m__delitem__) { + _ti->m__delitem__(this, _0, _1); + } else { + call_method(_0, __delitem__, _1); + } + } + DISPATCH() + /*****************************************/ + case OP_BUILD_LONG: { + PyVar _0 = builtins->attr().try_get_likely_found(pk_id_long); + if(_0 == nullptr) AttributeError(builtins, pk_id_long); + TOP() = call(_0, TOP()); + } + DISPATCH() + case OP_BUILD_IMAG: { + PyVar _0 = builtins->attr().try_get_likely_found(pk_id_complex); + if(_0 == nullptr) AttributeError(builtins, pk_id_long); + TOP() = call(_0, VAR(0), TOP()); + } + DISPATCH() + case OP_BUILD_BYTES: { + const Str& s = CAST(Str&, TOP()); + unsigned char* p = (unsigned char*)std::malloc(s.size); + std::memcpy(p, s.data, s.size); + TOP() = VAR(Bytes(p, s.size)); + } + DISPATCH() + case OP_BUILD_TUPLE: { + PyVar _0 = VAR(STACK_VIEW(byte.arg).to_tuple()); + STACK_SHRINK(byte.arg); + PUSH(_0); + } + DISPATCH() + case OP_BUILD_LIST: { + PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); + STACK_SHRINK(byte.arg); + PUSH(_0); + } + DISPATCH() + case OP_BUILD_DICT: { + if(byte.arg == 0) { + PUSH(VAR(Dict())); + DISPATCH() + } + PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); + _0 = call(_t(tp_dict), _0); + STACK_SHRINK(byte.arg); + PUSH(_0); + } + DISPATCH() + case OP_BUILD_SET: { + PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); + _0 = call(builtins->attr(pk_id_set), _0); + STACK_SHRINK(byte.arg); + PUSH(_0); + } + DISPATCH() + case OP_BUILD_SLICE: { + PyVar _2 = POPX(); // step + PyVar _1 = POPX(); // stop + PyVar _0 = POPX(); // start + PUSH(VAR(Slice(_0, _1, _2))); + } + DISPATCH() + case OP_BUILD_STRING: { + SStream ss; + ArgsView view = STACK_VIEW(byte.arg); + for(PyVar obj: view) + ss << py_str(obj); + STACK_SHRINK(byte.arg); + PUSH(VAR(ss.str())); + } + DISPATCH() + /*****************************************/ + case OP_BUILD_TUPLE_UNPACK: { + List list; + __unpack_as_list(STACK_VIEW(byte.arg), list); + STACK_SHRINK(byte.arg); + PyVar _0 = VAR(list.to_tuple()); + PUSH(_0); + } + DISPATCH() + case OP_BUILD_LIST_UNPACK: { + List list; + __unpack_as_list(STACK_VIEW(byte.arg), list); + STACK_SHRINK(byte.arg); + PyVar _0 = VAR(std::move(list)); + PUSH(_0); + } + DISPATCH() + case OP_BUILD_DICT_UNPACK: { + Dict dict; + __unpack_as_dict(STACK_VIEW(byte.arg), dict); + STACK_SHRINK(byte.arg); + PyVar _0 = VAR(std::move(dict)); + PUSH(_0); + } + DISPATCH() + case OP_BUILD_SET_UNPACK: { + List list; + __unpack_as_list(STACK_VIEW(byte.arg), list); + STACK_SHRINK(byte.arg); + PyVar _0 = VAR(std::move(list)); + _0 = call(builtins->attr(pk_id_set), _0); + PUSH(_0); + } + DISPATCH() + /*****************************************/ +#define BINARY_OP_SPECIAL(func) \ + _ti = _tp_info(_0); \ + if(_ti->m##func) { \ + TOP() = _ti->m##func(this, _0, _1); \ + } else { \ + PyVar self; \ + PyVar _2 = get_unbound_method(_0, func, &self, false); \ + if(_2 != nullptr) \ + TOP() = call_method(self, _2, _1); \ + else \ + TOP() = NotImplemented; \ + } -#define BINARY_OP_RSPECIAL(op, func) \ - if(is_not_implemented(TOP())){ \ - PyVar self; \ - PyVar _2 = get_unbound_method(_1, func, &self, false); \ - if(_2 != nullptr) TOP() = call_method(self, _2, _0); \ - else BinaryOptError(op, _0, _1); \ - if(is_not_implemented(TOP())) BinaryOptError(op, _0, _1); \ - } +#define BINARY_OP_RSPECIAL(op, func) \ + if(is_not_implemented(TOP())) { \ + PyVar self; \ + PyVar _2 = get_unbound_method(_1, func, &self, false); \ + if(_2 != nullptr) \ + TOP() = call_method(self, _2, _0); \ + else \ + BinaryOptError(op, _0, _1); \ + if(is_not_implemented(TOP())) BinaryOptError(op, _0, _1); \ + } - case OP_BINARY_TRUEDIV:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__truediv__); - if(is_not_implemented(TOP())) BinaryOptError("/", _0, _1); - } DISPATCH() - case OP_BINARY_POW:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__pow__); - if(is_not_implemented(TOP())) BinaryOptError("**", _0, _1); - } DISPATCH() - case OP_BINARY_ADD:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(+) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__add__); - BINARY_OP_RSPECIAL("+", __radd__); - } DISPATCH() - case OP_BINARY_SUB:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(-) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__sub__); - BINARY_OP_RSPECIAL("-", __rsub__); - } DISPATCH() - case OP_BINARY_MUL:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(*) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__mul__); - BINARY_OP_RSPECIAL("*", __rmul__); - } DISPATCH() - case OP_BINARY_FLOORDIV:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_DIV_OP(/) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__floordiv__); - if(is_not_implemented(TOP())) BinaryOptError("//", _0, _1); - } DISPATCH() - case OP_BINARY_MOD:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_DIV_OP(%) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__mod__); - if(is_not_implemented(TOP())) BinaryOptError("%", _0, _1); - } DISPATCH() - case OP_COMPARE_LT:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(<) - TOP() = VAR(py_lt(_0, _1)); - } DISPATCH() - case OP_COMPARE_LE:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(<=) - TOP() = VAR(py_le(_0, _1)); - } DISPATCH() - case OP_COMPARE_EQ:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - TOP() = VAR(py_eq(_0, _1)); - } DISPATCH() - case OP_COMPARE_NE:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - TOP() = VAR(py_ne(_0, _1)); - } DISPATCH() - case OP_COMPARE_GT:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(>) - TOP() = VAR(py_gt(_0, _1)); - } DISPATCH() - case OP_COMPARE_GE:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(>=) - TOP() = VAR(py_ge(_0, _1)); - } DISPATCH() - case OP_BITWISE_LSHIFT:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(<<) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__lshift__); - if(is_not_implemented(TOP())) BinaryOptError("<<", _0, _1); - } DISPATCH() - case OP_BITWISE_RSHIFT:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(>>) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__rshift__); - if(is_not_implemented(TOP())) BinaryOptError(">>", _0, _1); - } DISPATCH() - case OP_BITWISE_AND:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(&) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__and__); - if(is_not_implemented(TOP())) BinaryOptError("&", _0, _1); - } DISPATCH() - case OP_BITWISE_OR:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(|) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__or__); - if(is_not_implemented(TOP())) BinaryOptError("|", _0, _1); - } DISPATCH() - case OP_BITWISE_XOR:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - PREDICT_INT_OP(^) - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__xor__); - if(is_not_implemented(TOP())) BinaryOptError("^", _0, _1); - } DISPATCH() - case OP_BINARY_MATMUL:{ - PyVar _1 = POPX(); - PyVar _0 = TOP(); - const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__matmul__); - if(is_not_implemented(TOP())) BinaryOptError("@", _0, _1); - } DISPATCH() + case OP_BINARY_TRUEDIV: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__truediv__); + if(is_not_implemented(TOP())) BinaryOptError("/", _0, _1); + } + DISPATCH() + case OP_BINARY_POW: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__pow__); + if(is_not_implemented(TOP())) BinaryOptError("**", _0, _1); + } + DISPATCH() + case OP_BINARY_ADD: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(+) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__add__); + BINARY_OP_RSPECIAL("+", __radd__); + } + DISPATCH() + case OP_BINARY_SUB: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(-) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__sub__); + BINARY_OP_RSPECIAL("-", __rsub__); + } + DISPATCH() + case OP_BINARY_MUL: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(*) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__mul__); + BINARY_OP_RSPECIAL("*", __rmul__); + } + DISPATCH() + case OP_BINARY_FLOORDIV: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_DIV_OP(/) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__floordiv__); + if(is_not_implemented(TOP())) BinaryOptError("//", _0, _1); + } + DISPATCH() + case OP_BINARY_MOD: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_DIV_OP(%) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__mod__); + if(is_not_implemented(TOP())) BinaryOptError("%", _0, _1); + } + DISPATCH() + case OP_COMPARE_LT: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(<) + TOP() = VAR(py_lt(_0, _1)); + } + DISPATCH() + case OP_COMPARE_LE: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(<=) + TOP() = VAR(py_le(_0, _1)); + } + DISPATCH() + case OP_COMPARE_EQ: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + TOP() = VAR(py_eq(_0, _1)); + } + DISPATCH() + case OP_COMPARE_NE: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + TOP() = VAR(py_ne(_0, _1)); + } + DISPATCH() + case OP_COMPARE_GT: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(>) + TOP() = VAR(py_gt(_0, _1)); + } + DISPATCH() + case OP_COMPARE_GE: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(>=) + TOP() = VAR(py_ge(_0, _1)); + } + DISPATCH() + case OP_BITWISE_LSHIFT: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(<<) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__lshift__); + if(is_not_implemented(TOP())) BinaryOptError("<<", _0, _1); + } + DISPATCH() + case OP_BITWISE_RSHIFT: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(>>) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__rshift__); + if(is_not_implemented(TOP())) BinaryOptError(">>", _0, _1); + } + DISPATCH() + case OP_BITWISE_AND: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(&) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__and__); + if(is_not_implemented(TOP())) BinaryOptError("&", _0, _1); + } + DISPATCH() + case OP_BITWISE_OR: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(|) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__or__); + if(is_not_implemented(TOP())) BinaryOptError("|", _0, _1); + } + DISPATCH() + case OP_BITWISE_XOR: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + PREDICT_INT_OP(^) + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__xor__); + if(is_not_implemented(TOP())) BinaryOptError("^", _0, _1); + } + DISPATCH() + case OP_BINARY_MATMUL: { + PyVar _1 = POPX(); + PyVar _0 = TOP(); + const PyTypeInfo* _ti; + BINARY_OP_SPECIAL(__matmul__); + if(is_not_implemented(TOP())) BinaryOptError("@", _0, _1); + } + DISPATCH() #undef BINARY_OP_SPECIAL #undef BINARY_OP_RSPECIAL #undef PREDICT_INT_OP - case OP_IS_OP:{ - PyVar _1 = POPX(); // rhs - PyVar _0 = TOP(); // lhs - TOP() = _0 == _1 ? True : False; - } DISPATCH() - case OP_IS_NOT_OP:{ - PyVar _1 = POPX(); // rhs - PyVar _0 = TOP(); // lhs - TOP() = _0 != _1 ? True : False; - } DISPATCH() - case OP_CONTAINS_OP:{ - // a in b -> b __contains__ a - auto _ti = _tp_info(TOP()); - PyVar _0; - if(_ti->m__contains__){ - _0 = _ti->m__contains__(this, TOP(), SECOND()); - }else{ - _0 = call_method(TOP(), __contains__, SECOND()); - } - POP(); - TOP() = VAR(static_cast((int)CAST(bool, _0) ^ byte.arg)); - } DISPATCH() - /*****************************************/ - case OP_JUMP_FORWARD: - DISPATCH_JUMP((int16_t)byte.arg) - case OP_POP_JUMP_IF_FALSE: - if(!py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg) - DISPATCH() - case OP_POP_JUMP_IF_TRUE: - if(py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg) - DISPATCH() - case OP_JUMP_IF_TRUE_OR_POP: - if(py_bool(TOP())){ - DISPATCH_JUMP((int16_t)byte.arg) - }else{ - POP(); - DISPATCH() - } - case OP_JUMP_IF_FALSE_OR_POP: - if(!py_bool(TOP())){ - DISPATCH_JUMP((int16_t)byte.arg) - }else{ - POP(); - DISPATCH() - } - case OP_SHORTCUT_IF_FALSE_OR_POP: - if(!py_bool(TOP())){ // [b, False] - STACK_SHRINK(2); // [] - PUSH(vm->False); // [False] - DISPATCH_JUMP((int16_t)byte.arg) - } else{ - POP(); // [b] - DISPATCH() - } - case OP_LOOP_CONTINUE: - // just an alias of OP_JUMP_FORWARD - DISPATCH_JUMP((int16_t)byte.arg) - case OP_LOOP_BREAK: { - frame->prepare_jump_break(&s_data, frame->ip()+byte.arg); - DISPATCH_JUMP((int16_t)byte.arg) - } - case OP_JUMP_ABSOLUTE_TOP: - DISPATCH_JUMP_ABSOLUTE(_CAST(int, POPX())) - case OP_GOTO: { - StrName _name(byte.arg); - int target = frame->co->labels.try_get_likely_found(_name); - if(target < 0) RuntimeError(_S("label ", _name.escape(), " not found")); - frame->prepare_jump_break(&s_data, target); - DISPATCH_JUMP_ABSOLUTE(target) - } - /*****************************************/ - case OP_FSTRING_EVAL:{ - PyVar _0 = frame->co->consts[byte.arg]; - std::string_view string = CAST(Str&, _0).sv(); - auto it = __cached_codes.find(string); - CodeObject_ code; - if(it == __cached_codes.end()){ - code = vm->compile(string, "", EVAL_MODE, true); - __cached_codes[string] = code; - }else{ - code = it->second; - } - _0 = vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals); - PUSH(_0); - } DISPATCH() - case OP_REPR: - TOP() = VAR(py_repr(TOP())); - DISPATCH() - case OP_CALL:{ - if(heap._should_auto_collect()) heap._auto_collect(); - PyVar _0 = vectorcall( - byte.arg & 0xFF, // ARGC - (byte.arg>>8) & 0xFF, // KWARGC - true - ); - if(_0 == PY_OP_CALL){ - frame = &callstack.top(); - goto __NEXT_FRAME; - } - PUSH(_0); - } DISPATCH() - case OP_CALL_TP:{ - if(heap._should_auto_collect()) heap._auto_collect(); - PyVar _0; - PyVar _1; - PyVar _2; - // [callable, , args: tuple, kwargs: dict | NULL] - if(byte.arg){ - _2 = POPX(); - _1 = POPX(); - for(PyVar obj: _CAST(Tuple&, _1)) PUSH(obj); - _CAST(Dict&, _2).apply([this](PyVar k, PyVar v){ - PUSH(VAR(StrName(CAST(Str&, k)).index)); - PUSH(v); - }); - _0 = vectorcall( - _CAST(Tuple&, _1).size(), // ARGC - _CAST(Dict&, _2).size(), // KWARGC - true - ); - }else{ - // no **kwargs - _1 = POPX(); - for(PyVar obj: _CAST(Tuple&, _1)) PUSH(obj); - _0 = vectorcall( - _CAST(Tuple&, _1).size(), // ARGC - 0, // KWARGC - true - ); - } - if(_0 == PY_OP_CALL){ - frame = &callstack.top(); - goto __NEXT_FRAME; - } - PUSH(_0); - } DISPATCH() - case OP_RETURN_VALUE:{ - PyVar _0 = byte.arg == BC_NOARG ? POPX() : None; - __pop_frame(); - if(frame == base_frame){ // [ frameBase<- ] - return _0; - }else{ - frame = &callstack.top(); - PUSH(_0); - goto __NEXT_FRAME; - } - } DISPATCH() - case OP_YIELD_VALUE: return PY_OP_YIELD; - /*****************************************/ - case OP_LIST_APPEND:{ - PyVar _0 = POPX(); - PK_OBJ_GET(List, SECOND()).push_back(_0); - } DISPATCH() - case OP_DICT_ADD: { - PyVar _0 = POPX(); - const Tuple& t = PK_OBJ_GET(Tuple, _0); - PK_OBJ_GET(Dict, SECOND()).set(this, t[0], t[1]); - } DISPATCH() - case OP_SET_ADD:{ - PyVar _0 = POPX(); - call_method(SECOND(), pk_id_add, _0); - } DISPATCH() - /*****************************************/ - case OP_UNARY_NEGATIVE: - TOP() = py_negate(TOP()); - DISPATCH() - case OP_UNARY_NOT:{ - PyVar _0 = TOP(); - if(_0==True) TOP()=False; - else if(_0==False) TOP()=True; - else TOP() = VAR(!py_bool(_0)); - } DISPATCH() - case OP_UNARY_STAR: - TOP() = VAR(StarWrapper(byte.arg, TOP())); - DISPATCH() - case OP_UNARY_INVERT:{ - PyVar _0; - auto _ti = _tp_info(TOP()); - if(_ti->m__invert__) _0 = _ti->m__invert__(this, TOP()); - else _0 = call_method(TOP(), __invert__); - TOP() = _0; - } DISPATCH() - /*****************************************/ - case OP_GET_ITER: - TOP() = py_iter(TOP()); - DISPATCH() - case OP_GET_ITER_NEW: { - // This opcode always creates a temporary iterator object - const PyTypeInfo* _ti = _tp_info(TOP()); - if(_ti->op__iter__){ - PyVar _0 = POPX(); - _ti->op__iter__(this, _0); - }else{ - TOP() = py_iter(TOP()); - } - DISPATCH() - } - case OP_FOR_ITER:{ - PyVar _0 = py_next(TOP()); - if(_0 == StopIteration){ - int target = frame->prepare_loop_break(&s_data); - DISPATCH_JUMP_ABSOLUTE(target) - } else{ - PUSH(_0); - DISPATCH() - } - } - case OP_FOR_ITER_STORE_FAST:{ - PyVar _0 = py_next(TOP()); - if(_0 == StopIteration){ - int target = frame->prepare_loop_break(&s_data); - DISPATCH_JUMP_ABSOLUTE(target) - }else{ - frame->_locals[byte.arg] = _0; - DISPATCH() - } - } - case OP_FOR_ITER_STORE_GLOBAL:{ - PyVar _0 = py_next(TOP()); - if(_0 == StopIteration){ - int target = frame->prepare_loop_break(&s_data); - DISPATCH_JUMP_ABSOLUTE(target) - }else{ - frame->f_globals().set(StrName(byte.arg), _0); - DISPATCH() - } - } - case OP_FOR_ITER_YIELD_VALUE:{ - PyVar _0 = py_next(TOP()); - if(_0 == StopIteration){ - int target = frame->prepare_loop_break(&s_data); - DISPATCH_JUMP_ABSOLUTE(target) - }else{ - PUSH(_0); - return PY_OP_YIELD; - } - } - case OP_FOR_ITER_UNPACK:{ - PyVar _0 = TOP(); - const PyTypeInfo* _ti = _tp_info(_0); - if(_ti->op__next__){ - unsigned n = _ti->op__next__(this, _0); - if(n == 0){ - // StopIteration - int target = frame->prepare_loop_break(&s_data); - DISPATCH_JUMP_ABSOLUTE(target) - }else if(n == 1){ - // UNPACK_SEQUENCE - __op_unpack_sequence(byte.arg); - }else{ - if(n != byte.arg){ - ValueError(_S("expected ", (int)byte.arg, " values to unpack, got ", (int)n)); + case OP_IS_OP: { + PyVar _1 = POPX(); // rhs + PyVar _0 = TOP(); // lhs + TOP() = _0 == _1 ? True : False; + } + DISPATCH() + case OP_IS_NOT_OP: { + PyVar _1 = POPX(); // rhs + PyVar _0 = TOP(); // lhs + TOP() = _0 != _1 ? True : False; + } + DISPATCH() + case OP_CONTAINS_OP: { + // a in b -> b __contains__ a + auto _ti = _tp_info(TOP()); + PyVar _0; + if(_ti->m__contains__) { + _0 = _ti->m__contains__(this, TOP(), SECOND()); + } else { + _0 = call_method(TOP(), __contains__, SECOND()); + } + POP(); + TOP() = VAR(static_cast((int)CAST(bool, _0) ^ byte.arg)); + } + DISPATCH() + /*****************************************/ + case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg) + case OP_POP_JUMP_IF_FALSE: + if(!py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg) + DISPATCH() + case OP_POP_JUMP_IF_TRUE: + if(py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg) + DISPATCH() + case OP_JUMP_IF_TRUE_OR_POP: + if(py_bool(TOP())) { + DISPATCH_JUMP((int16_t)byte.arg) + } else { + POP(); + DISPATCH() + } + case OP_JUMP_IF_FALSE_OR_POP: + if(!py_bool(TOP())) { + DISPATCH_JUMP((int16_t)byte.arg) + } else { + POP(); + DISPATCH() + } + case OP_SHORTCUT_IF_FALSE_OR_POP: + if(!py_bool(TOP())) { // [b, False] + STACK_SHRINK(2); // [] + PUSH(vm->False); // [False] + DISPATCH_JUMP((int16_t)byte.arg) + } else { + POP(); // [b] + DISPATCH() + } + case OP_LOOP_CONTINUE: + // just an alias of OP_JUMP_FORWARD + DISPATCH_JUMP((int16_t)byte.arg) + case OP_LOOP_BREAK: { + frame->prepare_jump_break(&s_data, frame->ip() + byte.arg); + DISPATCH_JUMP((int16_t)byte.arg) + } + case OP_JUMP_ABSOLUTE_TOP: DISPATCH_JUMP_ABSOLUTE(_CAST(int, POPX())) + case OP_GOTO: { + StrName _name(byte.arg); + int target = frame->co->labels.try_get_likely_found(_name); + if(target < 0) RuntimeError(_S("label ", _name.escape(), " not found")); + frame->prepare_jump_break(&s_data, target); + DISPATCH_JUMP_ABSOLUTE(target) + } + /*****************************************/ + case OP_FSTRING_EVAL: { + PyVar _0 = frame->co->consts[byte.arg]; + std::string_view string = CAST(Str&, _0).sv(); + auto it = __cached_codes.find(string); + CodeObject_ code; + if(it == __cached_codes.end()) { + code = vm->compile(string, "", EVAL_MODE, true); + __cached_codes[string] = code; + } else { + code = it->second; + } + _0 = vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals); + PUSH(_0); + } + DISPATCH() + case OP_REPR: TOP() = VAR(py_repr(TOP())); DISPATCH() + case OP_CALL: { + if(heap._should_auto_collect()) heap._auto_collect(); + PyVar _0 = vectorcall(byte.arg & 0xFF, // ARGC + (byte.arg >> 8) & 0xFF, // KWARGC + true); + if(_0 == PY_OP_CALL) { + frame = &callstack.top(); + goto __NEXT_FRAME; + } + PUSH(_0); + } + DISPATCH() + case OP_CALL_TP: { + if(heap._should_auto_collect()) heap._auto_collect(); + PyVar _0; + PyVar _1; + PyVar _2; + // [callable, , args: tuple, kwargs: dict | NULL] + if(byte.arg) { + _2 = POPX(); + _1 = POPX(); + for(PyVar obj: _CAST(Tuple&, _1)) + PUSH(obj); + _CAST(Dict&, _2).apply([this](PyVar k, PyVar v) { + PUSH(VAR(StrName(CAST(Str&, k)).index)); + PUSH(v); + }); + _0 = vectorcall(_CAST(Tuple&, _1).size(), // ARGC + _CAST(Dict&, _2).size(), // KWARGC + true); + } else { + // no **kwargs + _1 = POPX(); + for(PyVar obj: _CAST(Tuple&, _1)) + PUSH(obj); + _0 = vectorcall(_CAST(Tuple&, _1).size(), // ARGC + 0, // KWARGC + true); + } + if(_0 == PY_OP_CALL) { + frame = &callstack.top(); + goto __NEXT_FRAME; + } + PUSH(_0); + } + DISPATCH() + case OP_RETURN_VALUE: { + PyVar _0 = byte.arg == BC_NOARG ? POPX() : None; + __pop_frame(); + if(frame == base_frame) { // [ frameBase<- ] + return _0; + } else { + frame = &callstack.top(); + PUSH(_0); + goto __NEXT_FRAME; + } + } + DISPATCH() + case OP_YIELD_VALUE: return PY_OP_YIELD; + /*****************************************/ + case OP_LIST_APPEND: { + PyVar _0 = POPX(); + PK_OBJ_GET(List, SECOND()).push_back(_0); + } + DISPATCH() + case OP_DICT_ADD: { + PyVar _0 = POPX(); + const Tuple& t = PK_OBJ_GET(Tuple, _0); + PK_OBJ_GET(Dict, SECOND()).set(this, t[0], t[1]); + } + DISPATCH() + case OP_SET_ADD: { + PyVar _0 = POPX(); + call_method(SECOND(), pk_id_add, _0); + } + DISPATCH() + /*****************************************/ + case OP_UNARY_NEGATIVE: TOP() = py_negate(TOP()); DISPATCH() + case OP_UNARY_NOT: { + PyVar _0 = TOP(); + if(_0 == True) + TOP() = False; + else if(_0 == False) + TOP() = True; + else + TOP() = VAR(!py_bool(_0)); + } + DISPATCH() + case OP_UNARY_STAR: TOP() = VAR(StarWrapper(byte.arg, TOP())); DISPATCH() + case OP_UNARY_INVERT: { + PyVar _0; + auto _ti = _tp_info(TOP()); + if(_ti->m__invert__) + _0 = _ti->m__invert__(this, TOP()); + else + _0 = call_method(TOP(), __invert__); + TOP() = _0; + } + DISPATCH() + /*****************************************/ + case OP_GET_ITER: TOP() = py_iter(TOP()); DISPATCH() + case OP_GET_ITER_NEW: { + // This opcode always creates a temporary iterator object + const PyTypeInfo* _ti = _tp_info(TOP()); + if(_ti->op__iter__) { + PyVar _0 = POPX(); + _ti->op__iter__(this, _0); + } else { + TOP() = py_iter(TOP()); + } + DISPATCH() + } + case OP_FOR_ITER: { + PyVar _0 = py_next(TOP()); + if(_0 == StopIteration) { + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP_ABSOLUTE(target) + } else { + PUSH(_0); + DISPATCH() + } + } + case OP_FOR_ITER_STORE_FAST: { + PyVar _0 = py_next(TOP()); + if(_0 == StopIteration) { + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP_ABSOLUTE(target) + } else { + frame->_locals[byte.arg] = _0; + DISPATCH() + } + } + case OP_FOR_ITER_STORE_GLOBAL: { + PyVar _0 = py_next(TOP()); + if(_0 == StopIteration) { + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP_ABSOLUTE(target) + } else { + frame->f_globals().set(StrName(byte.arg), _0); + DISPATCH() + } + } + case OP_FOR_ITER_YIELD_VALUE: { + PyVar _0 = py_next(TOP()); + if(_0 == StopIteration) { + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP_ABSOLUTE(target) + } else { + PUSH(_0); + return PY_OP_YIELD; + } + } + case OP_FOR_ITER_UNPACK: { + PyVar _0 = TOP(); + const PyTypeInfo* _ti = _tp_info(_0); + if(_ti->op__next__) { + unsigned n = _ti->op__next__(this, _0); + if(n == 0) { + // StopIteration + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP_ABSOLUTE(target) + } else if(n == 1) { + // UNPACK_SEQUENCE + __op_unpack_sequence(byte.arg); + } else { + if(n != byte.arg) { + ValueError(_S("expected ", (int)byte.arg, " values to unpack, got ", (int)n)); + } + } + } else { + // FOR_ITER + _0 = call_method(_0, __next__); + if(_0 != StopIteration) { + PUSH(_0); + // UNPACK_SEQUENCE + __op_unpack_sequence(byte.arg); + } else { + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP_ABSOLUTE(target) + } + } + } + DISPATCH() + /*****************************************/ + case OP_IMPORT_PATH: { + PyVar _0 = frame->co->consts[byte.arg]; + PUSH(py_import(CAST(Str&, _0))); + } + DISPATCH() + case OP_POP_IMPORT_STAR: { + PyVar _0 = POPX(); // pop the module + PyVar _1 = _0->attr().try_get(__all__); + StrName _name; + if(_1 != nullptr) { + for(PyVar key: CAST(List&, _1)) { + _name = StrName::get(CAST(Str&, key).sv()); + PyVar value = _0->attr().try_get_likely_found(_name); + if(value == nullptr) { + ImportError(_S("cannot import name ", _name.escape())); + } else { + frame->f_globals().set(_name, value); + } + } + } else { + for(auto& [name, value]: _0->attr().items()) { + std::string_view s = name.sv(); + if(s.empty() || s[0] == '_') continue; + frame->f_globals().set(name, value); + } + } + } + DISPATCH() + /*****************************************/ + case OP_UNPACK_SEQUENCE: { + __op_unpack_sequence(byte.arg); + } + DISPATCH() + case OP_UNPACK_EX: { + auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!! + PyVar _0 = py_iter(POPX()); + const PyTypeInfo* _ti = _tp_info(_0); + PyVar _1; + for(int i = 0; i < byte.arg; i++) { + _1 = _py_next(_ti, _0); + if(_1 == StopIteration) ValueError("not enough values to unpack"); + PUSH(_1); + } + List extras; + while(true) { + _1 = _py_next(_ti, _0); + if(_1 == StopIteration) break; + extras.push_back(_1); + } + PUSH(VAR(std::move(extras))); + } + DISPATCH() + /*****************************************/ + case OP_BEGIN_CLASS: { + StrName _name(byte.arg); + PyVar _0 = POPX(); // super + if(_0 == None) _0 = _t(tp_object); + check_type(_0, tp_type); + __curr_class = new_type_object(frame->_module, _name, PK_OBJ_GET(Type, _0), true); + } + DISPATCH() + case OP_END_CLASS: { + assert(__curr_class != nullptr); + StrName _name(byte.arg); + frame->_module->attr().set(_name, __curr_class); + // call on_end_subclass + PyTypeInfo* ti = &_all_types[__curr_class->as()]; + if(ti->base != tp_object) { + PyTypeInfo* base_ti = &_all_types[ti->base]; + if(base_ti->on_end_subclass) base_ti->on_end_subclass(this, ti); + } + __curr_class = nullptr; + } + DISPATCH() + case OP_STORE_CLASS_ATTR: { + assert(__curr_class != nullptr); + StrName _name(byte.arg); + PyVar _0 = POPX(); + if(is_type(_0, tp_function)) { PK_OBJ_GET(Function, _0)._class = __curr_class; } + __curr_class->attr().set(_name, _0); + } + DISPATCH() + case OP_BEGIN_CLASS_DECORATION: { + PUSH(__curr_class); + } + DISPATCH() + case OP_END_CLASS_DECORATION: { + __curr_class = POPX().get(); + } + DISPATCH() + case OP_ADD_CLASS_ANNOTATION: { + assert(__curr_class != nullptr); + StrName _name(byte.arg); + Type type = __curr_class->as(); + _all_types[type].annotated_fields.push_back(_name); + } + DISPATCH() + /*****************************************/ + case OP_WITH_ENTER: PUSH(call_method(TOP(), __enter__)); DISPATCH() + case OP_WITH_EXIT: + call_method(TOP(), __exit__); + POP(); + DISPATCH() + /*****************************************/ + case OP_TRY_ENTER: { + frame->set_unwind_target(s_data._sp); + DISPATCH() + } + case OP_EXCEPTION_MATCH: { + PyVar assumed_type = POPX(); + check_type(assumed_type, tp_type); + PyVar e_obj = TOP(); + bool ok = isinstance(e_obj, PK_OBJ_GET(Type, assumed_type)); + PUSH(VAR(ok)); + } + DISPATCH() + case OP_RAISE: { + if(is_type(TOP(), tp_type)) { TOP() = call(TOP()); } + if(!isinstance(TOP(), tp_exception)) { TypeError("exceptions must derive from Exception"); } + _error(POPX()); + } + DISPATCH() + case OP_RAISE_ASSERT: + if(byte.arg) { + Str msg = py_str(TOP()); + POP(); + AssertionError(msg); + } else { + AssertionError(); + } + DISPATCH() + case OP_RE_RAISE: __raise_exc(true); DISPATCH() + case OP_POP_EXCEPTION: __last_exception = POPX().get(); DISPATCH() + /*****************************************/ + case OP_FORMAT_STRING: { + PyVar _0 = POPX(); + const Str& spec = CAST(Str&, frame->co->consts[byte.arg]); + PUSH(__format_object(_0, spec)); + } + DISPATCH() + /*****************************************/ + case OP_INC_FAST: { + PyVar* p = &frame->_locals[byte.arg]; + if(*p == PY_NULL) vm->NameError(frame->co->varnames[byte.arg]); + *p = VAR(CAST(i64, *p) + 1); + } + DISPATCH() + case OP_DEC_FAST: { + PyVar* p = &frame->_locals[byte.arg]; + if(*p == PY_NULL) vm->NameError(frame->co->varnames[byte.arg]); + *p = VAR(CAST(i64, *p) - 1); + } + DISPATCH() + case OP_INC_GLOBAL: { + StrName _name(byte.arg); + PyVar* p = frame->f_globals().try_get_2_likely_found(_name); + if(p == nullptr) vm->NameError(_name); + *p = VAR(CAST(i64, *p) + 1); + } + DISPATCH() + case OP_DEC_GLOBAL: { + StrName _name(byte.arg); + PyVar* p = frame->f_globals().try_get_2_likely_found(_name); + if(p == nullptr) vm->NameError(_name); + *p = VAR(CAST(i64, *p) - 1); + } + DISPATCH() + /*****************************************/ + default: PK_UNREACHABLE() } } - }else{ - // FOR_ITER - _0 = call_method(_0, __next__); - if(_0 != StopIteration){ - PUSH(_0); - // UNPACK_SEQUENCE - __op_unpack_sequence(byte.arg); - }else{ - int target = frame->prepare_loop_break(&s_data); - DISPATCH_JUMP_ABSOLUTE(target) - } - } - } DISPATCH() - /*****************************************/ - case OP_IMPORT_PATH:{ - PyVar _0 = frame->co->consts[byte.arg]; - PUSH(py_import(CAST(Str&, _0))); - } DISPATCH() - case OP_POP_IMPORT_STAR: { - PyVar _0 = POPX(); // pop the module - PyVar _1 = _0->attr().try_get(__all__); - StrName _name; - if(_1 != nullptr){ - for(PyVar key: CAST(List&, _1)){ - _name = StrName::get(CAST(Str&, key).sv()); - PyVar value = _0->attr().try_get_likely_found(_name); - if(value == nullptr){ - ImportError(_S("cannot import name ", _name.escape())); - }else{ - frame->f_globals().set(_name, value); - } - } - }else{ - for(auto& [name, value]: _0->attr().items()){ - std::string_view s = name.sv(); - if(s.empty() || s[0] == '_') continue; - frame->f_globals().set(name, value); - } - } - } DISPATCH() - /*****************************************/ - case OP_UNPACK_SEQUENCE:{ - __op_unpack_sequence(byte.arg); - } DISPATCH() - case OP_UNPACK_EX: { - auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!! - PyVar _0 = py_iter(POPX()); - const PyTypeInfo* _ti = _tp_info(_0); - PyVar _1; - for(int i=0; i_module, _name, PK_OBJ_GET(Type, _0), true); - } DISPATCH() - case OP_END_CLASS: { - assert(__curr_class != nullptr); - StrName _name(byte.arg); - frame->_module->attr().set(_name, __curr_class); - // call on_end_subclass - PyTypeInfo* ti = &_all_types[__curr_class->as()]; - if(ti->base != tp_object){ - PyTypeInfo* base_ti = &_all_types[ti->base]; - if(base_ti->on_end_subclass) base_ti->on_end_subclass(this, ti); - } - __curr_class = nullptr; - } DISPATCH() - case OP_STORE_CLASS_ATTR:{ - assert(__curr_class != nullptr); - StrName _name(byte.arg); - PyVar _0 = POPX(); - if(is_type(_0, tp_function)){ - PK_OBJ_GET(Function, _0)._class = __curr_class; - } - __curr_class->attr().set(_name, _0); - } DISPATCH() - case OP_BEGIN_CLASS_DECORATION:{ - PUSH(__curr_class); - } DISPATCH() - case OP_END_CLASS_DECORATION:{ - __curr_class = POPX().get(); - } DISPATCH() - case OP_ADD_CLASS_ANNOTATION: { - assert(__curr_class != nullptr); - StrName _name(byte.arg); - Type type = __curr_class->as(); - _all_types[type].annotated_fields.push_back(_name); - } DISPATCH() - /*****************************************/ - case OP_WITH_ENTER: - PUSH(call_method(TOP(), __enter__)); - DISPATCH() - case OP_WITH_EXIT: - call_method(TOP(), __exit__); - POP(); - DISPATCH() - /*****************************************/ - case OP_TRY_ENTER: { - frame->set_unwind_target(s_data._sp); - DISPATCH() - } - case OP_EXCEPTION_MATCH: { - PyVar assumed_type = POPX(); - check_type(assumed_type, tp_type); - PyVar e_obj = TOP(); - bool ok = isinstance(e_obj, PK_OBJ_GET(Type, assumed_type)); - PUSH(VAR(ok)); - } DISPATCH() - case OP_RAISE: { - if(is_type(TOP(), tp_type)){ - TOP() = call(TOP()); - } - if(!isinstance(TOP(), tp_exception)){ - TypeError("exceptions must derive from Exception"); - } - _error(POPX()); - } DISPATCH() - case OP_RAISE_ASSERT: - if(byte.arg){ - Str msg = py_str(TOP()); - POP(); - AssertionError(msg); - }else{ - AssertionError(); - } - DISPATCH() - case OP_RE_RAISE: __raise_exc(true); DISPATCH() - case OP_POP_EXCEPTION: __last_exception = POPX().get(); DISPATCH() - /*****************************************/ - case OP_FORMAT_STRING: { - PyVar _0 = POPX(); - const Str& spec = CAST(Str&, frame->co->consts[byte.arg]); - PUSH(__format_object(_0, spec)); - } DISPATCH() - /*****************************************/ - case OP_INC_FAST:{ - PyVar* p = &frame->_locals[byte.arg]; - if(*p == PY_NULL) vm->NameError(frame->co->varnames[byte.arg]); - *p = VAR(CAST(i64, *p) + 1); - } DISPATCH() - case OP_DEC_FAST:{ - PyVar* p = &frame->_locals[byte.arg]; - if(*p == PY_NULL) vm->NameError(frame->co->varnames[byte.arg]); - *p = VAR(CAST(i64, *p) - 1); - } DISPATCH() - case OP_INC_GLOBAL:{ - StrName _name(byte.arg); - PyVar* p = frame->f_globals().try_get_2_likely_found(_name); - if(p == nullptr) vm->NameError(_name); - *p = VAR(CAST(i64, *p) + 1); - } DISPATCH() - case OP_DEC_GLOBAL:{ - StrName _name(byte.arg); - PyVar* p = frame->f_globals().try_get_2_likely_found(_name); - if(p == nullptr) vm->NameError(_name); - *p = VAR(CAST(i64, *p) - 1); - } DISPATCH() - /*****************************************/ - default: PK_UNREACHABLE() - } -} -/**********************************************************************/ + /**********************************************************************/ PK_UNREACHABLE() - }catch(InternalException internal){ + } catch(InternalException internal) { __internal_exception = internal; - if(internal.type == InternalExceptionType::Unhandled){ + if(internal.type == InternalExceptionType::Unhandled) { __last_exception = POPX().get(); Exception& _e = __last_exception->as(); bool is_base_frame_to_be_popped = frame == base_frame; __pop_frame(); - if(callstack.empty()){ + if(callstack.empty()) { // propagate to the top level throw TopLevelException(this, &_e); } frame = &callstack.top(); PUSH(__last_exception); - if(is_base_frame_to_be_popped){ - throw InternalException(InternalExceptionType::ToBeRaised); - } + if(is_base_frame_to_be_popped) { throw InternalException(InternalExceptionType::ToBeRaised); } } } } @@ -1082,4 +1191,4 @@ __NEXT_STEP: #undef DISPATCH_JUMP #undef CEVAL_STEP_CALLBACK -} // namespace pkpy +} // namespace pkpy diff --git a/src/interpreter/cffi.cpp b/src/interpreter/cffi.cpp index ecaade5e..7d91d736 100644 --- a/src/interpreter/cffi.cpp +++ b/src/interpreter/cffi.cpp @@ -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(cls, reinterpret_cast(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(cls, reinterpret_cast(addr)); + }); - vm->bind__hash__(type->as(), [](VM* vm, PyVar obj){ - obj_get_t self = PK_OBJ_GET(VoidP, obj); - return reinterpret_cast(self.ptr); - }); + vm->bind__hash__(type->as(), [](VM* vm, PyVar obj) { + obj_get_t self = PK_OBJ_GET(VoidP, obj); + return reinterpret_cast(self.ptr); + }); - vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str{ - obj_get_t self = PK_OBJ_GET(VoidP, obj); - return _S(""); - }); + vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str { + obj_get_t self = PK_OBJ_GET(VoidP, obj); + return _S(""); + }); -#define BIND_CMP(name, op) \ - vm->bind##name(type->as(), [](VM* vm, PyVar lhs, PyVar rhs){ \ - if(!vm->isinstance(rhs, vm->_tp_user())) 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(), [](VM* vm, PyVar lhs, PyVar rhs) { \ + if(!vm->isinstance(rhs, vm->_tp_user())) 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(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(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; ibind_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; iValueError("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(std::move(buffer)); - }, {}, BindType::STATICMETHOD); + }, + {}, + BindType::STATICMETHOD); - vm->bind__repr__(type->as(), [](VM* vm, PyVar obj){ - Struct& self = _CAST(Struct&, obj); - SStream ss; - ss << ""; - return ss.str(); - }); + vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) { + Struct& self = _CAST(Struct&, obj); + SStream ss; + ss << ""; + return ss.str(); + }); - vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args){ - Struct& self = _CAST(Struct&, args[0]); - return vm->new_user_object(self.p); - }); + vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args) { + Struct& self = _CAST(Struct&, args[0]); + return vm->new_user_object(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(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(vm->_tp(args[0]), self); + }); - vm->bind__eq__(type->as(), [](VM* vm, PyVar lhs, PyVar rhs){ - Struct& self = _CAST(Struct&, lhs); - if(!vm->is_user_type(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(), [](VM* vm, PyVar lhs, PyVar rhs) { + Struct& self = _CAST(Struct&, lhs); + if(!vm->is_user_type(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(mod, "void_p", VM::tp_object, true); vm->register_user_class(mod, "struct", VM::tp_object, true); - + mod->attr().set("NULL", vm->new_user_object(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())){ - vm->ValueError("expected a subclass of void_p"); - } + if(!vm->issubclass(cls, vm->_tp_user())) { vm->ValueError("expected a subclass of void_p"); } return vm->new_object(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(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(ptr.ptr); return vm->new_object(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(&val, sizeof(T)); \ - }); \ - type = vm->new_type_object(mod, CNAME "_p", vm->_tp_user(), true); \ - mod->attr().set(CNAME "_p", type); \ - type_t = type->as(); \ - vm->bind_func(type, "read", 1, [](VM* vm, ArgsView args){ \ - obj_get_t 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 = 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 = 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 = 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 = PK_OBJ_GET(VoidP, lhs); \ - i64 offset = CAST(i64, rhs); \ - T* target = (T*)voidp.ptr; \ - return vm->new_object(lhs.type, target + offset); \ - }); \ - vm->bind__sub__(type_t, [](VM* vm, PyVar lhs, PyVar rhs){ \ - obj_get_t voidp = PK_OBJ_GET(VoidP, lhs); \ - i64 offset = CAST(i64, rhs); \ - T* target = (T*)voidp.ptr; \ - return vm->new_object(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(&val, sizeof(T)); \ + }); \ + type = vm->new_type_object(mod, CNAME "_p", vm->_tp_user(), true); \ + mod->attr().set(CNAME "_p", type); \ + type_t = type->as(); \ + vm->bind_func(type, "read", 1, [](VM* vm, ArgsView args) { \ + obj_get_t 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 = 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 = 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 = 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 = PK_OBJ_GET(VoidP, lhs); \ + i64 offset = CAST(i64, rhs); \ + T* target = (T*)voidp.ptr; \ + return vm->new_object(lhs.type, target + offset); \ + }); \ + vm->bind__sub__(type_t, [](VM* vm, PyVar lhs, PyVar rhs) { \ + obj_get_t voidp = PK_OBJ_GET(VoidP, lhs); \ + i64 offset = CAST(i64, rhs); \ + T* target = (T*)voidp.ptr; \ + return vm->new_object(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 = 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 = 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(p); -} +PyVar from_void_p(VM* vm, void* p) { return vm->new_user_object(p); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/interpreter/frame.cpp b/src/interpreter/frame.cpp index a4b90059..243faa04 100644 --- a/src/interpreter/frame.cpp +++ b/src/interpreter/frame.cpp @@ -3,126 +3,128 @@ #include -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(); - 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(); + 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(); - 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(); + 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().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().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 \ No newline at end of file +void CallStack::pushx(LinkedFrame* p) { + p->f_back = _tail; + _tail = p; + ++_size; +} +} // namespace pkpy diff --git a/src/interpreter/gc.cpp b/src/interpreter/gc.cpp index eb0e480d..8ed0085d 100644 --- a/src/interpreter/gc.cpp +++ b/src/interpreter/gc.cpp @@ -1,55 +1,56 @@ #include "pocketpy/interpreter/gc.hpp" -namespace pkpy{ +namespace pkpy { - int ManagedHeap::sweep(){ - vector 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 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 \ No newline at end of file +int ManagedHeap::collect() { + assert(_gc_lock_counter == 0); + mark(); + int freed = sweep(); + return freed; +} +} // namespace pkpy diff --git a/src/interpreter/iter.cpp b/src/interpreter/iter.cpp index 1ff7826e..e1f27f1d 100644 --- a/src/interpreter/iter.cpp +++ b/src/interpreter/iter.cpp @@ -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(), [](VM* vm, PyVar _0){ return _0; }); - vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0) { + return _0; + }); + vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0){ return _0; }); - vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0) { + return _0; + }); + vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0){ return _0; }); - vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0) { + return _0; + }); + vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0){ return _0; }); - vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0) { + return _0; + }); + vm->bind__next__(type->as(), [](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().count; - if(count < 0){ - void* new_p = p + count; - p[1]._1 = reinterpret_cast(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().count; + if(count < 0) { + void* new_p = p + count; + p[1]._1 = reinterpret_cast(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(), [](VM* vm, PyVar _0){ return _0; }); - vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0){ return _0; }); - vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0) { + return _0; + }); + vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0) { + return _0; + }); + vm->bind__next__(type->as(), [](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(std::move(frame), buffer); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/interpreter/profiler.cpp b/src/interpreter/profiler.cpp index 5052dd69..17819500 100644 --- a/src/interpreter/profiler.cpp +++ b/src/interpreter/profiler.cpp @@ -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 \ No newline at end of file +} // namespace pkpy diff --git a/src/interpreter/vm.cpp b/src/interpreter/vm.cpp index 2f4eff88..dc3cd794 100644 --- a/src/interpreter/vm.cpp +++ b/src/interpreter/vm.cpp @@ -4,460 +4,451 @@ #include #include -static const char* OP_NAMES[] = { - #define OPCODE(name) #name, - #include "pocketpy/opcodes.h" - #undef OPCODE +const static char* OP_NAMES[] = { +#define OPCODE(name) #name, +#include "pocketpy/opcodes.h" +#undef OPCODE }; -namespace pkpy{ +namespace pkpy { - struct JsonSerializer{ - VM* vm; - PyVar root; - SStream ss; +struct JsonSerializer { + VM* vm; + PyVar root; + SStream ss; - JsonSerializer(VM* vm, PyVar root) : vm(vm), root(root) {} + JsonSerializer(VM* vm, PyVar root) : vm(vm), root(root) {} - template - void write_array(T& arr){ - ss << '['; - for(int i=0; i + void write_array(T& arr) { + ss << '['; + for(int i = 0; i < arr.size(); i++) { + if(i != 0) ss << ", "; + write_object(arr[i]); + } + ss << ']'; + } + + void write_dict(Dict& dict) { + ss << '{'; + bool first = true; + dict.apply([&](PyVar k, PyVar v) { + if(!first) ss << ", "; + first = false; + if(!is_type(k, VM::tp_str)) { + vm->TypeError(_S("json keys must be string, got ", _type_name(vm, vm->_tp(k)))); } - ss << ']'; - } + ss << _CAST(Str&, k).escape(false) << ": "; + write_object(v); + }); + ss << '}'; + } - void write_dict(Dict& dict){ - ss << '{'; - bool first = true; - dict.apply([&](PyVar k, PyVar v){ - if(!first) ss << ", "; - first = false; - if(!is_type(k, VM::tp_str)){ - vm->TypeError(_S("json keys must be string, got ", _type_name(vm, vm->_tp(k)))); - } - ss << _CAST(Str&, k).escape(false) << ": "; - write_object(v); - }); - ss << '}'; + void write_object(PyVar obj) { + Type obj_t = vm->_tp(obj); + if(obj == vm->None) { + ss << "null"; + } else if(obj_t == vm->tp_int) { + ss << _CAST(i64, obj); + } else if(obj_t == vm->tp_float) { + f64 val = _CAST(f64, obj); + if(std::isinf(val) || std::isnan(val)) vm->ValueError("cannot jsonify 'nan' or 'inf'"); + ss << val; + } else if(obj_t == vm->tp_bool) { + ss << (obj == vm->True ? "true" : "false"); + } else if(obj_t == vm->tp_str) { + _CAST(Str&, obj).escape_(ss, false); + } else if(obj_t == vm->tp_list) { + write_array(_CAST(List&, obj)); + } else if(obj_t == vm->tp_tuple) { + write_array(_CAST(Tuple&, obj)); + } else if(obj_t == vm->tp_dict) { + write_dict(_CAST(Dict&, obj)); + } else { + vm->TypeError(_S("unrecognized type ", _type_name(vm, obj_t).escape())); } + } - void write_object(PyVar obj){ - Type obj_t = vm->_tp(obj); - if(obj == vm->None){ - ss << "null"; - }else if(obj_t == vm->tp_int){ - ss << _CAST(i64, obj); - }else if(obj_t == vm->tp_float){ - f64 val = _CAST(f64, obj); - if(std::isinf(val) || std::isnan(val)) vm->ValueError("cannot jsonify 'nan' or 'inf'"); - ss << val; - }else if(obj_t == vm->tp_bool){ - ss << (obj == vm->True ? "true" : "false"); - }else if(obj_t == vm->tp_str){ - _CAST(Str&, obj).escape_(ss, false); - }else if(obj_t == vm->tp_list){ - write_array(_CAST(List&, obj)); - }else if(obj_t == vm->tp_tuple){ - write_array(_CAST(Tuple&, obj)); - }else if(obj_t == vm->tp_dict){ - write_dict(_CAST(Dict&, obj)); - }else{ - vm->TypeError(_S("unrecognized type ", _type_name(vm, obj_t).escape())); - } - } + Str serialize() { + auto _lock = vm->heap.gc_scope_lock(); + write_object(root); + return ss.str(); + } +}; - Str serialize(){ - auto _lock = vm->heap.gc_scope_lock(); - write_object(root); - return ss.str(); - } +VM::VM(bool enable_os) : heap(this), enable_os(enable_os) { + this->vm = this; + this->__c.error = nullptr; + _ceval_on_step = nullptr; + _stdout = [](const char* buf, int size) { + std::cout.write(buf, size); }; + _stderr = [](const char* buf, int size) { + std::cerr.write(buf, size); + }; + builtins = nullptr; + _main = nullptr; + __last_exception = nullptr; + _import_handler = [](const char* name, int* out_size) -> unsigned char* { + return nullptr; + }; + __init_builtin_types(); +} - VM::VM(bool enable_os) : heap(this), enable_os(enable_os) { - this->vm = this; - this->__c.error = nullptr; - _ceval_on_step = nullptr; - _stdout = [](const char* buf, int size) { std::cout.write(buf, size); }; - _stderr = [](const char* buf, int size) { std::cerr.write(buf, size); }; - builtins = nullptr; - _main = nullptr; - __last_exception = nullptr; - _import_handler = [](const char* name, int* out_size) -> unsigned char*{ return nullptr; }; - __init_builtin_types(); - } - - Str VM::py_str(PyVar obj){ - const PyTypeInfo* ti = _tp_info(obj); - if(ti->m__str__) return ti->m__str__(this, obj); - PyVar self; - PyVar f = get_unbound_method(obj, __str__, &self, false); - if(self != PY_NULL){ - PyVar retval = call_method(self, f); - if(!is_type(retval, tp_str)){ - throw std::runtime_error("object.__str__ must return str"); - } - return PK_OBJ_GET(Str, retval); - } - return py_repr(obj); - } - - Str VM::py_repr(PyVar obj){ - const PyTypeInfo* ti = _tp_info(obj); - if(ti->m__repr__) return ti->m__repr__(this, obj); - PyVar retval = call_method(obj, __repr__); - if(!is_type(retval, tp_str)){ - throw std::runtime_error("object.__repr__ must return str"); - } +Str VM::py_str(PyVar obj) { + const PyTypeInfo* ti = _tp_info(obj); + if(ti->m__str__) return ti->m__str__(this, obj); + PyVar self; + PyVar f = get_unbound_method(obj, __str__, &self, false); + if(self != PY_NULL) { + PyVar retval = call_method(self, f); + if(!is_type(retval, tp_str)) { throw std::runtime_error("object.__str__ must return str"); } return PK_OBJ_GET(Str, retval); } + return py_repr(obj); +} - Str VM::py_json(PyVar obj){ - auto j = JsonSerializer(this, obj); - return j.serialize(); +Str VM::py_repr(PyVar obj) { + const PyTypeInfo* ti = _tp_info(obj); + if(ti->m__repr__) return ti->m__repr__(this, obj); + PyVar retval = call_method(obj, __repr__); + if(!is_type(retval, tp_str)) { throw std::runtime_error("object.__repr__ must return str"); } + return PK_OBJ_GET(Str, retval); +} + +Str VM::py_json(PyVar obj) { + auto j = JsonSerializer(this, obj); + return j.serialize(); +} + +PyVar VM::py_iter(PyVar obj) { + const PyTypeInfo* ti = _tp_info(obj); + if(ti->m__iter__) return ti->m__iter__(this, obj); + PyVar self; + PyVar iter_f = get_unbound_method(obj, __iter__, &self, false); + if(self != PY_NULL) return call_method(self, iter_f); + TypeError(_type_name(vm, _tp(obj)).escape() + " object is not iterable"); + return nullptr; +} + +ArgsView VM::cast_array_view(PyVar obj) { + if(is_type(obj, VM::tp_list)) { + List& list = PK_OBJ_GET(List, obj); + return ArgsView(list.begin(), list.end()); + } else if(is_type(obj, VM::tp_tuple)) { + Tuple& tuple = PK_OBJ_GET(Tuple, obj); + return ArgsView(tuple.begin(), tuple.end()); } + TypeError(_S("expected list or tuple, got ", _type_name(this, _tp(obj)).escape())); +} - PyVar VM::py_iter(PyVar obj){ - const PyTypeInfo* ti = _tp_info(obj); - if(ti->m__iter__) return ti->m__iter__(this, obj); - PyVar self; - PyVar iter_f = get_unbound_method(obj, __iter__, &self, false); - if(self != PY_NULL) return call_method(self, iter_f); - TypeError(_type_name(vm, _tp(obj)).escape() + " object is not iterable"); - return nullptr; - } +void VM::set_main_argv(int argc, char** argv) { + PyVar mod = vm->_modules["sys"]; + List argv_(argc); + for(int i = 0; i < argc; i++) + argv_[i] = VAR(std::string_view(argv[i])); + mod->attr().set("argv", VAR(std::move(argv_))); +} - ArgsView VM::cast_array_view(PyVar obj){ - if(is_type(obj, VM::tp_list)){ - List& list = PK_OBJ_GET(List, obj); - return ArgsView(list.begin(), list.end()); - }else if(is_type(obj, VM::tp_tuple)){ - Tuple& tuple = PK_OBJ_GET(Tuple, obj); - return ArgsView(tuple.begin(), tuple.end()); - } - TypeError(_S("expected list or tuple, got ", _type_name(this, _tp(obj)).escape())); - } +PyVar* VM::find_name_in_mro(Type cls, StrName name) { + PyVar* val; + do { + val = _t(cls)->attr().try_get_2(name); + if(val != nullptr) return val; + cls = _all_types[cls].base; + if(!cls) break; + } while(true); + return nullptr; +} - void VM::set_main_argv(int argc, char** argv){ - PyVar mod = vm->_modules["sys"]; - List argv_(argc); - for(int i=0; iattr().set("argv", VAR(std::move(argv_))); - } +bool VM::isinstance(PyVar obj, Type base) { return issubclass(_tp(obj), base); } - PyVar* VM::find_name_in_mro(Type cls, StrName name){ - PyVar* val; - do{ - val = _t(cls)->attr().try_get_2(name); - if(val != nullptr) return val; - cls = _all_types[cls].base; - if(!cls) break; - }while(true); - return nullptr; - } +bool VM::issubclass(Type cls, Type base) { + do { + if(cls == base) return true; + Type next = _all_types[cls].base; + if(!next) break; + cls = next; + } while(true); + return false; +} - bool VM::isinstance(PyVar obj, Type base){ - return issubclass(_tp(obj), base); - } - - bool VM::issubclass(Type cls, Type base){ - do{ - if(cls == base) return true; - Type next = _all_types[cls].base; - if(!next) break; - cls = next; - }while(true); - return false; - } - - PyVar VM::exec(std::string_view source, Str filename, CompileMode mode, PyObject* _module){ - if(_module == nullptr) _module = _main; - try { +PyVar VM::exec(std::string_view source, Str filename, CompileMode mode, PyObject* _module) { + if(_module == nullptr) _module = _main; + try { #if PK_DEBUG_PRECOMPILED_EXEC == 1 - Str precompiled = vm->precompile(source, filename, mode); - source = precompiled.sv(); + Str precompiled = vm->precompile(source, filename, mode); + source = precompiled.sv(); #endif - CodeObject_ code = compile(source, filename, mode); - return _exec(code, _module); - }catch (TopLevelException e){ - stderr_write(e.summary() + "\n"); - } - catch(const std::exception& e) { - Str msg = "An std::exception occurred! It could be a bug.\n"; - msg = msg + e.what() + "\n"; - stderr_write(msg); - } - catch(NeedMoreLines){ - throw; - } - catch(...) { - Str msg = "An unknown exception occurred! It could be a bug. Please report it to @blueloveTH on GitHub.\n"; - stderr_write(msg); - } - callstack.clear(); - s_data.clear(); - return nullptr; + CodeObject_ code = compile(source, filename, mode); + return _exec(code, _module); + } catch(TopLevelException e) { stderr_write(e.summary() + "\n"); } catch(const std::exception& e) { + Str msg = "An std::exception occurred! It could be a bug.\n"; + msg = msg + e.what() + "\n"; + stderr_write(msg); + } catch(NeedMoreLines) { throw; } catch(...) { + Str msg = "An unknown exception occurred! It could be a bug. Please report it to @blueloveTH on GitHub.\n"; + stderr_write(msg); } + callstack.clear(); + s_data.clear(); + return nullptr; +} - PyVar VM::exec(std::string_view source){ - return exec(source, "main.py", EXEC_MODE); +PyVar VM::exec(std::string_view source) { return exec(source, "main.py", EXEC_MODE); } + +PyVar VM::eval(std::string_view source) { return exec(source, "", EVAL_MODE); } + +PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled, PyTypeInfo::Vt vt) { + PyObject* obj = heap._new(tp_type, Type(_all_types.size())); + const PyTypeInfo& base_info = _all_types[base]; + if(!base_info.subclass_enabled) { + Str error = _S("type ", base_info.name.escape(), " is not `subclass_enabled`"); + throw std::runtime_error(error.c_str()); } - - PyVar VM::eval(std::string_view source){ - return exec(source, "", EVAL_MODE); - } - - PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled, PyTypeInfo::Vt vt){ - PyObject* obj = heap._new(tp_type, Type(_all_types.size())); - const PyTypeInfo& base_info = _all_types[base]; - if(!base_info.subclass_enabled){ - Str error = _S("type ", base_info.name.escape(), " is not `subclass_enabled`"); + if(base_info.vt) { + if(vt) { + Str error = _S("type ", base_info.name.escape(), " has a custom vtable, cannot override"); throw std::runtime_error(error.c_str()); + } else { + // promote base vt to its subclass + vt = base_info.vt; } - if(base_info.vt){ - if(vt){ - Str error = _S("type ", base_info.name.escape(), " has a custom vtable, cannot override"); - throw std::runtime_error(error.c_str()); - }else{ - // promote base vt to its subclass - vt = base_info.vt; - } - } - _all_types.emplace_back(obj, base, mod, name, subclass_enabled, vt); - return obj; } + _all_types.emplace_back(obj, base, mod, name, subclass_enabled, vt); + return obj; +} - bool VM::py_eq(PyVar lhs, PyVar rhs){ - if(is_int(lhs) && is_int(rhs)) return lhs.as() == rhs.as(); - const PyTypeInfo* ti = _tp_info(lhs); - PyVar res; - if(ti->m__eq__){ - res = ti->m__eq__(this, lhs, rhs); - if(!is_not_implemented(res)) return res == vm->True; - } - res = call_method(lhs, __eq__, rhs); +bool VM::py_eq(PyVar lhs, PyVar rhs) { + if(is_int(lhs) && is_int(rhs)) return lhs.as() == rhs.as(); + const PyTypeInfo* ti = _tp_info(lhs); + PyVar res; + if(ti->m__eq__) { + res = ti->m__eq__(this, lhs, rhs); if(!is_not_implemented(res)) return res == vm->True; + } + res = call_method(lhs, __eq__, rhs); + if(!is_not_implemented(res)) return res == vm->True; - ti = _tp_info(rhs); - if(ti->m__eq__){ - res = ti->m__eq__(this, rhs, lhs); - if(!is_not_implemented(res)) return res == vm->True; - } - res = call_method(rhs, __eq__, lhs); + ti = _tp_info(rhs); + if(ti->m__eq__) { + res = ti->m__eq__(this, rhs, lhs); if(!is_not_implemented(res)) return res == vm->True; - return false; + } + res = call_method(rhs, __eq__, lhs); + if(!is_not_implemented(res)) return res == vm->True; + return false; +} + +PyVar VM::py_op(std::string_view name) { + PyVar func; + auto it = __cached_op_funcs.find(name); + if(it == __cached_op_funcs.end()) { + func = py_import("operator")->attr(StrName::get(name)); + __cached_op_funcs[name] = func; + } else { + func = it->second; + } + return func; +} + +i64 VM::normalized_index(i64 index, int size) { + if(index < 0) index += size; + if(index < 0 || index >= size) { IndexError(std::to_string(index) + " not in [0, " + std::to_string(size) + ")"); } + return index; +} + +PyVar VM::_py_next(const PyTypeInfo* ti, PyVar obj) { + if(ti->op__next__) { + unsigned n = ti->op__next__(this, obj); + return __pack_next_retval(n); + } + return call_method(obj, __next__); +} + +PyVar VM::py_next(PyVar obj) { + const PyTypeInfo* ti = _tp_info(obj); + return _py_next(ti, obj); +} + +bool VM::py_callable(PyVar obj) { + Type cls = vm->_tp(obj); + switch(cls.index) { + case VM::tp_function.index: return true; + case VM::tp_native_func.index: return true; + case VM::tp_bound_method.index: return true; + case VM::tp_type.index: return true; + } + return vm->find_name_in_mro(cls, __call__) != nullptr; +} + +PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key) { + auto _lock = heap.gc_scope_lock(); + const Tuple& args_tuple = PK_OBJ_GET(Tuple, args); // from *args, it must be a tuple + if(key == vm->None && args_tuple.size() == 2) { + // fast path + PyVar a = args_tuple[0]; + PyVar b = args_tuple[1]; + return (this->*op)(a, b) ? a : b; } - PyVar VM::py_op(std::string_view name){ - PyVar func; - auto it = __cached_op_funcs.find(name); - if(it == __cached_op_funcs.end()){ - func = py_import("operator")->attr(StrName::get(name)); - __cached_op_funcs[name] = func; - }else{ - func = it->second; + if(args_tuple.size() == 0) TypeError("expected at least 1 argument, got 0"); + + ArgsView view(nullptr, nullptr); + if(args_tuple.size() == 1) { + view = cast_array_view(args_tuple[0]); + } else { + view = ArgsView(args_tuple); + } + + if(view.empty()) ValueError("arg is an empty sequence"); + PyVar res = view[0]; + + if(key == vm->None) { + for(int i = 1; i < view.size(); i++) { + if((this->*op)(view[i], res)) res = view[i]; } - return func; - } - - i64 VM::normalized_index(i64 index, int size){ - if(index < 0) index += size; - if(index < 0 || index >= size){ - IndexError(std::to_string(index) + " not in [0, " + std::to_string(size) + ")"); - } - return index; - } - - PyVar VM::_py_next(const PyTypeInfo* ti, PyVar obj){ - if(ti->op__next__){ - unsigned n = ti->op__next__(this, obj); - return __pack_next_retval(n); - } - return call_method(obj, __next__); - } - - PyVar VM::py_next(PyVar obj){ - const PyTypeInfo* ti = _tp_info(obj); - return _py_next(ti, obj); - } - - bool VM::py_callable(PyVar obj){ - Type cls = vm->_tp(obj); - switch(cls.index){ - case VM::tp_function.index: return true; - case VM::tp_native_func.index: return true; - case VM::tp_bound_method.index: return true; - case VM::tp_type.index: return true; - } - return vm->find_name_in_mro(cls, __call__) != nullptr; - } - - PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key){ + } else { auto _lock = heap.gc_scope_lock(); - const Tuple& args_tuple = PK_OBJ_GET(Tuple, args); // from *args, it must be a tuple - if(key==vm->None && args_tuple.size()==2){ - // fast path - PyVar a = args_tuple[0]; - PyVar b = args_tuple[1]; - return (this->*op)(a, b) ? a : b; + for(int i = 1; i < view.size(); i++) { + PyVar a = call(key, view[i]); + PyVar b = call(key, res); + if((this->*op)(a, b)) res = view[i]; } + } + return res; +} - if(args_tuple.size() == 0) TypeError("expected at least 1 argument, got 0"); - - ArgsView view(nullptr, nullptr); - if(args_tuple.size()==1){ - view = cast_array_view(args_tuple[0]); - }else{ - view = ArgsView(args_tuple); +PyObject* VM::py_import(Str path, bool throw_err) { + if(path.empty()) vm->ValueError("empty module name"); + static auto f_join = [](const vector& cpnts) { + SStream ss; + for(int i = 0; i < cpnts.size(); i++) { + if(i != 0) ss << "."; + ss << cpnts[i]; } + return ss.str(); + }; - if(view.empty()) ValueError("arg is an empty sequence"); - PyVar res = view[0]; - - if(key == vm->None){ - for(int i=1; i*op)(view[i], res)) res = view[i]; - } - }else{ - auto _lock = heap.gc_scope_lock(); - for(int i=1; i*op)(a, b)) res = view[i]; - } + if(path[0] == '.') { + if(__import_context.pending.empty()) { ImportError("relative import outside of package"); } + Str curr_path = __import_context.pending.back(); + bool curr_is_init = __import_context.pending_is_init.back(); + // convert relative path to absolute path + vector cpnts = curr_path.split('.'); + int prefix = 0; // how many dots in the prefix + for(int i = 0; i < path.length(); i++) { + if(path[i] == '.') + prefix++; + else + break; } - return res; + if(prefix > cpnts.size()) ImportError("attempted relative import beyond top-level package"); + path = path.substr(prefix); // remove prefix + for(int i = (int)curr_is_init; i < prefix; i++) + cpnts.pop_back(); + if(!path.empty()) cpnts.push_back(path.sv()); + path = f_join(cpnts); } - PyObject* VM::py_import(Str path, bool throw_err){ - if(path.empty()) vm->ValueError("empty module name"); - static auto f_join = [](const vector& cpnts){ - SStream ss; - for(int i=0; i cpnts = curr_path.split('.'); - int prefix = 0; // how many dots in the prefix - for(int i=0; i cpnts.size()) ImportError("attempted relative import beyond top-level package"); - path = path.substr(prefix); // remove prefix - for(int i=(int)curr_is_init; i path_cpnts = path.split('.'); + // check circular import + if(__import_context.pending.size() > 128) { ImportError("maximum recursion depth exceeded while importing"); } + + // try import + Str filename = path.replace('.', PK_PLATFORM_SEP) + ".py"; + Str source; + bool is_init = false; + auto it = _lazy_modules.find(name); + if(it == _lazy_modules.end()) { + int out_size; + unsigned char* out = _import_handler(filename.c_str(), &out_size); + if(out == nullptr) { + filename = path.replace('.', PK_PLATFORM_SEP).str() + PK_PLATFORM_SEP + "__init__.py"; + is_init = true; + out = _import_handler(filename.c_str(), &out_size); } - - assert(path.begin()[0] != '.' && path.end()[-1] != '.'); - - // check existing module - StrName name(path); - PyVar ext_mod = _modules.try_get(name); - if(ext_mod != nullptr) return ext_mod.get(); - - vector path_cpnts = path.split('.'); - // check circular import - if(__import_context.pending.size() > 128){ - ImportError("maximum recursion depth exceeded while importing"); + if(out == nullptr) { + if(throw_err) + ImportError(_S("module ", path.escape(), " not found")); + else + return nullptr; } - - // try import - Str filename = path.replace('.', PK_PLATFORM_SEP) + ".py"; - Str source; - bool is_init = false; - auto it = _lazy_modules.find(name); - if(it == _lazy_modules.end()){ - int out_size; - unsigned char* out = _import_handler(filename.c_str(), &out_size); - if(out == nullptr){ - filename = path.replace('.', PK_PLATFORM_SEP).str() + PK_PLATFORM_SEP + "__init__.py"; - is_init = true; - out = _import_handler(filename.c_str(), &out_size); - } - if(out == nullptr){ - if(throw_err) ImportError(_S("module ", path.escape(), " not found")); - else return nullptr; - } - assert(out_size >= 0); - source = Str(std::string_view((char*)out, out_size)); - std::free(out); - }else{ - source = it->second; - _lazy_modules.erase(it); - } - auto _ = __import_context.scope(path, is_init); - CodeObject_ code = compile(source, filename, EXEC_MODE); - - Str name_cpnt = path_cpnts.back(); - path_cpnts.pop_back(); - PyObject* new_mod = new_module(name_cpnt, f_join(path_cpnts)); - _exec(code, new_mod); - return new_mod; + assert(out_size >= 0); + source = Str(std::string_view((char*)out, out_size)); + std::free(out); + } else { + source = it->second; + _lazy_modules.erase(it); } + auto _ = __import_context.scope(path, is_init); + CodeObject_ code = compile(source, filename, EXEC_MODE); - VM::~VM() { - // clear managed heap - for(PyObject* obj: heap.gen) heap._delete(obj); - for(PyObject* obj: heap._no_gc) heap._delete(obj); - // clear everything - callstack.clear(); - s_data.clear(); - _all_types.clear(); - _modules.clear(); - _lazy_modules.clear(); - } + Str name_cpnt = path_cpnts.back(); + path_cpnts.pop_back(); + PyObject* new_mod = new_module(name_cpnt, f_join(path_cpnts)); + _exec(code, new_mod); + return new_mod; +} -PyVar VM::py_negate(PyVar obj){ +VM::~VM() { + // clear managed heap + for(PyObject* obj: heap.gen) + heap._delete(obj); + for(PyObject* obj: heap._no_gc) + heap._delete(obj); + // clear everything + callstack.clear(); + s_data.clear(); + _all_types.clear(); + _modules.clear(); + _lazy_modules.clear(); +} + +PyVar VM::py_negate(PyVar obj) { const PyTypeInfo* ti = _tp_info(obj); if(ti->m__neg__) return ti->m__neg__(this, obj); return call_method(obj, __neg__); } -bool VM::__py_bool_non_trivial(PyVar obj){ +bool VM::__py_bool_non_trivial(PyVar obj) { if(obj == None) return false; if(is_int(obj)) return _CAST(i64, obj) != 0; if(is_float(obj)) return _CAST(f64, obj) != 0.0; PyVar self; PyVar len_f = get_unbound_method(obj, __len__, &self, false); - if(self != PY_NULL){ + if(self != PY_NULL) { PyVar ret = call_method(self, len_f); return CAST(i64, ret) != 0; } return true; } -void VM::__obj_gc_mark(PyObject* obj){ +void VM::__obj_gc_mark(PyObject* obj) { if(obj->gc_marked) return; obj->gc_marked = true; const PyTypeInfo* ti = _tp_info(obj->type); if(ti->vt._gc_mark) ti->vt._gc_mark(obj->_value_ptr(), this); - if(obj->is_attr_valid()){ - obj->attr().apply([this](StrName _, PyVar obj){ - if (obj.is_ptr) vm->__obj_gc_mark((obj).get()); + if(obj->is_attr_valid()) { + obj->attr().apply([this](StrName _, PyVar obj) { + if(obj.is_ptr) vm->__obj_gc_mark((obj).get()); }); } } -void VM::__stack_gc_mark(PyVar* begin, PyVar* end){ - for(PyVar* it=begin; it!=end; it++){ - if(it->is_ptr){ +void VM::__stack_gc_mark(PyVar* begin, PyVar* end) { + for(PyVar* it = begin; it != end; it++) { + if(it->is_ptr) { __obj_gc_mark(it->get()); - }else{ - if(it->type == tp_stack_memory){ + } else { + if(it->type == tp_stack_memory) { // [sm:3, _0, _1, _2, sm:-3] int count = it->as().count; if(count > 0) it += count; @@ -466,7 +457,7 @@ void VM::__stack_gc_mark(PyVar* begin, PyVar* end){ } } -void* VM::__stack_alloc(int size){ +void* VM::__stack_alloc(int size) { int count = size / sizeof(PyVar) + 1; s_data.emplace(tp_stack_memory, StackMemory(count)); void* out = s_data._sp; @@ -475,56 +466,56 @@ void* VM::__stack_alloc(int size){ return out; } -List VM::py_list(PyVar it){ +List VM::py_list(PyVar it) { auto _lock = heap.gc_scope_lock(); it = py_iter(it); List list; const PyTypeInfo* info = _tp_info(it); PyVar obj = _py_next(info, it); - while(obj != StopIteration){ + while(obj != StopIteration) { list.push_back(obj); obj = _py_next(info, it); } return list; } - - -void VM::parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step){ - auto clip = [](int value, int min, int max){ +void VM::parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step) { + auto clip = [](int value, int min, int max) { if(value < min) return min; if(value > max) return max; return value; }; - if(s.step == None) step = 1; - else step = CAST(int, s.step); + if(s.step == None) + step = 1; + else + step = CAST(int, s.step); if(step == 0) ValueError("slice step cannot be zero"); - if(step > 0){ - if(s.start == None){ + if(step > 0) { + if(s.start == None) { start = 0; - }else{ + } else { start = CAST(int, s.start); if(start < 0) start += length; start = clip(start, 0, length); } - if(s.stop == None){ + if(s.stop == None) { stop = length; - }else{ + } else { stop = CAST(int, s.stop); if(stop < 0) stop += length; stop = clip(stop, 0, length); } - }else{ - if(s.start == None){ + } else { + if(s.start == None) { start = length - 1; - }else{ + } else { start = CAST(int, s.start); if(start < 0) start += length; start = clip(start, -1, length - 1); } - if(s.stop == None){ + if(s.stop == None) { stop = -1; - }else{ + } else { stop = CAST(int, s.stop); if(stop < 0) stop += length; stop = clip(stop, -1, length - 1); @@ -532,14 +523,14 @@ void VM::parse_int_slice(const Slice& s, int length, int& start, int& stop, int& } } -i64 VM::py_hash(PyVar obj){ +i64 VM::py_hash(PyVar obj) { // https://docs.python.org/3.10/reference/datamodel.html#object.__hash__ const PyTypeInfo* ti = _tp_info(obj); if(ti->m__hash__) return ti->m__hash__(this, obj); PyVar self; PyVar f = get_unbound_method(obj, __hash__, &self, false); - if(f != nullptr){ + if(f != nullptr) { PyVar ret = call_method(self, f); return CAST(i64, ret); } @@ -547,23 +538,24 @@ i64 VM::py_hash(PyVar obj){ if(ti == &_all_types[tp_object]) return obj.hash(); // otherwise, we check if it has a custom __eq__ other than object.__eq__ bool has_custom_eq = false; - if(ti->m__eq__) has_custom_eq = true; - else{ + if(ti->m__eq__) + has_custom_eq = true; + else { f = get_unbound_method(obj, __eq__, &self, false); has_custom_eq = f != _t(tp_object)->attr(__eq__); } - if(has_custom_eq){ + if(has_custom_eq) { TypeError(_S("unhashable type: ", ti->name.escape())); - }else{ + } else { return obj.hash(); } } -PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar locals){ +PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar locals) { Frame* frame = &vm->callstack.top(); // fast path - if(globals == vm->None && locals == vm->None){ + if(globals == vm->None && locals == vm->None) { return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals); } @@ -575,18 +567,18 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local NameDict_ locals_closure = nullptr; Dict* locals_dict = nullptr; - if(globals == vm->None){ + if(globals == vm->None) { globals_obj = frame->_module; - }else{ - if(is_type(globals, VM::tp_mappingproxy)){ + } else { + if(is_type(globals, VM::tp_mappingproxy)) { globals_obj = PK_OBJ_GET(MappingProxy, globals).obj; - }else{ + } else { check_compatible_type(globals, VM::tp_dict); // make a temporary object and copy globals into it globals_obj = new_object(VM::tp_object).get(); globals_obj->_attr = new NameDict(); globals_dict = &PK_OBJ_GET(Dict, globals); - globals_dict->apply([&](PyVar k, PyVar v){ + globals_dict->apply([&](PyVar k, PyVar v) { globals_obj->attr().set(CAST(Str&, k), v); }); } @@ -594,50 +586,53 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local PyVar retval = nullptr; - if(locals == vm->None){ - retval = vm->_exec(code, globals_obj); // only globals - }else{ + if(locals == vm->None) { + retval = vm->_exec(code, globals_obj); // only globals + } else { check_compatible_type(locals, VM::tp_dict); locals_dict = &PK_OBJ_GET(Dict, locals); locals_closure = std::make_shared(); - locals_dict->apply([&](PyVar k, PyVar v){ + locals_dict->apply([&](PyVar k, PyVar v) { locals_closure->set(CAST(Str&, k), v); }); - PyObject* _callable = heap.gcnew(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure); + PyObject* _callable = + heap.gcnew(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure); retval = vm->_exec(code.get(), globals_obj, _callable, vm->s_data._sp); } - if(globals_dict){ + if(globals_dict) { globals_dict->clear(); - globals_obj->attr().apply([&](StrName k, PyVar v){ + globals_obj->attr().apply([&](StrName k, PyVar v) { globals_dict->set(vm, VAR(k.sv()), v); }); } - if(locals_dict){ + if(locals_dict) { locals_dict->clear(); - locals_closure->apply([&](StrName k, PyVar v){ + locals_closure->apply([&](StrName k, PyVar v) { locals_dict->set(vm, VAR(k.sv()), v); }); } return retval; } -void VM::py_exec(std::string_view source, PyVar globals, PyVar locals){ +void VM::py_exec(std::string_view source, PyVar globals, PyVar locals) { CodeObject_ code = vm->compile(source, "", EXEC_MODE, true); __py_exec_internal(code, globals, locals); } -PyVar VM::py_eval(std::string_view source, PyVar globals, PyVar locals){ +PyVar VM::py_eval(std::string_view source, PyVar globals, PyVar locals) { CodeObject_ code = vm->compile(source, "", EVAL_MODE, true); return __py_exec_internal(code, globals, locals); } -PyVar VM::__format_object(PyVar obj, Str spec){ +PyVar VM::__format_object(PyVar obj, Str spec) { if(spec.empty()) return VAR(py_str(obj)); char type; - switch(spec.end()[-1]){ - case 'f': case 'd': case 's': + switch(spec.end()[-1]) { + case 'f': + case 'd': + case 's': type = spec.end()[-1]; spec = spec.substr(0, spec.length() - 1); break; @@ -645,69 +640,71 @@ PyVar VM::__format_object(PyVar obj, Str spec){ } char pad_c = ' '; - for(char c: std::string_view("0-=*#@!~")){ - if(spec[0] == c){ + for(char c: std::string_view("0-=*#@!~")) { + if(spec[0] == c) { pad_c = c; spec = spec.substr(1); break; } } char align; - if(spec[0] == '^'){ + if(spec[0] == '^') { align = '^'; spec = spec.substr(1); - }else if(spec[0] == '>'){ + } else if(spec[0] == '>') { align = '>'; spec = spec.substr(1); - }else if(spec[0] == '<'){ + } else if(spec[0] == '<') { align = '<'; spec = spec.substr(1); - }else{ - if(is_int(obj) || is_float(obj)) align = '>'; - else align = '<'; + } else { + if(is_int(obj) || is_float(obj)) + align = '>'; + else + align = '<'; } int dot = spec.index("."); int width, precision; - try{ - if(dot >= 0){ - if(dot == 0){ + try { + if(dot >= 0) { + if(dot == 0) { width = -1; - }else{ + } else { width = std::stoi(spec.substr(0, dot).str()); } - precision = std::stoi(spec.substr(dot+1).str()); - }else{ + precision = std::stoi(spec.substr(dot + 1).str()); + } else { width = std::stoi(spec.str()); precision = -1; } - }catch(...){ - ValueError("invalid format specifer"); - } + } catch(...) { ValueError("invalid format specifer"); } if(type != 'f' && dot >= 0) ValueError("precision not allowed in the format specifier"); Str ret; - if(type == 'f'){ + if(type == 'f') { f64 val = CAST(f64, obj); if(precision < 0) precision = 6; SStream ss; ss.setprecision(precision); ss << val; ret = ss.str(); - }else if(type == 'd'){ + } else if(type == 'd') { ret = std::to_string(CAST(i64, obj)); - }else if(type == 's'){ + } else if(type == 's') { ret = CAST(Str&, obj); - }else{ + } else { ret = py_str(obj); } - if(width != -1 && width > ret.length()){ + if(width != -1 && width > ret.length()) { int pad = width - ret.length(); - if(align == '>' || align == '<'){ + if(align == '>' || align == '<') { std::string padding(pad, pad_c); - if(align == '>') ret = padding.c_str() + ret; - else ret = ret + padding.c_str(); - }else{ // ^ + if(align == '>') + ret = padding.c_str() + ret; + else + ret = ret + padding.c_str(); + } else { // ^ int pad_left = pad / 2; int pad_right = pad - pad_left; std::string padding_left(pad_left, pad_c); @@ -728,72 +725,82 @@ PyObject* VM::new_module(Str name, Str package) { // we do not allow override in order to avoid memory leak // it is because Module objects are not garbage collected - if(_modules.contains(name)){ - throw std::runtime_error(_S("module ", name.escape(), " already exists").str()); - } + if(_modules.contains(name)) { throw std::runtime_error(_S("module ", name.escape(), " already exists").str()); } // set it into _modules _modules.set(name, obj); return obj; } -static std::string _opcode_argstr(VM* vm, int i, Bytecode byte, const CodeObject* co){ +static std::string _opcode_argstr(VM* vm, int i, Bytecode byte, const CodeObject* co) { SStream ss; - if(byte.is_forward_jump()){ + if(byte.is_forward_jump()) { std::string argStr = std::to_string((int16_t)byte.arg); ss << (i64)(int16_t)byte.arg; ss << " (to " << (i64)((int16_t)byte.arg + i) << ")"; return ss.str().str(); } ss << (i64)byte.arg; - switch(byte.op){ - case OP_LOAD_CONST: case OP_FORMAT_STRING: case OP_IMPORT_PATH: + switch(byte.op) { + case OP_LOAD_CONST: + case OP_FORMAT_STRING: + case OP_IMPORT_PATH: if(vm != nullptr) ss << " (" << vm->py_repr(co->consts[byte.arg]) << ")"; break; - case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL: - case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR: - case OP_BEGIN_CLASS: case OP_GOTO: - case OP_DELETE_GLOBAL: case OP_INC_GLOBAL: case OP_DEC_GLOBAL: case OP_STORE_CLASS_ATTR: case OP_FOR_ITER_STORE_GLOBAL: - ss << " (" << StrName(byte.arg).sv() << ")"; - break; - case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST: case OP_INC_FAST: case OP_DEC_FAST: - case OP_FOR_ITER_STORE_FAST: case OP_LOAD_SUBSCR_FAST: case OP_STORE_SUBSCR_FAST: - ss << " (" << co->varnames[byte.arg].sv() << ")"; - break; - case OP_LOAD_FUNCTION: - ss << " (" << co->func_decls[byte.arg]->code->name << ")"; - break; + case OP_LOAD_NAME: + case OP_LOAD_GLOBAL: + case OP_LOAD_NONLOCAL: + case OP_STORE_GLOBAL: + case OP_LOAD_ATTR: + case OP_LOAD_METHOD: + case OP_STORE_ATTR: + case OP_DELETE_ATTR: + case OP_BEGIN_CLASS: + case OP_GOTO: + case OP_DELETE_GLOBAL: + case OP_INC_GLOBAL: + case OP_DEC_GLOBAL: + case OP_STORE_CLASS_ATTR: + case OP_FOR_ITER_STORE_GLOBAL: ss << " (" << StrName(byte.arg).sv() << ")"; break; + case OP_LOAD_FAST: + case OP_STORE_FAST: + case OP_DELETE_FAST: + case OP_INC_FAST: + case OP_DEC_FAST: + case OP_FOR_ITER_STORE_FAST: + case OP_LOAD_SUBSCR_FAST: + case OP_STORE_SUBSCR_FAST: ss << " (" << co->varnames[byte.arg].sv() << ")"; break; + case OP_LOAD_FUNCTION: ss << " (" << co->func_decls[byte.arg]->code->name << ")"; break; } return ss.str().str(); } -Str VM::disassemble(CodeObject_ co){ - auto pad = [](const Str& s, const int n){ +Str VM::disassemble(CodeObject_ co) { + auto pad = [](const Str& s, const int n) { if(s.length() >= n) return s.substr(0, n); return s + std::string(n - s.length(), ' '); }; vector jumpTargets; - for(int i=0; icodes.size(); i++){ + for(int i = 0; i < co->codes.size(); i++) { Bytecode byte = co->codes[i]; - if(byte.is_forward_jump()){ - jumpTargets.push_back((int16_t)byte.arg + i); - } + if(byte.is_forward_jump()) { jumpTargets.push_back((int16_t)byte.arg + i); } } SStream ss; int prev_line = -1; - for(int i=0; icodes.size(); i++){ + for(int i = 0; i < co->codes.size(); i++) { const Bytecode& byte = co->codes[i]; Str line = std::to_string(co->lines[i].lineno); - if(co->lines[i].lineno == prev_line) line = ""; - else{ + if(co->lines[i].lineno == prev_line) + line = ""; + else { if(prev_line != -1) ss << "\n"; prev_line = co->lines[i].lineno; } std::string pointer; - if(jumpTargets.contains(i)){ + if(jumpTargets.contains(i)) { pointer = "-> "; - }else{ + } else { pointer = " "; } ss << pad(line, 8) << pointer << pad(std::to_string(i), 3); @@ -805,8 +812,9 @@ Str VM::disassemble(CodeObject_ co){ if(i != co->codes.size() - 1) ss << '\n'; } - for(auto& decl: co->func_decls){ - ss << "\n\n" << "Disassembly of " << decl->code->name << ":\n"; + for(auto& decl: co->func_decls) { + ss << "\n\n" + << "Disassembly of " << decl->code->name << ":\n"; ss << disassemble(decl->code); } ss << "\n"; @@ -820,65 +828,58 @@ void VM::__log_s_data(const char* title) { SStream ss; if(title) ss << title << " | "; std::map sp_bases; - callstack.apply([&](Frame& f){ + callstack.apply([&](Frame& f) { assert(f._sp_base != nullptr); sp_bases[f._sp_base] += 1; }); Frame* frame = &callstack.top(); int line = frame->curr_lineno(); ss << frame->co->name << ":" << line << " ["; - for(PyVar* p=s_data.begin(); p!=s_data.end(); p++){ + for(PyVar* p = s_data.begin(); p != s_data.end(); p++) { ss << std::string(sp_bases[p], '|'); if(sp_bases[p] > 0) ss << " "; - if(*p == PY_NULL) ss << "NULL"; - else{ - switch(p->type){ + if(*p == PY_NULL) + ss << "NULL"; + else { + switch(p->type) { case tp_none_type: ss << "None"; break; case tp_int: ss << _CAST(i64, *p); break; case tp_float: ss << _CAST(f64, *p); break; case tp_bool: ss << ((*p == True) ? "True" : "False"); break; case tp_str: ss << _CAST(Str, *p).escape(); break; - case tp_function: - ss << p->obj_get().decl->code->name << "()"; - break; - case tp_type: - ss << "obj_get()).escape() + ">"; - break; - case tp_list: - ss << "list(size=" << p->obj_get().size() << ")"; - break; - case tp_tuple: - ss << "tuple(size=" << p->obj_get().size() << ")"; - break; + case tp_function: ss << p->obj_get().decl->code->name << "()"; break; + case tp_type: ss << "obj_get()).escape() + ">"; break; + case tp_list: ss << "list(size=" << p->obj_get().size() << ")"; break; + case tp_tuple: ss << "tuple(size=" << p->obj_get().size() << ")"; break; case tp_stack_memory: { int count = p->obj_get().count; ss << "M[" << count << "]"; if(count > 0) p += count; break; } - default: - ss << "(" << _type_name(this, p->type) << ")"; - break; + default: ss << "(" << _type_name(this, p->type) << ")"; break; } } ss << ", "; } std::string output = ss.str().str(); if(!s_data.empty()) { - output.pop_back(); output.pop_back(); + output.pop_back(); + output.pop_back(); } output.push_back(']'); Bytecode byte = *frame->_ip; - std::cout << output << " " << OP_NAMES[byte.op] << " " << _opcode_argstr(nullptr, frame->ip(), byte, frame->co) << std::endl; + std::cout << output << " " << OP_NAMES[byte.op] << " " << _opcode_argstr(nullptr, frame->ip(), byte, frame->co) + << std::endl; } #endif -void VM::__init_builtin_types(){ - _all_types.emplace_back(nullptr, Type(), nullptr, "", false); // 0 is not used +void VM::__init_builtin_types() { + _all_types.emplace_back(nullptr, Type(), nullptr, "", false); // 0 is not used _all_types.emplace_back(heap._new(tp_type, tp_object), Type(), nullptr, "object", true); _all_types.emplace_back(heap._new(tp_type, tp_type), tp_object, nullptr, "type", false); - auto validate = [](Type type, PyObject* ret){ + auto validate = [](Type type, PyObject* ret) { Type ret_t = ret->as(); if(ret_t != type) exit(-3); }; @@ -920,7 +921,7 @@ void VM::__init_builtin_types(){ this->StopIteration = new_type_object(nullptr, "StopIteration", tp_exception, true); this->builtins = new_module("builtins"); - + // setup public types builtins->attr().set("type", _t(tp_type)); builtins->attr().set("object", _t(tp_object)); @@ -945,36 +946,36 @@ void VM::__init_builtin_types(){ this->_main = new_module("__main__"); } -void VM::__unpack_as_list(ArgsView args, List& list){ +void VM::__unpack_as_list(ArgsView args, List& list) { auto _lock = heap.gc_scope_lock(); - for(PyVar obj: args){ - if(is_type(obj, tp_star_wrapper)){ + for(PyVar obj: args) { + if(is_type(obj, tp_star_wrapper)) { const StarWrapper& w = _CAST(StarWrapper&, obj); // maybe this check should be done in the compile time if(w.level != 1) TypeError("expected level 1 star wrapper"); PyVar _0 = py_iter(w.obj); const PyTypeInfo* info = _tp_info(_0); PyVar _1 = _py_next(info, _0); - while(_1 != StopIteration){ + while(_1 != StopIteration) { list.push_back(_1); _1 = _py_next(info, _0); } - }else{ + } else { list.push_back(obj); } } } -void VM::__unpack_as_dict(ArgsView args, Dict& dict){ +void VM::__unpack_as_dict(ArgsView args, Dict& dict) { auto _lock = heap.gc_scope_lock(); - for(PyVar obj: args){ - if(is_type(obj, tp_star_wrapper)){ + for(PyVar obj: args) { + if(is_type(obj, tp_star_wrapper)) { const StarWrapper& w = _CAST(StarWrapper&, obj); // maybe this check should be done in the compile time if(w.level != 2) TypeError("expected level 2 star wrapper"); const Dict& other = CAST(Dict&, w.obj); dict.update(this, other); - }else{ + } else { const Tuple& t = CAST(Tuple&, obj); if(t.size() != 2) TypeError("expected tuple of length 2"); dict.set(this, t[0], t[1]); @@ -982,89 +983,88 @@ void VM::__unpack_as_dict(ArgsView args, Dict& dict){ } } - -void VM::__prepare_py_call(PyVar* buffer, ArgsView args, ArgsView kwargs, const FuncDecl_& decl){ +void VM::__prepare_py_call(PyVar* buffer, ArgsView args, ArgsView kwargs, const FuncDecl_& decl) { const CodeObject* co = decl->code.get(); int decl_argc = decl->args.size(); - if(args.size() < decl_argc){ - vm->TypeError(_S( - co->name, "() takes ", decl_argc, " positional arguments but ", args.size(), " were given" - )); + if(args.size() < decl_argc) { + vm->TypeError(_S(co->name, "() takes ", decl_argc, " positional arguments but ", args.size(), " were given")); } int i = 0; // prepare args std::memset(buffer, 0, co->nlocals * sizeof(PyVar)); - for(int index: decl->args) buffer[index] = args[i++]; + for(int index: decl->args) + buffer[index] = args[i++]; // prepare kwdefaults - for(auto& kv: decl->kwargs) buffer[kv.index] = kv.value; - + for(auto& kv: decl->kwargs) + buffer[kv.index] = kv.value; + // handle *args - if(decl->starred_arg != -1){ + if(decl->starred_arg != -1) { ArgsView vargs(args.begin() + i, args.end()); buffer[decl->starred_arg] = VAR(vargs.to_tuple()); i += vargs.size(); - }else{ + } else { // kwdefaults override - for(auto& kv: decl->kwargs){ + for(auto& kv: decl->kwargs) { if(i >= args.size()) break; buffer[kv.index] = args[i++]; } if(i < args.size()) TypeError(_S("too many arguments", " (", decl->code->name, ')')); } - + PyVar vkwargs; - if(decl->starred_kwarg != -1){ + if(decl->starred_kwarg != -1) { vkwargs = VAR(Dict()); buffer[decl->starred_kwarg] = vkwargs; - }else{ + } else { vkwargs = nullptr; } - for(int j=0; jkw_to_index.try_get_likely_found(key); // if key is an explicit key, set as local variable - if(index >= 0){ - buffer[index] = kwargs[j+1]; - }else{ + if(index >= 0) { + buffer[index] = kwargs[j + 1]; + } else { // otherwise, set as **kwargs if possible - if(vkwargs == nullptr){ + if(vkwargs == nullptr) { TypeError(_S(key.escape(), " is an invalid keyword argument for ", co->name, "()")); - }else{ + } else { Dict& dict = _CAST(Dict&, vkwargs); - dict.set(this, VAR(key.sv()), kwargs[j+1]); + dict.set(this, VAR(key.sv()), kwargs[j + 1]); } } } } -PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ - PyVar* p1 = s_data._sp - KWARGC*2; +PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) { + PyVar* p1 = s_data._sp - KWARGC * 2; PyVar* p0 = p1 - ARGC - 2; // [callable, , args..., kwargs...] // ^p0 ^p1 ^_sp - PyVar callable = p1[-ARGC-2]; + PyVar callable = p1[-ARGC - 2]; Type callable_t = _tp(callable); // handle boundmethod, do a patch - if(callable_t == tp_bound_method){ + if(callable_t == tp_bound_method) { assert(p0[1] == PY_NULL); BoundMethod& bm = PK_OBJ_GET(BoundMethod, callable); - callable = bm.func; // get unbound method + callable = bm.func; // get unbound method callable_t = _tp(callable); p1[-(ARGC + 2)] = bm.func; p1[-(ARGC + 1)] = bm.self; // [unbound, self, args..., kwargs...] } - ArgsView args(p0[1]==PY_NULL ? (p0+2) : (p0+1), p1); + ArgsView args(p0[1] == PY_NULL ? (p0 + 2) : (p0 + 1), p1); ArgsView kwargs(p1, s_data._sp); PyVar* _base = args.begin(); - if(callable_t == tp_function){ + if(callable_t == tp_function) { /*****************_py_call*****************/ // check stack overflow if(s_data.is_overflow()) StackOverflowError(); @@ -1072,15 +1072,22 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ const Function& fn = PK_OBJ_GET(Function, callable); const CodeObject* co = fn.decl->code.get(); - switch(fn.decl->type){ + switch(fn.decl->type) { case FuncType::NORMAL: __prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl); // copy buffer back to stack s_data.reset(_base + co->nlocals); - for(int j=0; jnlocals; j++) _base[j] = __vectorcall_buffer[j]; + for(int j = 0; j < co->nlocals; j++) + _base[j] = __vectorcall_buffer[j]; break; case FuncType::SIMPLE: - if(args.size() != fn.decl->args.size()) TypeError(_S(co->name, "() takes ", fn.decl->args.size(), " positional arguments but ", args.size(), " were given")); + if(args.size() != fn.decl->args.size()) + TypeError(_S(co->name, + "() takes ", + fn.decl->args.size(), + " positional arguments but ", + args.size(), + " were given")); if(!kwargs.empty()) TypeError(_S(co->name, "() takes no keyword arguments")); // [callable, , args..., local_vars...] // ^p0 ^p1 ^_sp @@ -1089,7 +1096,13 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ std::memset(p1, 0, (char*)s_data._sp - (char*)p1); break; case FuncType::EMPTY: - if(args.size() != fn.decl->args.size()) TypeError(_S(co->name, "() takes ", fn.decl->args.size(), " positional arguments but ", args.size(), " were given")); + if(args.size() != fn.decl->args.size()) + TypeError(_S(co->name, + "() takes ", + fn.decl->args.size(), + " positional arguments but ", + args.size(), + " were given")); if(!kwargs.empty()) TypeError(_S(co->name, "() takes no keyword arguments")); s_data.reset(p0); return None; @@ -1097,10 +1110,8 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ __prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl); s_data.reset(p0); callstack.emplace(nullptr, co, fn._module, callable.get(), nullptr); - return __py_generator( - callstack.popx(), - ArgsView(__vectorcall_buffer, __vectorcall_buffer + co->nlocals) - ); + return __py_generator(callstack.popx(), + ArgsView(__vectorcall_buffer, __vectorcall_buffer + co->nlocals)); default: PK_UNREACHABLE() }; @@ -1111,22 +1122,23 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ /*****************_py_call*****************/ } - if(callable_t == tp_native_func){ + if(callable_t == tp_native_func) { const auto& f = PK_OBJ_GET(NativeFunc, callable); PyVar ret; - if(f.decl != nullptr){ + if(f.decl != nullptr) { int co_nlocals = f.decl->code->nlocals; __prepare_py_call(__vectorcall_buffer, args, kwargs, f.decl); // copy buffer back to stack s_data.reset(_base + co_nlocals); - for(int j=0; jTypeError(_S("expected ", f.argc, " arguments, got ", args.size())); - } + if(KWARGC != 0) + TypeError( + "old-style native_func does not accept keyword arguments. If you want to skip this check, specify `argc` to -1"); + if(args.size() != f.argc) { vm->TypeError(_S("expected ", f.argc, " arguments, got ", args.size())); } } ret = f.call(this, args); } @@ -1134,28 +1146,30 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ return ret; } - if(callable_t == tp_type){ + if(callable_t == tp_type) { // [type, NULL, args..., kwargs...] PyVar new_f = *find_name_in_mro(PK_OBJ_GET(Type, callable), __new__); PyVar obj; - assert(new_f != nullptr && p0[1]==PY_NULL); + assert(new_f != nullptr && p0[1] == PY_NULL); if(new_f == __cached_object_new) { // fast path for object.__new__ obj = vm->new_object(PK_OBJ_GET(Type, callable)); - }else{ + } else { PUSH(new_f); PUSH(PY_NULL); - PUSH(callable); // cls - for(PyVar o: args) PUSH(o); - for(PyVar o: kwargs) PUSH(o); + PUSH(callable); // cls + for(PyVar o: args) + PUSH(o); + for(PyVar o: kwargs) + PUSH(o); // if obj is not an instance of `cls`, the behavior is undefined - obj = vectorcall(ARGC+1, KWARGC); + obj = vectorcall(ARGC + 1, KWARGC); } // __init__ PyVar self; callable = get_unbound_method(obj, __init__, &self, false); - if (callable != nullptr) { + if(callable != nullptr) { callable_t = _tp(callable); // replace `NULL` with `self` p1[-(ARGC + 2)] = callable; @@ -1164,7 +1178,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ vectorcall(ARGC, KWARGC); // We just discard the return value of `__init__` // in cpython it raises a TypeError if the return value is not None - }else{ + } else { // manually reset the stack s_data.reset(p0); } @@ -1174,7 +1188,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ // handle `__call__` overload PyVar self; PyVar call_f = get_unbound_method(callable, __call__, &self, false); - if(self != PY_NULL){ + if(self != PY_NULL) { p1[-(ARGC + 2)] = call_f; p1[-(ARGC + 1)] = self; // [call_f, self, args..., kwargs...] @@ -1183,7 +1197,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){ TypeError(_type_name(vm, callable_t).escape() + " object is not callable"); } -void VM::delattr(PyVar _0, StrName _name){ +void VM::delattr(PyVar _0, StrName _name) { const PyTypeInfo* ti = _tp_info(_0); if(ti->m__delattr__ && ti->m__delattr__(this, _0, _name)) return; if(is_tagged(_0) || !_0->is_attr_valid()) TypeError("cannot delete attribute"); @@ -1191,59 +1205,55 @@ void VM::delattr(PyVar _0, StrName _name){ } // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance -PyVar VM::getattr(PyVar obj, StrName name, bool throw_err){ +PyVar VM::getattr(PyVar obj, StrName name, bool throw_err) { Type objtype(0); // handle super() proxy - if(is_type(obj, tp_super)){ + if(is_type(obj, tp_super)) { const Super& super = PK_OBJ_GET(Super, obj); obj = super.first; objtype = super.second; - }else{ + } else { objtype = _tp(obj); } PyVar* cls_var = find_name_in_mro(objtype, name); - if(cls_var != nullptr){ + if(cls_var != nullptr) { // handle descriptor - if(is_type(*cls_var, tp_property)){ + if(is_type(*cls_var, tp_property)) { const Property& prop = PK_OBJ_GET(Property, *cls_var); return call(prop.getter, obj); } } // handle instance __dict__ - if(!is_tagged(obj) && obj->is_attr_valid()){ + if(!is_tagged(obj) && obj->is_attr_valid()) { PyVar* val; - if(obj.type == tp_type){ + if(obj.type == tp_type) { val = find_name_in_mro(PK_OBJ_GET(Type, obj), name); - if(val != nullptr){ + if(val != nullptr) { if(is_tagged(*val)) return *val; if(val->type == tp_staticmethod) return PK_OBJ_GET(StaticMethod, *val).func; if(val->type == tp_classmethod) return VAR(BoundMethod(obj, PK_OBJ_GET(ClassMethod, *val).func)); return *val; } - }else{ + } else { val = obj->attr().try_get_2_likely_found(name); if(val != nullptr) return *val; } } - if(cls_var != nullptr){ + if(cls_var != nullptr) { // bound method is non-data descriptor - if(!is_tagged(*cls_var)){ - switch(cls_var->type.index){ - case tp_function.index: - return VAR(BoundMethod(obj, *cls_var)); - case tp_native_func.index: - return VAR(BoundMethod(obj, *cls_var)); - case tp_staticmethod.index: - return PK_OBJ_GET(StaticMethod, *cls_var).func; - case tp_classmethod.index: - return VAR(BoundMethod(_t(objtype), PK_OBJ_GET(ClassMethod, *cls_var).func)); + if(!is_tagged(*cls_var)) { + switch(cls_var->type.index) { + case tp_function.index: return VAR(BoundMethod(obj, *cls_var)); + case tp_native_func.index: return VAR(BoundMethod(obj, *cls_var)); + case tp_staticmethod.index: return PK_OBJ_GET(StaticMethod, *cls_var).func; + case tp_classmethod.index: return VAR(BoundMethod(_t(objtype), PK_OBJ_GET(ClassMethod, *cls_var).func)); } } return *cls_var; } const PyTypeInfo* ti = &_all_types[objtype]; - if(ti->m__getattr__){ + if(ti->m__getattr__) { PyVar ret = ti->m__getattr__(this, obj, name); if(ret) return ret; } @@ -1254,67 +1264,59 @@ PyVar VM::getattr(PyVar obj, StrName name, bool throw_err){ // used by OP_LOAD_METHOD // try to load a unbound method (fallback to `getattr` if not found) -PyVar VM::get_unbound_method(PyVar obj, StrName name, PyVar* self, bool throw_err, bool fallback){ +PyVar VM::get_unbound_method(PyVar obj, StrName name, PyVar* self, bool throw_err, bool fallback) { self->set_null(); Type objtype(0); // handle super() proxy - if(is_type(obj, tp_super)){ + if(is_type(obj, tp_super)) { const Super& super = PK_OBJ_GET(Super, obj); obj = super.first; objtype = super.second; - }else{ + } else { objtype = _tp(obj); } PyVar* cls_var = find_name_in_mro(objtype, name); - if(fallback){ - if(cls_var != nullptr){ + if(fallback) { + if(cls_var != nullptr) { // handle descriptor - if(is_type(*cls_var, tp_property)){ + if(is_type(*cls_var, tp_property)) { const Property& prop = PK_OBJ_GET(Property, *cls_var); return call(prop.getter, obj); } } // handle instance __dict__ - if(!is_tagged(obj) && obj->is_attr_valid()){ + if(!is_tagged(obj) && obj->is_attr_valid()) { PyVar* val; - if(obj.type == tp_type){ + if(obj.type == tp_type) { val = find_name_in_mro(PK_OBJ_GET(Type, obj), name); - if(val != nullptr){ + if(val != nullptr) { if(is_tagged(*val)) return *val; if(val->type == tp_staticmethod) return PK_OBJ_GET(StaticMethod, *val).func; if(val->type == tp_classmethod) return VAR(BoundMethod(obj, PK_OBJ_GET(ClassMethod, *val).func)); return *val; } - }else{ + } else { val = obj->attr().try_get_2_likely_found(name); if(val != nullptr) return *val; } } } - if(cls_var != nullptr){ - if(!is_tagged(*cls_var)){ - switch(cls_var->type.index){ - case tp_function.index: - *self = obj; - break; - case tp_native_func.index: - *self = obj; - break; - case tp_staticmethod.index: - self->set_null(); - return PK_OBJ_GET(StaticMethod, *cls_var).func; - case tp_classmethod.index: - *self = _t(objtype); - return PK_OBJ_GET(ClassMethod, *cls_var).func; + if(cls_var != nullptr) { + if(!is_tagged(*cls_var)) { + switch(cls_var->type.index) { + case tp_function.index: *self = obj; break; + case tp_native_func.index: *self = obj; break; + case tp_staticmethod.index: self->set_null(); return PK_OBJ_GET(StaticMethod, *cls_var).func; + case tp_classmethod.index: *self = _t(objtype); return PK_OBJ_GET(ClassMethod, *cls_var).func; } } return *cls_var; } const PyTypeInfo* ti = &_all_types[objtype]; - if(fallback && ti->m__getattr__){ + if(fallback && ti->m__getattr__) { PyVar ret = ti->m__getattr__(this, obj, name); if(ret) return ret; } @@ -1323,24 +1325,24 @@ PyVar VM::get_unbound_method(PyVar obj, StrName name, PyVar* self, bool throw_er return nullptr; } -void VM::setattr(PyVar obj, StrName name, PyVar value){ +void VM::setattr(PyVar obj, StrName name, PyVar value) { Type objtype(0); // handle super() proxy - if(is_type(obj, tp_super)){ + if(is_type(obj, tp_super)) { Super& super = PK_OBJ_GET(Super, obj); obj = super.first; objtype = super.second; - }else{ + } else { objtype = _tp(obj); } PyVar* cls_var = find_name_in_mro(objtype, name); - if(cls_var != nullptr){ + if(cls_var != nullptr) { // handle descriptor - if(is_type(*cls_var, tp_property)){ + if(is_type(*cls_var, tp_property)) { const Property& prop = _CAST(Property&, *cls_var); - if(prop.setter != vm->None){ + if(prop.setter != vm->None) { call(prop.setter, obj, value); - }else{ + } else { TypeError(_S("readonly attribute: ", name.escape())); } return; @@ -1348,7 +1350,7 @@ void VM::setattr(PyVar obj, StrName name, PyVar value){ } const PyTypeInfo* ti = &_all_types[objtype]; - if(ti->m__setattr__){ + if(ti->m__setattr__) { ti->m__setattr__(this, obj, name, value); return; } @@ -1360,55 +1362,43 @@ void VM::setattr(PyVar obj, StrName name, PyVar value){ PyObject* VM::bind_func(PyObject* obj, StrName name, int argc, NativeFuncC fn, any userdata, BindType bt) { PyObject* nf = heap.gcnew(tp_native_func, fn, argc, std::move(userdata)); - switch(bt){ + switch(bt) { case BindType::DEFAULT: break; - case BindType::STATICMETHOD: - nf = heap.gcnew(tp_staticmethod, nf); - break; - case BindType::CLASSMETHOD: - nf = heap.gcnew(tp_classmethod, nf); - break; + case BindType::STATICMETHOD: nf = heap.gcnew(tp_staticmethod, nf); break; + case BindType::CLASSMETHOD: nf = heap.gcnew(tp_classmethod, nf); break; } if(obj != nullptr) obj->attr().set(name, nf); return nf; } -PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, any userdata, BindType bt){ +PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, any userdata, BindType bt) { return bind(obj, sig, nullptr, fn, std::move(userdata), bt); } -PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, any userdata, BindType bt){ +PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, any userdata, BindType bt) { CodeObject_ co; - try{ + try { // fn(a, b, *c, d=1) -> None co = compile(_S("def ", sig, " : pass"), "", EXEC_MODE); - }catch(TopLevelException){ - throw std::runtime_error("invalid signature: " + std::string(sig)); - } - if(co->func_decls.size() != 1){ - throw std::runtime_error("expected 1 function declaration"); - } + } catch(TopLevelException) { throw std::runtime_error("invalid signature: " + std::string(sig)); } + if(co->func_decls.size() != 1) { throw std::runtime_error("expected 1 function declaration"); } FuncDecl_ decl = co->func_decls[0]; decl->docstring = docstring; PyObject* f_obj = heap.gcnew(tp_native_func, fn, decl, std::move(userdata)); - switch(bt){ - case BindType::STATICMETHOD: - f_obj = heap.gcnew(tp_staticmethod, f_obj); - break; - case BindType::CLASSMETHOD: - f_obj = heap.gcnew(tp_classmethod, f_obj); - break; - case BindType::DEFAULT: - break; + switch(bt) { + case BindType::STATICMETHOD: f_obj = heap.gcnew(tp_staticmethod, f_obj); break; + case BindType::CLASSMETHOD: f_obj = heap.gcnew(tp_classmethod, f_obj); break; + case BindType::DEFAULT: break; } if(obj != nullptr) obj->attr().set(decl->code->name, f_obj); return f_obj; } -PyObject* VM::bind_property(PyObject* obj, const char* name, NativeFuncC fget, NativeFuncC fset){ +PyObject* VM::bind_property(PyObject* obj, const char* name, NativeFuncC fget, NativeFuncC fset) { 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); PyVar _0 = new_object(tp_native_func, fget, 1); PyVar _1 = vm->None; @@ -1418,9 +1408,11 @@ PyObject* VM::bind_property(PyObject* obj, const char* name, NativeFuncC fget, N return prop; } -void VM::__builtin_error(StrName type){ _error(call(builtins->attr(type))); } -void VM::__builtin_error(StrName type, PyVar arg){ _error(call(builtins->attr(type), arg)); } -void VM::__builtin_error(StrName type, const Str& msg){ __builtin_error(type, VAR(msg)); } +void VM::__builtin_error(StrName type) { _error(call(builtins->attr(type))); } + +void VM::__builtin_error(StrName type, PyVar arg) { _error(call(builtins->attr(type), arg)); } + +void VM::__builtin_error(StrName type, const Str& msg) { __builtin_error(type, VAR(msg)); } void VM::BinaryOptError(const char* op, PyVar _0, PyVar _1) { StrName name_0 = _type_name(vm, _tp(_0)); @@ -1428,18 +1420,21 @@ void VM::BinaryOptError(const char* op, PyVar _0, PyVar _1) { TypeError(_S("unsupported operand type(s) for ", op, ": ", name_0.escape(), " and ", name_1.escape())); } -void VM::AttributeError(PyVar obj, StrName name){ - if(isinstance(obj, vm->tp_type)){ - __builtin_error("AttributeError", _S("type object ", _type_name(vm, PK_OBJ_GET(Type, obj)).escape(), " has no attribute ", name.escape())); - }else{ - __builtin_error("AttributeError", _S(_type_name(vm, _tp(obj)).escape(), " object has no attribute ", name.escape())); +void VM::AttributeError(PyVar obj, StrName name) { + if(isinstance(obj, vm->tp_type)) { + __builtin_error( + "AttributeError", + _S("type object ", _type_name(vm, PK_OBJ_GET(Type, obj)).escape(), " has no attribute ", name.escape())); + } else { + __builtin_error("AttributeError", + _S(_type_name(vm, _tp(obj)).escape(), " object has no attribute ", name.escape())); } } -void VM::_error(PyVar e_obj){ +void VM::_error(PyVar e_obj) { assert(isinstance(e_obj, tp_exception)); Exception& e = PK_OBJ_GET(Exception, e_obj); - if(callstack.empty()){ + if(callstack.empty()) { e.is_re = false; __last_exception = e_obj.get(); throw TopLevelException(this, &e); @@ -1448,10 +1443,10 @@ void VM::_error(PyVar e_obj){ __raise_exc(); } -void VM::__raise_exc(bool re_raise){ +void VM::__raise_exc(bool re_raise) { Frame* frame = &callstack.top(); Exception& e = PK_OBJ_GET(Exception, s_data.top()); - if(!re_raise){ + if(!re_raise) { e._ip_on_error = frame->ip(); e._code_on_error = (void*)frame->co; } @@ -1459,48 +1454,59 @@ void VM::__raise_exc(bool re_raise){ int actual_ip = frame->ip(); if(e._ip_on_error >= 0 && e._code_on_error == (void*)frame->co) actual_ip = e._ip_on_error; - int current_line = frame->co->lines[actual_ip].lineno; // current line + int current_line = frame->co->lines[actual_ip].lineno; // current line auto current_f_name = frame->co->name.sv(); // current function name if(frame->_callable == nullptr) current_f_name = ""; // not in a function e.st_push(frame->co->src, current_line, nullptr, current_f_name); - if(next_ip >= 0){ + if(next_ip >= 0) { throw InternalException(InternalExceptionType::Handled, next_ip); - }else{ + } else { throw InternalException(InternalExceptionType::Unhandled); } } +StrName _type_name(VM* vm, Type type) { return vm->_all_types[type].name; } -StrName _type_name(VM *vm, Type type){ - return vm->_all_types[type].name; -} - - -void VM::bind__getitem__(Type type, PyVar (*f)(VM*, PyVar, PyVar)){ +void VM::bind__getitem__(Type type, PyVar (*f)(VM*, PyVar, PyVar)) { _all_types[type].m__getitem__ = f; - bind_func(type, __getitem__, 2, [](VM* vm, ArgsView args){ - return lambda_get_userdata(args.begin())(vm, args[0], args[1]); - }, f); + bind_func( + type, + __getitem__, + 2, + [](VM* vm, ArgsView args) { + return lambda_get_userdata(args.begin())(vm, args[0], args[1]); + }, + f); } -void VM::bind__setitem__(Type type, void (*f)(VM*, PyVar, PyVar, PyVar)){ +void VM::bind__setitem__(Type type, void (*f)(VM*, PyVar, PyVar, PyVar)) { _all_types[type].m__setitem__ = f; - bind_func(type, __setitem__, 3, [](VM* vm, ArgsView args){ - lambda_get_userdata(args.begin())(vm, args[0], args[1], args[2]); - return vm->None; - }, f); + bind_func( + type, + __setitem__, + 3, + [](VM* vm, ArgsView args) { + lambda_get_userdata(args.begin())(vm, args[0], args[1], args[2]); + return vm->None; + }, + f); } -void VM::bind__delitem__(Type type, void (*f)(VM*, PyVar, PyVar)){ +void VM::bind__delitem__(Type type, void (*f)(VM*, PyVar, PyVar)) { _all_types[type].m__delitem__ = f; - bind_func(type, __delitem__, 2, [](VM* vm, ArgsView args){ - lambda_get_userdata(args.begin())(vm, args[0], args[1]); - return vm->None; - }, f); + bind_func( + type, + __delitem__, + 2, + [](VM* vm, ArgsView args) { + lambda_get_userdata(args.begin())(vm, args[0], args[1]); + return vm->None; + }, + f); } -PyVar VM::__pack_next_retval(unsigned n){ +PyVar VM::__pack_next_retval(unsigned n) { if(n == 0) return StopIteration; if(n == 1) return s_data.popx(); PyVar retval = VAR(s_data.view(n).to_tuple()); @@ -1508,137 +1514,178 @@ PyVar VM::__pack_next_retval(unsigned n){ return retval; } -void VM::bind__next__(Type type, unsigned (*f)(VM*, PyVar)){ +void VM::bind__next__(Type type, unsigned (*f)(VM*, PyVar)) { _all_types[type].op__next__ = f; - bind_func(type, __next__, 1, [](VM* vm, ArgsView args){ - int n = lambda_get_userdata(args.begin())(vm, args[0]); - return vm->__pack_next_retval(n); - }, f); + bind_func( + type, + __next__, + 1, + [](VM* vm, ArgsView args) { + int n = lambda_get_userdata(args.begin())(vm, args[0]); + return vm->__pack_next_retval(n); + }, + f); } -void VM::bind__next__(Type type, PyVar (*f)(VM*, PyVar)){ - bind_func(type, __next__, 1, [](VM* vm, ArgsView args){ - auto f = lambda_get_userdata(args.begin()); - return f(vm, args[0]); - }, f); +void VM::bind__next__(Type type, PyVar (*f)(VM*, PyVar)) { + bind_func( + type, + __next__, + 1, + [](VM* vm, ArgsView args) { + auto f = lambda_get_userdata(args.begin()); + return f(vm, args[0]); + }, + f); } -#define BIND_UNARY_SPECIAL(name) \ - void VM::bind##name(Type type, PyVar (*f)(VM*, PyVar)){ \ - _all_types[type].m##name = f; \ - bind_func(type, name, 1, [](VM* vm, ArgsView args){ \ - return lambda_get_userdata(args.begin())(vm, args[0]);\ - }, f); \ +#define BIND_UNARY_SPECIAL(name) \ + void VM::bind##name(Type type, PyVar (*f)(VM*, PyVar)) { \ + _all_types[type].m##name = f; \ + bind_func( \ + type, \ + name, \ + 1, \ + [](VM* vm, ArgsView args) { \ + return lambda_get_userdata(args.begin())(vm, args[0]); \ + }, \ + f); \ } - BIND_UNARY_SPECIAL(__iter__) - BIND_UNARY_SPECIAL(__neg__) - BIND_UNARY_SPECIAL(__invert__) +BIND_UNARY_SPECIAL(__iter__) +BIND_UNARY_SPECIAL(__neg__) +BIND_UNARY_SPECIAL(__invert__) #undef BIND_UNARY_SPECIAL -void VM::bind__str__(Type type, Str (*f)(VM*, PyVar)){ +void VM::bind__str__(Type type, Str (*f)(VM*, PyVar)) { _all_types[type].m__str__ = f; - bind_func(type, __str__, 1, [](VM* vm, ArgsView args){ - Str s = lambda_get_userdata(args.begin())(vm, args[0]); - return VAR(s); - }, f); + bind_func( + type, + __str__, + 1, + [](VM* vm, ArgsView args) { + Str s = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(s); + }, + f); } -void VM::bind__repr__(Type type, Str (*f)(VM*, PyVar)){ +void VM::bind__repr__(Type type, Str (*f)(VM*, PyVar)) { _all_types[type].m__repr__ = f; - bind_func(type, __repr__, 1, [](VM* vm, ArgsView args){ - Str s = lambda_get_userdata(args.begin())(vm, args[0]); - return VAR(s); - }, f); + bind_func( + type, + __repr__, + 1, + [](VM* vm, ArgsView args) { + Str s = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(s); + }, + f); } -void VM::bind__hash__(Type type, i64 (*f)(VM*, PyVar)){ +void VM::bind__hash__(Type type, i64 (*f)(VM*, PyVar)) { _all_types[type].m__hash__ = f; - bind_func(type, __hash__, 1, [](VM* vm, ArgsView args){ - i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); - return VAR(ret); - }, f); + bind_func( + type, + __hash__, + 1, + [](VM* vm, ArgsView args) { + i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(ret); + }, + f); } -void VM::bind__len__(Type type, i64 (*f)(VM*, PyVar)){ +void VM::bind__len__(Type type, i64 (*f)(VM*, PyVar)) { _all_types[type].m__len__ = f; - bind_func(type, __len__, 1, [](VM* vm, ArgsView args){ - i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); - return VAR(ret); - }, f); + bind_func( + type, + __len__, + 1, + [](VM* vm, ArgsView args) { + i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(ret); + }, + f); } - -#define BIND_BINARY_SPECIAL(name) \ - void VM::bind##name(Type type, BinaryFuncC f){ \ - _all_types[type].m##name = f; \ - bind_func(type, name, 2, [](VM* vm, ArgsView args){ \ - return lambda_get_userdata(args.begin())(vm, args[0], args[1]);\ - }, f); \ +#define BIND_BINARY_SPECIAL(name) \ + void VM::bind##name(Type type, BinaryFuncC f) { \ + _all_types[type].m##name = f; \ + bind_func( \ + type, \ + name, \ + 2, \ + [](VM* vm, ArgsView args) { \ + return lambda_get_userdata(args.begin())(vm, args[0], args[1]); \ + }, \ + f); \ } - BIND_BINARY_SPECIAL(__eq__) - BIND_BINARY_SPECIAL(__lt__) - BIND_BINARY_SPECIAL(__le__) - BIND_BINARY_SPECIAL(__gt__) - BIND_BINARY_SPECIAL(__ge__) - BIND_BINARY_SPECIAL(__contains__) +BIND_BINARY_SPECIAL(__eq__) +BIND_BINARY_SPECIAL(__lt__) +BIND_BINARY_SPECIAL(__le__) +BIND_BINARY_SPECIAL(__gt__) +BIND_BINARY_SPECIAL(__ge__) +BIND_BINARY_SPECIAL(__contains__) - BIND_BINARY_SPECIAL(__add__) - BIND_BINARY_SPECIAL(__sub__) - BIND_BINARY_SPECIAL(__mul__) - BIND_BINARY_SPECIAL(__truediv__) - BIND_BINARY_SPECIAL(__floordiv__) - BIND_BINARY_SPECIAL(__mod__) - BIND_BINARY_SPECIAL(__pow__) - BIND_BINARY_SPECIAL(__matmul__) +BIND_BINARY_SPECIAL(__add__) +BIND_BINARY_SPECIAL(__sub__) +BIND_BINARY_SPECIAL(__mul__) +BIND_BINARY_SPECIAL(__truediv__) +BIND_BINARY_SPECIAL(__floordiv__) +BIND_BINARY_SPECIAL(__mod__) +BIND_BINARY_SPECIAL(__pow__) +BIND_BINARY_SPECIAL(__matmul__) - BIND_BINARY_SPECIAL(__lshift__) - BIND_BINARY_SPECIAL(__rshift__) - BIND_BINARY_SPECIAL(__and__) - BIND_BINARY_SPECIAL(__or__) - BIND_BINARY_SPECIAL(__xor__) +BIND_BINARY_SPECIAL(__lshift__) +BIND_BINARY_SPECIAL(__rshift__) +BIND_BINARY_SPECIAL(__and__) +BIND_BINARY_SPECIAL(__or__) +BIND_BINARY_SPECIAL(__xor__) #undef BIND_BINARY_SPECIAL - -void Dict::_probe_0(VM* vm, PyVar key, bool &ok, int &i) const{ +void Dict::_probe_0(VM* vm, PyVar key, bool& ok, int& i) const { ok = false; i64 hash = vm->py_hash(key); i = hash & _mask; - for(int j=0; j<_capacity; j++) { - if(_items[i].first != nullptr){ - if(vm->py_eq(_items[i].first, key)) { ok = true; break; } - }else{ + for(int j = 0; j < _capacity; j++) { + if(_items[i].first != nullptr) { + if(vm->py_eq(_items[i].first, key)) { + ok = true; + break; + } + } else { if(_items[i].second == nullptr) break; } // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166 - i = ((5*i) + 1) & _mask; + i = ((5 * i) + 1) & _mask; } } -void Dict::_probe_1(VM* vm, PyVar key, bool &ok, int &i) const{ +void Dict::_probe_1(VM* vm, PyVar key, bool& ok, int& i) const { ok = false; i = vm->py_hash(key) & _mask; while(_items[i].first != nullptr) { - if(vm->py_eq(_items[i].first, key)) { ok = true; break; } + if(vm->py_eq(_items[i].first, key)) { + ok = true; + break; + } // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166 - i = ((5*i) + 1) & _mask; + i = ((5 * i) + 1) & _mask; } } - #if PK_ENABLE_PROFILER -void NextBreakpoint::_step(VM* vm){ +void NextBreakpoint::_step(VM* vm) { int curr_callstack_size = vm->callstack.size(); int curr_lineno = vm->callstack.top().curr_lineno(); - if(should_step_into){ - if(curr_callstack_size != callstack_size || curr_lineno != lineno){ - vm->__breakpoint(); - } - }else{ + if(should_step_into) { + if(curr_callstack_size != callstack_size || curr_lineno != lineno) { vm->__breakpoint(); } + } else { if(curr_callstack_size == callstack_size) { if(curr_lineno != lineno) vm->__breakpoint(); - }else if(curr_callstack_size < callstack_size){ + } else if(curr_callstack_size < callstack_size) { // returning vm->__breakpoint(); } @@ -1646,42 +1693,42 @@ void NextBreakpoint::_step(VM* vm){ } #endif -void VM::__pop_frame(){ +void VM::__pop_frame() { s_data.reset(callstack.top()._sp_base); callstack.pop(); #if PK_ENABLE_PROFILER - if(!_next_breakpoint.empty() && callstack.size()<_next_breakpoint.callstack_size){ + if(!_next_breakpoint.empty() && callstack.size() < _next_breakpoint.callstack_size) { _next_breakpoint = NextBreakpoint(); } #endif } -void VM::__breakpoint(){ +void VM::__breakpoint() { #if PK_ENABLE_PROFILER _next_breakpoint = NextBreakpoint(); bool show_where = false; bool show_headers = true; - - while(true){ + + while(true) { vector frames; LinkedFrame* lf = callstack._tail; - while(lf != nullptr){ + while(lf != nullptr) { frames.push_back(lf); lf = lf->f_back; if(frames.size() >= 4) break; } - if(show_headers){ - for(int i=frames.size()-1; i>=0; i--){ - if(!show_where && i!=0) continue; + if(show_headers) { + for(int i = frames.size() - 1; i >= 0; i--) { + if(!show_where && i != 0) continue; SStream ss; Frame* frame = &frames[i]->frame; int lineno = frame->curr_lineno(); ss << "File \"" << frame->co->src->filename << "\", line " << lineno; - if(frame->_callable){ + if(frame->_callable) { ss << ", in "; ss << frame->_callable->as().decl->code->name; } @@ -1696,12 +1743,12 @@ void VM::__breakpoint(){ Frame* frame_0 = &frames[0]->frame; std::string line; - if(!std::getline(std::cin, line)){ + if(!std::getline(std::cin, line)) { stdout_write("--KeyboardInterrupt--\n"); continue; } - if(line == "h" || line == "help"){ + if(line == "h" || line == "help") { stdout_write("h, help: show this help message\n"); stdout_write("q, quit: exit the debugger\n"); stdout_write("n, next: execute next line\n"); @@ -1715,26 +1762,24 @@ void VM::__breakpoint(){ stdout_write("!: execute statement\n"); continue; } - if(line == "q" || line == "quit") { - vm->RuntimeError("pdb quit"); - } - if(line == "n" || line == "next"){ + if(line == "q" || line == "quit") { vm->RuntimeError("pdb quit"); } + if(line == "n" || line == "next") { vm->_next_breakpoint = NextBreakpoint(vm->callstack.size(), frame_0->curr_lineno(), false); break; } - if(line == "s" || line == "step"){ + if(line == "s" || line == "step") { vm->_next_breakpoint = NextBreakpoint(vm->callstack.size(), frame_0->curr_lineno(), true); break; } - if(line == "w" || line == "where"){ + if(line == "w" || line == "where") { show_where = !show_where; show_headers = true; continue; } if(line == "c" || line == "continue") break; - if(line == "a" || line == "args"){ + if(line == "a" || line == "args") { int i = 0; - for(PyVar obj: frame_0->_locals){ + for(PyVar obj: frame_0->_locals) { if(obj == PY_NULL) continue; StrName name = frame_0->co->varnames[i++]; stdout_write(_S(name.sv(), " = ", vm->py_repr(obj), '\n')); @@ -1745,45 +1790,47 @@ void VM::__breakpoint(){ bool is_list = line == "l" || line == "list"; bool is_longlist = line == "ll" || line == "longlist"; - if(is_list || is_longlist){ + if(is_list || is_longlist) { if(frame_0->co->src->is_precompiled) continue; int lineno = frame_0->curr_lineno(); int start, end; - if(is_list){ + if(is_list) { int max_line = frame_0->co->src->line_starts.size() + 1; - start = (std::max)(1, lineno-5); - end = (std::min)(max_line, lineno+5); - }else{ + start = (std::max)(1, lineno - 5); + end = (std::min)(max_line, lineno + 5); + } else { start = frame_0->co->start_line; end = frame_0->co->end_line; if(start == -1 || end == -1) continue; } - + SStream ss; int max_width = std::to_string(end).size(); - for(int i=start; i<=end; i++){ + for(int i = start; i <= end; i++) { int spaces = max_width - std::to_string(i).size(); ss << std::string(spaces, ' ') << std::to_string(i); - if(i == lineno) ss << " -> "; - else ss << " "; + if(i == lineno) + ss << " -> "; + else + ss << " "; ss << frame_0->co->src->get_line(i) << '\n'; } stdout_write(ss.str()); continue; } - + int space = line.find_first_of(' '); - if(space != -1){ + if(space != -1) { std::string cmd = line.substr(0, space); - std::string arg = line.substr(space+1); - if(arg.empty()) continue; // ignore empty command - if(cmd == "p" || cmd == "print"){ + std::string arg = line.substr(space + 1); + if(arg.empty()) continue; // ignore empty command + if(cmd == "p" || cmd == "print") { CodeObject_ code = compile(arg, "", EVAL_MODE, true); PyVar retval = vm->_exec(code.get(), frame_0->_module, frame_0->_callable, frame_0->_locals); stdout_write(vm->py_repr(retval)); stdout_write("\n"); - }else if(cmd == "!"){ + } else if(cmd == "!") { CodeObject_ code = compile(arg, "", EXEC_MODE, true); vm->_exec(code.get(), frame_0->_module, frame_0->_callable, frame_0->_locals); } @@ -1794,67 +1841,60 @@ void VM::__breakpoint(){ } /**************************************************************************/ -void Function::_gc_mark(VM* vm) const{ +void Function::_gc_mark(VM* vm) const { decl->_gc_mark(vm); - if(_closure){ - _closure->apply([=](StrName _, PyVar obj){ + if(_closure) { + _closure->apply([=](StrName _, PyVar obj) { vm->obj_gc_mark(obj); }); } } -void NativeFunc::_gc_mark(VM* vm) const{ +void NativeFunc::_gc_mark(VM* vm) const { if(decl) decl->_gc_mark(vm); } -void FuncDecl::_gc_mark(VM* vm) const{ +void FuncDecl::_gc_mark(VM* vm) const { code->_gc_mark(vm); - for(int i=0; iobj_gc_mark(kwargs[i].value); + for(int i = 0; i < kwargs.size(); i++) + vm->obj_gc_mark(kwargs[i].value); } -void List::_gc_mark(VM* vm) const{ - for(PyVar obj: *this) vm->obj_gc_mark(obj); +void List::_gc_mark(VM* vm) const { + for(PyVar obj: *this) + vm->obj_gc_mark(obj); } -void Tuple::_gc_mark(VM* vm) const{ - for(PyVar obj: *this) vm->obj_gc_mark(obj); +void Tuple::_gc_mark(VM* vm) const { + for(PyVar obj: *this) + vm->obj_gc_mark(obj); } -void MappingProxy::_gc_mark(VM* vm) const{ - vm->__obj_gc_mark(obj); -} +void MappingProxy::_gc_mark(VM* vm) const { vm->__obj_gc_mark(obj); } -void BoundMethod::_gc_mark(VM* vm) const{ +void BoundMethod::_gc_mark(VM* vm) const { vm->obj_gc_mark(func); vm->obj_gc_mark(self); } -void StarWrapper::_gc_mark(VM* vm) const{ - vm->obj_gc_mark(obj); -} +void StarWrapper::_gc_mark(VM* vm) const { vm->obj_gc_mark(obj); } -void StaticMethod::_gc_mark(VM* vm) const{ - vm->obj_gc_mark(func); -} +void StaticMethod::_gc_mark(VM* vm) const { vm->obj_gc_mark(func); } -void ClassMethod::_gc_mark(VM* vm) const{ - vm->obj_gc_mark(func); -} +void ClassMethod::_gc_mark(VM* vm) const { vm->obj_gc_mark(func); } -void Property::_gc_mark(VM* vm) const{ +void Property::_gc_mark(VM* vm) const { vm->obj_gc_mark(getter); vm->obj_gc_mark(setter); } -void Slice::_gc_mark(VM* vm) const{ +void Slice::_gc_mark(VM* vm) const { vm->obj_gc_mark(start); vm->obj_gc_mark(stop); vm->obj_gc_mark(step); } -void Super::_gc_mark(VM* vm) const{ - vm->obj_gc_mark(first); -} +void Super::_gc_mark(VM* vm) const { vm->obj_gc_mark(first); } void Frame::_gc_mark(VM* vm) const { vm->obj_gc_mark(_module); @@ -1864,9 +1904,13 @@ void Frame::_gc_mark(VM* vm) const { } void ManagedHeap::mark() { - for(PyObject* obj: _no_gc) vm->__obj_gc_mark(obj); - vm->callstack.apply([this](Frame& frame){ frame._gc_mark(vm); }); - for(auto [_, co]: vm->__cached_codes) co->_gc_mark(vm); + for(PyObject* obj: _no_gc) + vm->__obj_gc_mark(obj); + vm->callstack.apply([this](Frame& frame) { + frame._gc_mark(vm); + }); + for(auto [_, co]: vm->__cached_codes) + co->_gc_mark(vm); vm->obj_gc_mark(vm->__last_exception); vm->obj_gc_mark(vm->__curr_class); vm->obj_gc_mark(vm->__c.error); @@ -1874,23 +1918,25 @@ void ManagedHeap::mark() { if(_gc_marker_ex) _gc_marker_ex(vm); } -void ManagedHeap::_delete(PyObject* obj){ +void ManagedHeap::_delete(PyObject* obj) { const PyTypeInfo* ti = vm->_tp_info(obj->type); if(ti->vt._dtor) ti->vt._dtor(obj->_value_ptr()); - delete obj->_attr; // delete __dict__ if exists + delete obj->_attr; // delete __dict__ if exists PoolObject_dealloc(obj); } -void Dict::_gc_mark(VM* vm) const{ - apply([vm](PyVar k, PyVar v){ +void Dict::_gc_mark(VM* vm) const { + apply([vm](PyVar k, PyVar v) { vm->obj_gc_mark(k); vm->obj_gc_mark(v); }); } void CodeObject::_gc_mark(VM* vm) const { - for(PyVar v : consts) vm->obj_gc_mark(v); - for(auto& decl: func_decls) decl->_gc_mark(vm); + for(PyVar v: consts) + vm->obj_gc_mark(v); + for(auto& decl: func_decls) + decl->_gc_mark(vm); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/array2d.cpp b/src/modules/array2d.cpp index a3bf66d8..ad05171c 100644 --- a/src/modules/array2d.cpp +++ b/src/modules/array2d.cpp @@ -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(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(), [](VM* vm, PyVar _0, PyVar _1){ + vm->bind__getitem__(type->as(), [](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 row = xy[1].as(); 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& 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(), [](VM* vm, PyVar _0, PyVar _1, PyVar _2){ + vm->bind__setitem__(type->as(), [](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 row = xy[1].as(); 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(_2)){ + if(!vm->is_user_type(_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(), [](VM* vm, PyVar _0){ + vm->bind__len__(type->as(), [](VM* vm, PyVar _0) { Array2d& self = PK_OBJ_GET(Array2d, _0); return (i64)self.numel; }); - vm->bind__repr__(type->as(), [](VM* vm, PyVar _0) -> Str{ + vm->bind__repr__(type->as(), [](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& 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& 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(), [](VM* vm, PyVar _0, PyVar _1){ + vm->bind__eq__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { Array2d& self = PK_OBJ_GET(Array2d, _0); if(!vm->is_user_type(_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& 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(), [](VM* vm, PyVar _0) { return _0; }); - vm->bind__next__(type->as(), [](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(), [](VM* vm, PyVar _0) { + return _0; + }); + vm->bind__next__(type->as(), [](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(mod, "array2d", VM::tp_object, true); vm->register_user_class(mod, "_array2d_iter"); Type array2d_iter_t = vm->_tp_user(); - 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(_0, &_0.obj_get()); }); - 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(vm->_tp_user(), _0, &_0.obj_get()); }; } - -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/base64.cpp b/src/modules/base64.cpp index 8d111955..0be841c8 100644 --- a/src/modules/base64.cpp +++ b/src/modules/base64.cpp @@ -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 \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/csv.cpp b/src/modules/csv.cpp index 97f1becf..55d72246 100644 --- a/src/modules/csv.cpp +++ b/src/modules/csv.cpp @@ -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; iValueError("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; iValueError("row.size() != header.size()"); - } + if(row.size() != header.size()) { vm->ValueError("row.size() != header.size()"); } Dict row_dict; - for(int j=0; jbind(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 \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/easing.cpp b/src/modules/easing.cpp index f06202d5..ebd8e12d 100644 --- a/src/modules/easing.cpp +++ b/src/modules/easing.cpp @@ -3,193 +3,161 @@ #include -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 \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/io.cpp b/src/modules/io.cpp index 8c264267..6bc551b3 100644 --- a/src/modules/io.cpp +++ b/src/modules/io.cpp @@ -6,7 +6,7 @@ #include #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::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(cls, vm, - py_cast(vm, args[1]), - py_cast(vm, args[2])); + return vm->new_object(cls, vm, py_cast(vm, args[1]), py_cast(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(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(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; ibind_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 \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/linalg.cpp b/src/modules/linalg.cpp index 997b1e58..2051b5d8 100644 --- a/src/modules/linalg.cpp +++ b/src/modules/linalg.cpp @@ -1,81 +1,82 @@ #include "pocketpy/modules/linalg.hpp" #include "pocketpy/interpreter/bindings.hpp" -namespace pkpy{ +namespace pkpy { -#define BIND_VEC_VEC_OP(D, name, op) \ - vm->bind##name(type->as(), [](VM* vm, PyVar _0, PyVar _1){ \ - Vec##D self = _CAST(Vec##D, _0); \ - Vec##D other = CAST(Vec##D, _1); \ - return VAR(self op other); \ - }); +#define BIND_VEC_VEC_OP(D, name, op) \ + vm->bind##name(type->as(), [](VM* vm, PyVar _0, PyVar _1) { \ + Vec##D self = _CAST(Vec##D, _0); \ + Vec##D other = CAST(Vec##D, _1); \ + return VAR(self op other); \ + }); -#define BIND_VEC_FLOAT_OP(D, name, op) \ - vm->bind##name(type->as(), [](VM* vm, PyVar _0, PyVar _1){ \ - Vec##D self = _CAST(Vec##D, _0); \ - f64 other = CAST(f64, _1); \ - return VAR(self op other); \ - }); +#define BIND_VEC_FLOAT_OP(D, name, op) \ + vm->bind##name(type->as(), [](VM* vm, PyVar _0, PyVar _1) { \ + Vec##D self = _CAST(Vec##D, _0); \ + f64 other = CAST(f64, _1); \ + return VAR(self op other); \ + }); -#define BIND_VEC_FUNCTION_0(T, name) \ - vm->bind_func(type, #name, 1, [](VM* vm, ArgsView args){ \ - T self = _CAST(T, args[0]); \ - return VAR(self.name()); \ - }); +#define BIND_VEC_FUNCTION_0(T, name) \ + vm->bind_func(type, #name, 1, [](VM* vm, ArgsView args) { \ + T self = _CAST(T, args[0]); \ + return VAR(self.name()); \ + }); -#define BIND_VEC_FUNCTION_1(T, name) \ - vm->bind_func(type, #name, 2, [](VM* vm, ArgsView args){ \ - T self = _CAST(T, args[0]); \ - T other = CAST(T, args[1]); \ - return VAR(self.name(other)); \ - }); +#define BIND_VEC_FUNCTION_1(T, name) \ + vm->bind_func(type, #name, 2, [](VM* vm, ArgsView args) { \ + T self = _CAST(T, args[0]); \ + T other = CAST(T, args[1]); \ + return VAR(self.name(other)); \ + }); -#define BIND_VEC_MUL_OP(D) \ - vm->bind__mul__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ \ - Vec##D self = _CAST(Vec##D, _0); \ - if(vm->is_user_type(_1)){ \ - Vec##D other = _CAST(Vec##D, _1); \ - return VAR(self * other); \ - } \ - f64 other = CAST(f64, _1); \ - return VAR(self * other); \ - }); \ - vm->bind_func(type, "__rmul__", 2, [](VM* vm, ArgsView args){ \ - Vec##D self = _CAST(Vec##D, args[0]); \ - f64 other = CAST(f64, args[1]); \ - return VAR(self * other); \ - }); \ - vm->bind__truediv__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ \ - Vec##D self = _CAST(Vec##D, _0); \ - f64 other = CAST(f64, _1); \ - return VAR(self / other); \ - }); +#define BIND_VEC_MUL_OP(D) \ + vm->bind__mul__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { \ + Vec##D self = _CAST(Vec##D, _0); \ + if(vm->is_user_type(_1)) { \ + Vec##D other = _CAST(Vec##D, _1); \ + return VAR(self * other); \ + } \ + f64 other = CAST(f64, _1); \ + return VAR(self * other); \ + }); \ + vm->bind_func(type, "__rmul__", 2, [](VM* vm, ArgsView args) { \ + Vec##D self = _CAST(Vec##D, args[0]); \ + f64 other = CAST(f64, args[1]); \ + return VAR(self * other); \ + }); \ + vm->bind__truediv__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { \ + Vec##D self = _CAST(Vec##D, _0); \ + f64 other = CAST(f64, _1); \ + return VAR(self / other); \ + }); -#define BIND_VEC_GETITEM(D) \ - vm->bind__getitem__(type->as(), [](VM* vm, PyVar obj, PyVar index){ \ - Vec##D self = _CAST(Vec##D, obj); \ - i64 i = CAST(i64, index); \ - if(i < 0 || i >= D) vm->IndexError("index out of range"); \ - return VAR(self[i]); \ - }); +#define BIND_VEC_GETITEM(D) \ + vm->bind__getitem__(type->as(), [](VM* vm, PyVar obj, PyVar index) { \ + Vec##D self = _CAST(Vec##D, obj); \ + i64 i = CAST(i64, index); \ + if(i < 0 || i >= D) vm->IndexError("index out of range"); \ + return VAR(self[i]); \ + }); -#define BIND_SSO_VEC_COMMON(D) \ - vm->bind__eq__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ \ - Vec##D self = _CAST(Vec##D, _0); \ - if(!vm->is_user_type(_1)) return vm->NotImplemented; \ - Vec##D other = _CAST(Vec##D, _1); \ - return VAR(self == other); \ - }); \ - vm->bind_func(type, "__getnewargs__", 1, [](VM* vm, ArgsView args){ \ - Vec##D self = _CAST(Vec##D, args[0]); \ - Tuple t(D); \ - for(int i=0; ibind__eq__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { \ + Vec##D self = _CAST(Vec##D, _0); \ + if(!vm->is_user_type(_1)) return vm->NotImplemented; \ + Vec##D other = _CAST(Vec##D, _1); \ + return VAR(self == other); \ + }); \ + vm->bind_func(type, "__getnewargs__", 1, [](VM* vm, ArgsView args) { \ + Vec##D self = _CAST(Vec##D, args[0]); \ + Tuple t(D); \ + for(int i = 0; i < D; i++) \ + t[i] = VAR(self[i]); \ + return VAR(std::move(t)); \ + }); // https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Math/Vector2.cs#L289 -static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float smoothTime, float maxSpeed, float deltaTime) -{ +static Vec2 + SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float smoothTime, float maxSpeed, float deltaTime) { // Based on Game Programming Gems 4 Chapter 1.10 smoothTime = (std::max)(0.0001F, smoothTime); float omega = 2.0F / smoothTime; @@ -92,8 +93,7 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s float maxChangeSq = maxChange * maxChange; float sqDist = change_x * change_x + change_y * change_y; - if (sqDist > maxChangeSq) - { + if(sqDist > maxChangeSq) { float mag = std::sqrt(sqDist); change_x = change_x / mag * maxChange; change_y = change_y / mag * maxChange; @@ -117,8 +117,7 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s float outMinusOrig_x = output_x - originalTo.x; float outMinusOrig_y = output_y - originalTo.y; - if (origMinusCurrent_x * outMinusOrig_x + origMinusCurrent_y * outMinusOrig_y > 0) - { + if(origMinusCurrent_x * outMinusOrig_x + origMinusCurrent_y * outMinusOrig_y > 0) { output_x = originalTo.x; output_y = originalTo.y; @@ -128,18 +127,21 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s return Vec2(output_x, output_y); } - void Vec2::_register(VM* vm, PyObject* mod, PyObject* type){ - type->attr().set("ZERO", vm->new_user_object(0, 0)); - type->attr().set("ONE", vm->new_user_object(1, 1)); +void Vec2::_register(VM* vm, PyObject* mod, PyObject* type) { + type->attr().set("ZERO", vm->new_user_object(0, 0)); + type->attr().set("ONE", vm->new_user_object(1, 1)); - vm->bind_func(type, __new__, 3, [](VM* vm, ArgsView args){ - float x = CAST_F(args[1]); - float y = CAST_F(args[2]); - return vm->new_object(PK_OBJ_GET(Type, args[0]), x, y); - }); + vm->bind_func(type, __new__, 3, [](VM* vm, ArgsView args) { + float x = CAST_F(args[1]); + float y = CAST_F(args[2]); + return vm->new_object(PK_OBJ_GET(Type, args[0]), x, y); + }); - // @staticmethod - vm->bind(type, "smooth_damp(current: vec2, target: vec2, current_velocity_: vec2, smooth_time: float, max_speed: float, delta_time: float) -> vec2", [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind( + type, + "smooth_damp(current: vec2, target: vec2, current_velocity_: vec2, smooth_time: float, max_speed: float, delta_time: float) -> vec2", + [](VM* vm, ArgsView args) { Vec2 current = CAST(Vec2, args[0]); Vec2 target = CAST(Vec2, args[1]); Vec2 current_velocity_ = CAST(Vec2, args[2]); @@ -148,122 +150,129 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s float delta_time = CAST_F(args[5]); Vec2 ret = SmoothDamp(current, target, current_velocity_, smooth_time, max_speed, delta_time); return VAR(Tuple(VAR(ret), VAR(current_velocity_))); - }, {}, BindType::STATICMETHOD); + }, + {}, + BindType::STATICMETHOD); - // @staticmethod - vm->bind(type, "angle(__from: vec2, __to: vec2) -> float", [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind( + type, + "angle(__from: vec2, __to: vec2) -> float", + [](VM* vm, ArgsView args) { Vec2 __from = CAST(Vec2, args[0]); Vec2 __to = CAST(Vec2, args[1]); float val = atan2f(__to.y, __to.x) - atan2f(__from.y, __from.x); const float PI = 3.1415926535897932384f; - if(val > PI) val -= 2*PI; - if(val < -PI) val += 2*PI; + if(val > PI) val -= 2 * PI; + if(val < -PI) val += 2 * PI; return VAR(val); - }, {}, BindType::STATICMETHOD); + }, + {}, + BindType::STATICMETHOD); - vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str{ - Vec2 self = _CAST(Vec2, obj); - SStream ss; - ss.setprecision(3); - ss << "vec2(" << self.x << ", " << self.y << ")"; - return ss.str(); - }); + vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str { + Vec2 self = _CAST(Vec2, obj); + SStream ss; + ss.setprecision(3); + ss << "vec2(" << self.x << ", " << self.y << ")"; + return ss.str(); + }); - vm->bind_func(type, "rotate", 2, [](VM* vm, ArgsView args){ - Vec2 self = _CAST(Vec2, args[0]); - float radian = CAST(f64, args[1]); - return vm->new_user_object(self.rotate(radian)); - }); + vm->bind_func(type, "rotate", 2, [](VM* vm, ArgsView args) { + Vec2 self = _CAST(Vec2, args[0]); + float radian = CAST(f64, args[1]); + return vm->new_user_object(self.rotate(radian)); + }); - PY_READONLY_FIELD(Vec2, "x", x) - PY_READONLY_FIELD(Vec2, "y", y) + PY_READONLY_FIELD(Vec2, "x", x) + PY_READONLY_FIELD(Vec2, "y", y) - BIND_VEC_VEC_OP(2, __add__, +) - BIND_VEC_VEC_OP(2, __sub__, -) - BIND_VEC_MUL_OP(2) - BIND_VEC_FLOAT_OP(2, __truediv__, /) - BIND_VEC_FUNCTION_1(Vec2, dot) - BIND_VEC_FUNCTION_1(Vec2, cross) - BIND_VEC_FUNCTION_0(Vec2, length) - BIND_VEC_FUNCTION_0(Vec2, length_squared) - BIND_VEC_FUNCTION_0(Vec2, normalize) - BIND_VEC_GETITEM(2) - BIND_SSO_VEC_COMMON(2) - } + BIND_VEC_VEC_OP(2, __add__, +) + BIND_VEC_VEC_OP(2, __sub__, -) + BIND_VEC_MUL_OP(2) + BIND_VEC_FLOAT_OP(2, __truediv__, /) + BIND_VEC_FUNCTION_1(Vec2, dot) + BIND_VEC_FUNCTION_1(Vec2, cross) + BIND_VEC_FUNCTION_0(Vec2, length) + BIND_VEC_FUNCTION_0(Vec2, length_squared) + BIND_VEC_FUNCTION_0(Vec2, normalize) + BIND_VEC_GETITEM(2) + BIND_SSO_VEC_COMMON(2) +} - void Vec3::_register(VM* vm, PyObject* mod, PyObject* type){ - type->attr().set("ZERO", vm->new_user_object(0, 0, 0)); - type->attr().set("ONE", vm->new_user_object(1, 1, 1)); +void Vec3::_register(VM* vm, PyObject* mod, PyObject* type) { + type->attr().set("ZERO", vm->new_user_object(0, 0, 0)); + type->attr().set("ONE", vm->new_user_object(1, 1, 1)); - vm->bind_func(type, __new__, 4, [](VM* vm, ArgsView args){ - float x = CAST_F(args[1]); - float y = CAST_F(args[2]); - float z = CAST_F(args[3]); - return vm->new_object(PK_OBJ_GET(Type, args[0]), x, y, z); - }); + vm->bind_func(type, __new__, 4, [](VM* vm, ArgsView args) { + float x = CAST_F(args[1]); + float y = CAST_F(args[2]); + float z = CAST_F(args[3]); + return vm->new_object(PK_OBJ_GET(Type, args[0]), x, y, z); + }); - vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str{ - Vec3 self = _CAST(Vec3, obj); - SStream ss; - ss.setprecision(3); - ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")"; - return ss.str(); - }); + vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str { + Vec3 self = _CAST(Vec3, obj); + SStream ss; + ss.setprecision(3); + ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")"; + return ss.str(); + }); - PY_READONLY_FIELD(Vec3, "x", x) - PY_READONLY_FIELD(Vec3, "y", y) - PY_READONLY_FIELD(Vec3, "z", z) + PY_READONLY_FIELD(Vec3, "x", x) + PY_READONLY_FIELD(Vec3, "y", y) + PY_READONLY_FIELD(Vec3, "z", z) - BIND_VEC_VEC_OP(3, __add__, +) - BIND_VEC_VEC_OP(3, __sub__, -) - BIND_VEC_MUL_OP(3) - BIND_VEC_FUNCTION_1(Vec3, dot) - BIND_VEC_FUNCTION_1(Vec3, cross) - BIND_VEC_FUNCTION_0(Vec3, length) - BIND_VEC_FUNCTION_0(Vec3, length_squared) - BIND_VEC_FUNCTION_0(Vec3, normalize) - BIND_VEC_GETITEM(3) - BIND_SSO_VEC_COMMON(3) - } + BIND_VEC_VEC_OP(3, __add__, +) + BIND_VEC_VEC_OP(3, __sub__, -) + BIND_VEC_MUL_OP(3) + BIND_VEC_FUNCTION_1(Vec3, dot) + BIND_VEC_FUNCTION_1(Vec3, cross) + BIND_VEC_FUNCTION_0(Vec3, length) + BIND_VEC_FUNCTION_0(Vec3, length_squared) + BIND_VEC_FUNCTION_0(Vec3, normalize) + BIND_VEC_GETITEM(3) + BIND_SSO_VEC_COMMON(3) +} - void Vec4::_register(VM* vm, PyObject* mod, PyObject* type){ - PY_STRUCT_LIKE(Vec4) +void Vec4::_register(VM* vm, PyObject* mod, PyObject* type) { + PY_STRUCT_LIKE(Vec4) - type->attr().set("ZERO", vm->new_user_object(0, 0, 0, 0)); - type->attr().set("ONE", vm->new_user_object(1, 1, 1, 1)); + type->attr().set("ZERO", vm->new_user_object(0, 0, 0, 0)); + type->attr().set("ONE", vm->new_user_object(1, 1, 1, 1)); - vm->bind_func(type, __new__, 5, [](VM* vm, ArgsView args){ - float x = CAST_F(args[1]); - float y = CAST_F(args[2]); - float z = CAST_F(args[3]); - float w = CAST_F(args[4]); - return vm->new_object(PK_OBJ_GET(Type, args[0]), x, y, z, w); - }); + vm->bind_func(type, __new__, 5, [](VM* vm, ArgsView args) { + float x = CAST_F(args[1]); + float y = CAST_F(args[2]); + float z = CAST_F(args[3]); + float w = CAST_F(args[4]); + return vm->new_object(PK_OBJ_GET(Type, args[0]), x, y, z, w); + }); - vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str{ - Vec4 self = _CAST(Vec4&, obj); - SStream ss; - ss.setprecision(3); - ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")"; - return ss.str(); - }); + vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str { + Vec4 self = _CAST(Vec4&, obj); + SStream ss; + ss.setprecision(3); + ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")"; + return ss.str(); + }); - PY_FIELD(Vec4, "x", x) - PY_FIELD(Vec4, "y", y) - PY_FIELD(Vec4, "z", z) - PY_FIELD(Vec4, "w", w) + PY_FIELD(Vec4, "x", x) + PY_FIELD(Vec4, "y", y) + PY_FIELD(Vec4, "z", z) + PY_FIELD(Vec4, "w", w) - BIND_VEC_VEC_OP(4, __add__, +) - BIND_VEC_VEC_OP(4, __sub__, -) - BIND_VEC_MUL_OP(4) - BIND_VEC_FUNCTION_1(Vec4&, dot) - BIND_VEC_FUNCTION_1(Vec4&, copy_) - BIND_VEC_FUNCTION_0(Vec4&, length) - BIND_VEC_FUNCTION_0(Vec4&, length_squared) - BIND_VEC_FUNCTION_0(Vec4&, normalize) - BIND_VEC_FUNCTION_0(Vec4&, normalize_) - BIND_VEC_GETITEM(4) - } + BIND_VEC_VEC_OP(4, __add__, +) + BIND_VEC_VEC_OP(4, __sub__, -) + BIND_VEC_MUL_OP(4) + BIND_VEC_FUNCTION_1(Vec4&, dot) + BIND_VEC_FUNCTION_1(Vec4&, copy_) + BIND_VEC_FUNCTION_0(Vec4&, length) + BIND_VEC_FUNCTION_0(Vec4&, length_squared) + BIND_VEC_FUNCTION_0(Vec4&, normalize) + BIND_VEC_FUNCTION_0(Vec4&, normalize_) + BIND_VEC_GETITEM(4) +} #undef BIND_VEC_VEC_OP #undef BIND_VEC_MUL_OP @@ -271,284 +280,300 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s #undef BIND_VEC_FUNCTION_1 #undef BIND_VEC_GETITEM - void Mat3x3::_register(VM* vm, PyObject* mod, PyObject* type){ - PY_STRUCT_LIKE(Mat3x3) +void Mat3x3::_register(VM* vm, PyObject* mod, PyObject* type) { + PY_STRUCT_LIKE(Mat3x3) - vm->bind_func(type, __new__, -1, [](VM* vm, ArgsView args){ - if(args.size() == 1+0) return vm->new_object(PK_OBJ_GET(Type, args[0]), Mat3x3::zeros()); - if(args.size() == 1+1){ - const List& list = CAST(List&, args[1]); - if(list.size() != 9) vm->TypeError("Mat3x3.__new__ takes a list of 9 floats"); - Mat3x3 mat; - for(int i=0; i<9; i++) mat.v[i] = CAST_F(list[i]); - return vm->new_object(PK_OBJ_GET(Type, args[0]), mat); - } - if(args.size() == 1+9){ - Mat3x3 mat; - for(int i=0; i<9; i++) mat.v[i] = CAST_F(args[1+i]); - return vm->new_object(PK_OBJ_GET(Type, args[0]), mat); - } - vm->TypeError(_S("Mat3x3.__new__ takes 0 or 1 or 9 arguments, got ", args.size()-1)); + vm->bind_func(type, __new__, -1, [](VM* vm, ArgsView args) { + if(args.size() == 1 + 0) return vm->new_object(PK_OBJ_GET(Type, args[0]), Mat3x3::zeros()); + if(args.size() == 1 + 1) { + const List& list = CAST(List&, args[1]); + if(list.size() != 9) vm->TypeError("Mat3x3.__new__ takes a list of 9 floats"); + Mat3x3 mat; + for(int i = 0; i < 9; i++) + mat.v[i] = CAST_F(list[i]); + return vm->new_object(PK_OBJ_GET(Type, args[0]), mat); + } + if(args.size() == 1 + 9) { + Mat3x3 mat; + for(int i = 0; i < 9; i++) + mat.v[i] = CAST_F(args[1 + i]); + return vm->new_object(PK_OBJ_GET(Type, args[0]), mat); + } + vm->TypeError(_S("Mat3x3.__new__ takes 0 or 1 or 9 arguments, got ", args.size() - 1)); + return vm->None; + }); + + vm->bind_func(type, "copy_", 2, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + const Mat3x3& other = CAST(Mat3x3&, args[1]); + self = other; + return vm->None; + }); + + vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str { + const Mat3x3& self = _CAST(Mat3x3&, obj); + SStream ss; + ss.setprecision(3); + ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n"; + ss << " " << self._21 << ", " << self._22 << ", " << self._23 << ",\n"; + ss << " " << self._31 << ", " << self._32 << ", " << self._33 << "])"; + return ss.str(); + }); + + vm->bind__getitem__(type->as(), [](VM* vm, PyVar obj, PyVar index) { + Mat3x3& self = _CAST(Mat3x3&, obj); + Tuple& t = CAST(Tuple&, index); + if(t.size() != 2) { vm->TypeError("Mat3x3.__getitem__ takes a tuple of 2 integers"); } + i64 i = CAST(i64, t[0]); + i64 j = CAST(i64, t[1]); + if(i < 0 || i >= 3 || j < 0 || j >= 3) { vm->IndexError("index out of range"); } + return VAR(self.m[i][j]); + }); + + vm->bind__setitem__(type->as(), [](VM* vm, PyVar obj, PyVar index, PyVar value) { + Mat3x3& self = _CAST(Mat3x3&, obj); + const Tuple& t = CAST(Tuple&, index); + if(t.size() != 2) { vm->TypeError("Mat3x3.__setitem__ takes a tuple of 2 integers"); } + i64 i = CAST(i64, t[0]); + i64 j = CAST(i64, t[1]); + if(i < 0 || i >= 3 || j < 0 || j >= 3) { vm->IndexError("index out of range"); } + self.m[i][j] = CAST_F(value); + }); + + vm->bind_field(type, "_11", &Mat3x3::_11); + vm->bind_field(type, "_12", &Mat3x3::_12); + vm->bind_field(type, "_13", &Mat3x3::_13); + vm->bind_field(type, "_21", &Mat3x3::_21); + vm->bind_field(type, "_22", &Mat3x3::_22); + vm->bind_field(type, "_23", &Mat3x3::_23); + vm->bind_field(type, "_31", &Mat3x3::_31); + vm->bind_field(type, "_32", &Mat3x3::_32); + vm->bind_field(type, "_33", &Mat3x3::_33); + + vm->bind__add__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { + Mat3x3& self = _CAST(Mat3x3&, _0); + Mat3x3& other = CAST(Mat3x3&, _1); + return vm->new_user_object(self + other); + }); + + vm->bind__sub__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { + Mat3x3& self = _CAST(Mat3x3&, _0); + Mat3x3& other = CAST(Mat3x3&, _1); + return vm->new_user_object(self - other); + }); + + vm->bind__mul__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { + Mat3x3& self = _CAST(Mat3x3&, _0); + f64 other = CAST_F(_1); + return vm->new_user_object(self * other); + }); + + vm->bind_func(type, "__rmul__", 2, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + f64 other = CAST_F(args[1]); + return vm->new_user_object(self * other); + }); + + vm->bind__truediv__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { + Mat3x3& self = _CAST(Mat3x3&, _0); + f64 other = CAST_F(_1); + return vm->new_user_object(self / other); + }); + + vm->bind__matmul__(type->as(), [](VM* vm, PyVar _0, PyVar _1) { + Mat3x3& self = _CAST(Mat3x3&, _0); + if(vm->is_user_type(_1)) { + const Mat3x3& other = _CAST(Mat3x3&, _1); + return vm->new_user_object(self.matmul(other)); + } + if(vm->is_user_type(_1)) { + const Vec3 other = _CAST(Vec3, _1); + return vm->new_user_object(self.matmul(other)); + } + return vm->NotImplemented; + }); + + vm->bind(type, "matmul(self, other: mat3x3, out: mat3x3 = None)", [](VM* vm, ArgsView args) { + const Mat3x3& self = _CAST(Mat3x3&, args[0]); + const Mat3x3& other = CAST(Mat3x3&, args[1]); + if(args[2] == vm->None) { + return vm->new_user_object(self.matmul(other)); + } else { + Mat3x3& out = CAST(Mat3x3&, args[2]); + out = self.matmul(other); return vm->None; - }); + } + }); - vm->bind_func(type, "copy_", 2, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - const Mat3x3& other = CAST(Mat3x3&, args[1]); - self = other; - return vm->None; - }); + vm->bind_func(type, "determinant", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + return VAR(self.determinant()); + }); - vm->bind__repr__(type->as(), [](VM* vm, PyVar obj) -> Str{ - const Mat3x3& self = _CAST(Mat3x3&, obj); - SStream ss; - ss.setprecision(3); - ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n"; - ss << " " << self._21 << ", " << self._22 << ", " << self._23 << ",\n"; - ss << " " << self._31 << ", " << self._32 << ", " << self._33 << "])"; - return ss.str(); - }); + vm->bind_func(type, "transpose", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + return vm->new_user_object(self.transpose()); + }); - vm->bind__getitem__(type->as(), [](VM* vm, PyVar obj, PyVar index){ - Mat3x3& self = _CAST(Mat3x3&, obj); - Tuple& t = CAST(Tuple&, index); - if(t.size() != 2){ - vm->TypeError("Mat3x3.__getitem__ takes a tuple of 2 integers"); - } - i64 i = CAST(i64, t[0]); - i64 j = CAST(i64, t[1]); - if(i < 0 || i >= 3 || j < 0 || j >= 3){ - vm->IndexError("index out of range"); - } - return VAR(self.m[i][j]); - }); + vm->bind__invert__(type->as(), [](VM* vm, PyVar obj) { + Mat3x3& self = _CAST(Mat3x3&, obj); + Mat3x3 ret; + if(!self.inverse(ret)) vm->ValueError("matrix is not invertible"); + return vm->new_user_object(ret); + }); - vm->bind__setitem__(type->as(), [](VM* vm, PyVar obj, PyVar index, PyVar value){ - Mat3x3& self = _CAST(Mat3x3&, obj); - const Tuple& t = CAST(Tuple&, index); - if(t.size() != 2){ - vm->TypeError("Mat3x3.__setitem__ takes a tuple of 2 integers"); - } - i64 i = CAST(i64, t[0]); - i64 j = CAST(i64, t[1]); - if(i < 0 || i >= 3 || j < 0 || j >= 3){ - vm->IndexError("index out of range"); - } - self.m[i][j] = CAST_F(value); - }); + vm->bind_func(type, "inverse", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + Mat3x3 ret; + if(!self.inverse(ret)) vm->ValueError("matrix is not invertible"); + return vm->new_user_object(ret); + }); - vm->bind_field(type, "_11", &Mat3x3::_11); - vm->bind_field(type, "_12", &Mat3x3::_12); - vm->bind_field(type, "_13", &Mat3x3::_13); - vm->bind_field(type, "_21", &Mat3x3::_21); - vm->bind_field(type, "_22", &Mat3x3::_22); - vm->bind_field(type, "_23", &Mat3x3::_23); - vm->bind_field(type, "_31", &Mat3x3::_31); - vm->bind_field(type, "_32", &Mat3x3::_32); - vm->bind_field(type, "_33", &Mat3x3::_33); + vm->bind_func(type, "inverse_", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + Mat3x3 ret; + if(!self.inverse(ret)) vm->ValueError("matrix is not invertible"); + self = ret; + return vm->None; + }); - vm->bind__add__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ - Mat3x3& self = _CAST(Mat3x3&, _0); - Mat3x3& other = CAST(Mat3x3&, _1); - return vm->new_user_object(self + other); - }); + vm->bind_func(type, "transpose_", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + self = self.transpose(); + return vm->None; + }); - vm->bind__sub__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ - Mat3x3& self = _CAST(Mat3x3&, _0); - Mat3x3& other = CAST(Mat3x3&, _1); - return vm->new_user_object(self - other); - }); - - vm->bind__mul__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ - Mat3x3& self = _CAST(Mat3x3&, _0); - f64 other = CAST_F(_1); - return vm->new_user_object(self * other); - }); - - vm->bind_func(type, "__rmul__", 2, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - f64 other = CAST_F(args[1]); - return vm->new_user_object(self * other); - }); - - vm->bind__truediv__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ - Mat3x3& self = _CAST(Mat3x3&, _0); - f64 other = CAST_F(_1); - return vm->new_user_object(self / other); - }); - - vm->bind__matmul__(type->as(), [](VM* vm, PyVar _0, PyVar _1){ - Mat3x3& self = _CAST(Mat3x3&, _0); - if(vm->is_user_type(_1)){ - const Mat3x3& other = _CAST(Mat3x3&, _1); - return vm->new_user_object(self.matmul(other)); - } - if(vm->is_user_type(_1)){ - const Vec3 other = _CAST(Vec3, _1); - return vm->new_user_object(self.matmul(other)); - } - return vm->NotImplemented; - }); - - vm->bind(type, "matmul(self, other: mat3x3, out: mat3x3 = None)", [](VM* vm, ArgsView args){ - const Mat3x3& self = _CAST(Mat3x3&, args[0]); - const Mat3x3& other = CAST(Mat3x3&, args[1]); - if(args[2] == vm->None){ - return vm->new_user_object(self.matmul(other)); - }else{ - Mat3x3& out = CAST(Mat3x3&, args[2]); - out = self.matmul(other); - return vm->None; - } - }); - - vm->bind_func(type, "determinant", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - return VAR(self.determinant()); - }); - - vm->bind_func(type, "transpose", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - return vm->new_user_object(self.transpose()); - }); - - vm->bind__invert__(type->as(), [](VM* vm, PyVar obj){ - Mat3x3& self = _CAST(Mat3x3&, obj); - Mat3x3 ret; - if(!self.inverse(ret)) vm->ValueError("matrix is not invertible"); - return vm->new_user_object(ret); - }); - - vm->bind_func(type, "inverse", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - Mat3x3 ret; - if(!self.inverse(ret)) vm->ValueError("matrix is not invertible"); - return vm->new_user_object(ret); - }); - - vm->bind_func(type, "inverse_", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - Mat3x3 ret; - if(!self.inverse(ret)) vm->ValueError("matrix is not invertible"); - self = ret; - return vm->None; - }); - - vm->bind_func(type, "transpose_", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - self = self.transpose(); - return vm->None; - }); - - // @staticmethod - vm->bind_func(type, "zeros", 0, [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind_func( + type, + "zeros", + 0, + [](VM* vm, ArgsView args) { return vm->new_user_object(Mat3x3::zeros()); - }, {}, BindType::STATICMETHOD); + }, + {}, + BindType::STATICMETHOD); - // @staticmethod - vm->bind_func(type, "ones", 0, [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind_func( + type, + "ones", + 0, + [](VM* vm, ArgsView args) { return vm->new_user_object(Mat3x3::ones()); - }, {}, BindType::STATICMETHOD); + }, + {}, + BindType::STATICMETHOD); - // @staticmethod - vm->bind_func(type, "identity", 0, [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind_func( + type, + "identity", + 0, + [](VM* vm, ArgsView args) { return vm->new_user_object(Mat3x3::identity()); - }, {}, BindType::STATICMETHOD); + }, + {}, + BindType::STATICMETHOD); - /*************** affine transformations ***************/ - // @staticmethod - vm->bind(type, "trs(t: vec2, r: float, s: vec2)", [](VM* vm, ArgsView args){ + /*************** affine transformations ***************/ + // @staticmethod + vm->bind( + type, + "trs(t: vec2, r: float, s: vec2)", + [](VM* vm, ArgsView args) { Vec2 t = CAST(Vec2, args[0]); f64 r = CAST_F(args[1]); Vec2 s = CAST(Vec2, args[2]); return vm->new_user_object(Mat3x3::trs(t, r, s)); - }, {}, BindType::STATICMETHOD); + }, + {}, + BindType::STATICMETHOD); - vm->bind(type, "copy_trs_(self, t: vec2, r: float, s: vec2)", [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - Vec2 t = CAST(Vec2, args[1]); - f64 r = CAST_F(args[2]); - Vec2 s = CAST(Vec2, args[3]); - self = Mat3x3::trs(t, r, s); - return vm->None; - }); + vm->bind(type, "copy_trs_(self, t: vec2, r: float, s: vec2)", [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + Vec2 t = CAST(Vec2, args[1]); + f64 r = CAST_F(args[2]); + Vec2 s = CAST(Vec2, args[3]); + self = Mat3x3::trs(t, r, s); + return vm->None; + }); - vm->bind(type, "copy_t_(self, t: vec2)", [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - Vec2 t = CAST(Vec2, args[1]); - self = Mat3x3::trs(t, self._r(), self._s()); - return vm->None; - }); + vm->bind(type, "copy_t_(self, t: vec2)", [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + Vec2 t = CAST(Vec2, args[1]); + self = Mat3x3::trs(t, self._r(), self._s()); + return vm->None; + }); - vm->bind(type, "copy_r_(self, r: float)", [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - f64 r = CAST_F(args[1]); - self = Mat3x3::trs(self._t(), r, self._s()); - return vm->None; - }); + vm->bind(type, "copy_r_(self, r: float)", [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + f64 r = CAST_F(args[1]); + self = Mat3x3::trs(self._t(), r, self._s()); + return vm->None; + }); - vm->bind(type, "copy_s_(self, s: vec2)", [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - Vec2 s = CAST(Vec2, args[1]); - self = Mat3x3::trs(self._t(), self._r(), s); - return vm->None; - }); + vm->bind(type, "copy_s_(self, s: vec2)", [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + Vec2 s = CAST(Vec2, args[1]); + self = Mat3x3::trs(self._t(), self._r(), s); + return vm->None; + }); - vm->bind_func(type, "is_affine", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - return VAR(self.is_affine()); - }); + vm->bind_func(type, "is_affine", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + return VAR(self.is_affine()); + }); - vm->bind_func(type, "_t", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - return vm->new_user_object(self._t()); - }); + vm->bind_func(type, "_t", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + return vm->new_user_object(self._t()); + }); - vm->bind_func(type, "_r", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - return VAR(self._r()); - }); + vm->bind_func(type, "_r", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + return VAR(self._r()); + }); - vm->bind_func(type, "_s", 1, [](VM* vm, ArgsView args){ - Mat3x3& self = _CAST(Mat3x3&, args[0]); - return vm->new_user_object(self._s()); - }); + vm->bind_func(type, "_s", 1, [](VM* vm, ArgsView args) { + Mat3x3& self = _CAST(Mat3x3&, args[0]); + return vm->new_user_object(self._s()); + }); - vm->bind_func(type, "transform_point", 2, [](VM* vm, ArgsView args){ - const Mat3x3& self = _CAST(Mat3x3&, args[0]); - Vec2 v = CAST(Vec2, args[1]); - Vec2 res(self._11 * v.x + self._12 * v.y + self._13, self._21 * v.x + self._22 * v.y + self._23); - return vm->new_user_object(res); - }); + vm->bind_func(type, "transform_point", 2, [](VM* vm, ArgsView args) { + const Mat3x3& self = _CAST(Mat3x3&, args[0]); + Vec2 v = CAST(Vec2, args[1]); + Vec2 res(self._11 * v.x + self._12 * v.y + self._13, self._21 * v.x + self._22 * v.y + self._23); + return vm->new_user_object(res); + }); - vm->bind_func(type, "inverse_transform_point", 2, [](VM* vm, ArgsView args){ - const Mat3x3& self = _CAST(Mat3x3&, args[0]); - Vec2 v = CAST(Vec2, args[1]); - Mat3x3 inv; - if(!self.inverse(inv)) vm->ValueError("matrix is not invertible"); - Vec2 res(inv._11 * v.x + inv._12 * v.y + inv._13, inv._21 * v.x + inv._22 * v.y + inv._23); - return vm->new_user_object(res); - }); + vm->bind_func(type, "inverse_transform_point", 2, [](VM* vm, ArgsView args) { + const Mat3x3& self = _CAST(Mat3x3&, args[0]); + Vec2 v = CAST(Vec2, args[1]); + Mat3x3 inv; + if(!self.inverse(inv)) vm->ValueError("matrix is not invertible"); + Vec2 res(inv._11 * v.x + inv._12 * v.y + inv._13, inv._21 * v.x + inv._22 * v.y + inv._23); + return vm->new_user_object(res); + }); - vm->bind_func(type, "transform_vector", 2, [](VM* vm, ArgsView args){ - const Mat3x3& self = _CAST(Mat3x3&, args[0]); - Vec2 v = CAST(Vec2, args[1]); - Vec2 res(self._11 * v.x + self._12 * v.y, self._21 * v.x + self._22 * v.y); - return vm->new_user_object(res); - }); + vm->bind_func(type, "transform_vector", 2, [](VM* vm, ArgsView args) { + const Mat3x3& self = _CAST(Mat3x3&, args[0]); + Vec2 v = CAST(Vec2, args[1]); + Vec2 res(self._11 * v.x + self._12 * v.y, self._21 * v.x + self._22 * v.y); + return vm->new_user_object(res); + }); - vm->bind_func(type, "inverse_transform_vector", 2, [](VM* vm, ArgsView args){ - const Mat3x3& self = _CAST(Mat3x3&, args[0]); - Vec2 v = CAST(Vec2, args[1]); - Mat3x3 inv; - if(!self.inverse(inv)) vm->ValueError("matrix is not invertible"); - Vec2 res(inv._11 * v.x + inv._12 * v.y, inv._21 * v.x + inv._22 * v.y); - return vm->new_user_object(res); - }); - } + vm->bind_func(type, "inverse_transform_vector", 2, [](VM* vm, ArgsView args) { + const Mat3x3& self = _CAST(Mat3x3&, args[0]); + Vec2 v = CAST(Vec2, args[1]); + Mat3x3 inv; + if(!self.inverse(inv)) vm->ValueError("matrix is not invertible"); + Vec2 res(inv._11 * v.x + inv._12 * v.y, inv._21 * v.x + inv._22 * v.y); + return vm->new_user_object(res); + }); +} - -void add_module_linalg(VM* vm){ +void add_module_linalg(VM* vm) { PyObject* linalg = vm->new_module("linalg"); vm->register_user_class(linalg, "vec2", VM::tp_object); @@ -561,138 +586,132 @@ void add_module_linalg(VM* vm){ linalg->attr().set("mat3x3_p", float_p); } +/////////////// mat3x3 /////////////// +Mat3x3::Mat3x3() {} - /////////////// mat3x3 /////////////// - Mat3x3::Mat3x3() {} - Mat3x3::Mat3x3(float _11, float _12, float _13, - float _21, float _22, float _23, - float _31, float _32, float _33) - : _11(_11), _12(_12), _13(_13) - , _21(_21), _22(_22), _23(_23) - , _31(_31), _32(_32), _33(_33) {} +Mat3x3::Mat3x3(float _11, float _12, float _13, float _21, float _22, float _23, float _31, float _32, float _33) : + _11(_11), _12(_12), _13(_13), _21(_21), _22(_22), _23(_23), _31(_31), _32(_32), _33(_33) {} - Mat3x3 Mat3x3::zeros(){ - return Mat3x3(0, 0, 0, 0, 0, 0, 0, 0, 0); +Mat3x3 Mat3x3::zeros() { return Mat3x3(0, 0, 0, 0, 0, 0, 0, 0, 0); } + +Mat3x3 Mat3x3::ones() { return Mat3x3(1, 1, 1, 1, 1, 1, 1, 1, 1); } + +Mat3x3 Mat3x3::identity() { return Mat3x3(1, 0, 0, 0, 1, 0, 0, 0, 1); } + +Mat3x3 Mat3x3::operator+ (const Mat3x3& other) const { + Mat3x3 ret; + for(int i = 0; i < 9; ++i) + ret.v[i] = v[i] + other.v[i]; + return ret; +} + +Mat3x3 Mat3x3::operator- (const Mat3x3& other) const { + Mat3x3 ret; + for(int i = 0; i < 9; ++i) + ret.v[i] = v[i] - other.v[i]; + return ret; +} + +Mat3x3 Mat3x3::operator* (float scalar) const { + Mat3x3 ret; + for(int i = 0; i < 9; ++i) + ret.v[i] = v[i] * scalar; + return ret; +} + +Mat3x3 Mat3x3::operator/ (float scalar) const { + Mat3x3 ret; + for(int i = 0; i < 9; ++i) + ret.v[i] = v[i] / scalar; + return ret; +} + +bool Mat3x3::operator== (const Mat3x3& other) const { + for(int i = 0; i < 9; ++i) { + if(!isclose(v[i], other.v[i])) return false; } + return true; +} - Mat3x3 Mat3x3::ones(){ - return Mat3x3(1, 1, 1, 1, 1, 1, 1, 1, 1); +bool Mat3x3::operator!= (const Mat3x3& other) const { + for(int i = 0; i < 9; ++i) { + if(!isclose(v[i], other.v[i])) return true; } + return false; +} - Mat3x3 Mat3x3::identity(){ - return Mat3x3(1, 0, 0, 0, 1, 0, 0, 0, 1); - } +Mat3x3 Mat3x3::matmul(const Mat3x3& other) const { + Mat3x3 out; + out._11 = _11 * other._11 + _12 * other._21 + _13 * other._31; + out._12 = _11 * other._12 + _12 * other._22 + _13 * other._32; + out._13 = _11 * other._13 + _12 * other._23 + _13 * other._33; + out._21 = _21 * other._11 + _22 * other._21 + _23 * other._31; + out._22 = _21 * other._12 + _22 * other._22 + _23 * other._32; + out._23 = _21 * other._13 + _22 * other._23 + _23 * other._33; + out._31 = _31 * other._11 + _32 * other._21 + _33 * other._31; + out._32 = _31 * other._12 + _32 * other._22 + _33 * other._32; + out._33 = _31 * other._13 + _32 * other._23 + _33 * other._33; + return out; +} - Mat3x3 Mat3x3::operator+(const Mat3x3& other) const{ - Mat3x3 ret; - for (int i=0; i<9; ++i) ret.v[i] = v[i] + other.v[i]; - return ret; - } +Vec3 Mat3x3::matmul(const Vec3& other) const { + Vec3 out; + out.x = _11 * other.x + _12 * other.y + _13 * other.z; + out.y = _21 * other.x + _22 * other.y + _23 * other.z; + out.z = _31 * other.x + _32 * other.y + _33 * other.z; + return out; +} - Mat3x3 Mat3x3::operator-(const Mat3x3& other) const{ - Mat3x3 ret; - for (int i=0; i<9; ++i) ret.v[i] = v[i] - other.v[i]; - return ret; - } +float Mat3x3::determinant() const { + return _11 * _22 * _33 + _12 * _23 * _31 + _13 * _21 * _32 - _11 * _23 * _32 - _12 * _21 * _33 - _13 * _22 * _31; +} - Mat3x3 Mat3x3::operator*(float scalar) const{ - Mat3x3 ret; - for (int i=0; i<9; ++i) ret.v[i] = v[i] * scalar; - return ret; - } +Mat3x3 Mat3x3::transpose() const { + Mat3x3 ret; + ret._11 = _11; + ret._12 = _21; + ret._13 = _31; + ret._21 = _12; + ret._22 = _22; + ret._23 = _32; + ret._31 = _13; + ret._32 = _23; + ret._33 = _33; + return ret; +} - Mat3x3 Mat3x3::operator/(float scalar) const{ - Mat3x3 ret; - for (int i=0; i<9; ++i) ret.v[i] = v[i] / scalar; - return ret; - } +bool Mat3x3::inverse(Mat3x3& out) const { + float det = determinant(); + if(isclose(det, 0)) return false; + float inv_det = 1.0f / det; + out._11 = (_22 * _33 - _23 * _32) * inv_det; + out._12 = (_13 * _32 - _12 * _33) * inv_det; + out._13 = (_12 * _23 - _13 * _22) * inv_det; + out._21 = (_23 * _31 - _21 * _33) * inv_det; + out._22 = (_11 * _33 - _13 * _31) * inv_det; + out._23 = (_13 * _21 - _11 * _23) * inv_det; + out._31 = (_21 * _32 - _22 * _31) * inv_det; + out._32 = (_12 * _31 - _11 * _32) * inv_det; + out._33 = (_11 * _22 - _12 * _21) * inv_det; + return true; +} - bool Mat3x3::operator==(const Mat3x3& other) const{ - for (int i=0; i<9; ++i){ - if (!isclose(v[i], other.v[i])) return false; - } - return true; - } +Mat3x3 Mat3x3::trs(Vec2 t, float radian, Vec2 s) { + float cr = cosf(radian); + float sr = sinf(radian); + return Mat3x3(s.x * cr, -s.y * sr, t.x, s.x * sr, s.y * cr, t.y, 0.0f, 0.0f, 1.0f); +} - bool Mat3x3::operator!=(const Mat3x3& other) const{ - for (int i=0; i<9; ++i){ - if (!isclose(v[i], other.v[i])) return true; - } - return false; - } +bool Mat3x3::is_affine() const { + float det = _11 * _22 - _12 * _21; + if(isclose(det, 0)) return false; + return _31 == 0.0f && _32 == 0.0f && _33 == 1.0f; +} - Mat3x3 Mat3x3::matmul(const Mat3x3& other) const{ - Mat3x3 out; - out._11 = _11 * other._11 + _12 * other._21 + _13 * other._31; - out._12 = _11 * other._12 + _12 * other._22 + _13 * other._32; - out._13 = _11 * other._13 + _12 * other._23 + _13 * other._33; - out._21 = _21 * other._11 + _22 * other._21 + _23 * other._31; - out._22 = _21 * other._12 + _22 * other._22 + _23 * other._32; - out._23 = _21 * other._13 + _22 * other._23 + _23 * other._33; - out._31 = _31 * other._11 + _32 * other._21 + _33 * other._31; - out._32 = _31 * other._12 + _32 * other._22 + _33 * other._32; - out._33 = _31 * other._13 + _32 * other._23 + _33 * other._33; - return out; - } +Vec2 Mat3x3::_t() const { return Vec2(_13, _23); } - Vec3 Mat3x3::matmul(const Vec3& other) const{ - Vec3 out; - out.x = _11 * other.x + _12 * other.y + _13 * other.z; - out.y = _21 * other.x + _22 * other.y + _23 * other.z; - out.z = _31 * other.x + _32 * other.y + _33 * other.z; - return out; - } +float Mat3x3::_r() const { return atan2f(_21, _11); } - float Mat3x3::determinant() const{ - return _11 * _22 * _33 + _12 * _23 * _31 + _13 * _21 * _32 - - _11 * _23 * _32 - _12 * _21 * _33 - _13 * _22 * _31; - } +Vec2 Mat3x3::_s() const { return Vec2(sqrtf(_11 * _11 + _21 * _21), sqrtf(_12 * _12 + _22 * _22)); } - Mat3x3 Mat3x3::transpose() const{ - Mat3x3 ret; - ret._11 = _11; ret._12 = _21; ret._13 = _31; - ret._21 = _12; ret._22 = _22; ret._23 = _32; - ret._31 = _13; ret._32 = _23; ret._33 = _33; - return ret; - } - - bool Mat3x3::inverse(Mat3x3& out) const{ - float det = determinant(); - if (isclose(det, 0)) return false; - float inv_det = 1.0f / det; - out._11 = (_22 * _33 - _23 * _32) * inv_det; - out._12 = (_13 * _32 - _12 * _33) * inv_det; - out._13 = (_12 * _23 - _13 * _22) * inv_det; - out._21 = (_23 * _31 - _21 * _33) * inv_det; - out._22 = (_11 * _33 - _13 * _31) * inv_det; - out._23 = (_13 * _21 - _11 * _23) * inv_det; - out._31 = (_21 * _32 - _22 * _31) * inv_det; - out._32 = (_12 * _31 - _11 * _32) * inv_det; - out._33 = (_11 * _22 - _12 * _21) * inv_det; - return true; - } - - Mat3x3 Mat3x3::trs(Vec2 t, float radian, Vec2 s){ - float cr = cosf(radian); - float sr = sinf(radian); - return Mat3x3(s.x * cr, -s.y * sr, t.x, - s.x * sr, s.y * cr, t.y, - 0.0f, 0.0f, 1.0f); - } - - bool Mat3x3::is_affine() const{ - float det = _11 * _22 - _12 * _21; - if(isclose(det, 0)) return false; - return _31 == 0.0f && _32 == 0.0f && _33 == 1.0f; - } - - Vec2 Mat3x3::_t() const { return Vec2(_13, _23); } - float Mat3x3::_r() const { return atan2f(_21, _11); } - Vec2 Mat3x3::_s() const { - return Vec2( - sqrtf(_11 * _11 + _21 * _21), - sqrtf(_12 * _12 + _22 * _22) - ); - } - -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/modules.cpp b/src/modules/modules.cpp index 427c3bd8..c3cdca41 100644 --- a/src/modules/modules.cpp +++ b/src/modules/modules.cpp @@ -8,9 +8,9 @@ #include #include -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(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(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_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::infinity())); mod->attr().set("nan", VAR(std::numeric_limits::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, "", 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(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(mod, "LineProfiler"); } #else -void add_module_line_profiler(VM* vm){ - (void)vm; -} +void add_module_line_profiler(VM* vm) { (void)vm; } #endif -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/modules/random.cpp b/src/modules/random.cpp index 1545bbca..5ca8da7a 100644 --- a/src/modules/random.cpp +++ b/src/modules/random.cpp @@ -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> 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> 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> 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(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 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(mod, "Random"); PyVar instance = vm->new_user_object(); @@ -230,4 +220,4 @@ void add_module_random(VM* vm){ mod->attr().set("choices", vm->getattr(instance, "choices")); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/objects/builtins.cpp b/src/objects/builtins.cpp index 60461a5b..1e70a44c 100644 --- a/src/objects/builtins.cpp +++ b/src/objects/builtins.cpp @@ -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 \ No newline at end of file +namespace pkpy { +const PyVar PY_OP_CALL(Type(), new PyObject(Type())); +const PyVar PY_OP_YIELD(Type(), new PyObject(Type())); +} // namespace pkpy diff --git a/src/objects/codeobject.cpp b/src/objects/codeobject.cpp index 6560287f..28087330 100644 --- a/src/objects/codeobject.cpp +++ b/src/objects/codeobject.cpp @@ -1,9 +1,9 @@ #include "pocketpy/objects/codeobject.hpp" -namespace pkpy{ +namespace pkpy { - CodeObject::CodeObject(std::shared_ptr 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 \ No newline at end of file +CodeObject::CodeObject(std::shared_ptr 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 diff --git a/src/objects/dict.cpp b/src/objects/dict.cpp index cfde252d..184d7c1c 100644 --- a/src/objects/dict.cpp +++ b/src/objects/dict.cpp @@ -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 \ No newline at end of file + 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 diff --git a/src/objects/error.cpp b/src/objects/error.cpp index 41f772b5..0c61066b 100644 --- a/src/objects/error.cpp +++ b/src/objects/error.cpp @@ -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 \ No newline at end of file +} // namespace pkpy diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 15fc536f..51a99a4a 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,5 +1,5 @@ #include "pocketpy/objects/object.hpp" -namespace pkpy{ - PyVar::PyVar(PyObject* p): PyVar(p->type, p) {} -} // namespace pkpy \ No newline at end of file +namespace pkpy { +PyVar::PyVar(PyObject* p) : PyVar(p->type, p) {} +} // namespace pkpy diff --git a/src/objects/sourcedata.cpp b/src/objects/sourcedata.cpp index 0e63ab84..f723a3b4 100644 --- a/src/objects/sourcedata.cpp +++ b/src/objects/sourcedata.cpp @@ -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 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 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 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 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 \ No newline at end of file + return ss.str(); +} +} // namespace pkpy diff --git a/src/objects/tuplelist.cpp b/src/objects/tuplelist.cpp index 75a3b58a..5e00bad5 100644 --- a/src/objects/tuplelist.cpp +++ b/src/objects/tuplelist.cpp @@ -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 #include -namespace pkpy{ +namespace pkpy { #ifdef PK_USE_CJSON void add_module_cjson(VM* vm); #endif -template -PyVar PyArrayGetItem(VM* vm, PyVar _0, PyVar _1){ +template +PyVar PyArrayGetItem(VM* vm, PyVar _0, PyVar _1) { static_assert(std::is_same_v || std::is_same_v); const T& self = _CAST(T&, _0); - if(is_int(_1)){ + if(is_int(_1)) { i64 index = _1.as(); index = vm->normalized_index(index, self.size()); return self[index]; } - if(is_type(_1, vm->tp_slice)){ + if(is_type(_1, vm->tp_slice)) { const Slice& s = _CAST(Slice&, _1); int start, stop, step; vm->parse_int_slice(s, self.size(), start, stop, step); @@ -46,16 +46,16 @@ PyVar PyArrayGetItem(VM* vm, PyVar _0, PyVar _1){ } void __init_builtins(VM* _vm) { -#define BIND_NUM_ARITH_OPT(name, op) \ - _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) { \ - if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs)); \ - if(is_float(rhs)) return VAR(_CAST(i64, lhs) op _CAST(f64, rhs)); \ - return vm->NotImplemented; \ - }); \ - _vm->bind##name(VM::tp_float, [](VM* vm, PyVar lhs, PyVar rhs) { \ - if(is_int(rhs)) return VAR(_CAST(f64, lhs) op _CAST(i64, rhs)); \ - if(is_float(rhs)) return VAR(_CAST(f64, lhs) op _CAST(f64, rhs)); \ - return vm->NotImplemented; \ +#define BIND_NUM_ARITH_OPT(name, op) \ + _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) { \ + if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs)); \ + if(is_float(rhs)) return VAR(_CAST(i64, lhs) op _CAST(f64, rhs)); \ + return vm->NotImplemented; \ + }); \ + _vm->bind##name(VM::tp_float, [](VM* vm, PyVar lhs, PyVar rhs) { \ + if(is_int(rhs)) return VAR(_CAST(f64, lhs) op _CAST(i64, rhs)); \ + if(is_float(rhs)) return VAR(_CAST(f64, lhs) op _CAST(f64, rhs)); \ + return vm->NotImplemented; \ }); BIND_NUM_ARITH_OPT(__add__, +) @@ -64,16 +64,16 @@ void __init_builtins(VM* _vm) { #undef BIND_NUM_ARITH_OPT -#define BIND_NUM_LOGICAL_OPT(name, op) \ - _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) { \ - if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs)); \ - if(is_float(rhs)) return VAR(_CAST(i64, lhs) op _CAST(f64, rhs)); \ - return vm->NotImplemented; \ - }); \ - _vm->bind##name(VM::tp_float, [](VM* vm, PyVar lhs, PyVar rhs) { \ - if(is_int(rhs)) return VAR(_CAST(f64, lhs) op _CAST(i64, rhs)); \ - if(is_float(rhs)) return VAR(_CAST(f64, lhs) op _CAST(f64, rhs)); \ - return vm->NotImplemented; \ +#define BIND_NUM_LOGICAL_OPT(name, op) \ + _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) { \ + if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs)); \ + if(is_float(rhs)) return VAR(_CAST(i64, lhs) op _CAST(f64, rhs)); \ + return vm->NotImplemented; \ + }); \ + _vm->bind##name(VM::tp_float, [](VM* vm, PyVar lhs, PyVar rhs) { \ + if(is_int(rhs)) return VAR(_CAST(f64, lhs) op _CAST(i64, rhs)); \ + if(is_float(rhs)) return VAR(_CAST(f64, lhs) op _CAST(f64, rhs)); \ + return vm->NotImplemented; \ }); BIND_NUM_LOGICAL_OPT(__eq__, ==) @@ -81,7 +81,7 @@ void __init_builtins(VM* _vm) { BIND_NUM_LOGICAL_OPT(__le__, <=) BIND_NUM_LOGICAL_OPT(__gt__, >) BIND_NUM_LOGICAL_OPT(__ge__, >=) - + #undef BIND_NUM_ARITH_OPT #undef BIND_NUM_LOGICAL_OPT @@ -96,24 +96,24 @@ void __init_builtins(VM* _vm) { _vm->bind_func(_vm->builtins, "super", -1, [](VM* vm, ArgsView args) { PyObject* class_arg = nullptr; PyVar self_arg = nullptr; - if(args.size() == 2){ + if(args.size() == 2) { class_arg = args[0].get(); self_arg = args[1]; - }else if(args.size() == 0){ + } else if(args.size() == 0) { Frame* frame = &vm->callstack.top(); - if(frame->_callable != nullptr){ + if(frame->_callable != nullptr) { class_arg = frame->_callable->as()._class; if(frame->_locals.size() > 0) self_arg = frame->_locals[0]; } - if(class_arg == nullptr || self_arg == nullptr){ + if(class_arg == nullptr || self_arg == nullptr) { vm->TypeError("super(): unable to determine the class context, use super(class, self) instead"); } - }else{ + } else { vm->TypeError("super() takes 0 or 2 arguments"); } vm->check_type(class_arg, vm->tp_type); Type type = class_arg->as(); - if(!vm->isinstance(self_arg, type)){ + if(!vm->isinstance(self_arg, type)) { StrName _0 = _type_name(vm, vm->_tp(self_arg)); StrName _1 = _type_name(vm, type); vm->TypeError("super(): " + _0.escape() + " is not an instance of " + _1.escape()); @@ -134,9 +134,9 @@ void __init_builtins(VM* _vm) { }); _vm->bind_func(_vm->builtins, "isinstance", 2, [](VM* vm, ArgsView args) { - if(is_type(args[1], vm->tp_tuple)){ + if(is_type(args[1], vm->tp_tuple)) { Tuple& types = _CAST(Tuple&, args[1]); - for(PyVar type : types){ + for(PyVar type: types) { vm->check_type(type, vm->tp_type); if(vm->isinstance(args[0], type->as())) return vm->True; } @@ -176,11 +176,11 @@ void __init_builtins(VM* _vm) { return vm->None; }); - _vm->bind(_vm->builtins, "max(*args, key=None)", [](VM* vm, ArgsView args){ + _vm->bind(_vm->builtins, "max(*args, key=None)", [](VM* vm, ArgsView args) { return vm->__minmax_reduce(&VM::py_gt, args[0], args[1]); }); - _vm->bind(_vm->builtins, "min(*args, key=None)", [](VM* vm, ArgsView args){ + _vm->bind(_vm->builtins, "min(*args, key=None)", [](VM* vm, ArgsView args) { return vm->__minmax_reduce(&VM::py_lt, args[0], args[1]); }); @@ -194,19 +194,19 @@ void __init_builtins(VM* _vm) { return VAR(vm->py_callable(args[0])); }); - _vm->bind_func(_vm->builtins, "__import__", 1, [](VM* vm, ArgsView args) -> PyVar{ + _vm->bind_func(_vm->builtins, "__import__", 1, [](VM* vm, ArgsView args) -> PyVar { const Str& name = CAST(Str&, args[0]); return vm->py_import(name); }); _vm->bind_func(_vm->builtins, "divmod", 2, [](VM* vm, ArgsView args) { - if(is_int(args[0])){ + if(is_int(args[0])) { i64 lhs = _CAST(i64, args[0]); i64 rhs = CAST(i64, args[1]); if(rhs == 0) vm->ZeroDivisionError(); auto res = std::div(lhs, rhs); return VAR(Tuple(VAR(res.quot), VAR(res.rem))); - }else{ + } else { return vm->call_method(args[0], __divmod__, args[1]); } }); @@ -225,13 +225,13 @@ void __init_builtins(VM* _vm) { const Str& source = CAST(Str&, args[0]); const Str& filename = CAST(Str&, args[1]); const Str& mode = CAST(Str&, args[2]); - if(mode == "exec"){ + if(mode == "exec") { return VAR(vm->precompile(source, filename, EXEC_MODE)); - }else if(mode == "eval"){ + } else if(mode == "eval") { return VAR(vm->precompile(source, filename, EVAL_MODE)); - }else if(mode == "single"){ + } else if(mode == "single") { return VAR(vm->precompile(source, filename, CELL_MODE)); - }else{ + } else { vm->ValueError("compile() mode must be 'exec', 'eval' or 'single'"); return vm->None; } @@ -242,30 +242,30 @@ void __init_builtins(VM* _vm) { return vm->None; }); - _vm->bind_func(_vm->builtins, "repr", 1, [](VM* vm, ArgsView args){ + _vm->bind_func(_vm->builtins, "repr", 1, [](VM* vm, ArgsView args) { return VAR(vm->py_repr(args[0])); }); - _vm->bind_func(_vm->builtins, "len", 1, [](VM* vm, ArgsView args){ + _vm->bind_func(_vm->builtins, "len", 1, [](VM* vm, ArgsView args) { const PyTypeInfo* ti = vm->_tp_info(args[0]); if(ti->m__len__) return VAR(ti->m__len__(vm, args[0])); return vm->call_method(args[0], __len__); }); - _vm->bind_func(_vm->builtins, "hash", 1, [](VM* vm, ArgsView args){ + _vm->bind_func(_vm->builtins, "hash", 1, [](VM* vm, ArgsView args) { i64 value = vm->py_hash(args[0]); return VAR(value); }); _vm->bind_func(_vm->builtins, "chr", 1, [](VM* vm, ArgsView args) { i64 i = CAST(i64, args[0]); - if (i < 0 || i >= 128) vm->ValueError("chr() arg not in [0, 128)"); + if(i < 0 || i >= 128) vm->ValueError("chr() arg not in [0, 128)"); return VAR(std::string(1, (char)i)); }); _vm->bind_func(_vm->builtins, "ord", 1, [](VM* vm, ArgsView args) { const Str& s = CAST(Str&, args[0]); - if (s.length()!=1) vm->TypeError("ord() expected an ASCII character"); + if(s.length() != 1) vm->TypeError("ord() expected an ASCII character"); return VAR((i64)(s[0])); }); @@ -279,11 +279,11 @@ void __init_builtins(VM* _vm) { }); _vm->bind_func(_vm->builtins, "getattr", -1, [](VM* vm, ArgsView args) { - if(args.size()!=2 && args.size()!=3) vm->TypeError("getattr() takes 2 or 3 arguments"); + if(args.size() != 2 && args.size() != 3) vm->TypeError("getattr() takes 2 or 3 arguments"); StrName name = CAST(Str&, args[1]); PyVar val = vm->getattr(args[0], name, false); - if(val == nullptr){ - if(args.size()==2) vm->AttributeError(args[0], name); + if(val == nullptr) { + if(args.size() == 2) vm->AttributeError(args[0], name); return args[2]; } return val; @@ -313,10 +313,13 @@ void __init_builtins(VM* _vm) { _vm->bind_func(_vm->builtins, "bin", 1, [](VM* vm, ArgsView args) { SStream ss; i64 x = CAST(i64, args[0]); - if(x < 0){ ss << "-"; x = -x; } + if(x < 0) { + ss << "-"; + x = -x; + } ss << "0b"; std::string bits; - while(x){ + while(x) { bits += (x & 1) ? '1' : '0'; x >>= 1; } @@ -328,7 +331,7 @@ void __init_builtins(VM* _vm) { _vm->bind_func(_vm->builtins, "dir", 1, [](VM* vm, ArgsView args) { vector names; - if(!is_tagged(args[0]) && args[0]->is_attr_valid()){ + if(!is_tagged(args[0]) && args[0]->is_attr_valid()) { auto keys = args[0]->attr().keys(); names.extend(keys.begin(), keys.end()); } @@ -337,16 +340,16 @@ void __init_builtins(VM* _vm) { names.extend(keys.begin(), keys.end()); std::sort(names.begin(), names.end()); List ret; - for(int i=0; i0 && names[i] == names[i-1]) continue; + if(i > 0 && names[i] == names[i - 1]) continue; ret.push_back(VAR(names[i].sv())); } return VAR(std::move(ret)); }); // tp_object - _vm->bind__repr__(VM::tp_object, [](VM* vm, PyVar obj) -> Str{ + _vm->bind__repr__(VM::tp_object, [](VM* vm, PyVar obj) -> Str { assert(!is_tagged(obj)); SStream ss; ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at "; @@ -356,7 +359,7 @@ void __init_builtins(VM* _vm) { }); _vm->bind__eq__(VM::tp_object, [](VM* vm, PyVar _0, PyVar _1) { - return VAR(_0 == _1); + return VAR(_0 == _1); }); _vm->__cached_object_new = _vm->bind_func(VM::tp_object, __new__, 1, [](VM* vm, ArgsView args) { @@ -370,12 +373,19 @@ void __init_builtins(VM* _vm) { // tp_range _vm->bind_func(VM::tp_range, __new__, -1, [](VM* vm, ArgsView args) { - args._begin += 1; // skip cls + args._begin += 1; // skip cls Range r; - switch (args.size()) { + switch(args.size()) { case 1: r.stop = CAST(i64, args[0]); break; - case 2: r.start = CAST(i64, args[0]); r.stop = CAST(i64, args[1]); break; - case 3: r.start = CAST(i64, args[0]); r.stop = CAST(i64, args[1]); r.step = CAST(i64, args[2]); break; + case 2: + r.start = CAST(i64, args[0]); + r.stop = CAST(i64, args[1]); + break; + case 3: + r.start = CAST(i64, args[0]); + r.stop = CAST(i64, args[1]); + r.step = CAST(i64, args[2]); + break; default: vm->TypeError("expected 1-3 arguments, got " + std::to_string(args.size())); } if(r.step == 0) vm->ValueError("range() arg 3 must not be zero"); @@ -384,24 +394,24 @@ void __init_builtins(VM* _vm) { _vm->bind__iter__(VM::tp_range, [](VM* vm, PyVar _0) { const Range& r = PK_OBJ_GET(Range, _0); - if(r.step > 0){ + if(r.step > 0) { return vm->new_user_object(r); - }else{ + } else { return vm->new_user_object(r); } }); - _vm->_all_types[VM::tp_range].op__iter__ = [](VM* vm, PyVar _0){ + _vm->_all_types[VM::tp_range].op__iter__ = [](VM* vm, PyVar _0) { const Range& r = PK_OBJ_GET(Range, _0); - if(r.step > 0){ + if(r.step > 0) { vm->new_stack_object(vm->_tp_user(), r); - }else{ + } else { vm->new_stack_object(vm->_tp_user(), r); } }; - + // tp_nonetype _vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyVar _0) -> Str { - return "None"; + return "None"; }); // tp_float / tp_float @@ -415,7 +425,7 @@ void __init_builtins(VM* _vm) { }); auto py_number_pow = [](VM* vm, PyVar _0, PyVar _1) { - if(is_int(_0) && is_int(_1)){ + if(is_int(_0) && is_int(_1)) { i64 lhs = _CAST(i64, _0); i64 rhs = _CAST(i64, _1); if(rhs < 0) { @@ -423,13 +433,13 @@ void __init_builtins(VM* _vm) { return VAR((f64)std::pow(lhs, rhs)); } i64 ret = 1; - while(rhs){ + while(rhs) { if(rhs & 1) ret *= lhs; lhs *= lhs; rhs >>= 1; } return VAR(ret); - }else{ + } else { return VAR((f64)std::pow(CAST_F(_0), CAST_F(_1))); } }; @@ -438,36 +448,31 @@ void __init_builtins(VM* _vm) { _vm->bind__pow__(VM::tp_float, py_number_pow); _vm->bind_func(VM::tp_int, __new__, -1, [](VM* vm, ArgsView args) { - if(args.size() == 1+0) return VAR(0); + if(args.size() == 1 + 0) return VAR(0); // 1 arg - if(args.size() == 1+1){ - switch(vm->_tp(args[1])){ - case VM::tp_float: - return VAR((i64)_CAST(f64, args[1])); - case VM::tp_int: - return args[1]; - case VM::tp_bool: - return VAR(args[1]==vm->True ? 1 : 0); - case VM::tp_str: - break; - default: - vm->TypeError("invalid arguments for int()"); + if(args.size() == 1 + 1) { + switch(vm->_tp(args[1])) { + case VM::tp_float: return VAR((i64)_CAST(f64, args[1])); + case VM::tp_int: return args[1]; + case VM::tp_bool: return VAR(args[1] == vm->True ? 1 : 0); + case VM::tp_str: break; + default: vm->TypeError("invalid arguments for int()"); } } // 2+ args -> error - if(args.size() > 1+2) vm->TypeError("int() takes at most 2 arguments"); + if(args.size() > 1 + 2) vm->TypeError("int() takes at most 2 arguments"); // 1 or 2 args with str int base = 10; - if(args.size() == 1+2) base = CAST(i64, args[2]); + if(args.size() == 1 + 2) base = CAST(i64, args[2]); const Str& s = CAST(Str&, args[1]); std::string_view sv = s.sv(); bool negative = false; - if(!sv.empty() && (sv[0] == '+' || sv[0] == '-')){ + if(!sv.empty() && (sv[0] == '+' || sv[0] == '-')) { negative = sv[0] == '-'; sv.remove_prefix(1); } i64 val; - if(parse_uint(sv, &val, base) != IntParsingResult::Success){ + if(parse_uint(sv, &val, base) != IntParsingResult::Success) { vm->ValueError(_S("invalid literal for int() with base ", base, ": ", s.escape())); } if(negative) val = -val; @@ -490,20 +495,29 @@ void __init_builtins(VM* _vm) { i64 x = _CAST(i64, args[0]); if(x < 0) x = -x; int bits = 0; - while(x){ x >>= 1; bits++; } + while(x) { + x >>= 1; + bits++; + } return VAR(bits); }); - _vm->bind__repr__(VM::tp_int, [](VM* vm, PyVar obj) -> Str{ + _vm->bind__repr__(VM::tp_int, [](VM* vm, PyVar obj) -> Str { return std::to_string(_CAST(i64, obj)); }); - _vm->bind__neg__(VM::tp_int, [](VM* vm, PyVar obj) { return VAR(-_CAST(i64, obj)); }); - _vm->bind__hash__(VM::tp_int, [](VM* vm, PyVar obj) { return _CAST(i64, obj); }); - _vm->bind__invert__(VM::tp_int, [](VM* vm, PyVar obj) { return VAR(~_CAST(i64, obj)); }); + _vm->bind__neg__(VM::tp_int, [](VM* vm, PyVar obj) { + return VAR(-_CAST(i64, obj)); + }); + _vm->bind__hash__(VM::tp_int, [](VM* vm, PyVar obj) { + return _CAST(i64, obj); + }); + _vm->bind__invert__(VM::tp_int, [](VM* vm, PyVar obj) { + return VAR(~_CAST(i64, obj)); + }); -#define INT_BITWISE_OP(name, op) \ - _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) { \ - return VAR(_CAST(i64, lhs) op CAST(i64, rhs)); \ +#define INT_BITWISE_OP(name, op) \ + _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) { \ + return VAR(_CAST(i64, lhs) op CAST(i64, rhs)); \ }); INT_BITWISE_OP(__lshift__, <<) @@ -515,20 +529,15 @@ void __init_builtins(VM* _vm) { #undef INT_BITWISE_OP _vm->bind_func(VM::tp_float, __new__, -1, [](VM* vm, ArgsView args) { - if(args.size() == 1+0) return VAR(0.0); - if(args.size() > 1+1) vm->TypeError("float() takes at most 1 argument"); + if(args.size() == 1 + 0) return VAR(0.0); + if(args.size() > 1 + 1) vm->TypeError("float() takes at most 1 argument"); // 1 arg - switch(vm->_tp(args[1])){ - case VM::tp_int: - return VAR((f64)CAST(i64, args[1])); - case VM::tp_float: - return args[1]; - case VM::tp_bool: - return VAR(args[1]==vm->True ? 1.0 : 0.0); - case VM::tp_str: - break; - default: - vm->TypeError("invalid arguments for float()"); + switch(vm->_tp(args[1])) { + case VM::tp_int: return VAR((f64)CAST(i64, args[1])); + case VM::tp_float: return args[1]; + case VM::tp_bool: return VAR(args[1] == vm->True ? 1.0 : 0.0); + case VM::tp_str: break; + default: vm->TypeError("invalid arguments for float()"); } // str to float const Str& s = PK_OBJ_GET(Str, args[1]); @@ -537,12 +546,10 @@ void __init_builtins(VM* _vm) { double float_out; char* p_end; - try{ + try { float_out = std::strtod(s.data, &p_end); if(p_end != s.end()) throw 1; - }catch(...){ - vm->ValueError("invalid literal for float(): " + s.escape()); - } + } catch(...) { vm->ValueError("invalid literal for float(): " + s.escape()); } return VAR(float_out); }); @@ -551,7 +558,9 @@ void __init_builtins(VM* _vm) { return (i64)std::hash()(val); }); - _vm->bind__neg__(VM::tp_float, [](VM* vm, PyVar _0) { return VAR(-_CAST(f64, _0)); }); + _vm->bind__neg__(VM::tp_float, [](VM* vm, PyVar _0) { + return VAR(-_CAST(f64, _0)); + }); _vm->bind__repr__(VM::tp_float, [](VM* vm, PyVar _0) -> Str { f64 val = _CAST(f64, _0); @@ -581,14 +590,16 @@ void __init_builtins(VM* _vm) { const Str& self = _CAST(Str&, _0); i64 n = CAST(i64, _1); SStream ss; - for(i64 i = 0; i < n; i++) ss << self.sv(); + for(i64 i = 0; i < n; i++) + ss << self.sv(); return VAR(ss.str()); }); _vm->bind_func(VM::tp_str, "__rmul__", 2, [](VM* vm, ArgsView args) { const Str& self = _CAST(Str&, args[0]); i64 n = CAST(i64, args[1]); SStream ss; - for(i64 i = 0; i < n; i++) ss << self.sv(); + for(i64 i = 0; i < n; i++) + ss << self.sv(); return VAR(ss.str()); }); _vm->bind__contains__(VM::tp_str, [](VM* vm, PyVar _0, PyVar _1) { @@ -596,17 +607,21 @@ void __init_builtins(VM* _vm) { return VAR(self.index(CAST(Str&, _1)) != -1); }); - _vm->bind_func(VM::tp_str, __str__, 1, [](VM* vm, ArgsView args) { return args[0]; }); - _vm->bind__iter__(VM::tp_str, [](VM* vm, PyVar _0) { return vm->new_user_object(_0); }); + _vm->bind_func(VM::tp_str, __str__, 1, [](VM* vm, ArgsView args) { + return args[0]; + }); + _vm->bind__iter__(VM::tp_str, [](VM* vm, PyVar _0) { + return vm->new_user_object(_0); + }); _vm->bind__repr__(VM::tp_str, [](VM* vm, PyVar _0) -> Str { const Str& self = _CAST(Str&, _0); return self.escape(); }); -#define BIND_CMP_STR(name, op) \ - _vm->bind##name(VM::tp_str, [](VM* vm, PyVar lhs, PyVar rhs) { \ - if(!is_type(rhs, vm->tp_str)) return vm->NotImplemented; \ - return VAR(_CAST(Str&, lhs) op _CAST(Str&, rhs)); \ +#define BIND_CMP_STR(name, op) \ + _vm->bind##name(VM::tp_str, [](VM* vm, PyVar lhs, PyVar rhs) { \ + if(!is_type(rhs, vm->tp_str)) return vm->NotImplemented; \ + return VAR(_CAST(Str&, lhs) op _CAST(Str&, rhs)); \ }); BIND_CMP_STR(__eq__, ==) @@ -618,7 +633,7 @@ void __init_builtins(VM* _vm) { _vm->bind__getitem__(VM::tp_str, [](VM* vm, PyVar _0, PyVar _1) { const Str& self = PK_OBJ_GET(Str, _0); - if(is_type(_1, vm->tp_slice)){ + if(is_type(_1, vm->tp_slice)) { const Slice& s = _CAST(Slice&, _1); int start, stop, step; vm->parse_int_slice(s, self.u8_length(), start, stop, step); @@ -643,13 +658,14 @@ void __init_builtins(VM* _vm) { const Str& sep = CAST(Str&, args[1]); if(sep.empty()) vm->ValueError("empty separator"); vector parts; - if(sep.size == 1){ + if(sep.size == 1) { parts = self.split(sep[0]); - }else{ + } else { parts = self.split(sep); } List ret(parts.size()); - for(int i=0; i parts = self.split('\n'); List ret(parts.size()); - for(int i=0; iValueError("argument 'start' can't be negative"); + if(start < 0) vm->ValueError("argument 'start' can't be negative"); int index = self.index(value, start); if(index < 0) vm->ValueError("substring not found"); return VAR(index); @@ -681,7 +698,7 @@ void __init_builtins(VM* _vm) { const Str& self = _CAST(Str&, args[0]); const Str& value = CAST(Str&, args[1]); int start = CAST(int, args[2]); - if (start < 0) vm->ValueError("argument 'start' can't be negative"); + if(start < 0) vm->ValueError("argument 'start' can't be negative"); return VAR(self.index(value, start)); }); @@ -696,7 +713,7 @@ void __init_builtins(VM* _vm) { const Str& suffix = CAST(Str&, args[1]); int offset = self.length() - suffix.length(); if(offset < 0) return vm->False; - bool ok = memcmp(self.data+offset, suffix.data, suffix.length()) == 0; + bool ok = memcmp(self.data + offset, suffix.data, suffix.length()) == 0; return VAR(ok); }); @@ -711,10 +728,10 @@ void __init_builtins(VM* _vm) { auto _lock = vm->heap.gc_scope_lock(); const Str& self = _CAST(Str&, args[0]); SStream ss; - PyVar it = vm->py_iter(args[1]); // strong ref + PyVar it = vm->py_iter(args[1]); // strong ref const PyTypeInfo* info = vm->_tp_info(args[1]); PyVar obj = vm->_py_next(info, it); - while(obj != vm->StopIteration){ + while(obj != vm->StopIteration) { if(!ss.empty()) ss << self; ss << CAST(Str&, obj); obj = vm->_py_next(info, it); @@ -734,9 +751,9 @@ void __init_builtins(VM* _vm) { _vm->bind(_vm->_t(VM::tp_str), "strip(self, chars=None)", [](VM* vm, ArgsView args) { const Str& self = _CAST(Str&, args[0]); - if(args[1] == vm->None){ + if(args[1] == vm->None) { return VAR(self.strip()); - }else{ + } else { const Str& chars = CAST(Str&, args[1]); return VAR(self.strip(true, true, chars)); } @@ -744,9 +761,9 @@ void __init_builtins(VM* _vm) { _vm->bind(_vm->_t(VM::tp_str), "lstrip(self, chars=None)", [](VM* vm, ArgsView args) { const Str& self = _CAST(Str&, args[0]); - if(args[1] == vm->None){ + if(args[1] == vm->None) { return VAR(self.lstrip()); - }else{ + } else { const Str& chars = CAST(Str&, args[1]); return VAR(self.strip(true, false, chars)); } @@ -754,9 +771,9 @@ void __init_builtins(VM* _vm) { _vm->bind(_vm->_t(VM::tp_str), "rstrip(self, chars=None)", [](VM* vm, ArgsView args) { const Str& self = _CAST(Str&, args[0]); - if(args[1] == vm->None){ + if(args[1] == vm->None) { return VAR(self.rstrip()); - }else{ + } else { const Str& chars = CAST(Str&, args[1]); return VAR(self.strip(false, true, chars)); } @@ -769,7 +786,8 @@ void __init_builtins(VM* _vm) { int delta = width - self.u8_length(); if(delta <= 0) return args[0]; SStream ss; - for(int i=0; iTypeError("The fill character must be exactly one character long"); + if(fillchar.u8_length() != 1) vm->TypeError("The fill character must be exactly one character long"); SStream ss; ss << self; - for(int i=0; iTypeError("The fill character must be exactly one character long"); + if(fillchar.u8_length() != 1) vm->TypeError("The fill character must be exactly one character long"); SStream ss; - for(int i=0; ibind(_vm->_t(VM::tp_list), "sort(self, key=None, reverse=False)", [](VM* vm, ArgsView args) { List& self = _CAST(List&, args[0]); PyVar key = args[1]; - if(key == vm->None){ - std::stable_sort(self.begin(), self.end(), [vm](PyVar a, PyVar b){ + if(key == vm->None) { + std::stable_sort(self.begin(), self.end(), [vm](PyVar a, PyVar b) { return vm->py_lt(a, b); }); - }else{ - std::stable_sort(self.begin(), self.end(), [vm, key](PyVar a, PyVar b){ + } else { + std::stable_sort(self.begin(), self.end(), [vm, key](PyVar a, PyVar b) { return vm->py_lt(vm->call(key, a), vm->call(key, b)); }); } bool reverse = CAST(bool, args[2]); - if(reverse){ - std::reverse(self.begin(), self.end()); - } + if(reverse) { std::reverse(self.begin(), self.end()); } return vm->None; }); - _vm->bind__repr__(VM::tp_list, [](VM* vm, PyVar _0) -> Str{ + _vm->bind__repr__(VM::tp_list, [](VM* vm, PyVar _0) -> Str { if(vm->_repr_recursion_set.contains(_0)) return "[...]"; List& iterable = _CAST(List&, _0); SStream ss; ss << '['; vm->_repr_recursion_set.push_back(_0); - for(int i=0; ipy_repr(iterable[i]); - if(i != iterable.size()-1) ss << ", "; + if(i != iterable.size() - 1) ss << ", "; } vm->_repr_recursion_set.pop_back(); ss << ']'; return ss.str(); }); - _vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyVar _0) -> Str{ + _vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyVar _0) -> Str { Tuple& iterable = _CAST(Tuple&, _0); SStream ss; ss << '('; - if(iterable.size() == 1){ + if(iterable.size() == 1) { ss << vm->py_repr(iterable[0]); ss << ','; - }else{ - for(int i=0; ipy_repr(iterable[i]); - if(i != iterable.size()-1) ss << ", "; + if(i != iterable.size() - 1) ss << ", "; } } ss << ')'; @@ -855,22 +873,24 @@ void __init_builtins(VM* _vm) { }); _vm->bind_func(VM::tp_list, __new__, -1, [](VM* vm, ArgsView args) { - if(args.size() == 1+0) return VAR(List()); - if(args.size() == 1+1) return VAR(vm->py_list(args[1])); + if(args.size() == 1 + 0) return VAR(List()); + if(args.size() == 1 + 1) return VAR(vm->py_list(args[1])); vm->TypeError("list() takes 0 or 1 arguments"); return vm->None; }); _vm->bind__contains__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) { List& self = _CAST(List&, _0); - for(PyVar i: self) if(vm->py_eq(i, _1)) return vm->True; + for(PyVar i: self) + if(vm->py_eq(i, _1)) return vm->True; return vm->False; }); _vm->bind_func(VM::tp_list, "count", 2, [](VM* vm, ArgsView args) { List& self = _CAST(List&, args[0]); int count = 0; - for(PyVar i: self) if(vm->py_eq(i, args[1])) count++; + for(PyVar i: self) + if(vm->py_eq(i, args[1])) count++; return VAR(count); }); @@ -879,7 +899,7 @@ void __init_builtins(VM* _vm) { if(!is_type(_1, vm->tp_list)) return vm->NotImplemented; List& b = _CAST(List&, _1); if(a.size() != b.size()) return vm->False; - for(int i=0; ipy_eq(a[i], b[i])) return vm->False; } return vm->True; @@ -889,7 +909,7 @@ void __init_builtins(VM* _vm) { List& self = _CAST(List&, args[0]); PyVar obj = args[1]; int start = CAST(int, args[2]); - for(int i=start; ipy_eq(self[i], obj)) return VAR(i); } vm->ValueError(vm->py_repr(obj) + " is not in list"); @@ -899,8 +919,8 @@ void __init_builtins(VM* _vm) { _vm->bind_func(VM::tp_list, "remove", 2, [](VM* vm, ArgsView args) { List& self = _CAST(List&, args[0]); PyVar obj = args[1]; - for(int i=0; ipy_eq(self[i], obj)){ + for(int i = 0; i < self.size(); i++) { + if(vm->py_eq(self[i], obj)) { self.erase(i); return vm->None; } @@ -911,13 +931,13 @@ void __init_builtins(VM* _vm) { _vm->bind_func(VM::tp_list, "pop", -1, [](VM* vm, ArgsView args) { List& self = _CAST(List&, args[0]); - if(args.size() == 1+0){ + if(args.size() == 1 + 0) { if(self.empty()) vm->IndexError("pop from empty list"); PyVar retval = self.back(); self.pop_back(); return retval; } - if(args.size() == 1+1){ + if(args.size() == 1 + 1) { i64 index = CAST(i64, args[1]); index = vm->normalized_index(index, self.size()); PyVar ret = self[index]; @@ -937,10 +957,10 @@ void __init_builtins(VM* _vm) { _vm->bind_func(VM::tp_list, "extend", 2, [](VM* vm, ArgsView args) { auto _lock = vm->heap.gc_scope_lock(); List& self = _CAST(List&, args[0]); - PyVar it = vm->py_iter(args[1]); // strong ref + PyVar it = vm->py_iter(args[1]); // strong ref const PyTypeInfo* info = vm->_tp_info(args[1]); PyVar obj = vm->_py_next(info, it); - while(obj != vm->StopIteration){ + while(obj != vm->StopIteration) { self.push_back(obj); obj = vm->_py_next(info, it); } @@ -959,7 +979,8 @@ void __init_builtins(VM* _vm) { int n = _CAST(int, _1); List result; result.reserve(self.size() * n); - for(int i = 0; i < n; i++) result.extend(self.begin(), self.end()); + for(int i = 0; i < n; i++) + result.extend(self.begin(), self.end()); return VAR(std::move(result)); }); _vm->bind_func(VM::tp_list, "__rmul__", 2, [](VM* vm, ArgsView args) { @@ -968,7 +989,8 @@ void __init_builtins(VM* _vm) { int n = _CAST(int, args[1]); List result; result.reserve(self.size() * n); - for(int i = 0; i < n; i++) result.extend(self.begin(), self.end()); + for(int i = 0; i < n; i++) + result.extend(self.begin(), self.end()); return VAR(std::move(result)); }); @@ -987,21 +1009,21 @@ void __init_builtins(VM* _vm) { return vm->None; }); - _vm->bind_func(VM::tp_list, "copy", 1, [](VM* vm, ArgsView args){ + _vm->bind_func(VM::tp_list, "copy", 1, [](VM* vm, ArgsView args) { const List& self = _CAST(List&, args[0]); return VAR(List(explicit_copy_t(), self)); }); -#define BIND_RICH_CMP(name, op, _t, _T) \ - _vm->bind__##name##__(_vm->_t, [](VM* vm, PyVar lhs, PyVar rhs){ \ - if(!is_type(rhs, vm->_t)) return vm->NotImplemented; \ - auto& a = _CAST(_T&, lhs); \ - auto& b = _CAST(_T&, rhs); \ - for(int i=0; ipy_eq(a[i], b[i])) continue; \ - return VAR(vm->py_##name(a[i], b[i])); \ - } \ - return VAR(a.size() op b.size()); \ +#define BIND_RICH_CMP(name, op, _t, _T) \ + _vm->bind__##name##__(_vm->_t, [](VM* vm, PyVar lhs, PyVar rhs) { \ + if(!is_type(rhs, vm->_t)) return vm->NotImplemented; \ + auto& a = _CAST(_T&, lhs); \ + auto& b = _CAST(_T&, rhs); \ + for(int i = 0; i < a.size() && i < b.size(); i++) { \ + if(vm->py_eq(a[i], b[i])) continue; \ + return VAR(vm->py_##name(a[i], b[i])); \ + } \ + return VAR(a.size() op b.size()); \ }); BIND_RICH_CMP(lt, <, tp_list, List) @@ -1031,19 +1053,19 @@ void __init_builtins(VM* _vm) { List& self = _CAST(List&, _0); return vm->new_user_object(_0.get(), self.begin(), self.end()); }); - _vm->_all_types[VM::tp_list].op__iter__ = [](VM* vm, PyVar _0){ + _vm->_all_types[VM::tp_list].op__iter__ = [](VM* vm, PyVar _0) { List& self = _CAST(List&, _0); vm->new_stack_object(vm->_tp_user(), _0.get(), self.begin(), self.end()); }; _vm->bind__getitem__(VM::tp_list, PyArrayGetItem); - _vm->bind__setitem__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1, PyVar _2){ + _vm->bind__setitem__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1, PyVar _2) { List& self = _CAST(List&, _0); i64 i = CAST(i64, _1); i = vm->normalized_index(i, self.size()); self[i] = _2; }); - _vm->bind__delitem__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1){ + _vm->bind__delitem__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) { List& self = _CAST(List&, _0); i64 i = CAST(i64, _1); i = vm->normalized_index(i, self.size()); @@ -1051,8 +1073,8 @@ void __init_builtins(VM* _vm) { }); _vm->bind_func(VM::tp_tuple, __new__, -1, [](VM* vm, ArgsView args) { - if(args.size() == 1+0) return VAR(Tuple(0)); - if(args.size() == 1+1){ + if(args.size() == 1 + 0) return VAR(Tuple(0)); + if(args.size() == 1 + 1) { List list = vm->py_list(args[1]); return VAR(list.to_tuple()); } @@ -1062,14 +1084,16 @@ void __init_builtins(VM* _vm) { _vm->bind__contains__(VM::tp_tuple, [](VM* vm, PyVar obj, PyVar item) { Tuple& self = _CAST(Tuple&, obj); - for(PyVar i: self) if(vm->py_eq(i, item)) return vm->True; + for(PyVar i: self) + if(vm->py_eq(i, item)) return vm->True; return vm->False; }); _vm->bind_func(VM::tp_tuple, "count", 2, [](VM* vm, ArgsView args) { Tuple& self = _CAST(Tuple&, args[0]); int count = 0; - for(PyVar i: self) if(vm->py_eq(i, args[1])) count++; + for(PyVar i: self) + if(vm->py_eq(i, args[1])) count++; return VAR(count); }); @@ -1086,7 +1110,7 @@ void __init_builtins(VM* _vm) { _vm->bind__hash__(VM::tp_tuple, [](VM* vm, PyVar _0) { i64 x = 1000003; - for (PyVar item: _CAST(Tuple&, _0)) { + for(PyVar item: _CAST(Tuple&, _0)) { i64 y = vm->py_hash(item); // recommended by Github Copilot x = x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2)); @@ -1098,7 +1122,7 @@ void __init_builtins(VM* _vm) { Tuple& self = _CAST(Tuple&, _0); return vm->new_user_object(_0.get(), self.begin(), self.end()); }); - _vm->_all_types[VM::tp_tuple].op__iter__ = [](VM* vm, PyVar _0){ + _vm->_all_types[VM::tp_tuple].op__iter__ = [](VM* vm, PyVar _0) { Tuple& self = _CAST(Tuple&, _0); vm->new_stack_object(vm->_tp_user(), _0.get(), self.begin(), self.end()); }; @@ -1113,7 +1137,7 @@ void __init_builtins(VM* _vm) { _vm->bind__hash__(VM::tp_bool, [](VM* vm, PyVar _0) { return (i64)_CAST(bool, _0); }); - _vm->bind__repr__(VM::tp_bool, [](VM* vm, PyVar _0) -> Str{ + _vm->bind__repr__(VM::tp_bool, [](VM* vm, PyVar _0) -> Str { bool val = _CAST(bool, _0); return val ? "True" : "False"; }); @@ -1134,20 +1158,20 @@ void __init_builtins(VM* _vm) { }); // tp_ellipsis / tp_NotImplementedType - _vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyVar _0) -> Str{ + _vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyVar _0) -> Str { return "..."; }); - _vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyVar _0) -> Str{ + _vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyVar _0) -> Str { return "NotImplemented"; }); // tp_bytes - _vm->bind_func(VM::tp_bytes, __new__, 2, [](VM* vm, ArgsView args){ + _vm->bind_func(VM::tp_bytes, __new__, 2, [](VM* vm, ArgsView args) { List& list = CAST(List&, args[1]); Bytes retval(list.size()); - for(int i=0; i255) vm->ValueError("byte must be in range[0, 256)"); + if(b < 0 || b > 255) vm->ValueError("byte must be in range[0, 256)"); retval[i] = (char)b; } return VAR(std::move(retval)); @@ -1155,14 +1179,14 @@ void __init_builtins(VM* _vm) { _vm->bind__getitem__(VM::tp_bytes, [](VM* vm, PyVar _0, PyVar _1) { const Bytes& self = PK_OBJ_GET(Bytes, _0); - if(is_type(_1, vm->tp_slice)){ + if(is_type(_1, vm->tp_slice)) { const Slice& s = _CAST(Slice&, _1); int start, stop, step; vm->parse_int_slice(s, self.size(), start, stop, step); int guess_max_size = abs(stop - start) / abs(step) + 1; if(guess_max_size > self.size()) guess_max_size = self.size(); unsigned char* buffer = (unsigned char*)std::malloc(guess_max_size); - int j = 0; // actual size + int j = 0; // actual size PK_SLICE_LOOP(i, start, stop, step) buffer[j++] = self[i]; return VAR(Bytes(buffer, j)); } @@ -1190,7 +1214,7 @@ void __init_builtins(VM* _vm) { const Bytes& self = _CAST(Bytes&, _0); SStream ss; ss << "b'"; - for(int i=0; iFalse; return VAR(memcmp(lhs.data(), rhs.data(), lhs.size()) == 0); }); - + // tp_slice _vm->bind_func(VM::tp_slice, __new__, 4, [](VM* vm, ArgsView args) { return VAR(Slice(args[1], args[2], args[3])); }); - _vm->bind__eq__(VM::tp_slice, [](VM* vm, PyVar _0, PyVar _1){ + _vm->bind__eq__(VM::tp_slice, [](VM* vm, PyVar _0, PyVar _1) { const Slice& self = _CAST(Slice&, _0); if(!is_type(_1, vm->tp_slice)) return vm->NotImplemented; const Slice& other = _CAST(Slice&, _1); @@ -1243,21 +1267,23 @@ void __init_builtins(VM* _vm) { _vm->bind_func(VM::tp_mappingproxy, "keys", 1, [](VM* vm, ArgsView args) { MappingProxy& self = _CAST(MappingProxy&, args[0]); List keys; - for(StrName name : self.attr().keys()) keys.push_back(VAR(name.sv())); + for(StrName name: self.attr().keys()) + keys.push_back(VAR(name.sv())); return VAR(std::move(keys)); }); _vm->bind_func(VM::tp_mappingproxy, "values", 1, [](VM* vm, ArgsView args) { MappingProxy& self = _CAST(MappingProxy&, args[0]); List values; - for(auto [k, v] : self.attr().items()) values.push_back(v); + for(auto [k, v]: self.attr().items()) + values.push_back(v); return VAR(std::move(values)); }); _vm->bind_func(VM::tp_mappingproxy, "items", 1, [](VM* vm, ArgsView args) { MappingProxy& self = _CAST(MappingProxy&, args[0]); List items; - for(auto [k, v] : self.attr().items()){ + for(auto [k, v]: self.attr().items()) { PyVar t = VAR(Tuple(VAR(k.sv()), v)); items.push_back(std::move(t)); } @@ -1268,7 +1294,7 @@ void __init_builtins(VM* _vm) { return (i64)_CAST(MappingProxy&, _0).attr().size(); }); - _vm->bind__eq__(VM::tp_mappingproxy, [](VM* vm, PyVar _0, PyVar _1){ + _vm->bind__eq__(VM::tp_mappingproxy, [](VM* vm, PyVar _0, PyVar _1) { const MappingProxy& a = _CAST(MappingProxy&, _0); if(!is_type(_1, VM::tp_mappingproxy)) return vm->NotImplemented; const MappingProxy& b = _CAST(MappingProxy&, _1); @@ -1291,14 +1317,14 @@ void __init_builtins(VM* _vm) { return ret; }); - _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyVar _0) -> Str{ + _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyVar _0) -> Str { if(vm->_repr_recursion_set.contains(_0)) return "{...}"; MappingProxy& self = _CAST(MappingProxy&, _0); SStream ss; ss << "mappingproxy({"; bool first = true; vm->_repr_recursion_set.push_back(_0); - for(auto [k, v] : self.attr().items()){ + for(auto [k, v]: self.attr().items()) { if(!first) ss << ", "; first = false; ss << k.escape() << ": "; @@ -1315,26 +1341,26 @@ void __init_builtins(VM* _vm) { }); // tp_dict - _vm->bind_func(VM::tp_dict, __new__, -1, [](VM* vm, ArgsView args){ + _vm->bind_func(VM::tp_dict, __new__, -1, [](VM* vm, ArgsView args) { Type cls_t = PK_OBJ_GET(Type, args[0]); return vm->new_object(cls_t); }); - _vm->bind_func(VM::tp_dict, __init__, -1, [](VM* vm, ArgsView args){ - if(args.size() == 1+0) return vm->None; - if(args.size() == 1+1){ + _vm->bind_func(VM::tp_dict, __init__, -1, [](VM* vm, ArgsView args) { + if(args.size() == 1 + 0) return vm->None; + if(args.size() == 1 + 1) { auto _lock = vm->heap.gc_scope_lock(); Dict& self = PK_OBJ_GET(Dict, args[0]); - if(is_type(args[1], vm->tp_dict)){ + if(is_type(args[1], vm->tp_dict)) { Dict& other = CAST(Dict&, args[1]); self.update(vm, other); return vm->None; } - if(is_type(args[1], vm->tp_list)){ + if(is_type(args[1], vm->tp_list)) { List& list = PK_OBJ_GET(List, args[1]); - for(PyVar item : list){ + for(PyVar item: list) { Tuple& t = CAST(Tuple&, item); - if(t.size() != 2){ + if(t.size() != 2) { vm->ValueError("dict() takes a list of tuples (key, value)"); return vm->None; } @@ -1354,13 +1380,11 @@ void __init_builtins(VM* _vm) { _vm->bind__getitem__(VM::tp_dict, [](VM* vm, PyVar _0, PyVar _1) { Dict& self = PK_OBJ_GET(Dict, _0); PyVar ret = self.try_get(vm, _1); - if(ret == nullptr){ + if(ret == nullptr) { // try __missing__ PyVar self; PyVar f_missing = vm->get_unbound_method(_0, __missing__, &self, false); - if(f_missing != nullptr){ - return vm->call_method(self, f_missing, _1); - } + if(f_missing != nullptr) { return vm->call_method(self, f_missing, _1); } vm->KeyError(_1); } return ret; @@ -1378,17 +1402,15 @@ void __init_builtins(VM* _vm) { }); _vm->bind_func(VM::tp_dict, "pop", -1, [](VM* vm, ArgsView args) { - if(args.size() != 2 && args.size() != 3){ + if(args.size() != 2 && args.size() != 3) { vm->TypeError("pop() expected 1 or 2 arguments"); return vm->None; } Dict& self = _CAST(Dict&, args[0]); PyVar value = self.try_get(vm, args[1]); - if(value == nullptr){ + if(value == nullptr) { if(args.size() == 2) vm->KeyError(args[1]); - if(args.size() == 3){ - return args[2]; - } + if(args.size() == 3) { return args[2]; } } self.del(vm, args[1]); return value; @@ -1406,11 +1428,11 @@ void __init_builtins(VM* _vm) { _vm->bind_func(VM::tp_dict, "get", -1, [](VM* vm, ArgsView args) { Dict& self = _CAST(Dict&, args[0]); - if(args.size() == 1+1){ + if(args.size() == 1 + 1) { PyVar ret = self.try_get(vm, args[1]); if(ret != nullptr) return ret; return vm->None; - }else if(args.size() == 1+2){ + } else if(args.size() == 1 + 2) { PyVar ret = self.try_get(vm, args[1]); if(ret != nullptr) return ret; return args[2]; @@ -1451,14 +1473,14 @@ void __init_builtins(VM* _vm) { return vm->None; }); - _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyVar _0) -> Str{ + _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyVar _0) -> Str { if(vm->_repr_recursion_set.contains(_0)) return "{...}"; Dict& self = _CAST(Dict&, _0); SStream ss; ss << "{"; bool first = true; vm->_repr_recursion_set.push_back(_0); - self.apply([&](PyVar k, PyVar v){ + self.apply([&](PyVar k, PyVar v) { if(!first) ss << ", "; first = false; ss << vm->py_repr(k) << ": " << vm->py_repr(v); @@ -1473,7 +1495,7 @@ void __init_builtins(VM* _vm) { if(!vm->isinstance(_1, vm->tp_dict)) return vm->NotImplemented; Dict& other = _CAST(Dict&, _1); if(self.size() != other.size()) return vm->False; - for(int i=0; ibind_func(VM::tp_property, __new__, -1, [](VM* vm, ArgsView args) { - if(args.size() == 1+1){ + if(args.size() == 1 + 1) { return VAR(Property(args[1], vm->None)); - }else if(args.size() == 1+2){ + } else if(args.size() == 1 + 2) { return VAR(Property(args[1], args[2])); } vm->TypeError("property() takes at most 2 arguments"); return vm->None; }); - + _vm->bind_property(_vm->_t(VM::tp_function), "__doc__", [](VM* vm, ArgsView args) { Function& func = _CAST(Function&, args[0]); if(!func.decl->docstring) return vm->None; @@ -1513,7 +1535,7 @@ void __init_builtins(VM* _vm) { }); // tp_exception - _vm->bind_func(VM::tp_exception, __new__, -1, [](VM* vm, ArgsView args) -> PyVar{ + _vm->bind_func(VM::tp_exception, __new__, -1, [](VM* vm, ArgsView args) -> PyVar { Type cls = PK_OBJ_GET(Type, args[0]); StrName cls_name = _type_name(vm, cls); PyObject* e_obj = vm->heap.gcnew(cls, cls_name); @@ -1522,11 +1544,11 @@ void __init_builtins(VM* _vm) { return e_obj; }); - _vm->bind(_vm->_t(VM::tp_exception), "__init__(self, msg=...)", [](VM* vm, ArgsView args){ + _vm->bind(_vm->_t(VM::tp_exception), "__init__(self, msg=...)", [](VM* vm, ArgsView args) { Exception& self = _CAST(Exception&, args[0]); - if(args[1] == vm->Ellipsis){ + if(args[1] == vm->Ellipsis) { self.msg = ""; - }else{ + } else { self.msg = CAST(Str, args[1]); } return vm->None; @@ -1537,7 +1559,7 @@ void __init_builtins(VM* _vm) { return _S(_type_name(vm, _0.type), '(', self.msg.escape(), ')'); }); - _vm->bind__str__(VM::tp_exception, [](VM* vm, PyVar _0) -> Str{ + _vm->bind__str__(VM::tp_exception, [](VM* vm, PyVar _0) -> Str { Exception& self = _CAST(Exception&, _0); return self.msg; }); @@ -1550,12 +1572,12 @@ void __init_builtins(VM* _vm) { _vm->register_user_class(_vm->builtins, "_dict_items_iter"); } -void VM::__post_init_builtin_types(){ +void VM::__post_init_builtin_types() { __init_builtins(this); bind_func(tp_module, __new__, -1, PK_ACTION(vm->NotImplementedError())); - _all_types[tp_module].m__getattr__ = [](VM* vm, PyVar obj, StrName name) -> PyVar{ + _all_types[tp_module].m__getattr__ = [](VM* vm, PyVar obj, StrName name) -> PyVar { const Str& path = CAST(Str&, obj->attr(__path__)); PyObject* retval = vm->py_import(_S(path, ".", name.sv()), false); if(retval) return retval; @@ -1571,11 +1593,11 @@ void VM::__post_init_builtin_types(){ }); // type - bind__getitem__(tp_type, [](VM* vm, PyVar self, PyVar _){ - return self; // for generics + bind__getitem__(tp_type, [](VM* vm, PyVar self, PyVar _) { + return self; // for generics }); - bind__repr__(tp_type, [](VM* vm, PyVar self) -> Str{ + bind__repr__(tp_type, [](VM* vm, PyVar self) -> Str { SStream ss; const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, self)]; ss << ""; @@ -1583,44 +1605,44 @@ void VM::__post_init_builtin_types(){ }); bind_property(_t(tp_object), "__class__", PK_LAMBDA(vm->_t(args[0]))); - bind_property(_t(tp_type), "__base__", [](VM* vm, ArgsView args){ + bind_property(_t(tp_type), "__base__", [](VM* vm, ArgsView args) { const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])]; return info.base ? vm->_all_types[info.base].obj : vm->None; }); - bind_property(_t(tp_type), "__name__", [](VM* vm, ArgsView args){ + bind_property(_t(tp_type), "__name__", [](VM* vm, ArgsView args) { const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])]; return VAR(info.name.sv()); }); - bind_property(_t(tp_type), "__module__", [](VM* vm, ArgsView args) -> PyVar{ + bind_property(_t(tp_type), "__module__", [](VM* vm, ArgsView args) -> PyVar { const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])]; if(info.mod == nullptr) return vm->None; return info.mod; }); - bind_property(_t(tp_bound_method), "__self__", [](VM* vm, ArgsView args){ + bind_property(_t(tp_bound_method), "__self__", [](VM* vm, ArgsView args) { return CAST(BoundMethod&, args[0]).self; }); - bind_property(_t(tp_bound_method), "__func__", [](VM* vm, ArgsView args){ + bind_property(_t(tp_bound_method), "__func__", [](VM* vm, ArgsView args) { return CAST(BoundMethod&, args[0]).func; }); - bind__eq__(tp_bound_method, [](VM* vm, PyVar lhs, PyVar rhs){ + bind__eq__(tp_bound_method, [](VM* vm, PyVar lhs, PyVar rhs) { if(!is_type(rhs, vm->tp_bound_method)) return vm->NotImplemented; const BoundMethod& _0 = PK_OBJ_GET(BoundMethod, lhs); const BoundMethod& _1 = PK_OBJ_GET(BoundMethod, rhs); return VAR(_0.self == _1.self && _0.func == _1.func); }); - bind_property(_t(tp_slice), "start", [](VM* vm, ArgsView args){ + bind_property(_t(tp_slice), "start", [](VM* vm, ArgsView args) { return CAST(Slice&, args[0]).start; }); - bind_property(_t(tp_slice), "stop", [](VM* vm, ArgsView args){ + bind_property(_t(tp_slice), "stop", [](VM* vm, ArgsView args) { return CAST(Slice&, args[0]).stop; }); - bind_property(_t(tp_slice), "step", [](VM* vm, ArgsView args){ + bind_property(_t(tp_slice), "step", [](VM* vm, ArgsView args) { return CAST(Slice&, args[0]).step; }); - bind_property(_t(tp_object), "__dict__", [](VM* vm, ArgsView args){ + bind_property(_t(tp_object), "__dict__", [](VM* vm, ArgsView args) { if(is_tagged(args[0]) || !args[0]->is_attr_valid()) return vm->None; return VAR(MappingProxy(args[0].get())); }); @@ -1630,9 +1652,9 @@ void VM::__post_init_builtin_types(){ const Str& _1 = CAST(Str&, args[1]); const Str& _2 = CAST(Str&, args[2]); SStream ss; - for(int i=0; i<_0.size(); i++){ + for(int i = 0; i < _0.size(); i++) { ss << vm->py_str(_0[i]); - if(i != _0.size()-1) ss << _1; + if(i != _0.size() - 1) ss << _1; } ss << _2; vm->stdout_write(ss.str()); @@ -1650,7 +1672,7 @@ void VM::__post_init_builtin_types(){ add_module_gc(this); add_module_random(this); add_module_base64(this); - + _lazy_modules["this"] = kPythonLibs_this; _lazy_modules["functools"] = kPythonLibs_functools; _lazy_modules["heapq"] = kPythonLibs_heapq; @@ -1665,7 +1687,7 @@ void VM::__post_init_builtin_types(){ _lazy_modules["operator"] = kPythonLibs_operator; _lazy_modules["collections"] = kPythonLibs_collections; - try{ + try { // initialize dummy func_decl for exec/eval CodeObject_ dynamic_co = compile("def _(): pass", "", EXEC_MODE); __dynamic_func_decl = dynamic_co->func_decls[0]; @@ -1674,13 +1696,13 @@ void VM::__post_init_builtin_types(){ this->_exec(code, this->builtins); code = compile(kPythonLibs__set, "", EXEC_MODE); this->_exec(code, this->builtins); - }catch(TopLevelException e){ + } catch(TopLevelException e) { std::cerr << e.summary() << std::endl; std::cerr << "failed to load builtins module!!" << std::endl; exit(1); } - if(enable_os){ + if(enable_os) { add_module_io(this); add_module_os(this); _import_handler = &_default_import_handler; @@ -1701,22 +1723,22 @@ void VM::__post_init_builtin_types(){ CodeObject_ VM::compile(std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope) { Compiler compiler(this, source, filename, mode, unknown_global_scope); - try{ + try { return compiler.compile(); - }catch(TopLevelException e){ + } catch(TopLevelException e) { _error(e.ptr->self()); return nullptr; } } -Str VM::precompile(std::string_view source, const Str& filename, CompileMode mode){ +Str VM::precompile(std::string_view source, const Str& filename, CompileMode mode) { Compiler compiler(this, source, filename, mode, false); - try{ + try { return compiler.precompile(); - }catch(TopLevelException e){ + } catch(TopLevelException e) { _error(e.ptr->self()); return nullptr; } } -} // namespace pkpy +} // namespace pkpy diff --git a/src/pocketpy_c.cpp b/src/pocketpy_c.cpp index c973acbc..0db2183c 100644 --- a/src/pocketpy_c.cpp +++ b/src/pocketpy_c.cpp @@ -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_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(); - 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 \ No newline at end of file +#endif // PK_NO_EXPORT_C_API diff --git a/src/tools/repl.cpp b/src/tools/repl.cpp index c0f6987d..9758653a 100644 --- a/src/tools/repl.cpp +++ b/src/tools/repl.cpp @@ -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= 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, "", 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; } -} \ No newline at end of file + try { + vm->exec(line, "", 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 diff --git a/src2/main.cpp b/src2/main.cpp index 85239752..70c6ed4c 100644 --- a/src2/main.cpp +++ b/src2/main.cpp @@ -4,34 +4,31 @@ #include #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 +#include 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(file)), - std::istreambuf_iterator()); + std::string src((std::istreambuf_iterator(file)), std::istreambuf_iterator()); 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; }