Ver Fonte

Format the world. (#259)

* add some clang-format offs.

* add formats.

* format the world.

* AllowShortIfStatementsOnASingleLine

* off REGION.

* Rollback vm.hpp

* Disable insert.
ykiko há 1 ano atrás
pai
commit
1c82060daf
93 ficheiros alterados com 9208 adições e 8698 exclusões
  1. 100 4
      .clang-format
  2. 1 1
      include/pocketpy.h
  3. 1 1
      include/pocketpy.hpp
  4. 39 37
      include/pocketpy/common/any.hpp
  5. 1 0
      include/pocketpy/common/config.h
  6. 1 0
      include/pocketpy/common/export.h
  7. 4 1
      include/pocketpy/common/gil.hpp
  8. 3 3
      include/pocketpy/common/memorypool.hpp
  9. 58 47
      include/pocketpy/common/namedict.hpp
  10. 76 65
      include/pocketpy/common/str.hpp
  11. 21 17
      include/pocketpy/common/traits.hpp
  12. 9 7
      include/pocketpy/common/types.hpp
  13. 22 22
      include/pocketpy/common/utils.hpp
  14. 120 89
      include/pocketpy/common/vector.hpp
  15. 1 0
      include/pocketpy/common/version.h
  16. 42 26
      include/pocketpy/compiler/compiler.hpp
  17. 172 107
      include/pocketpy/compiler/expr.hpp
  18. 56 48
      include/pocketpy/compiler/lexer.hpp
  19. 151 129
      include/pocketpy/interpreter/bindings.hpp
  20. 1 1
      include/pocketpy/interpreter/ceval.hpp
  21. 41 36
      include/pocketpy/interpreter/cffi.hpp
  22. 83 45
      include/pocketpy/interpreter/frame.hpp
  23. 21 21
      include/pocketpy/interpreter/gc.hpp
  24. 29 22
      include/pocketpy/interpreter/iter.hpp
  25. 6 5
      include/pocketpy/interpreter/profiler.hpp
  26. 256 146
      include/pocketpy/interpreter/vm.hpp
  27. 1 1
      include/pocketpy/modules/array2d.hpp
  28. 1 1
      include/pocketpy/modules/base64.hpp
  29. 1 1
      include/pocketpy/modules/csv.hpp
  30. 2 2
      include/pocketpy/modules/dataclasses.hpp
  31. 2 2
      include/pocketpy/modules/easing.hpp
  32. 5 5
      include/pocketpy/modules/io.hpp
  33. 131 54
      include/pocketpy/modules/linalg.hpp
  34. 2 2
      include/pocketpy/modules/modules.hpp
  35. 2 2
      include/pocketpy/modules/random.hpp
  36. 42 33
      include/pocketpy/objects/base.hpp
  37. 37 20
      include/pocketpy/objects/builtins.hpp
  38. 52 50
      include/pocketpy/objects/codeobject.hpp
  39. 12 12
      include/pocketpy/objects/dict.hpp
  40. 20 17
      include/pocketpy/objects/error.hpp
  41. 15 13
      include/pocketpy/objects/object.hpp
  42. 6 12
      include/pocketpy/objects/sourcedata.hpp
  43. 7 6
      include/pocketpy/objects/stackmemory.hpp
  44. 22 11
      include/pocketpy/objects/tuplelist.hpp
  45. 1 1
      include/pocketpy/opcodes.h
  46. 14 14
      include/pocketpy/pocketpy.hpp
  47. 89 90
      include/pocketpy/pocketpy_c.h
  48. 4 3
      include/pocketpy/tools/repl.hpp
  49. 1 1
      include/pocketpy_c.h
  50. 100 109
      include/pybind11/internal/builtins.h
  51. 116 123
      include/pybind11/internal/cast.h
  52. 160 167
      include/pybind11/internal/class.h
  53. 292 317
      include/pybind11/internal/cpp_function.h
  54. 128 130
      include/pybind11/internal/instance.h
  55. 83 92
      include/pybind11/internal/kernel.h
  56. 214 218
      include/pybind11/internal/object.h
  57. 115 115
      include/pybind11/internal/type_traits.h
  58. 179 189
      include/pybind11/internal/types.h
  59. 1 1
      include/pybind11/pybind11.h
  60. 2 2
      scripts/format.py
  61. 5 5
      src/common/any.cpp
  62. 66 64
      src/common/memorypool.cpp
  63. 403 436
      src/common/str.cpp
  64. 1201 1236
      src/compiler/compiler.cpp
  65. 714 708
      src/compiler/expr.cpp
  66. 2 1
      src/compiler/lexer.cpp
  67. 1097 988
      src/interpreter/ceval.cpp
  68. 198 188
      src/interpreter/cffi.cpp
  69. 107 105
      src/interpreter/frame.cpp
  70. 41 40
      src/interpreter/gc.cpp
  71. 126 112
      src/interpreter/iter.cpp
  72. 24 26
      src/interpreter/profiler.cpp
  73. 416 421
      src/interpreter/vm.cpp
  74. 113 118
      src/modules/array2d.cpp
  75. 83 97
      src/modules/base64.cpp
  76. 26 33
      src/modules/csv.cpp
  77. 34 31
      src/modules/dataclasses.cpp
  78. 75 111
      src/modules/easing.cpp
  79. 48 53
      src/modules/io.cpp
  80. 489 464
      src/modules/linalg.cpp
  81. 52 56
      src/modules/modules.cpp
  82. 45 55
      src/modules/random.cpp
  83. 4 4
      src/objects/builtins.cpp
  84. 6 6
      src/objects/codeobject.cpp
  85. 161 155
      src/objects/dict.cpp
  86. 18 16
      src/objects/error.cpp
  87. 3 3
      src/objects/object.cpp
  88. 59 57
      src/objects/sourcedata.cpp
  89. 19 14
      src/objects/tuplelist.cpp
  90. 223 199
      src/pocketpy.cpp
  91. 119 138
      src/pocketpy_c.cpp
  92. 34 34
      src/tools/repl.cpp
  93. 23 28
      src2/main.cpp

+ 100 - 4
.clang-format

@@ -1,6 +1,102 @@
-# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
-BasedOnStyle: Google
-IndentWidth: 4
+# clang-format configuration
+# compatible with clang-format 18
+
 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
 
-IndentPPDirectives: BeforeHash
+WhitespaceSensitiveMacros: ["PK_PROTECTED", "LUA_PROTECTED"]

+ 1 - 1
include/pocketpy.h

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

+ 1 - 1
include/pocketpy.hpp

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

+ 39 - 37
include/pocketpy/common/any.hpp

@@ -10,22 +10,24 @@
 
 namespace pkpy {
 
-template<typename T>
-inline constexpr bool is_any_sso_v = is_pod_v<T> && sizeof(T) <= sizeof(void*);
+template <typename T>
+constexpr inline bool is_any_sso_v = is_pod_v<T> && sizeof(T) <= sizeof(void*);
 
-struct any{
-    struct vtable{
+struct any {
+    struct vtable {
         const std::type_index type;
         void (*deleter)(void*);
 
-        template<typename T>
-        inline static vtable* get(){
+        template <typename T>
+        inline static vtable* get() {
             static_assert(std::is_same_v<T, std::decay_t<T>>);
-            if constexpr (is_any_sso_v<T>){
-                static vtable vt{ typeid(T), nullptr };
+            if constexpr(is_any_sso_v<T>) {
+                static vtable vt{typeid(T), nullptr};
                 return &vt;
-            }else{
-                static vtable vt{ typeid(T), [](void* ptr){ delete static_cast<T*>(ptr); } };
+            } else {
+                static vtable vt{typeid(T), [](void* ptr) {
+                                     delete static_cast<T*>(ptr);
+                                 }};
                 return &vt;
             }
         }
@@ -36,45 +38,45 @@ struct any{
 
     any() : data(nullptr), _vt(nullptr) {}
 
-    explicit operator bool() const { return _vt != nullptr; }
+    explicit operator bool () const { return _vt != nullptr; }
 
-    template<typename T>
-    any(T&& value){
+    template <typename T>
+    any(T&& value) {
         using U = std::decay_t<T>;
         static_assert(!std::is_same_v<U, any>, "any(const any&) is deleted");
         static_assert(sizeof(U) == sizeof(T));
-        if constexpr (is_any_sso_v<U>){
+        if constexpr(is_any_sso_v<U>) {
             std::memcpy(&data, &value, sizeof(U));
-        }else{
+        } else {
             data = new U(std::forward<T>(value));
         }
         _vt = vtable::get<U>();
     }
 
     any(any&& other) noexcept;
-    any& operator=(any&& other) noexcept;
+    any& operator= (any&& other) noexcept;
 
-    const std::type_index type_id() const{
-        return _vt ? _vt->type : typeid(void);
-    }
+    const std::type_index type_id() const { return _vt ? _vt->type : typeid(void); }
 
     any(const any& other) = delete;
-    any& operator=(const any& other) = delete;
+    any& operator= (const any& other) = delete;
 
-    ~any() { if(_vt && _vt->deleter) _vt->deleter(data); }
+    ~any() {
+        if(_vt && _vt->deleter) _vt->deleter(data);
+    }
 
-    template<typename T>
-    T& _cast() const noexcept{
+    template <typename T>
+    T& _cast() const noexcept {
         static_assert(std::is_same_v<T, std::decay_t<T>>);
-        if constexpr (is_any_sso_v<T>){
+        if constexpr(is_any_sso_v<T>) {
             return *((T*)(&data));
-        }else{
+        } else {
             return *(static_cast<T*>(data));
         }
     }
 
-    template<typename T>
-    T& cast() const{
+    template <typename T>
+    T& cast() const {
         static_assert(std::is_same_v<T, std::decay_t<T>>);
         if(type_id() != typeid(T)) __bad_any_cast(typeid(T), type_id());
         return _cast<T>();
@@ -83,29 +85,29 @@ struct any{
     static void __bad_any_cast(const std::type_index expected, const std::type_index actual);
 };
 
-template<typename T>
+template <typename T>
 struct function;
 
-template<typename Ret, typename... Params>
-struct function<Ret(Params...)>{
+template <typename Ret, typename... Params>
+struct function<Ret(Params...)> {
     any _impl;
     Ret (*_wrapper)(const any&, Params...);
 
-    function(): _impl(), _wrapper(nullptr) {}
+    function() : _impl(), _wrapper(nullptr) {}
 
-    explicit operator bool() const { return _wrapper != nullptr; }
+    explicit operator bool () const { return _wrapper != nullptr; }
 
-    template<typename F>
-    function(F&& f) : _impl(std::forward<F>(f)){
-        _wrapper = [](const any& impl, Params... params) -> Ret{
+    template <typename F>
+    function(F&& f) : _impl(std::forward<F>(f)) {
+        _wrapper = [](const any& impl, Params... params) -> Ret {
             return impl._cast<std::decay_t<F>>()(std::forward<Params>(params)...);
         };
     }
 
-    Ret operator()(Params... params) const{
+    Ret operator() (Params... params) const {
         assert(_wrapper);
         return _wrapper(_impl, std::forward<Params>(params)...);
     }
 };
 
-} // namespace pkpy
+}  // namespace pkpy

+ 1 - 0
include/pocketpy/common/config.h

@@ -1,4 +1,5 @@
 #pragma once
+// clang-format off
 
 /*************** feature settings ***************/
 

+ 1 - 0
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)

+ 4 - 1
include/pocketpy/common/gil.hpp

@@ -5,10 +5,13 @@
 #include <mutex>
 
 struct GIL {
-	inline static std::mutex _mutex;
+    inline static std::mutex _mutex;
+
     explicit GIL() { _mutex.lock(); }
+
     ~GIL() { _mutex.unlock(); }
 };
+
 #define PK_GLOBAL_SCOPE_LOCK() GIL _lock;
 
 #else

+ 3 - 3
include/pocketpy/common/memorypool.hpp

@@ -6,10 +6,10 @@
 #include <cassert>
 #include <string>
 
-namespace pkpy{
+namespace pkpy {
 
-inline const int kPoolExprBlockSize = 128;
-inline const int kPoolFrameBlockSize = 80;
+const inline int kPoolExprBlockSize = 128;
+const inline int kPoolFrameBlockSize = 80;
 
 void* PoolExpr_alloc() noexcept;
 void PoolExpr_dealloc(void*) noexcept;

+ 58 - 47
include/pocketpy/common/namedict.hpp

@@ -6,20 +6,22 @@
 
 #include <stdexcept>
 
-namespace pkpy{
-
-template<typename T>
-constexpr T default_invalid_value(){
-    if constexpr(std::is_same_v<int, T>) return -1;
-    else return nullptr;
+namespace pkpy {
+
+template <typename T>
+constexpr T default_invalid_value() {
+    if constexpr(std::is_same_v<int, T>)
+        return -1;
+    else
+        return nullptr;
 }
 
-template<typename T>
+template <typename T>
 struct NameDictImpl {
     PK_ALWAYS_PASS_BY_POINTER(NameDictImpl)
 
     using Item = std::pair<StrName, T>;
-    static constexpr uint16_t kInitialCapacity = 16;
+    constexpr static uint16_t kInitialCapacity = 16;
     static_assert(is_pod_v<T>);
 
     float _load_factor;
@@ -31,26 +33,30 @@ struct NameDictImpl {
 
     Item* _items;
 
-#define HASH_PROBE_1(key, ok, i)            \
-ok = false;                                 \
-i = key.index & _mask;                      \
-while(!_items[i].first.empty()) {           \
-    if(_items[i].first == (key)) { ok = true; break; }  \
-    i = (i + 1) & _mask;                                \
-}
+#define HASH_PROBE_1(key, ok, i)                                                                                       \
+    ok = false;                                                                                                        \
+    i = key.index & _mask;                                                                                             \
+    while(!_items[i].first.empty()) {                                                                                  \
+        if(_items[i].first == (key)) {                                                                                 \
+            ok = true;                                                                                                 \
+            break;                                                                                                     \
+        }                                                                                                              \
+        i = (i + 1) & _mask;                                                                                           \
+    }
 
 #define HASH_PROBE_0 HASH_PROBE_1
 
-    NameDictImpl(float load_factor=PK_INST_ATTR_LOAD_FACTOR): _load_factor(load_factor), _size(0) {
+    NameDictImpl(float load_factor = PK_INST_ATTR_LOAD_FACTOR) : _load_factor(load_factor), _size(0) {
         _set_capacity_and_alloc_items(kInitialCapacity);
     }
 
-    ~NameDictImpl(){ std::free(_items); }
+    ~NameDictImpl() { std::free(_items); }
 
     uint16_t size() const { return _size; }
+
     uint16_t capacity() const { return _capacity; }
 
-    void _set_capacity_and_alloc_items(uint16_t val){
+    void _set_capacity_and_alloc_items(uint16_t val) {
         _capacity = val;
         _critical_size = val * _load_factor;
         _mask = val - 1;
@@ -59,12 +65,13 @@ while(!_items[i].first.empty()) {           \
         std::memset(_items, 0, _capacity * sizeof(Item));
     }
 
-    void set(StrName key, T val){
-        bool ok; uint16_t i;
+    void set(StrName key, T val) {
+        bool ok;
+        uint16_t i;
         HASH_PROBE_1(key, ok, i);
         if(!ok) {
             _size++;
-            if(_size > _critical_size){
+            if(_size > _critical_size) {
                 _rehash_2x();
                 HASH_PROBE_1(key, ok, i);
             }
@@ -73,13 +80,14 @@ while(!_items[i].first.empty()) {           \
         _items[i].second = val;
     }
 
-    void _rehash_2x(){
+    void _rehash_2x() {
         Item* old_items = _items;
         uint16_t old_capacity = _capacity;
         _set_capacity_and_alloc_items(_capacity * 2);
-        for(uint16_t i=0; i<old_capacity; i++){
+        for(uint16_t i = 0; i < old_capacity; i++) {
             if(old_items[i].first.empty()) continue;
-            bool ok; uint16_t j;
+            bool ok;
+            uint16_t j;
             HASH_PROBE_1(old_items[i].first, ok, j);
             assert(!ok);
             _items[j] = old_items[i];
@@ -87,21 +95,23 @@ while(!_items[i].first.empty()) {           \
         std::free(old_items);
     }
 
-    T try_get(StrName key) const{
-        bool ok; uint16_t i;
+    T try_get(StrName key) const {
+        bool ok;
+        uint16_t i;
         HASH_PROBE_0(key, ok, i);
         if(!ok) return default_invalid_value<T>();
         return _items[i].second;
     }
 
-    T* try_get_2(StrName key) const{
-        bool ok; uint16_t i;
+    T* try_get_2(StrName key) const {
+        bool ok;
+        uint16_t i;
         HASH_PROBE_0(key, ok, i);
         if(!ok) return nullptr;
         return &_items[i].second;
     }
 
-    T try_get_likely_found(StrName key) const{
+    T try_get_likely_found(StrName key) const {
         uint16_t i = key.index & _mask;
         if(_items[i].first == key) return _items[i].second;
         i = (i + 1) & _mask;
@@ -109,7 +119,7 @@ while(!_items[i].first.empty()) {           \
         return try_get(key);
     }
 
-    T* try_get_2_likely_found(StrName key) const{
+    T* try_get_2_likely_found(StrName key) const {
         uint16_t i = key.index & _mask;
         if(_items[i].first == key) return &_items[i].second;
         i = (i + 1) & _mask;
@@ -117,8 +127,9 @@ while(!_items[i].first.empty()) {           \
         return try_get_2(key);
     }
 
-    bool del(StrName key){
-        bool ok; uint16_t i;
+    bool del(StrName key) {
+        bool ok;
+        uint16_t i;
         HASH_PROBE_0(key, ok, i);
         if(!ok) return false;
         _items[i].first = StrName();
@@ -127,7 +138,7 @@ while(!_items[i].first.empty()) {           \
         // tidy
         uint16_t pre_z = i;
         uint16_t z = (i + 1) & _mask;
-        while(!_items[z].first.empty()){
+        while(!_items[z].first.empty()) {
             uint16_t h = _items[z].first.index & _mask;
             if(h != i) break;
             std::swap(_items[pre_z], _items[z]);
@@ -137,56 +148,56 @@ while(!_items[i].first.empty()) {           \
         return true;
     }
 
-    template<typename __Func>
+    template <typename __Func>
     void apply(__Func func) const {
-        for(uint16_t i=0; i<_capacity; i++){
+        for(uint16_t i = 0; i < _capacity; i++) {
             if(_items[i].first.empty()) continue;
             func(_items[i].first, _items[i].second);
         }
     }
 
     bool contains(StrName key) const {
-        bool ok; uint16_t i;
+        bool ok;
+        uint16_t i;
         HASH_PROBE_0(key, ok, i);
         return ok;
     }
 
-    T operator[](StrName key) const {
+    T operator[] (StrName key) const {
         T* val = try_get_2_likely_found(key);
-        if(val == nullptr){
-            throw std::runtime_error(_S("NameDict key not found: ", key.escape()).str());
-        }
+        if(val == nullptr) { throw std::runtime_error(_S("NameDict key not found: ", key.escape()).str()); }
         return *val;
     }
 
     array<StrName> keys() const {
         array<StrName> v(_size);
         int j = 0;
-        for(uint16_t i=0; i<_capacity; i++){
+        for(uint16_t i = 0; i < _capacity; i++) {
             if(_items[i].first.empty()) continue;
             new (&v[j++]) StrName(_items[i].first);
         }
         return v;
     }
 
-    array<Item> items() const{
+    array<Item> items() const {
         array<Item> v(_size);
         int j = 0;
-        apply([&](StrName key, T val){
-            new(&v[j++]) Item(key, val);
+        apply([&](StrName key, T val) {
+            new (&v[j++]) Item(key, val);
         });
         return v;
     }
 
-    void clear(){
-        for(uint16_t i=0; i<_capacity; i++){
+    void clear() {
+        for(uint16_t i = 0; i < _capacity; i++) {
             _items[i].first = StrName();
             _items[i].second = nullptr;
         }
         _size = 0;
     }
+
 #undef HASH_PROBE_0
 #undef HASH_PROBE_1
 };
 
-} // namespace pkpy
+}  // namespace pkpy

+ 76 - 65
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<char *, int>);
+    Str(std::pair<char*, int>);
     Str(const Str& other);
     Str(Str&& other);
 
-    operator std::string_view() const { return sv(); }
+    operator std::string_view () const { return sv(); }
 
     const char* begin() const { return data; }
+
     const char* end() const { return data + size; }
-    char operator[](int idx) const { return data[idx]; }
+
+    char operator[] (int idx) const { return data[idx]; }
+
     int length() const { return size; }
+
     bool empty() const { return size == 0; }
-    size_t hash() const{ return std::hash<std::string_view>()(sv()); }
 
-    Str& operator=(const Str&);
-    Str operator+(const Str&) const;
-    Str operator+(const char*) const;
-    friend Str operator+(const char*, const Str&);
+    size_t hash() const { return std::hash<std::string_view>()(sv()); }
+
+    Str& operator= (const Str&);
+    Str operator+ (const Str&) const;
+    Str operator+ (const char*) const;
+    friend Str operator+ (const char*, const Str&);
 
-    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 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 char* p) const;
-    bool operator!=(const char* p) 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;
+    bool operator== (const Str& other) const;
+    bool operator!= (const Str& other) const;
+    bool operator< (const Str& other) const;
+    bool operator> (const Str& other) const;
+    bool operator<= (const Str& other) const;
+    bool operator>= (const Str& other) const;
 
     ~Str();
 
-    friend std::ostream& operator<<(std::ostream& os, const Str& str);
+    friend std::ostream& operator<< (std::ostream& os, const Str& str);
 
     const char* c_str() const { return data; }
+
     std::string_view sv() const { return std::string_view(data, size); }
+
     std::string str() const { return std::string(data, size); }
 
     Str substr(int start, int len) const;
     Str substr(int start) const;
     Str strip(bool left, bool right, const Str& chars) const;
-    Str strip(bool left=true, bool right=true) const;
+    Str strip(bool left = true, bool right = true) const;
+
     Str lstrip() const { return strip(true, false); }
+
     Str rstrip() const { return strip(false, true); }
+
     Str lower() const;
     Str upper() const;
-    Str escape(bool single_quote=true) const;
-    void escape_(SStream& ss, bool single_quote=true) const;
-    int index(const Str& sub, int start=0) const;
+    Str escape(bool single_quote = true) const;
+    void escape_(SStream& ss, bool single_quote = true) const;
+    int index(const Str& sub, int start = 0) const;
     Str replace(char old, char new_) const;
-    Str replace(const Str& old, const Str& new_, int count=-1) const;
+    Str replace(const Str& old, const Str& new_, int count = -1) const;
     vector<std::string_view> split(const Str& sep) const;
     vector<std::string_view> split(char sep) const;
     int count(const Str& sub) const;
@@ -95,32 +105,29 @@ struct Str{
 struct StrName {
     uint16_t index;
 
-    StrName(): index(0) {}
-    explicit StrName(uint16_t index): index(index) {}
-    StrName(const char* s): index(get(s).index) {}
-    StrName(const Str& s): index(get(s.sv()).index) {}
+    StrName() : index(0) {}
+
+    explicit StrName(uint16_t index) : index(index) {}
+
+    StrName(const char* s) : index(get(s).index) {}
+
+    StrName(const Str& s) : index(get(s.sv()).index) {}
+
+    std::string_view sv() const { return _r_interned()[index]; }
 
-    std::string_view sv() const { return _r_interned()[index];}
     const char* c_str() const { return _r_interned()[index].c_str(); }
 
     bool empty() const { return index == 0; }
+
     Str escape() const { return Str(sv()).escape(); }
 
-    bool operator==(const StrName& other) const noexcept {
-        return this->index == other.index;
-    }
+    bool operator== (const StrName& other) const noexcept { return this->index == other.index; }
 
-    bool operator!=(const StrName& other) const noexcept {
-        return this->index != other.index;
-    }
+    bool operator!= (const StrName& other) const noexcept { return this->index != other.index; }
 
-    bool operator<(const StrName& other) const noexcept {
-        return sv() < other.sv();
-    }
+    bool operator< (const StrName& other) const noexcept { return sv() < other.sv(); }
 
-    bool operator>(const StrName& other) const noexcept {
-        return sv() > other.sv();
-    }
+    bool operator> (const StrName& other) const noexcept { return sv() > other.sv(); }
 
     static StrName get(std::string_view s);
     static std::map<std::string_view, uint16_t>& _interned();
@@ -128,32 +135,34 @@ struct StrName {
     static uint32_t _pesudo_random_index;
 };
 
-struct SStream{
+struct SStream {
     PK_ALWAYS_PASS_BY_POINTER(SStream)
 
     vector<char> buffer;
     int _precision = -1;
 
     bool empty() const { return buffer.empty(); }
+
     void setprecision(int precision) { _precision = precision; }
 
     SStream() {}
-    SStream(int guess_size) { buffer.reserve(guess_size);}
+
+    SStream(int guess_size) { buffer.reserve(guess_size); }
 
     Str str();
 
-    SStream& operator<<(const Str&);
-    SStream& operator<<(const char*);
-    SStream& operator<<(int);
-    SStream& operator<<(size_t);
-    SStream& operator<<(i64);
-    SStream& operator<<(f64);
-    SStream& operator<<(const std::string&);
-    SStream& operator<<(std::string_view);
-    SStream& operator<<(char);
-    SStream& operator<<(StrName);
-
-    void write_hex(unsigned char, bool non_zero=false);
+    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(void*);
     void write_hex(i64);
 };
@@ -162,17 +171,19 @@ struct SStream{
 #undef _S
 #endif
 
-template<typename... Args>
+template <typename... Args>
 Str _S(Args&&... args) {
     SStream ss;
     (ss << ... << args);
     return ss.str();
 }
 
-struct CString{
-	const char* ptr;
-	CString(const char* ptr): ptr(ptr) {}
-    operator const char*() const { return ptr; }
+struct CString {
+    const char* ptr;
+
+    CString(const char* ptr) : ptr(ptr) {}
+
+    operator const char* () const { return ptr; }
 };
 
 // unary operators
@@ -234,4 +245,4 @@ extern const StrName pk_id_complex;
 
 #define DEF_SNAME(name) const static StrName name(#name)
 
-} // namespace pkpy
+}  // namespace pkpy

+ 21 - 17
include/pocketpy/common/traits.hpp

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

+ 9 - 7
include/pocketpy/common/types.hpp

@@ -2,10 +2,10 @@
 
 #include <cstdint>
 
-namespace pkpy{
+namespace pkpy {
 
-using i64 = int64_t;		// always 64-bit
-using f64 = double;			// always 64-bit
+using i64 = int64_t;  // always 64-bit
+using f64 = double;   // always 64-bit
 
 static_assert(sizeof(i64) == 8);
 static_assert(sizeof(f64) == 8);
@@ -16,13 +16,15 @@ struct explicit_copy_t {
 };
 
 // Dummy types
-struct DummyInstance { };
-struct DummyModule { };
-struct NoReturn { };
+struct DummyInstance {};
+
+struct DummyModule {};
+
+struct NoReturn {};
 
 // Forward declarations
 struct PyObject;
 struct Frame;
 class VM;
 
-};  // namespace pkpy
+};  // namespace pkpy

+ 22 - 22
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?i<stop:i>stop; i+=step)
+#define PK_SLICE_LOOP(i, start, stop, step) for(int i = start; step > 0 ? i < stop : i > stop; i += step)
+
+namespace pkpy {
 
-namespace pkpy{
-    
 // global constants
-inline const char* PK_HEX_TABLE = "0123456789abcdef";
-
-inline const char* kPlatformStrings[] = {
-    "win32",        // 0
-    "emscripten",   // 1
-    "ios",          // 2
-    "darwin",       // 3
-    "android",      // 4
-    "linux",        // 5
-    "unknown"       // 6
+const inline char* PK_HEX_TABLE = "0123456789abcdef";
+
+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

+ 120 - 89
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<T*, int> detach() noexcept {
@@ -69,7 +77,7 @@ struct array {
     }
 
     ~array() {
-        if (_data) {
+        if(_data) {
             std::destroy(begin(), end());
             std::free(_data);
         }
@@ -85,58 +93,63 @@ struct vector {
     using size_type = int;
 
     vector() : _data(nullptr), _capacity(0), _size(0) {}
-    vector(int size)
-        : _data((T*)std::malloc(sizeof(T) * size)),
-          _capacity(size),
-          _size(size) {}
-    vector(vector&& other) noexcept
-        : _data(other._data), _capacity(other._capacity), _size(other._size) {
+
+    vector(int size) : _data((T*)std::malloc(sizeof(T) * size)), _capacity(size), _size(size) {}
+
+    vector(vector&& other) noexcept : _data(other._data), _capacity(other._capacity), _size(other._size) {
         other._data = nullptr;
         other._capacity = 0;
         other._size = 0;
     }
+
     vector(const vector& other) = delete;
-    vector(explicit_copy_t, const vector& other)
-        : _data((T*)std::malloc(sizeof(T) * other._size)),
-          _capacity(other._size),
-          _size(other._size) {
-        for (int i = 0; i < _size; i++) _data[i] = other._data[i];
+
+    vector(explicit_copy_t, const vector& other) :
+        _data((T*)std::malloc(sizeof(T) * other._size)), _capacity(other._size), _size(other._size) {
+        for(int i = 0; i < _size; i++)
+            _data[i] = other._data[i];
     }
 
     // allow move
-    vector& operator=(vector&& other) noexcept {
-        if (_data) {
+    vector& operator= (vector&& other) noexcept {
+        if(_data) {
             std::destroy(begin(), end());
             std::free(_data);
         }
         new (this) vector(std::move(other));
         return *this;
     }
+
     // disallow copy
-    vector& operator=(const vector& other) = delete;
+    vector& operator= (const vector& other) = delete;
 
     bool empty() const { return _size == 0; }
+
     int size() const { return _size; }
+
     int capacity() const { return _capacity; }
+
     T& back() { return _data[_size - 1]; }
 
     T* begin() const { return _data; }
+
     T* end() const { return _data + _size; }
+
     T* data() const { return _data; }
 
     void reserve(int cap) {
-        if (cap < 4) cap = 4;  // minimum capacity
-        if (cap <= capacity()) return;
+        if(cap < 4) cap = 4;  // minimum capacity
+        if(cap <= capacity()) return;
         T* new_data = (T*)std::malloc(sizeof(T) * cap);
-        if constexpr (is_trivially_relocatable_v<T>) {
+        if constexpr(is_trivially_relocatable_v<T>) {
             std::memcpy(new_data, _data, sizeof(T) * _size);
         } else {
-            for (int i = 0; i < _size; i++) {
+            for(int i = 0; i < _size; i++) {
                 new (&new_data[i]) T(std::move(_data[i]));
                 _data[i].~T();
             }
         }
-        if (_data) std::free(_data);
+        if(_data) std::free(_data);
         _data = new_data;
         _capacity = cap;
     }
@@ -147,46 +160,49 @@ struct vector {
     }
 
     void push_back(const T& t) {
-        if (_size == _capacity) reserve(_capacity * 2);
+        if(_size == _capacity) reserve(_capacity * 2);
         new (&_data[_size++]) T(t);
     }
 
     void push_back(T&& t) {
-        if (_size == _capacity) reserve(_capacity * 2);
+        if(_size == _capacity) reserve(_capacity * 2);
         new (&_data[_size++]) T(std::move(t));
     }
 
     bool contains(const T& t) const {
-        for (int i = 0; i < _size; i++) {
-            if (_data[i] == t) return true;
+        for(int i = 0; i < _size; i++) {
+            if(_data[i] == t) return true;
         }
         return false;
     }
 
     template <typename... Args>
     void emplace_back(Args&&... args) {
-        if (_size == _capacity) reserve(_capacity * 2);
+        if(_size == _capacity) reserve(_capacity * 2);
         new (&_data[_size++]) T(std::forward<Args>(args)...);
     }
 
-    T& operator[](int i) { return _data[i]; }
-    const T& operator[](int i) const { return _data[i]; }
+    T& operator[] (int i) { return _data[i]; }
+
+    const T& operator[] (int i) const { return _data[i]; }
 
     void extend(T* begin, T* end) {
         int n = end - begin;
         reserve(_size + n);
-        for (int i = 0; i < n; i++) new (&_data[_size++]) T(begin[i]);
+        for(int i = 0; i < n; i++)
+            new (&_data[_size++]) T(begin[i]);
     }
 
     void insert(int index, const T& t) {
-        if (_size == _capacity) reserve(_capacity * 2);
-        for (int i = _size; i > index; i--) _data[i] = std::move(_data[i - 1]);
+        if(_size == _capacity) reserve(_capacity * 2);
+        for(int i = _size; i > index; i--)
+            _data[i] = std::move(_data[i - 1]);
         _data[index] = t;
         _size++;
     }
 
     void erase(int index) {
-        for (int i = index; i < _size - 1; i++)
+        for(int i = index; i < _size - 1; i++)
             _data[i] = std::move(_data[i + 1]);
         _size--;
     }
@@ -194,9 +210,7 @@ struct vector {
     void pop_back() {
         assert(_size > 0);
         _size--;
-        if constexpr (!std::is_trivially_destructible_v<T>) {
-            _data[_size].~T();
-        }
+        if constexpr(!std::is_trivially_destructible_v<T>) { _data[_size].~T(); }
     }
 
     void clear() {
@@ -219,7 +233,7 @@ struct vector {
     }
 
     ~vector() {
-        if (_data) {
+        if(_data) {
             std::destroy(begin(), end());
             std::free(_data);
         }
@@ -230,37 +244,49 @@ template <typename T, typename Container = vector<T>>
 class stack {
     Container vec;
 
-   public:
+public:
     void push(const T& t) { vec.push_back(t); }
+
     void push(T&& t) { vec.push_back(std::move(t)); }
+
     template <typename... Args>
     void emplace(Args&&... args) {
         vec.emplace_back(std::forward<Args>(args)...);
     }
+
     void pop() { vec.pop_back(); }
+
     void clear() { vec.clear(); }
+
     bool empty() const { return vec.empty(); }
+
     typename Container::size_type size() const { return vec.size(); }
+
     T& top() { return vec.back(); }
+
     const T& top() const { return vec.back(); }
+
     T popx() {
         T t = std::move(vec.back());
         vec.pop_back();
         return t;
     }
+
     void reserve(int n) { vec.reserve(n); }
+
     Container& container() { return vec; }
+
     const Container& container() const { return vec; }
 };
 
 template <typename T, typename Container = vector<T>>
 class stack_no_copy : public stack<T, Container> {
-   public:
+public:
     stack_no_copy() = default;
     stack_no_copy(const stack_no_copy& other) = delete;
-    stack_no_copy& operator=(const stack_no_copy& other) = delete;
+    stack_no_copy& operator= (const stack_no_copy& other) = delete;
     stack_no_copy(stack_no_copy&& other) noexcept = default;
-    stack_no_copy& operator=(stack_no_copy&& other) noexcept = default;
+    stack_no_copy& operator= (stack_no_copy&& other) noexcept = default;
 };
 
 }  // namespace pkpy
@@ -273,7 +299,7 @@ class small_vector {
     T* m_end;
     T* m_max;
 
-   public:
+public:
     using value_type = T;
     using size_type = int;
     using difference_type = int;
@@ -286,74 +312,82 @@ class small_vector {
     using reverse_iterator = std::reverse_iterator<iterator>;
     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
-    [[nodiscard]] bool is_small() const {
-        return m_begin == reinterpret_cast<const T*>(m_buffer);
-    }
+    [[nodiscard]] bool is_small() const { return m_begin == reinterpret_cast<const T*>(m_buffer); }
+
     [[nodiscard]] size_type size() const { return m_end - m_begin; }
+
     [[nodiscard]] size_type capacity() const { return m_max - m_begin; }
+
     [[nodiscard]] bool empty() const { return m_begin == m_end; }
 
     pointer data() { return m_begin; }
+
     const_pointer data() const { return m_begin; }
-    reference operator[](size_type index) { return m_begin[index]; }
-    const_reference operator[](size_type index) const { return m_begin[index]; }
+
+    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());
-    }
+
+    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_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
+
+private:
     static void uninitialized_copy_n(const void* src, size_type n, void* dest) {
-        if constexpr (std::is_trivially_copyable_v<T>) {
+        if constexpr(std::is_trivially_copyable_v<T>) {
             std::memcpy(dest, src, sizeof(T) * n);
         } else {
-            for (size_type i = 0; i < n; i++) {
+            for(size_type i = 0; i < n; i++) {
                 ::new ((T*)dest + i) T(*((const T*)src + i));
             }
         }
     }
 
     static void uninitialized_relocate_n(void* src, size_type n, void* dest) {
-        if constexpr (is_trivially_relocatable_v<T>) {
+        if constexpr(is_trivially_relocatable_v<T>) {
             std::memcpy(dest, src, sizeof(T) * n);
         } else {
-            for (size_type i = 0; i < n; i++) {
+            for(size_type i = 0; i < n; i++) {
                 ::new ((T*)dest + i) T(std::move(*((T*)src + i)));
                 ((T*)src + i)->~T();
             }
         }
     }
 
-   public:
-    small_vector()
-        : m_begin(reinterpret_cast<T*>(m_buffer)),
-          m_end(m_begin),
-          m_max(m_begin + N) {}
+public:
+    small_vector() : m_begin(reinterpret_cast<T*>(m_buffer)), m_end(m_begin), m_max(m_begin + N) {}
 
     small_vector(const small_vector& other) noexcept {
         const auto size = other.size();
         const auto capacity = other.capacity();
-        m_begin = reinterpret_cast<T*>(
-            other.is_small() ? m_buffer : std::malloc(sizeof(T) * capacity));
+        m_begin = reinterpret_cast<T*>(other.is_small() ? m_buffer : std::malloc(sizeof(T) * capacity));
         uninitialized_copy_n(other.m_begin, size, this->m_begin);
         m_end = m_begin + size;
         m_max = m_begin + capacity;
     }
 
     small_vector(small_vector&& other) noexcept {
-        if (other.is_small()) {
+        if(other.is_small()) {
             m_begin = reinterpret_cast<T*>(m_buffer);
             uninitialized_relocate_n(other.m_buffer, other.size(), m_buffer);
             m_end = m_begin + other.size();
@@ -368,16 +402,16 @@ class small_vector {
         other.m_max = other.m_begin + N;
     }
 
-    small_vector& operator=(const small_vector& other) noexcept {
-        if (this != &other) {
+    small_vector& operator= (const small_vector& other) noexcept {
+        if(this != &other) {
             ~small_vector();
             ::new (this) small_vector(other);
         }
         return *this;
     }
 
-    small_vector& operator=(small_vector&& other) noexcept {
-        if (this != &other) {
+    small_vector& operator= (small_vector&& other) noexcept {
+        if(this != &other) {
             ~small_vector();
             ::new (this) small_vector(std::move(other));
         }
@@ -386,21 +420,19 @@ class small_vector {
 
     ~small_vector() {
         std::destroy(m_begin, m_end);
-        if (!is_small()) std::free(m_begin);
+        if(!is_small()) std::free(m_begin);
     }
 
     template <typename... Args>
     void emplace_back(Args&&... args) noexcept {
-        if (m_end == m_max) {
+        if(m_end == m_max) {
             const auto new_capacity = capacity() * 2;
             const auto size = this->size();
-            if (!is_small()) {
-                if constexpr (is_trivially_relocatable_v<T>) {
-                    m_begin = (pointer)std::realloc(m_begin,
-                                                    sizeof(T) * new_capacity);
+            if(!is_small()) {
+                if constexpr(is_trivially_relocatable_v<T>) {
+                    m_begin = (pointer)std::realloc(m_begin, sizeof(T) * new_capacity);
                 } else {
-                    auto new_data =
-                        (pointer)std::malloc(sizeof(T) * new_capacity);
+                    auto new_data = (pointer)std::malloc(sizeof(T) * new_capacity);
                     uninitialized_relocate_n(m_begin, size, new_data);
                     std::free(m_begin);
                     m_begin = new_data;
@@ -418,13 +450,12 @@ class small_vector {
     }
 
     void push_back(const T& value) { emplace_back(value); }
+
     void push_back(T&& value) { emplace_back(std::move(value)); }
 
     void pop_back() {
         m_end--;
-        if constexpr (!std::is_trivially_destructible_v<T>) {
-            m_end->~T();
-        }
+        if constexpr(!std::is_trivially_destructible_v<T>) { m_end->~T(); }
     }
 
     void clear() {
@@ -435,11 +466,11 @@ class small_vector {
 
 template <typename T, std::size_t N>
 class small_vector_2 : public small_vector<T, N> {
-   public:
+public:
     small_vector_2() = default;
     small_vector_2(const small_vector_2& other) = delete;
-    small_vector_2& operator=(const small_vector_2& other) = delete;
+    small_vector_2& operator= (const small_vector_2& other) = delete;
     small_vector_2(small_vector_2&& other) = delete;
-    small_vector_2& operator=(small_vector_2&& other) = delete;
+    small_vector_2& operator= (small_vector_2&& other) = delete;
 };
-}  // namespace pkpy
+}  // namespace pkpy

+ 1 - 0
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

+ 42 - 26
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<CodeEmitContext> contexts;
     VM* vm;
-    bool unknown_global_scope;     // for eval/exec() call
+    bool unknown_global_scope;  // for eval/exec() call
     // for parsing token stream
     int i = 0;
     vector<Token> tokens;
 
-    const Token& prev() const{ return tokens[i-1]; }
-    const Token& curr() const{ return tokens[i]; }
-    const Token& next() const{ return tokens[i+1]; }
-    const Token& err() const{
+    const Token& prev() const { return tokens[i - 1]; }
+
+    const Token& curr() const { return tokens[i]; }
+
+    const Token& next() const { return tokens[i + 1]; }
+
+    const Token& err() const {
         if(i >= tokens.size()) return prev();
         return curr();
     }
-    void advance(int delta=1) { i += delta; }
+
+    void advance(int delta = 1) { i += delta; }
 
     CodeEmitContext* ctx() { return &contexts.top(); }
-    CompileMode mode() const{ return lexer.src->mode; }
+
+    CompileMode mode() const { return lexer.src->mode; }
+
     NameScope name_scope() const;
     CodeObject_ push_global_context();
     FuncDecl_ push_f_context(Str name);
@@ -48,13 +54,13 @@ class Compiler {
     void consume(TokenIndex expected);
     bool match_newlines_repl();
 
-    bool match_newlines(bool repl_throw=false);
+    bool match_newlines(bool repl_throw = false);
     bool match_end_stmt();
     void consume_end_stmt();
 
     /*************************************************/
     void EXPR();
-    void EXPR_TUPLE(bool allow_slice=false);
+    void EXPR_TUPLE(bool allow_slice = false);
     Expr_ EXPR_VARS();  // special case for `for loop` and `comp`
 
     template <typename T, typename... Args>
@@ -91,11 +97,11 @@ class Compiler {
     void exprSubscr();
     void exprLiteral0();
 
-    void compile_block_body(void (Compiler::*callback)()=nullptr);
+    void compile_block_body(void (Compiler::*callback)() = nullptr);
     void compile_normal_import();
     void compile_from_import();
-    bool is_expression(bool allow_slice=false);
-    void parse_expression(int precedence, bool allow_slice=false);
+    bool is_expression(bool allow_slice = false);
+    void parse_expression(int precedence, bool allow_slice = false);
     void compile_if_stmt();
     void compile_while_loop();
     void compile_for_loop();
@@ -106,32 +112,42 @@ class Compiler {
     void compile_stmt();
     void consume_type_hints();
     void _add_decorators(const Expr_vector& decorators);
-    void compile_class(const Expr_vector& decorators={});
+    void compile_class(const Expr_vector& decorators = {});
     void _compile_f_args(FuncDecl_ decl, bool enable_type_hints);
-    void compile_function(const Expr_vector& decorators={});
+    void compile_function(const Expr_vector& decorators = {});
 
     PyVar to_object(const TokenValue& value);
     PyVar read_literal();
 
-    void SyntaxError(Str msg){ lexer.throw_err("SyntaxError", msg, err().line, err().start); }
-    void SyntaxError(){ lexer.throw_err("SyntaxError", "invalid syntax", err().line, err().start); }
-    void IndentationError(Str msg){ lexer.throw_err("IndentationError", msg, err().line, err().start); }
+    void SyntaxError(Str msg) { lexer.throw_err("SyntaxError", msg, err().line, err().start); }
+
+    void SyntaxError() { lexer.throw_err("SyntaxError", "invalid syntax", err().line, err().start); }
+
+    void IndentationError(Str msg) { lexer.throw_err("IndentationError", msg, err().line, err().start); }
 
 public:
-    Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope=false);
+    Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope = false);
     Str precompile();
     void from_precompiled(const char* source);
     CodeObject_ compile();
 };
 
-struct TokenDeserializer{
+struct TokenDeserializer {
     const char* curr;
     const char* source;
 
-    TokenDeserializer(const char* source): curr(source), source(source) {}
-    char read_char(){ return *curr++; }
-    bool match_char(char c){ if(*curr == c) { curr++; return true; } return false; }
-    
+    TokenDeserializer(const char* source) : curr(source), source(source) {}
+
+    char read_char() { return *curr++; }
+
+    bool match_char(char c) {
+        if(*curr == c) {
+            curr++;
+            return true;
+        }
+        return false;
+    }
+
     std::string_view read_string(char c);
     Str read_string_from_hex(char c);
     int read_count();
@@ -139,4 +155,4 @@ struct TokenDeserializer{
     f64 read_float(char c);
 };
 
-} // namespace pkpy
+}  // namespace pkpy

+ 172 - 107
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<typename T>
-class unique_ptr_128{
+template <typename T>
+class unique_ptr_128 {
     T* ptr;
+
 public:
-    unique_ptr_128(): ptr(nullptr) {}
-    unique_ptr_128(T* ptr): ptr(ptr) {}
+    unique_ptr_128() : ptr(nullptr) {}
+
+    unique_ptr_128(T* ptr) : ptr(ptr) {}
+
     T* operator->() const { return ptr; }
+
     T* get() const { return ptr; }
-    T* detach() { T* p = ptr; ptr = nullptr; return p; }
+
+    T* detach() {
+        T* p = ptr;
+        ptr = nullptr;
+        return p;
+    }
 
     unique_ptr_128(const unique_ptr_128&) = delete;
-    unique_ptr_128& operator=(const unique_ptr_128&) = delete;
+    unique_ptr_128& operator= (const unique_ptr_128&) = delete;
 
-    bool operator==(std::nullptr_t) const { return ptr == nullptr; }
-    bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
+    bool operator== (std::nullptr_t) const { return ptr == nullptr; }
 
-    ~unique_ptr_128(){ if(ptr) { ptr->~T(); PoolExpr_dealloc(ptr); } }
+    bool operator!= (std::nullptr_t) const { return ptr != nullptr; }
 
-    template<typename U>
-    unique_ptr_128(unique_ptr_128<U>&& other): ptr(other.detach()) {}
+    ~unique_ptr_128() {
+        if(ptr) {
+            ptr->~T();
+            PoolExpr_dealloc(ptr);
+        }
+    }
 
-    operator bool() const { return ptr != nullptr; }
+    template <typename U>
+    unique_ptr_128(unique_ptr_128<U>&& other) : ptr(other.detach()) {}
 
-    template<typename U>
-    unique_ptr_128& operator=(unique_ptr_128<U>&& other) {
-        if(ptr) { ptr->~T(); PoolExpr_dealloc(ptr); };
+    operator bool () const { return ptr != nullptr; }
+
+    template <typename U>
+    unique_ptr_128& operator= (unique_ptr_128<U>&& other) {
+        if(ptr) {
+            ptr->~T();
+            PoolExpr_dealloc(ptr);
+        };
         ptr = other.detach();
         return *this;
     }
 
-    unique_ptr_128& operator=(std::nullptr_t) {
-        if(ptr) { ptr->~T(); PoolExpr_dealloc(ptr); }
+    unique_ptr_128& operator= (std::nullptr_t) {
+        if(ptr) {
+            ptr->~T();
+            PoolExpr_dealloc(ptr);
+        }
         ptr = nullptr;
         return *this;
     }
@@ -48,52 +69,54 @@ public:
 typedef unique_ptr_128<Expr> Expr_;
 typedef small_vector<Expr_, 4> Expr_vector;
 
-template<>
+template <>
 constexpr inline bool is_trivially_relocatable_v<Expr_> = true;
 
-struct Expr{
+struct Expr {
     int line = 0;
     virtual ~Expr() = default;
     virtual void emit_(CodeEmitContext* ctx) = 0;
+
     virtual bool is_literal() const { return false; }
+
     virtual bool is_json_object() const { return false; }
+
     virtual bool is_attrib() const { return false; }
+
     virtual bool is_subscr() const { return false; }
+
     virtual bool is_compare() const { return false; }
+
     virtual int star_level() const { return 0; }
+
     virtual bool is_tuple() const { return false; }
+
     virtual bool is_name() const { return false; }
+
     bool is_starred() const { return star_level() > 0; }
 
     // for OP_DELETE_XXX
-    [[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) {
-        return false;
-    }
+    [[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
 
     // for OP_STORE_XXX
-    [[nodiscard]] virtual bool emit_store(CodeEmitContext* ctx) {
-        return false;
-    }
+    [[nodiscard]] virtual bool emit_store(CodeEmitContext* ctx) { return false; }
 
-    virtual void emit_inplace(CodeEmitContext* ctx) {
-        emit_(ctx);
-    }
+    virtual void emit_inplace(CodeEmitContext* ctx) { emit_(ctx); }
 
-    [[nodiscard]] virtual bool emit_store_inplace(CodeEmitContext* ctx) {
-        return emit_store(ctx);
-    }
+    [[nodiscard]] virtual bool emit_store_inplace(CodeEmitContext* ctx) { return emit_store(ctx); }
 };
 
-struct CodeEmitContext{
+struct CodeEmitContext {
     VM* vm;
-    FuncDecl_ func;     // optional
-    CodeObject_ co;     // 1 CodeEmitContext <=> 1 CodeObject_
+    FuncDecl_ func;  // optional
+    CodeObject_ co;  // 1 CodeEmitContext <=> 1 CodeObject_
     // some bugs on MSVC (error C2280) when using Expr_vector
     // so we use stack_no_copy instead
     stack_no_copy<Expr_> s_expr;
     int level;
     vector<Str> global_names;
-    CodeEmitContext(VM* vm, CodeObject_ co, int level): vm(vm), co(co), level(level) {}
+
+    CodeEmitContext(VM* vm, CodeObject_ co, int level) : vm(vm), co(co), level(level) {}
 
     int curr_iblock = 0;
     bool is_compiling_class = false;
@@ -104,8 +127,8 @@ struct CodeEmitContext{
     int get_loop() const;
     CodeBlock* enter_block(CodeBlockType type);
     void exit_block();
-    void emit_expr();   // clear the expression stack and generate bytecode
-    int emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual=false);
+    void emit_expr();  // clear the expression stack and generate bytecode
+    int emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual = false);
     void revert_last_emit_();
     int emit_int(i64 value, int line);
     void patch_jump(int index);
@@ -118,150 +141,189 @@ struct CodeEmitContext{
     void try_merge_for_iter_store(int);
 };
 
-struct NameExpr: Expr{
+struct NameExpr : Expr {
     StrName name;
     NameScope scope;
-    NameExpr(StrName name, NameScope scope): name(name), scope(scope) {}
+
+    NameExpr(StrName name, NameScope scope) : name(name), scope(scope) {}
+
     void emit_(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
+
     bool is_name() const override { return true; }
 };
 
-struct InvertExpr: Expr{
+struct InvertExpr : Expr {
     Expr_ child;
-    InvertExpr(Expr_&& child): child(std::move(child)) {}
+
+    InvertExpr(Expr_&& child) : child(std::move(child)) {}
+
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct StarredExpr: Expr{
+struct StarredExpr : Expr {
     int level;
     Expr_ child;
-    StarredExpr(int level, Expr_&& child): level(level), child(std::move(child)) {}
+
+    StarredExpr(int level, Expr_&& child) : level(level), child(std::move(child)) {}
+
     int star_level() const override { return level; }
+
     void emit_(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
 };
 
-struct NotExpr: Expr{
+struct NotExpr : Expr {
     Expr_ child;
-    NotExpr(Expr_&& child): child(std::move(child)) {}
+
+    NotExpr(Expr_&& child) : child(std::move(child)) {}
+
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct AndExpr: Expr{
+struct AndExpr : Expr {
     Expr_ lhs;
     Expr_ rhs;
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct OrExpr: Expr{
+struct OrExpr : Expr {
     Expr_ lhs;
     Expr_ rhs;
     void emit_(CodeEmitContext* ctx) override;
 };
 
 // [None, True, False, ...]
-struct Literal0Expr: Expr{
+struct Literal0Expr : Expr {
     TokenIndex token;
-    Literal0Expr(TokenIndex token): token(token) {}
+
+    Literal0Expr(TokenIndex token) : token(token) {}
+
     bool is_json_object() const override { return true; }
 
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct LongExpr: Expr{
+struct LongExpr : Expr {
     Str s;
-    LongExpr(const Str& s): s(s) {}
+
+    LongExpr(const Str& s) : s(s) {}
+
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct BytesExpr: Expr{
+struct BytesExpr : Expr {
     Str s;
-    BytesExpr(const Str& s): s(s) {}
+
+    BytesExpr(const Str& s) : s(s) {}
+
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct ImagExpr: Expr{
+struct ImagExpr : Expr {
     f64 value;
-    ImagExpr(f64 value): value(value) {}
+
+    ImagExpr(f64 value) : value(value) {}
+
     void emit_(CodeEmitContext* ctx) override;
 };
 
 // @num, @str which needs to invoke OP_LOAD_CONST
-struct LiteralExpr: Expr{
+struct LiteralExpr : Expr {
     TokenValue value;
-    LiteralExpr(TokenValue value): value(value) {}
+
+    LiteralExpr(TokenValue value) : value(value) {}
+
     void emit_(CodeEmitContext* ctx) override;
+
     bool is_literal() const override { return true; }
+
     bool is_json_object() const override { return true; }
 };
 
-struct NegatedExpr: Expr{
+struct NegatedExpr : Expr {
     Expr_ child;
-    NegatedExpr(Expr_&& child): child(std::move(child)) {}
+
+    NegatedExpr(Expr_&& child) : child(std::move(child)) {}
+
     void emit_(CodeEmitContext* ctx) override;
+
     bool is_json_object() const override { return child->is_literal(); }
 };
 
-struct SliceExpr: Expr{
+struct SliceExpr : Expr {
     Expr_ start;
     Expr_ stop;
     Expr_ step;
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct DictItemExpr: Expr{
-    Expr_ key;      // maybe nullptr if it is **kwargs
+struct DictItemExpr : Expr {
+    Expr_ key;  // maybe nullptr if it is **kwargs
     Expr_ value;
+
     int star_level() const override { return value->star_level(); }
+
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct SequenceExpr: Expr{
+struct SequenceExpr : Expr {
     Expr_vector items;
-    SequenceExpr(Expr_vector&& items): items(std::move(items)) {}
+
+    SequenceExpr(Expr_vector&& items) : items(std::move(items)) {}
+
     virtual Opcode opcode() const = 0;
 
     void emit_(CodeEmitContext* ctx) override {
-        for(auto& item: items) item->emit_(ctx);
+        for(auto& item: items)
+            item->emit_(ctx);
         ctx->emit_(opcode(), items.size(), line);
     }
 };
 
-struct ListExpr: SequenceExpr{
+struct ListExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
+
     Opcode opcode() const override {
-        for(auto& e: items) if(e->is_starred()) return OP_BUILD_LIST_UNPACK;
+        for(auto& e: items)
+            if(e->is_starred()) return OP_BUILD_LIST_UNPACK;
         return OP_BUILD_LIST;
     }
 
     bool is_json_object() const override { return true; }
 };
 
-struct DictExpr: SequenceExpr{
+struct DictExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
+
     Opcode opcode() const override {
-        for(auto& e: items) if(e->is_starred()) return OP_BUILD_DICT_UNPACK;
+        for(auto& e: items)
+            if(e->is_starred()) return OP_BUILD_DICT_UNPACK;
         return OP_BUILD_DICT;
     }
 
     bool is_json_object() const override { return true; }
 };
 
-struct SetExpr: SequenceExpr{
+struct SetExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
+
     Opcode opcode() const override {
-        for(auto& e: items) if(e->is_starred()) return OP_BUILD_SET_UNPACK;
+        for(auto& e: items)
+            if(e->is_starred()) return OP_BUILD_SET_UNPACK;
         return OP_BUILD_SET;
     }
 };
 
-struct TupleExpr: SequenceExpr{
+struct TupleExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
+
     bool is_tuple() const override { return true; }
+
     Opcode opcode() const override {
-        for(auto& e: items) if(e->is_starred()) return OP_BUILD_TUPLE_UNPACK;
+        for(auto& e: items)
+            if(e->is_starred()) return OP_BUILD_TUPLE_UNPACK;
         return OP_BUILD_TUPLE;
     }
 
@@ -269,11 +331,11 @@ struct TupleExpr: SequenceExpr{
     bool emit_del(CodeEmitContext* ctx) override;
 };
 
-struct CompExpr: Expr{
-    Expr_ expr;       // loop expr
-    Expr_ vars;       // loop vars
-    Expr_ iter;       // loop iter
-    Expr_ cond;       // optional if condition
+struct CompExpr : Expr {
+    Expr_ expr;  // loop expr
+    Expr_ vars;  // loop vars
+    Expr_ iter;  // loop iter
+    Expr_ cond;  // optional if condition
 
     virtual Opcode op0() = 0;
     virtual Opcode op1() = 0;
@@ -281,25 +343,28 @@ struct CompExpr: Expr{
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct ListCompExpr: CompExpr{
+struct ListCompExpr : CompExpr {
     Opcode op0() override { return OP_BUILD_LIST; }
+
     Opcode op1() override { return OP_LIST_APPEND; }
 };
 
-struct DictCompExpr: CompExpr{
+struct DictCompExpr : CompExpr {
     Opcode op0() override { return OP_BUILD_DICT; }
+
     Opcode op1() override { return OP_DICT_ADD; }
 };
 
-struct SetCompExpr: CompExpr{
+struct SetCompExpr : CompExpr {
     Opcode op0() override { return OP_BUILD_SET; }
+
     Opcode op1() override { return OP_SET_ADD; }
 };
 
-struct LambdaExpr: Expr{
+struct LambdaExpr : Expr {
     FuncDecl_ decl;
 
-    LambdaExpr(FuncDecl_ decl): decl(decl) {}
+    LambdaExpr(FuncDecl_ decl) : decl(decl) {}
 
     void emit_(CodeEmitContext* ctx) override {
         int index = ctx->add_func_decl(decl);
@@ -307,17 +372,21 @@ struct LambdaExpr: Expr{
     }
 };
 
-struct FStringExpr: Expr{
+struct FStringExpr : Expr {
     Str src;
-    FStringExpr(const Str& src): src(src) {}
+
+    FStringExpr(const Str& src) : src(src) {}
+
     void _load_simple_expr(CodeEmitContext* ctx, Str expr);
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct SubscrExpr: Expr{
+struct SubscrExpr : Expr {
     Expr_ a;
     Expr_ b;
+
     bool is_subscr() const override { return true; }
+
     void emit_(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
@@ -326,10 +395,11 @@ struct SubscrExpr: Expr{
     bool emit_store_inplace(CodeEmitContext* ctx) override;
 };
 
-struct AttribExpr: Expr{
+struct AttribExpr : Expr {
     Expr_ a;
     StrName b;
-    AttribExpr(Expr_ a, StrName b): a(std::move(a)), b(b) {}
+
+    AttribExpr(Expr_ a, StrName b) : a(std::move(a)), b(b) {}
 
     void emit_(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
@@ -337,11 +407,12 @@ struct AttribExpr: Expr{
     void emit_method(CodeEmitContext* ctx);
 
     bool is_attrib() const override { return true; }
+
     void emit_inplace(CodeEmitContext* ctx) override;
     bool emit_store_inplace(CodeEmitContext* ctx) override;
 };
 
-struct CallExpr: Expr{
+struct CallExpr : Expr {
     Expr_ callable;
     Expr_vector args;
     // **a will be interpreted as a special keyword argument: {"**": a}
@@ -349,42 +420,36 @@ struct CallExpr: Expr{
     void emit_(CodeEmitContext* ctx) override;
 };
 
-struct GroupedExpr: Expr{
+struct GroupedExpr : Expr {
     Expr_ a;
-    GroupedExpr(Expr_&& a): a(std::move(a)) {}
 
-    void emit_(CodeEmitContext* ctx) override{
-        a->emit_(ctx);
-    }
+    GroupedExpr(Expr_&& a) : a(std::move(a)) {}
 
-    bool emit_del(CodeEmitContext* ctx) override {
-        return a->emit_del(ctx);
-    }
+    void emit_(CodeEmitContext* ctx) override { a->emit_(ctx); }
 
-    bool emit_store(CodeEmitContext* ctx) override {
-        return a->emit_store(ctx);
-    }
+    bool emit_del(CodeEmitContext* ctx) override { return a->emit_del(ctx); }
+
+    bool emit_store(CodeEmitContext* ctx) override { return a->emit_store(ctx); }
 };
 
-struct BinaryExpr: Expr{
+struct BinaryExpr : Expr {
     TokenIndex op;
     Expr_ lhs;
     Expr_ rhs;
     bool inplace;
 
-    BinaryExpr(bool inplace=false): inplace(inplace) {}
+    BinaryExpr(bool inplace = false) : inplace(inplace) {}
+
     bool is_compare() const override;
     void _emit_compare(CodeEmitContext*, small_vector_2<int, 6>&);
     void emit_(CodeEmitContext* ctx) override;
 };
 
-
-struct TernaryExpr: Expr{
+struct TernaryExpr : Expr {
     Expr_ cond;
     Expr_ true_expr;
     Expr_ false_expr;
     void emit_(CodeEmitContext* ctx) override;
 };
 
-
-} // namespace pkpy
+}  // namespace pkpy

+ 56 - 48
include/pocketpy/compiler/lexer.hpp

@@ -5,10 +5,11 @@
 
 #include <variant>
 
-namespace pkpy{
+namespace pkpy {
 
 typedef uint8_t TokenIndex;
 
+// clang-format off
 constexpr const char* kTokens[] = {
     "is not", "not in", "yield from",
     "@eof", "@eol", "@sof",
@@ -28,67 +29,71 @@ constexpr const char* kTokens[] = {
     "None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally",
     "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise"
 };
+// clang-format on
 
 using TokenValue = std::variant<std::monostate, i64, f64, Str>;
 const int kTokenCount = sizeof(kTokens) / sizeof(kTokens[0]);
 
 constexpr TokenIndex TK(const char token[]) {
-    for(int k=0; k<kTokenCount; k++){
+    for(int k = 0; k < kTokenCount; k++) {
         const char* i = kTokens[k];
         const char* j = token;
-        while(*i && *j && *i == *j) { i++; j++;}
+        while(*i && *j && *i == *j) {
+            i++;
+            j++;
+        }
         if(*i == *j) return k;
     }
     return 255;
 }
 
-inline constexpr bool is_raw_string_used(TokenIndex t){
-    return t == TK("@id") || t == TK("@long");
-}
+constexpr inline bool is_raw_string_used(TokenIndex t) { return t == TK("@id") || t == TK("@long"); }
 
 #define TK_STR(t) kTokens[t]
-const std::map<std::string_view, TokenIndex> kTokenKwMap = [](){
+const std::map<std::string_view, TokenIndex> kTokenKwMap = []() {
     std::map<std::string_view, TokenIndex> map;
-    for(int k=TK("class"); k<kTokenCount; k++) map[kTokens[k]] = k;
+    for(int k = TK("class"); k < kTokenCount; k++)
+        map[kTokens[k]] = k;
     return map;
 }();
 
-struct Token{
-  TokenIndex type;
-  const char* start;
-  int length;
-  int line;
-  int brackets_level;
-  TokenValue value;
+struct Token {
+    TokenIndex type;
+    const char* start;
+    int length;
+    int line;
+    int brackets_level;
+    TokenValue value;
+
+    Str str() const { return Str(start, length); }
 
-  Str str() const { return Str(start, length);}
-  std::string_view sv() const { return std::string_view(start, length);}
+    std::string_view sv() const { return std::string_view(start, length); }
 };
 
 // https://docs.python.org/3/reference/expressions.html#operator-precedence
 enum Precedence {
-  PREC_LOWEST,
-  PREC_LAMBDA,        // lambda
-  PREC_TERNARY,       // ?:
-  PREC_LOGICAL_OR,    // or
-  PREC_LOGICAL_AND,   // and
-  PREC_LOGICAL_NOT,   // not
-  /* https://docs.python.org/3/reference/expressions.html#comparisons
-   * Unlike C, all comparison operations in Python have the same priority,
-   * which is lower than that of any arithmetic, shifting or bitwise operation.
-   * Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics.
-   */
-  PREC_COMPARISION,   // < > <= >= != ==, in / is / is not / not in
-  PREC_BITWISE_OR,    // |
-  PREC_BITWISE_XOR,   // ^
-  PREC_BITWISE_AND,   // &
-  PREC_BITWISE_SHIFT, // << >>
-  PREC_TERM,          // + -
-  PREC_FACTOR,        // * / % // @
-  PREC_UNARY,         // - not ~
-  PREC_EXPONENT,      // **
-  PREC_PRIMARY,       // f() x[] a.b 1:2
-  PREC_HIGHEST,
+    PREC_LOWEST,
+    PREC_LAMBDA,       // lambda
+    PREC_TERNARY,      // ?:
+    PREC_LOGICAL_OR,   // or
+    PREC_LOGICAL_AND,  // and
+    PREC_LOGICAL_NOT,  // not
+    /* https://docs.python.org/3/reference/expressions.html#comparisons
+     * Unlike C, all comparison operations in Python have the same priority,
+     * which is lower than that of any arithmetic, shifting or bitwise operation.
+     * Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics.
+     */
+    PREC_COMPARISION,    // < > <= >= != ==, in / is / is not / not in
+    PREC_BITWISE_OR,     // |
+    PREC_BITWISE_XOR,    // ^
+    PREC_BITWISE_AND,    // &
+    PREC_BITWISE_SHIFT,  // << >>
+    PREC_TERM,           // + -
+    PREC_FACTOR,         // * / % // @
+    PREC_UNARY,          // - not ~
+    PREC_EXPONENT,       // **
+    PREC_PRIMARY,        // f() x[] a.b 1:2
+    PREC_HIGHEST,
 };
 
 enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
@@ -103,7 +108,8 @@ struct Lexer {
     stack_no_copy<int, small_vector_2<int, 8>> indents;
     int brackets_level = 0;
 
-    char peekchar() const{ return *curr_char; }
+    char peekchar() const { return *curr_char; }
+
     bool match_n_chars(int n, char c0);
     bool match_string(const char* s);
     int eat_spaces();
@@ -114,7 +120,7 @@ struct Lexer {
     int eat_name();
     void skip_line_comment();
     bool matchchar(char c);
-    void add_token(TokenIndex type, TokenValue value={});
+    void add_token(TokenIndex type, TokenValue value = {});
     void add_token_2(char c, TokenIndex one, TokenIndex two);
     Str eat_string_until(char quote, bool raw);
     void eat_string(char quote, StringType type);
@@ -125,16 +131,18 @@ struct Lexer {
     /***** Error Reporter *****/
     [[noreturn]] void throw_err(StrName type, Str msg);
     [[noreturn]] void throw_err(StrName type, Str msg, int lineno, const char* cursor);
-    [[noreturn]] void SyntaxError(Str msg){ throw_err("SyntaxError", msg); }
-    [[noreturn]] void SyntaxError(){ throw_err("SyntaxError", "invalid syntax"); }
-    [[noreturn]] void IndentationError(Str msg){ throw_err("IndentationError", msg); }
-    
+
+    [[noreturn]] void SyntaxError(Str msg) { throw_err("SyntaxError", msg); }
+
+    [[noreturn]] void SyntaxError() { throw_err("SyntaxError", "invalid syntax"); }
+
+    [[noreturn]] void IndentationError(Str msg) { throw_err("IndentationError", msg); }
+
     Lexer(VM* vm, std::shared_ptr<SourceData> src);
     vector<Token> run();
 };
 
-
-enum class IntParsingResult{
+enum class IntParsingResult {
     Success,
     Failure,
     Overflow,
@@ -142,4 +150,4 @@ enum class IntParsingResult{
 
 IntParsingResult parse_uint(std::string_view text, i64* out, int base);
 
-} // namespace pkpy
+}  // namespace pkpy

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

+ 1 - 1
include/pocketpy/interpreter/ceval.hpp

@@ -1,4 +1,4 @@
 #pragma once
 
 #include "pocketpy/interpreter/vm.hpp"
-// dummy header for ceval.cpp
+// dummy header for ceval.cpp

+ 41 - 36
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<T>(mod, #name, base);                                    \
-    }                                                                       
+#define PY_CLASS(T, mod, name)                                                                                         \
+    [[deprecated]] static Type _type(VM* vm) { return vm->_cxx_typeid_map[typeid(T)]; }                                \
+    [[deprecated]] static PyVar register_class(VM* vm, PyVar mod, Type base = VM::tp_object) {                         \
+        return vm->register_user_class<T>(mod, #name, base);                                                           \
+    }
 
-struct VoidP{
+struct VoidP {
     void* ptr;
-    VoidP(const void* ptr): ptr(const_cast<void*>(ptr)){}
 
-    bool operator==(const VoidP& other) const {
-        return ptr == other.ptr;
-    }
-    bool operator!=(const VoidP& other) const {
-        return ptr != other.ptr;
-    }
-    bool operator<(const VoidP& other) const { return ptr < other.ptr; }
-    bool operator<=(const VoidP& other) const { return ptr <= other.ptr; }
-    bool operator>(const VoidP& other) const { return ptr > other.ptr; }
-    bool operator>=(const VoidP& other) const { return ptr >= other.ptr; }
+    VoidP(const void* ptr) : ptr(const_cast<void*>(ptr)) {}
+
+    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{
+    bool operator> (const VoidP& other) const { return ptr > other.ptr; }
+
+    bool operator>= (const VoidP& other) const { return ptr >= other.ptr; }
+
+    Str hex() const {
         SStream ss;
         ss.write_hex(ptr);
         return ss.str();
@@ -34,11 +36,11 @@ struct VoidP{
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 };
 
-#define POINTER_VAR(Tp, NAME)    \
-    inline PyVar py_var(VM* vm, Tp val){    \
-        const static std::pair<StrName, StrName> P("c", NAME);      \
-        PyVar type = vm->_modules[P.first]->attr(P.second);         \
-        return vm->new_object<VoidP>(type->as<Type>(), val);  \
+#define POINTER_VAR(Tp, NAME)                                                                                          \
+    inline PyVar py_var(VM* vm, Tp val) {                                                                              \
+        const static std::pair<StrName, StrName> P("c", NAME);                                                         \
+        PyVar type = vm->_modules[P.first]->attr(P.second);                                                            \
+        return vm->new_object<VoidP>(type->as<Type>(), val);                                                           \
     }
 
 POINTER_VAR(char*, "char_p")
@@ -58,43 +60,46 @@ POINTER_VAR(const bool*, "bool_p")
 
 #undef POINTER_VAR
 
-
-struct Struct{
-    static constexpr int INLINE_SIZE = 24;
+struct Struct {
+    constexpr static int INLINE_SIZE = 24;
 
     char _inlined[INLINE_SIZE];
     char* p;
     int size;
 
-    Struct(int new_size, bool zero_init=true){
+    Struct(int new_size, bool zero_init = true) {
         this->size = new_size;
-        if(size <= INLINE_SIZE){
+        if(size <= INLINE_SIZE) {
             p = _inlined;
-        }else{
+        } else {
             p = (char*)std::malloc(size);
         }
         if(zero_init) std::memset(p, 0, size);
     }
 
-    Struct(void* p, int size): Struct(size, false){
+    Struct(void* p, int size) : Struct(size, false) {
         if(p != nullptr) std::memcpy(this->p, p, size);
     }
 
-    Struct(const Struct& other): Struct(other.p, other.size){}
-    ~Struct(){ if(p!=_inlined) std::free(p); }
+    Struct(const Struct& other) : Struct(other.p, other.size) {}
+
+    ~Struct() {
+        if(p != _inlined) std::free(p);
+    }
 
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 };
 
 /***********************************************/
-template<typename Tp>
-Tp to_void_p(VM* vm, PyVar var){
+template <typename Tp>
+Tp to_void_p(VM* vm, PyVar var) {
     static_assert(std::is_pointer_v<Tp>);
-    if(var == vm->None) return nullptr;     // None can be casted to any pointer implicitly
+    if(var == vm->None) return nullptr;  // None can be casted to any pointer implicitly
     VoidP& p = CAST(VoidP&, var);
     return reinterpret_cast<Tp>(p.ptr);
 }
+
 /*****************************************************************/
 void add_module_c(VM* vm);
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 83 - 45
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<typename... Args>
-    void emplace(Args&&... args){
-        new(_sp) PyVar(std::forward<Args>(args)...);
+    template <typename... Args>
+    void emplace(Args&&... args) {
+        new (_sp) PyVar(std::forward<Args>(args)...);
         ++_sp;
     }
 };
 
-struct UnwindTarget{
+struct UnwindTarget {
     UnwindTarget* next;
     int iblock;
     int offset;
 
-    UnwindTarget(int iblock, int offset): next(nullptr), iblock(iblock), offset(offset) {}
+    UnwindTarget(int iblock, int offset) : next(nullptr), iblock(iblock), offset(offset) {}
 };
 
 struct Frame {
@@ -82,36 +107,42 @@ struct Frame {
 
     const CodeObject* co;
     PyObject* _module;
-    PyObject* _callable;    // a function object or nullptr (global scope)
+    PyObject* _callable;  // a function object or nullptr (global scope)
     FastLocals _locals;
 
     // This list will be freed in __pop_frame
     UnwindTarget* _uw_list;
 
     NameDict& f_globals() { return _module->attr(); }
+
     PyVar* f_closure_try_get(StrName name);
+
     int ip() const { return _ip - co->codes.data(); }
 
     // function scope
-    Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, PyVar* _locals_base)
-            : _ip(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, _locals_base), _uw_list(nullptr) { }
+    Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, PyVar* _locals_base) :
+        _ip(co->codes.data() - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable),
+        _locals(co, _locals_base), _uw_list(nullptr) {}
 
     // exec/eval
-    Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, FastLocals _locals)
-            : _ip(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals), _uw_list(nullptr) { }
+    Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, FastLocals _locals) :
+        _ip(co->codes.data() - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals),
+        _uw_list(nullptr) {}
 
     // global scope
-    Frame(PyVar* p0, const CodeObject_& co, PyObject* _module)
-            : _ip(co->codes.data()-1), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr), _locals(co.get(), p0), _uw_list(nullptr) { }
+    Frame(PyVar* p0, const CodeObject_& co, PyObject* _module) :
+        _ip(co->codes.data() - 1), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr),
+        _locals(co.get(), p0), _uw_list(nullptr) {}
 
     PyVar* actual_sp_base() const { return _locals.a; }
+
     ArgsView stack_view(ValueStack* _s) const { return ArgsView(actual_sp_base(), _s->_sp); }
 
     [[nodiscard]] int prepare_jump_exception_handler(ValueStack*);
     void prepare_jump_break(ValueStack*, int);
     int _exit_block(ValueStack*, int);
 
-    [[nodiscard]] int prepare_loop_break(ValueStack* s_data){
+    [[nodiscard]] int prepare_loop_break(ValueStack* s_data) {
         int target = co->_get_block_codei(ip()).end;
         prepare_jump_break(s_data, target);
         return target;
@@ -126,29 +157,35 @@ struct Frame {
     ~Frame();
 };
 
-struct LinkedFrame{
+struct LinkedFrame {
     LinkedFrame* f_back;
     Frame frame;
-    template<typename... Args>
+
+    template <typename... Args>
     LinkedFrame(LinkedFrame* f_back, Args&&... args) : f_back(f_back), frame(std::forward<Args>(args)...) {}
 };
 
-
-struct CallStack{
+struct CallStack {
     static_assert(sizeof(LinkedFrame) <= 128);
 
     LinkedFrame* _tail;
     int _size;
-    CallStack(): _tail(nullptr), _size(0) {}
+
+    CallStack() : _tail(nullptr), _size(0) {}
 
     int size() const { return _size; }
+
     bool empty() const { return _size == 0; }
-    void clear(){ while(!empty()) pop(); }
 
-    template<typename... Args>
-    void emplace(Args&&... args){
+    void clear() {
+        while(!empty())
+            pop();
+    }
+
+    template <typename... Args>
+    void emplace(Args&&... args) {
         static_assert(sizeof(LinkedFrame) <= kPoolFrameBlockSize);
-        _tail = new(PoolFrame_alloc()) LinkedFrame(_tail, std::forward<Args>(args)...);
+        _tail = new (PoolFrame_alloc()) LinkedFrame(_tail, std::forward<Args>(args)...);
         ++_size;
     }
 
@@ -161,12 +198,13 @@ struct CallStack{
         return _tail->frame;
     }
 
-    template<typename Func>
-    void apply(Func&& f){
-        for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back) f(p->frame);
+    template <typename Func>
+    void apply(Func&& f) {
+        for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back)
+            f(p->frame);
     }
 
-    ~CallStack(){ clear(); }
+    ~CallStack() { clear(); }
 };
 
-}; // namespace pkpy
+};  // namespace pkpy

+ 21 - 21
include/pocketpy/interpreter/gc.hpp

@@ -6,54 +6,52 @@
 #include "pocketpy/objects/object.hpp"
 
 namespace pkpy {
-struct ManagedHeap{
+struct ManagedHeap {
     vector<PyObject*> _no_gc;
     vector<PyObject*> gen;
     VM* vm;
     void (*_gc_on_delete)(VM*, PyObject*) = nullptr;
     void (*_gc_marker_ex)(VM*) = nullptr;
 
-    ManagedHeap(VM* vm): vm(vm) {}
-    
+    ManagedHeap(VM* vm) : vm(vm) {}
+
     int gc_threshold = PK_GC_MIN_THRESHOLD;
     int gc_counter = 0;
 
     /********************/
     int _gc_lock_counter = 0;
-    struct ScopeLock{
+
+    struct ScopeLock {
         PK_ALWAYS_PASS_BY_POINTER(ScopeLock)
-        
+
         ManagedHeap* heap;
-        ScopeLock(ManagedHeap* heap): heap(heap){
-            heap->_gc_lock_counter++;
-        }
-        ~ScopeLock(){
-            heap->_gc_lock_counter--;
-        }
+
+        ScopeLock(ManagedHeap* heap) : heap(heap) { heap->_gc_lock_counter++; }
+
+        ~ScopeLock() { heap->_gc_lock_counter--; }
     };
 
-    ScopeLock gc_scope_lock(){
-        return ScopeLock(this);
-    }
+    ScopeLock gc_scope_lock() { return ScopeLock(this); }
+
     /********************/
 
-    template<typename T, typename... Args>
-    PyObject* gcnew(Type type, Args&&... args){
+    template <typename T, typename... Args>
+    PyObject* gcnew(Type type, Args&&... args) {
         using __T = std::decay_t<T>;
         static_assert(!is_sso_v<__T>, "gcnew cannot be used with SSO types");
         // https://github.com/pocketpy/pocketpy/issues/94#issuecomment-1594784476
-        PyObject* p = new(PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
+        PyObject* p = new (PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
         p->placement_new<__T>(std::forward<Args>(args)...);
         gen.push_back(p);
         gc_counter++;
         return p;
     }
 
-    template<typename T, typename... Args>
-    PyObject* _new(Type type, Args&&... args){
+    template <typename T, typename... Args>
+    PyObject* _new(Type type, Args&&... args) {
         using __T = std::decay_t<T>;
         static_assert(!is_sso_v<__T>);
-        PyObject* p = new(PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
+        PyObject* p = new (PoolObject_alloc(py_sizeof<__T>)) PyObject(type);
         p->placement_new<__T>(std::forward<Args>(args)...);
         _no_gc.push_back(p);
         return p;
@@ -67,9 +65,11 @@ struct ManagedHeap{
 
     int sweep();
     void _auto_collect();
+
     bool _should_auto_collect() const { return gc_counter >= gc_threshold; }
+
     int collect();
     void mark();
 };
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 29 - 22
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
+}  // namespace pkpy

+ 6 - 5
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<std::string_view, vector<_LineRecord>> records;
     stack_no_copy<_FrameRecord> frames;
@@ -35,4 +36,4 @@ struct LineProfiler{
     Str stats();
 };
 
-} // namespace pkpy
+}  // namespace pkpy

+ 256 - 146
include/pocketpy/interpreter/vm.hpp

@@ -11,67 +11,76 @@
 
 #include <stdexcept>
 
-namespace pkpy{
+namespace pkpy {
 
 /* Stack manipulation macros */
 // https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
-#define TOP()             (s_data.top())
-#define SECOND()          (s_data.second())
-#define THIRD()           (s_data.third())
-#define STACK_SHRINK(n)   (s_data.shrink(n))
-#define PUSH(v)           (s_data.push(v))
-#define POP()             (s_data.pop())
-#define POPX()            (s_data.popx())
-#define STACK_VIEW(n)     (s_data.view(n))
+#define TOP() (s_data.top())
+#define SECOND() (s_data.second())
+#define THIRD() (s_data.third())
+#define STACK_SHRINK(n) (s_data.shrink(n))
+#define PUSH(v) (s_data.push(v))
+#define POP() (s_data.pop())
+#define POPX() (s_data.popx())
+#define STACK_VIEW(n) (s_data.view(n))
 
 typedef PyVar (*BinaryFuncC)(VM*, PyVar, PyVar);
 typedef void (*RegisterFunc)(VM*, PyObject*, PyObject*);
 
 #if PK_ENABLE_PROFILER
-struct NextBreakpoint{
+struct NextBreakpoint {
     int callstack_size;
     int lineno;
     bool should_step_into;
-    NextBreakpoint(): callstack_size(0) {}
-    NextBreakpoint(int callstack_size, int lineno, bool should_step_into): callstack_size(callstack_size), lineno(lineno), should_step_into(should_step_into) {}
+
+    NextBreakpoint() : callstack_size(0) {}
+
+    NextBreakpoint(int callstack_size, int lineno, bool should_step_into) :
+        callstack_size(callstack_size), lineno(lineno), should_step_into(should_step_into) {}
+
     void _step(VM* vm);
+
     bool empty() const { return callstack_size == 0; }
 };
 #endif
 
-struct PyTypeInfo{
-    struct Vt{
+struct PyTypeInfo {
+    struct Vt {
         void (*_dtor)(void*);
         void (*_gc_mark)(void*, VM*);
 
-        Vt(): _dtor(nullptr), _gc_mark(nullptr) {}
+        Vt() : _dtor(nullptr), _gc_mark(nullptr) {}
 
-        operator bool() const { return _dtor || _gc_mark; }
+        operator bool () const { return _dtor || _gc_mark; }
 
-        template<typename T>
-        inline static Vt get(){
+        template <typename T>
+        inline static Vt get() {
             static_assert(std::is_same_v<T, std::decay_t<T>>);
             Vt vt;
-            if constexpr(!std::is_trivially_destructible_v<T>){
-                vt._dtor = [](void* p){ ((T*)p)->~T(); };
+            if constexpr(!std::is_trivially_destructible_v<T>) {
+                vt._dtor = [](void* p) {
+                    ((T*)p)->~T();
+                };
             }
-            if constexpr(has_gc_marker<T>::value){
-                vt._gc_mark = [](void* p, VM* vm){ ((T*)p)->_gc_mark(vm); };
+            if constexpr(has_gc_marker<T>::value) {
+                vt._gc_mark = [](void* p, VM* vm) {
+                    ((T*)p)->_gc_mark(vm);
+                };
             }
             return vt;
         }
     };
 
-    PyObject* obj;      // never be garbage collected
+    PyObject* obj;  // never be garbage collected
     Type base;
-    PyObject* mod;      // never be garbage collected
+    PyObject* mod;  // never be garbage collected
     StrName name;
     bool subclass_enabled;
     Vt vt;
 
-    PyTypeInfo(PyObject* obj, Type base, PyObject* mod, StrName name, bool subclass_enabled, Vt vt={}):
+    PyTypeInfo(PyObject* obj, Type base, PyObject* mod, StrName name, bool subclass_enabled, Vt vt = {}) :
         obj(obj), base(base), mod(mod), name(name), subclass_enabled(subclass_enabled), vt(vt) {}
-    
+
     vector<StrName> annotated_fields = {};
 
     // unary operators
@@ -122,52 +131,53 @@ struct PyTypeInfo{
     void (*on_end_subclass)(VM* vm, PyTypeInfo*) = nullptr;
 };
 
-struct ImportContext{
+struct ImportContext {
     PK_ALWAYS_PASS_BY_POINTER(ImportContext)
 
     vector<Str> pending;
-    vector<bool> pending_is_init;   // a.k.a __init__.py
+    vector<bool> pending_is_init;  // a.k.a __init__.py
 
     ImportContext() {}
 
-    struct Temp{
+    struct Temp {
         PK_ALWAYS_PASS_BY_POINTER(Temp)
 
         ImportContext* ctx;
-        Temp(ImportContext* ctx, Str name, bool is_init) : ctx(ctx){
+
+        Temp(ImportContext* ctx, Str name, bool is_init) : ctx(ctx) {
             ctx->pending.push_back(name);
             ctx->pending_is_init.push_back(is_init);
         }
-        ~Temp(){
+
+        ~Temp() {
             ctx->pending.pop_back();
             ctx->pending_is_init.pop_back();
         }
     };
 
-    Temp scope(Str name, bool is_init){
-        return {this, name, is_init};
-    }
+    Temp scope(Str name, bool is_init) { return {this, name, is_init}; }
 };
 
 class VM {
     PK_ALWAYS_PASS_BY_POINTER(VM)
-    
-    VM* vm;     // self reference to simplify code
+
+    VM* vm;  // self reference to simplify code
+
 public:
     ManagedHeap heap;
     ValueStack s_data;
     CallStack callstack;
     vector<PyTypeInfo> _all_types;
-    
-    NameDict _modules;                                 // loaded modules
-    std::map<StrName, Str> _lazy_modules;              // lazy loaded modules
 
-    struct{
+    NameDict _modules;                     // loaded modules
+    std::map<StrName, Str> _lazy_modules;  // lazy loaded modules
+
+    struct {
         PyObject* error;
         stack_no_copy<ArgsView> s_view;
     } __c;
 
-    PyVar StopIteration;        // a special Exception class
+    PyVar StopIteration;  // a special Exception class
     PyObject* builtins;
     PyObject* _main;
 
@@ -191,34 +201,36 @@ public:
 #endif
 
     void (*_ceval_on_step)(VM*, Frame*, Bytecode bc);
-    void(*_stdout)(const char*, int);
-    void(*_stderr)(const char*, int);
+    void (*_stdout)(const char*, int);
+    void (*_stderr)(const char*, int);
     unsigned char* (*_import_handler)(const char*, int*);
     // function<void(const char*, int)> _stdout;
     // function<void(const char*, int)> _stderr;
     // function<unsigned char*(const char*, int*)> _import_handler;
-    
+
     // for quick access
-    static constexpr Type tp_object=Type(1), tp_type=Type(2);
-    static constexpr Type tp_int=Type(kTpIntIndex), tp_float=Type(kTpFloatIndex), tp_bool=Type(5), tp_str=Type(6);
-    static constexpr Type tp_list=Type(7), tp_tuple=Type(8);
-    static constexpr Type tp_slice=Type(9), tp_range=Type(10), tp_module=Type(11);
-    static constexpr Type tp_function=Type(12), tp_native_func=Type(13), tp_bound_method=Type(14);
-    static constexpr Type tp_super=Type(15), tp_exception=Type(16), tp_bytes=Type(17), tp_mappingproxy=Type(18);
-    static constexpr Type tp_dict=Type(19), tp_property=Type(20), tp_star_wrapper=Type(21);
-    static constexpr Type tp_staticmethod=Type(22), tp_classmethod=Type(23);
-    static constexpr Type tp_none_type=Type(24), tp_not_implemented=Type(25), tp_ellipsis=Type(26);
-    static constexpr Type tp_stack_memory=Type(kTpStackMemoryIndex);
-
-    static constexpr PyVar True{const_sso_var(), tp_bool, 1};
-    static constexpr PyVar False{const_sso_var(), tp_bool, 0};
-    static constexpr PyVar None{const_sso_var(), tp_none_type, 0};
-    static constexpr PyVar NotImplemented{const_sso_var(), tp_not_implemented, 0};
-    static constexpr PyVar Ellipsis{const_sso_var(), tp_ellipsis, 0};
+    constexpr static Type tp_object = Type(1), tp_type = Type(2);
+    constexpr static Type tp_int = Type(kTpIntIndex), tp_float = Type(kTpFloatIndex), tp_bool = Type(5),
+                          tp_str = Type(6);
+    constexpr static Type tp_list = Type(7), tp_tuple = Type(8);
+    constexpr static Type tp_slice = Type(9), tp_range = Type(10), tp_module = Type(11);
+    constexpr static Type tp_function = Type(12), tp_native_func = Type(13), tp_bound_method = Type(14);
+    constexpr static Type tp_super = Type(15), tp_exception = Type(16), tp_bytes = Type(17), tp_mappingproxy = Type(18);
+    constexpr static Type tp_dict = Type(19), tp_property = Type(20), tp_star_wrapper = Type(21);
+    constexpr static Type tp_staticmethod = Type(22), tp_classmethod = Type(23);
+    constexpr static Type tp_none_type = Type(24), tp_not_implemented = Type(25), tp_ellipsis = Type(26);
+    constexpr static Type tp_stack_memory = Type(kTpStackMemoryIndex);
+
+    constexpr static PyVar True{const_sso_var(), tp_bool, 1};
+    constexpr static PyVar False{const_sso_var(), tp_bool, 0};
+    constexpr static PyVar None{const_sso_var(), tp_none_type, 0};
+    constexpr static PyVar NotImplemented{const_sso_var(), tp_not_implemented, 0};
+    constexpr static PyVar Ellipsis{const_sso_var(), tp_ellipsis, 0};
 
     const bool enable_os;
-    VM(bool enable_os=true);
+    VM(bool enable_os = true);
 
+    // clang-format off
 #if PK_REGION("Python Equivalents")
     Str py_str(PyVar obj);                              // x -> str(x)
     Str py_repr(PyVar obj);                             // x -> repr(x)
@@ -453,18 +465,19 @@ public:
         vm->s_data.emplace(p->type, p);
     }
 #endif
+    // clang-format on
 
-    template<typename T>
-    Type _find_type_in_cxx_typeid_map(){
+    template <typename T>
+    Type _find_type_in_cxx_typeid_map() {
         auto it = _cxx_typeid_map.find(typeid(T));
-        if(it == _cxx_typeid_map.end()){
-    #if __GNUC__ || __clang__
+        if(it == _cxx_typeid_map.end()) {
+#if __GNUC__ || __clang__
             throw std::runtime_error(__PRETTY_FUNCTION__ + std::string(" failed: T not found"));
-    #elif _MSC_VER
+#elif _MSC_VER
             throw std::runtime_error(__FUNCSIG__ + std::string(" failed: T not found"));
-    #else
+#else
             throw std::runtime_error("_find_type_in_cxx_typeid_map() failed: T not found");
-    #endif
+#endif
         }
         return it->second;
     }
@@ -485,17 +498,35 @@ public:
     void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl_&);
     void __unpack_as_list(ArgsView args, List& list);
     void __unpack_as_dict(ArgsView args, Dict& dict);
-    [[noreturn]] void __raise_exc(bool re_raise=false);
+    [[noreturn]] void __raise_exc(bool re_raise = false);
     [[noreturn]] void __builtin_error(StrName type);
     [[noreturn]] void __builtin_error(StrName type, PyVar arg);
     [[noreturn]] void __builtin_error(StrName type, const Str& msg);
     void __init_builtin_types();
     void __post_init_builtin_types();
-    void __push_varargs(){}
-    void __push_varargs(PyVar _0){ PUSH(_0); }
-    void __push_varargs(PyVar _0, PyVar _1){ PUSH(_0); PUSH(_1); }
-    void __push_varargs(PyVar _0, PyVar _1, PyVar _2){ PUSH(_0); PUSH(_1); PUSH(_2); }
-    void __push_varargs(PyVar _0, PyVar _1, PyVar _2, PyVar _3){ PUSH(_0); PUSH(_1); PUSH(_2); PUSH(_3); }
+
+    void __push_varargs() {}
+
+    void __push_varargs(PyVar _0) { PUSH(_0); }
+
+    void __push_varargs(PyVar _0, PyVar _1) {
+        PUSH(_0);
+        PUSH(_1);
+    }
+
+    void __push_varargs(PyVar _0, PyVar _1, PyVar _2) {
+        PUSH(_0);
+        PUSH(_1);
+        PUSH(_2);
+    }
+
+    void __push_varargs(PyVar _0, PyVar _1, PyVar _2, PyVar _3) {
+        PUSH(_0);
+        PUSH(_1);
+        PUSH(_2);
+        PUSH(_3);
+    }
+
     PyVar __pack_next_retval(unsigned);
     PyVar __minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key);
     bool __py_bool_non_trivial(PyVar);
@@ -504,128 +535,201 @@ public:
     void* __stack_alloc(int size);
 };
 
+template <typename T>
+constexpr inline bool is_immutable_v =
+    is_integral_v<T> || is_floating_point_v<T> || std::is_same_v<T, Str> || std::is_same_v<T, Tuple> ||
+    std::is_same_v<T, Bytes> || std::is_same_v<T, bool> || std::is_same_v<T, Range> || std::is_same_v<T, Slice> ||
+    std::is_pointer_v<T> || std::is_enum_v<T>;
+
+template <typename T>
+constexpr Type _find_type_in_const_cxx_typeid_map() {
+    return Type();
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Str>() {
+    return VM::tp_str;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<List>() {
+    return VM::tp_list;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Tuple>() {
+    return VM::tp_tuple;
+}
 
-template<typename T>
-inline constexpr bool is_immutable_v = is_integral_v<T> || is_floating_point_v<T>
-    || std::is_same_v<T, Str> || std::is_same_v<T, Tuple> || std::is_same_v<T, Bytes> || std::is_same_v<T, bool>
-    || std::is_same_v<T, Range> || std::is_same_v<T, Slice>
-    || std::is_pointer_v<T> || std::is_enum_v<T>;
-
-template<typename T> constexpr Type _find_type_in_const_cxx_typeid_map(){ return Type(); }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Str>(){ return VM::tp_str; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<List>(){ return VM::tp_list; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Tuple>(){ return VM::tp_tuple; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Function>(){ return VM::tp_function; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<NativeFunc>(){ return VM::tp_native_func; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<BoundMethod>(){ return VM::tp_bound_method; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Range>(){ return VM::tp_range; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Slice>(){ return VM::tp_slice; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Exception>(){ return VM::tp_exception; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Bytes>(){ return VM::tp_bytes; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<MappingProxy>(){ return VM::tp_mappingproxy; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Dict>(){ return VM::tp_dict; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<Property>(){ return VM::tp_property; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<StarWrapper>(){ return VM::tp_star_wrapper; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<StaticMethod>(){ return VM::tp_staticmethod; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<ClassMethod>(){ return VM::tp_classmethod; }
-template<> constexpr Type _find_type_in_const_cxx_typeid_map<StackMemory>(){ return VM::tp_stack_memory; }
-
-template<typename __T>
-PyVar py_var(VM* vm, __T&& value){
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Function>() {
+    return VM::tp_function;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<NativeFunc>() {
+    return VM::tp_native_func;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<BoundMethod>() {
+    return VM::tp_bound_method;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Range>() {
+    return VM::tp_range;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Slice>() {
+    return VM::tp_slice;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Exception>() {
+    return VM::tp_exception;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Bytes>() {
+    return VM::tp_bytes;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<MappingProxy>() {
+    return VM::tp_mappingproxy;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Dict>() {
+    return VM::tp_dict;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<Property>() {
+    return VM::tp_property;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<StarWrapper>() {
+    return VM::tp_star_wrapper;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<StaticMethod>() {
+    return VM::tp_staticmethod;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<ClassMethod>() {
+    return VM::tp_classmethod;
+}
+
+template <>
+constexpr Type _find_type_in_const_cxx_typeid_map<StackMemory>() {
+    return VM::tp_stack_memory;
+}
+
+template <typename __T>
+PyVar py_var(VM* vm, __T&& value) {
     using T = std::decay_t<__T>;
 
     static_assert(!std::is_same_v<T, PyVar>, "py_var(VM*, PyVar) is not allowed");
 
-    if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>){
+    if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, std::string> ||
+                 std::is_same_v<T, std::string_view>) {
         // str (shortcuts)
         return VAR(Str(std::forward<__T>(value)));
-    }else if constexpr(std::is_same_v<T, NoReturn>){
+    } else if constexpr(std::is_same_v<T, NoReturn>) {
         // NoneType
         return vm->None;
-    }else if constexpr(std::is_same_v<T, bool>){
+    } else if constexpr(std::is_same_v<T, bool>) {
         // bool
         return value ? vm->True : vm->False;
-    }else if constexpr(is_integral_v<T>){
+    } else if constexpr(is_integral_v<T>) {
         // int
         return PyVar(VM::tp_int, static_cast<i64>(value));
-    }else if constexpr(is_floating_point_v<T>){
+    } else if constexpr(is_floating_point_v<T>) {
         // float
         return PyVar(VM::tp_float, static_cast<f64>(value));
-    }else if constexpr(std::is_pointer_v<T>){
+    } else if constexpr(std::is_pointer_v<T>) {
         return from_void_p(vm, (void*)value);
-    }else{
+    } else {
         constexpr Type const_type = _find_type_in_const_cxx_typeid_map<T>();
-        if constexpr((bool)const_type){
-            if constexpr(is_sso_v<T>) return PyVar(const_type, value);
-            else return vm->heap.gcnew<T>(const_type, std::forward<__T>(value));
-        }else{
+        if constexpr((bool)const_type) {
+            if constexpr(is_sso_v<T>)
+                return PyVar(const_type, value);
+            else
+                return vm->heap.gcnew<T>(const_type, std::forward<__T>(value));
+        } else {
             Type type = vm->_find_type_in_cxx_typeid_map<T>();
-            if constexpr(is_sso_v<T>) return PyVar(type, value);
-            else return vm->heap.gcnew<T>(type, std::forward<__T>(value));
+            if constexpr(is_sso_v<T>)
+                return PyVar(type, value);
+            else
+                return vm->heap.gcnew<T>(type, std::forward<__T>(value));
         }
     }
 }
 
 // fast path for bool if py_var<> cannot be inlined
-inline PyVar py_var(VM* vm, bool value){
-    return value ? vm->True : vm->False;
-}
+inline PyVar py_var(VM* vm, bool value) { return value ? vm->True : vm->False; }
 
-template<typename __T, bool with_check>
+template <typename __T, bool with_check>
 __T _py_cast__internal(VM* vm, PyVar obj) {
     static_assert(!std::is_rvalue_reference_v<__T>, "rvalue reference is not allowed");
     using T = std::decay_t<__T>;
     static_assert(!(is_sso_v<T> && std::is_reference_v<__T>), "SSO types cannot be reference");
 
-    if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, CString>){
+    if constexpr(std::is_same_v<T, const char*> || std::is_same_v<T, CString>) {
         static_assert(!std::is_reference_v<__T>);
         // str (shortcuts)
         if(obj == vm->None) return nullptr;
         if constexpr(with_check) vm->check_type(obj, vm->tp_str);
         return PK_OBJ_GET(Str, obj).c_str();
-    }else if constexpr(std::is_same_v<T, bool>){
+    } else if constexpr(std::is_same_v<T, bool>) {
         static_assert(!std::is_reference_v<__T>);
         // bool
-        if constexpr(with_check){
+        if constexpr(with_check) {
             if(obj == vm->True) return true;
             if(obj == vm->False) return false;
             vm->TypeError("expected 'bool', got " + _type_name(vm, vm->_tp(obj)).escape());
         }
         return obj == vm->True;
-    }else if constexpr(is_integral_v<T>){
+    } else if constexpr(is_integral_v<T>) {
         static_assert(!std::is_reference_v<__T>);
         // int
-        if constexpr(with_check){
+        if constexpr(with_check) {
             if(is_int(obj)) return (T)obj.as<i64>();
             vm->TypeError("expected 'int', got " + _type_name(vm, vm->_tp(obj)).escape());
         }
         return (T)obj.as<i64>();
-    }else if constexpr(is_floating_point_v<T>){
+    } else if constexpr(is_floating_point_v<T>) {
         static_assert(!std::is_reference_v<__T>);
         if(is_float(obj)) return (T)obj.as<f64>();
         if(is_int(obj)) return (T)obj.as<i64>();
         vm->TypeError("expected 'int' or 'float', got " + _type_name(vm, vm->_tp(obj)).escape());
         return 0.0f;
-    }else if constexpr(std::is_enum_v<T>){
+    } else if constexpr(std::is_enum_v<T>) {
         static_assert(!std::is_reference_v<__T>);
         return (__T)_py_cast__internal<i64, with_check>(vm, obj);
-    }else if constexpr(std::is_pointer_v<T>){
+    } else if constexpr(std::is_pointer_v<T>) {
         static_assert(!std::is_reference_v<__T>);
         return to_void_p<T>(vm, obj);
-    }else{
+    } else {
         constexpr Type const_type = _find_type_in_const_cxx_typeid_map<T>();
-        if constexpr((bool)const_type){
-            if constexpr(with_check){
-                if constexpr(std::is_same_v<T, Exception>){
+        if constexpr((bool)const_type) {
+            if constexpr(with_check) {
+                if constexpr(std::is_same_v<T, Exception>) {
                     // Exception is `subclass_enabled`
                     vm->check_compatible_type(obj, const_type);
-                }else{
+                } else {
                     vm->check_type(obj, const_type);
                 }
             }
             return PK_OBJ_GET(T, obj);
-        }else{
-            if constexpr(with_check){
+        } else {
+            if constexpr(with_check) {
                 Type type = vm->_find_type_in_cxx_typeid_map<T>();
                 vm->check_compatible_type(obj, type);
             }
@@ -634,25 +738,31 @@ __T _py_cast__internal(VM* vm, PyVar obj) {
     }
 }
 
-template<typename __T>
-__T  py_cast(VM* vm, PyVar obj) { return _py_cast__internal<__T, true>(vm, obj); }
-template<typename __T>
-__T _py_cast(VM* vm, PyVar obj) { return _py_cast__internal<__T, false>(vm, obj); }
+template <typename __T>
+__T py_cast(VM* vm, PyVar obj) {
+    return _py_cast__internal<__T, true>(vm, obj);
+}
+
+template <typename __T>
+__T _py_cast(VM* vm, PyVar obj) {
+    return _py_cast__internal<__T, false>(vm, obj);
+}
 
-template<typename T>
-PyObject* VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _register, Type base, bool subclass_enabled){
+template <typename T>
+PyObject*
+    VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _register, Type base, bool subclass_enabled) {
     PyObject* type = new_type_object(mod, name, base, subclass_enabled, PyTypeInfo::Vt::get<T>());
     mod->attr().set(name, type);
     _cxx_typeid_map[typeid(T)] = type->as<Type>();
     _register(this, mod, type);
-    if(!type->attr().contains(__new__)){
+    if(!type->attr().contains(__new__)) {
         if constexpr(std::is_default_constructible_v<T>) {
-            bind_func(type, __new__, -1, [](VM* vm, ArgsView args){
+            bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
                 Type cls_t = args[0]->as<Type>();
                 return vm->new_object<T>(cls_t);
             });
-        }else{
-            bind_func(type, __new__, -1, [](VM* vm, ArgsView args){
+        } else {
+            bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
                 vm->NotImplementedError();
                 return vm->None;
             });
@@ -661,9 +771,9 @@ PyObject* VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _reg
     return type;
 }
 
-template<typename T>
-PyObject* VM::register_user_class(PyObject* mod, StrName name, Type base, bool subclass_enabled){
+template <typename T>
+PyObject* VM::register_user_class(PyObject* mod, StrName name, Type base, bool subclass_enabled) {
     return register_user_class<T>(mod, name, &T::_register, base, subclass_enabled);
 }
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 1 - 1
include/pocketpy/modules/array2d.hpp

@@ -6,4 +6,4 @@ namespace pkpy {
 
 void add_module_array2d(VM* vm);
 
-} // namespace pkpy
+}  // namespace pkpy

+ 1 - 1
include/pocketpy/modules/base64.hpp

@@ -6,4 +6,4 @@ namespace pkpy {
 
 void add_module_base64(VM* vm);
 
-} // namespace pkpy
+}  // namespace pkpy

+ 1 - 1
include/pocketpy/modules/csv.hpp

@@ -6,4 +6,4 @@ namespace pkpy {
 
 void add_module_csv(VM* vm);
 
-} // namespace pkpy
+}  // namespace pkpy

+ 2 - 2
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
+}  // namespace pkpy

+ 2 - 2
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
+}  // namespace pkpy

+ 5 - 5
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

+ 131 - 54
include/pocketpy/modules/linalg.hpp

@@ -5,90 +5,167 @@
 
 #include <cmath>
 
-namespace pkpy{
+namespace pkpy {
 
-inline bool isclose(float a, float b){ return std::fabs(a - b) < 1e-4; }
+inline bool isclose(float a, float b) { return std::fabs(a - b) < 1e-4; }
 
-struct Vec2{
+struct Vec2 {
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
     float x, y;
+
     Vec2() : x(0.0f), y(0.0f) {}
+
     Vec2(float x, float y) : x(x), y(y) {}
 
-    Vec2 operator+(const Vec2& v) const { return Vec2(x + v.x, y + v.y); }
-    Vec2 operator-(const Vec2& v) const { return Vec2(x - v.x, y - v.y); }
-    Vec2 operator*(float s) const { return Vec2(x * s, y * s); }
-    Vec2 operator*(const Vec2& v) const { return Vec2(x * v.x, y * v.y); }
-    Vec2 operator/(float s) const { return Vec2(x / s, y / s); }
-    Vec2 operator-() const { return Vec2(-x, -y); }
-    bool operator==(const Vec2& v) const { return isclose(x, v.x) && isclose(y, v.y); }
-    bool operator!=(const Vec2& v) const { return !isclose(x, v.x) || !isclose(y, v.y); }
-    float operator[](int i) const { return (&x)[i]; }
+    Vec2 operator+ (const Vec2& v) const { return Vec2(x + v.x, y + v.y); }
+
+    Vec2 operator- (const Vec2& v) const { return Vec2(x - v.x, y - v.y); }
+
+    Vec2 operator* (float s) const { return Vec2(x * s, y * s); }
+
+    Vec2 operator* (const Vec2& v) const { return Vec2(x * v.x, y * v.y); }
+
+    Vec2 operator/ (float s) const { return Vec2(x / s, y / s); }
+
+    Vec2 operator- () const { return Vec2(-x, -y); }
+
+    bool operator== (const Vec2& v) const { return isclose(x, v.x) && isclose(y, v.y); }
+
+    bool operator!= (const Vec2& v) const { return !isclose(x, v.x) || !isclose(y, v.y); }
+
+    float operator[] (int i) const { return (&x)[i]; }
+
     float dot(const Vec2& v) const { return x * v.x + y * v.y; }
+
     float cross(const Vec2& v) const { return x * v.y - y * v.x; }
+
     float length() const { return sqrtf(x * x + y * y); }
+
     float length_squared() const { return x * x + y * y; }
-    Vec2 normalize() const { float l = length(); return Vec2(x / l, y / l); }
-    Vec2 rotate(float radian) const { float cr = cosf(radian), sr = sinf(radian); return Vec2(x * cr - y * sr, x * sr + y * cr); }
+
+    Vec2 normalize() const {
+        float l = length();
+        return Vec2(x / l, y / l);
+    }
+
+    Vec2 rotate(float radian) const {
+        float cr = cosf(radian), sr = sinf(radian);
+        return Vec2(x * cr - y * sr, x * sr + y * cr);
+    }
 };
 
-struct Vec3{
+struct Vec3 {
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
     float x, y, z;
+
     Vec3() : x(0.0f), y(0.0f), z(0.0f) {}
+
     Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
 
-    Vec3 operator+(const Vec3& v) const { return Vec3(x + v.x, y + v.y, z + v.z); }
-    Vec3 operator-(const Vec3& v) const { return Vec3(x - v.x, y - v.y, z - v.z); }
-    Vec3 operator*(float s) const { return Vec3(x * s, y * s, z * s); }
-    Vec3 operator*(const Vec3& v) const { return Vec3(x * v.x, y * v.y, z * v.z); }
-    Vec3 operator/(float s) const { return Vec3(x / s, y / s, z / s); }
-    Vec3 operator-() const { return Vec3(-x, -y, -z); }
-    bool operator==(const Vec3& v) const { return isclose(x, v.x) && isclose(y, v.y) && isclose(z, v.z); }
-    bool operator!=(const Vec3& v) const { return !isclose(x, v.x) || !isclose(y, v.y) || !isclose(z, v.z); }
-    float operator[](int i) const { return (&x)[i]; }
+    Vec3 operator+ (const Vec3& v) const { return Vec3(x + v.x, y + v.y, z + v.z); }
+
+    Vec3 operator- (const Vec3& v) const { return Vec3(x - v.x, y - v.y, z - v.z); }
+
+    Vec3 operator* (float s) const { return Vec3(x * s, y * s, z * s); }
+
+    Vec3 operator* (const Vec3& v) const { return Vec3(x * v.x, y * v.y, z * v.z); }
+
+    Vec3 operator/ (float s) const { return Vec3(x / s, y / s, z / s); }
+
+    Vec3 operator- () const { return Vec3(-x, -y, -z); }
+
+    bool operator== (const Vec3& v) const { return isclose(x, v.x) && isclose(y, v.y) && isclose(z, v.z); }
+
+    bool operator!= (const Vec3& v) const { return !isclose(x, v.x) || !isclose(y, v.y) || !isclose(z, v.z); }
+
+    float operator[] (int i) const { return (&x)[i]; }
+
     float dot(const Vec3& v) const { return x * v.x + y * v.y + z * v.z; }
+
     Vec3 cross(const Vec3& v) const { return Vec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); }
+
     float length() const { return sqrtf(x * x + y * y + z * z); }
+
     float length_squared() const { return x * x + y * y + z * z; }
-    Vec3 normalize() const { float l = length(); return Vec3(x / l, y / l, z / l); }
+
+    Vec3 normalize() const {
+        float l = length();
+        return Vec3(x / l, y / l, z / l);
+    }
 };
 
-struct Vec4{
+struct Vec4 {
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
     float x, y, z, w;
+
     Vec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {}
+
     Vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
 
-    Vec4 operator+(const Vec4& v) const { return Vec4(x + v.x, y + v.y, z + v.z, w + v.w); }
-    Vec4 operator-(const Vec4& v) const { return Vec4(x - v.x, y - v.y, z - v.z, w - v.w); }
-    Vec4 operator*(float s) const { return Vec4(x * s, y * s, z * s, w * s); }
-    Vec4 operator*(const Vec4& v) const { return Vec4(x * v.x, y * v.y, z * v.z, w * v.w); }
-    Vec4 operator/(float s) const { return Vec4(x / s, y / s, z / s, w / s); }
-    Vec4 operator-() const { return Vec4(-x, -y, -z, -w); }
-    bool operator==(const Vec4& v) const { return isclose(x, v.x) && isclose(y, v.y) && isclose(z, v.z) && isclose(w, v.w); }
-    bool operator!=(const Vec4& v) const { return !isclose(x, v.x) || !isclose(y, v.y) || !isclose(z, v.z) || !isclose(w, v.w); }
-    float operator[](int i) const { return (&x)[i]; }
+    Vec4 operator+ (const Vec4& v) const { return Vec4(x + v.x, y + v.y, z + v.z, w + v.w); }
+
+    Vec4 operator- (const Vec4& v) const { return Vec4(x - v.x, y - v.y, z - v.z, w - v.w); }
+
+    Vec4 operator* (float s) const { return Vec4(x * s, y * s, z * s, w * s); }
+
+    Vec4 operator* (const Vec4& v) const { return Vec4(x * v.x, y * v.y, z * v.z, w * v.w); }
+
+    Vec4 operator/ (float s) const { return Vec4(x / s, y / s, z / s, w / s); }
+
+    Vec4 operator- () const { return Vec4(-x, -y, -z, -w); }
+
+    bool operator== (const Vec4& v) const {
+        return isclose(x, v.x) && isclose(y, v.y) && isclose(z, v.z) && isclose(w, v.w);
+    }
+
+    bool operator!= (const Vec4& v) const {
+        return !isclose(x, v.x) || !isclose(y, v.y) || !isclose(z, v.z) || !isclose(w, v.w);
+    }
+
+    float operator[] (int i) const { return (&x)[i]; }
+
     float dot(const Vec4& v) const { return x * v.x + y * v.y + z * v.z + w * v.w; }
+
     float length() const { return sqrtf(x * x + y * y + z * z + w * w); }
+
     float length_squared() const { return x * x + y * y + z * z + w * w; }
-    Vec4 normalize() const { float l = length(); return Vec4(x / l, y / l, z / l, w / l); }
-    NoReturn normalize_() { float l = length(); x /= l; y /= l; z /= l; w /= l; return {}; }
-    NoReturn copy_(const Vec4& v) { x = v.x; y = v.y; z = v.z; w = v.w; return {}; }
+
+    Vec4 normalize() const {
+        float l = length();
+        return Vec4(x / l, y / l, z / l, w / l);
+    }
+
+    NoReturn normalize_() {
+        float l = length();
+        x /= l;
+        y /= l;
+        z /= l;
+        w /= l;
+        return {};
+    }
+
+    NoReturn copy_(const Vec4& v) {
+        x = v.x;
+        y = v.y;
+        z = v.z;
+        w = v.w;
+        return {};
+    }
 };
 
-struct Mat3x3{
+struct Mat3x3 {
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
     union {
         struct {
-            float        _11, _12, _13;
-            float        _21, _22, _23;
-            float        _31, _32, _33;
+            float _11, _12, _13;
+            float _21, _22, _23;
+            float _31, _32, _33;
         };
+
         float m[3][3];
         float v[9];
     };
@@ -100,14 +177,14 @@ struct Mat3x3{
     static Mat3x3 ones();
     static Mat3x3 identity();
 
-    Mat3x3 operator+(const Mat3x3& other) const;
-    Mat3x3 operator-(const Mat3x3& other) const;
-    Mat3x3 operator*(float scalar) const;
-    Mat3x3 operator/(float scalar) const;
+    Mat3x3 operator+ (const Mat3x3& other) const;
+    Mat3x3 operator- (const Mat3x3& other) const;
+    Mat3x3 operator* (float scalar) const;
+    Mat3x3 operator/ (float scalar) const;
+
+    bool operator== (const Mat3x3& other) const;
+    bool operator!= (const Mat3x3& other) const;
 
-    bool operator==(const Mat3x3& other) const;
-    bool operator!=(const Mat3x3& other) const;
-    
     Mat3x3 matmul(const Mat3x3& other) const;
     Vec3 matmul(const Vec3& other) const;
 
@@ -130,9 +207,9 @@ static_assert(is_pod_v<Vec3>);
 static_assert(is_pod_v<Vec4>);
 static_assert(is_pod_v<Mat3x3>);
 
-template<>
-inline constexpr bool is_sso_v<Vec2> = true;
-template<>
-inline constexpr bool is_sso_v<Vec3> = true;
+template <>
+constexpr inline bool is_sso_v<Vec2> = true;
+template <>
+constexpr inline bool is_sso_v<Vec3> = true;
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 2 - 2
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
+}  // namespace pkpy

+ 2 - 2
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
+}  // namespace pkpy

+ 42 - 33
include/pocketpy/objects/base.hpp

@@ -8,25 +8,31 @@
 #include <cstdlib>
 #include <cstring>
 
-namespace pkpy{
+namespace pkpy {
 
 struct Type {
-	int16_t index;
-	constexpr Type(): index(0) {}
-	explicit constexpr Type(int index): index(index) {}
-	bool operator==(Type other) const { return this->index == other.index; }
-	bool operator!=(Type other) const { return this->index != other.index; }
-    constexpr operator int() const { return index; }
+    int16_t index;
+
+    constexpr Type() : index(0) {}
+
+    explicit constexpr Type(int index) : index(index) {}
+
+    bool operator== (Type other) const { return this->index == other.index; }
+
+    bool operator!= (Type other) const { return this->index != other.index; }
+
+    constexpr operator int () const { return index; }
 };
 
 struct const_sso_var {};
 
-struct PyVar final{
+struct PyVar final {
     Type type;
     bool is_ptr;
     uint8_t flags;
     // 12 bytes SSO
-    int _0; i64 _1;
+    int _0;
+    i64 _1;
 
     // uninitialized
     PyVar() = default;
@@ -36,45 +42,50 @@ struct PyVar final{
 
     /* We must initialize all members to allow == operator to work correctly */
     // constexpr initialized
-    constexpr PyVar(const const_sso_var&, Type type, int value): type(type), is_ptr(false), flags(0), _0(value), _1(0) {}
+    constexpr PyVar(const const_sso_var&, Type type, int value) :
+        type(type), is_ptr(false), flags(0), _0(value), _1(0) {}
+
     // zero initialized
-    constexpr PyVar(std::nullptr_t): type(0), is_ptr(false), flags(0), _0(0), _1(0) {}
+    constexpr PyVar(std::nullptr_t) : type(0), is_ptr(false), flags(0), _0(0), _1(0) {}
+
     // PyObject* initialized (is_sso = false)
-    PyVar(Type type, PyObject* p): type(type), is_ptr(true), flags(0), _0(0), _1(reinterpret_cast<i64>(p)) {}
+    PyVar(Type type, PyObject* p) : type(type), is_ptr(true), flags(0), _0(0), _1(reinterpret_cast<i64>(p)) {}
+
     // SSO initialized (is_sso = true)
-    template<typename T>
-    PyVar(Type type, T value): type(type), is_ptr(false), flags(0), _0(0), _1(0) {
+    template <typename T>
+    PyVar(Type type, T value) : type(type), is_ptr(false), flags(0), _0(0), _1(0) {
         static_assert(sizeof(T) <= 12, "SSO size exceeded");
         as<T>() = value;
     }
 
-    template<typename T>
-    T& as(){
+    template <typename T>
+    T& as() {
         static_assert(!std::is_reference_v<T>);
-        if constexpr(sizeof(T) <= 8){
+        if constexpr(sizeof(T) <= 8) {
             return reinterpret_cast<T&>(_1);
-        }else{
+        } else {
             return reinterpret_cast<T&>(_0);
         }
     }
 
-    explicit operator bool() const { return (bool)type; }
+    explicit operator bool () const { return (bool)type; }
 
-    void set_null() { _qword(0) = 0; _qword(1) = 0; }
+    void set_null() {
+        _qword(0) = 0;
+        _qword(1) = 0;
+    }
 
     i64 _qword(int i) const { return ((const i64*)this)[i]; }
+
     i64& _qword(int i) { return ((i64*)this)[i]; }
 
-    bool operator==(const PyVar& other) const {
-        return _qword(0) == other._qword(0) && _qword(1) == other._qword(1);
-    }
+    bool operator== (const PyVar& other) const { return _qword(0) == other._qword(0) && _qword(1) == other._qword(1); }
 
-    bool operator!=(const PyVar& other) const {
-        return _qword(0) != other._qword(0) || _qword(1) != other._qword(1);
-    }
+    bool operator!= (const PyVar& other) const { return _qword(0) != other._qword(0) || _qword(1) != other._qword(1); }
+
+    bool operator== (std::nullptr_t) const { return !(bool)type; }
 
-    bool operator==(std::nullptr_t) const { return !(bool)type; }
-    bool operator!=(std::nullptr_t) const { return (bool)type; }
+    bool operator!= (std::nullptr_t) const { return (bool)type; }
 
     PyObject* get() const {
         assert(is_ptr);
@@ -88,14 +99,12 @@ struct PyVar final{
 
     i64 hash() const { return _0 + _1; }
 
-    template<typename T>
+    template <typename T>
     obj_get_t<T> obj_get();
 
     // for std::map<>
-    bool operator<(const PyVar& other) const {
-        return memcmp(this, &other, sizeof(PyVar)) < 0;
-    }
+    bool operator< (const PyVar& other) const { return memcmp(this, &other, sizeof(PyVar)) < 0; }
 };
 
 static_assert(sizeof(PyVar) == 16 && is_pod_v<PyVar>);
-}   // namespace pkpy
+}  // namespace pkpy

+ 37 - 20
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<unsigned char>;
 
-struct Super{
+struct Super {
     PyVar first;
     Type second;
+
     Super(PyVar first, Type second) : first(first), second(second) {}
+
     void _gc_mark(VM*) const;
 };
 
@@ -58,16 +69,19 @@ struct Slice {
     PyVar start;
     PyVar stop;
     PyVar step;
+
     Slice(PyVar start, PyVar stop, PyVar step) : start(start), stop(stop), step(step) {}
+
     void _gc_mark(VM*) const;
 };
 
-
-inline const int kTpIntIndex = 3;
-inline const int kTpFloatIndex = 4;
+const inline int kTpIntIndex = 3;
+const inline int kTpFloatIndex = 4;
 
 inline bool is_tagged(PyVar p) noexcept { return !p.is_ptr; }
+
 inline bool is_float(PyVar p) noexcept { return p.type.index == kTpFloatIndex; }
+
 inline bool is_int(PyVar p) noexcept { return p.type.index == kTpIntIndex; }
 
 inline bool is_type(PyVar obj, Type type) {
@@ -80,23 +94,26 @@ inline bool is_type(PyObject* p, Type type) {
     return p->type == type;
 }
 
-struct MappingProxy{
+struct MappingProxy {
     PyObject* obj;
+
     MappingProxy(PyObject* obj) : obj(obj) {}
+
     NameDict& attr() { return obj->attr(); }
+
     void _gc_mark(VM*) const;
 };
 
 StrName _type_name(VM* vm, Type type);
-template<typename T> T to_void_p(VM*, PyVar);
+template <typename T>
+T to_void_p(VM*, PyVar);
 PyVar from_void_p(VM*, void*);
 
-
-template<typename T>
-obj_get_t<T> PyVar::obj_get(){
-    if constexpr(is_sso_v<T>){
+template <typename T>
+obj_get_t<T> PyVar::obj_get() {
+    if constexpr(is_sso_v<T>) {
         return as<T>();
-    }else{
+    } else {
         assert(is_ptr);
         void* v = ((PyObject*)_1)->_value_ptr();
         return *reinterpret_cast<T*>(v);
@@ -119,6 +136,6 @@ obj_get_t<T> PyVar::obj_get(){
 
 #define PY_NULL nullptr
 extern PyVar const PY_OP_CALL;
-extern PyVar const PY_OP_YIELD;
+extern const PyVar PY_OP_YIELD;
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 52 - 50
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<PyVar(VM*, ArgsView)>;
@@ -13,7 +13,7 @@ using NativeFuncC = function<PyVar(VM*, ArgsView)>;
 typedef PyVar (*NativeFuncC)(VM*, ArgsView);
 #endif
 
-enum class BindType{
+enum class BindType {
     DEFAULT,
     STATICMETHOD,
     CLASSMETHOD,
@@ -21,24 +21,23 @@ enum class BindType{
 
 enum NameScope { NAME_LOCAL, NAME_GLOBAL, NAME_GLOBAL_UNKNOWN };
 
-enum Opcode: uint8_t {
-    #define OPCODE(name) OP_##name,
-    #include "pocketpy/opcodes.h"
-    #undef OPCODE
+enum Opcode : uint8_t {
+
+#define OPCODE(name) OP_##name,
+#include "pocketpy/opcodes.h"
+#undef OPCODE
 };
 
-struct Bytecode{
+struct Bytecode {
     uint8_t op;
     uint16_t arg;
 
-    void set_signed_arg(int arg){
+    void set_signed_arg(int arg) {
         assert(arg >= INT16_MIN && arg <= INT16_MAX);
         this->arg = (int16_t)arg;
     }
 
-    bool is_forward_jump() const{
-        return op >= OP_JUMP_FORWARD && op <= OP_LOOP_BREAK;
-    }
+    bool is_forward_jump() const { return op >= OP_JUMP_FORWARD && op <= OP_LOOP_BREAK; }
 };
 
 enum class CodeBlockType {
@@ -49,20 +48,20 @@ enum class CodeBlockType {
     TRY_EXCEPT,
 };
 
-inline const uint8_t BC_NOARG = 0;
-inline const int BC_KEEPLINE = -1;
+const inline uint8_t BC_NOARG = 0;
+const inline int BC_KEEPLINE = -1;
 
 struct CodeBlock {
     CodeBlockType type;
-    int parent;         // parent index in blocks
-    int start;          // start index of this block in codes, inclusive
-    int end;            // end index of this block in codes, exclusive
-    int end2;           // ...
+    int parent;  // parent index in blocks
+    int start;   // start index of this block in codes, inclusive
+    int end;     // end index of this block in codes, exclusive
+    int end2;    // ...
 
-    CodeBlock(CodeBlockType type, int parent, int start):
+    CodeBlock(CodeBlockType type, int parent, int start) :
         type(type), parent(parent), start(start), end(-1), end2(-1) {}
 
-    int get_break_end() const{
+    int get_break_end() const {
         if(end2 != -1) return end2;
         return end;
     }
@@ -74,10 +73,10 @@ using CodeObject_ = std::shared_ptr<CodeObject>;
 using FuncDecl_ = std::shared_ptr<FuncDecl>;
 
 struct CodeObject {
-    struct LineInfo{
-        int lineno;             // line number for each bytecode
-        bool is_virtual;        // whether this bytecode is virtual (not in source code)
-        int iblock;             // block index
+    struct LineInfo {
+        int lineno;       // line number for each bytecode
+        bool is_virtual;  // whether this bytecode is virtual (not in source code)
+        int iblock;       // block index
     };
 
     std::shared_ptr<SourceData> src;
@@ -85,10 +84,10 @@ struct CodeObject {
 
     vector<Bytecode> codes;
     vector<LineInfo> lines;
-    
-    small_vector_2<PyVar, 8> consts;         // constants
-    small_vector_2<StrName, 8> varnames;     // local variables
-    int nlocals;                             // varnames.size()
+
+    small_vector_2<PyVar, 8> consts;      // constants
+    small_vector_2<StrName, 8> varnames;  // local variables
+    int nlocals;                          // varnames.size()
 
     NameDictInt varnames_inv;
     vector<CodeBlock> blocks;
@@ -98,15 +97,13 @@ struct CodeObject {
     int start_line;
     int end_line;
 
-    const CodeBlock& _get_block_codei(int codei) const{
-        return blocks[lines[codei].iblock];
-    }
+    const CodeBlock& _get_block_codei(int codei) const { return blocks[lines[codei].iblock]; }
 
     CodeObject(std::shared_ptr<SourceData> src, const Str& name);
     void _gc_mark(VM*) const;
 };
 
-enum class FuncType{
+enum class FuncType {
     UNSET,
     NORMAL,
     SIMPLE,
@@ -116,63 +113,68 @@ enum class FuncType{
 
 struct FuncDecl {
     struct KwArg {
-        int index;              // index in co->varnames
-        StrName key;            // name of this argument
-        PyVar value;        // default value
+        int index;    // index in co->varnames
+        StrName key;  // name of this argument
+        PyVar value;  // default value
     };
-    CodeObject_ code;           // code object of this function
+
+    CodeObject_ code;  // code object of this function
 
     small_vector_2<int, 6> args;      // indices in co->varnames
     small_vector_2<KwArg, 6> kwargs;  // indices in co->varnames
 
-    int starred_arg = -1;       // index in co->varnames, -1 if no *arg
-    int starred_kwarg = -1;     // index in co->varnames, -1 if no **kwarg
-    bool nested = false;        // whether this function is nested
+    int starred_arg = -1;    // index in co->varnames, -1 if no *arg
+    int starred_kwarg = -1;  // index in co->varnames, -1 if no **kwarg
+    bool nested = false;     // whether this function is nested
 
-    const char* docstring;      // docstring of this function (weak ref)
+    const char* docstring;  // docstring of this function (weak ref)
 
     FuncType type = FuncType::UNSET;
 
     NameDictInt kw_to_index;
 
-    void add_kwarg(int index, StrName key, PyVar value){
+    void add_kwarg(int index, StrName key, PyVar value) {
         kw_to_index.set(key, index);
         kwargs.push_back(KwArg{index, key, value});
     }
-    
+
     void _gc_mark(VM*) const;
 };
 
 struct NativeFunc {
     NativeFuncC f;
-    int argc;           // old style argc-based call
-    FuncDecl_ decl;     // new style decl-based call
+    int argc;        // old style argc-based call
+    FuncDecl_ decl;  // new style decl-based call
     any _userdata;
 
-    NativeFunc(NativeFuncC f, int argc, any userdata={}): f(f), argc(argc), decl(nullptr), _userdata(std::move(userdata)) {}
-    NativeFunc(NativeFuncC f, FuncDecl_ decl, any userdata={}): f(f), argc(-1), decl(decl), _userdata(std::move(userdata)) {}
+    NativeFunc(NativeFuncC f, int argc, any userdata = {}) :
+        f(f), argc(argc), decl(nullptr), _userdata(std::move(userdata)) {}
+
+    NativeFunc(NativeFuncC f, FuncDecl_ decl, any userdata = {}) :
+        f(f), argc(-1), decl(decl), _userdata(std::move(userdata)) {}
 
     PyVar call(VM* vm, ArgsView args) const { return f(vm, args); }
+
     void _gc_mark(VM*) const;
 };
 
-struct Function{
+struct Function {
     FuncDecl_ decl;
     PyObject* _module;  // weak ref
     PyObject* _class;   // weak ref
     NameDict_ _closure;
 
-    explicit Function(FuncDecl_ decl, PyObject* _module, PyObject* _class, NameDict_ _closure):
+    explicit Function(FuncDecl_ decl, PyObject* _module, PyObject* _class, NameDict_ _closure) :
         decl(decl), _module(_module), _class(_class), _closure(_closure) {}
 
     void _gc_mark(VM*) const;
 };
 
-template<typename T>
-T& lambda_get_userdata(PyVar* p){
+template <typename T>
+T& lambda_get_userdata(PyVar* p) {
     static_assert(std::is_same_v<T, std::decay_t<T>>);
     int offset = p[-1] != nullptr ? -1 : -2;
     return p[offset].obj_get<NativeFunc>()._userdata.cast<T>();
 }
 
-} // namespace pkpy
+}  // namespace pkpy

+ 12 - 12
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<typename __Func>
+    template <typename __Func>
     void apply(__Func f) const {
         int i = _head_idx;
-        while(i != -1){
+        while(i != -1) {
             f(_items[i].first, _items[i].second);
             i = _items[i].next;
         }
@@ -63,4 +63,4 @@ struct Dict{
     void _gc_mark(VM*) const;
 };
 
-} // namespace pkpy
+}  // namespace pkpy

+ 20 - 17
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<SourceData> src;
         int lineno;
         const char* cursor;
@@ -39,20 +40,21 @@ struct Exception {
 
         Str snapshot() const { return src->snapshot(lineno, cursor, name); }
 
-        Frame(std::shared_ptr<SourceData> src, int lineno, const char* cursor, std::string_view name):
+        Frame(std::shared_ptr<SourceData> src, int lineno, const char* cursor, std::string_view name) :
             src(src), lineno(lineno), cursor(cursor), name(name) {}
     };
 
     stack<Frame> stacktrace;
-    Exception(StrName type): type(type), is_re(true), _ip_on_error(-1), _code_on_error(nullptr), _self(nullptr) {}
 
-    PyObject* self() const{
+    Exception(StrName type) : type(type), is_re(true), _ip_on_error(-1), _code_on_error(nullptr), _self(nullptr) {}
+
+    PyObject* self() const {
         assert(_self != nullptr);
         return _self;
     }
 
-    template<typename... Args>
-    void st_push(Args&&... args){
+    template <typename... Args>
+    void st_push(Args&&... args) {
         if(stacktrace.size() >= 7) return;
         stacktrace.emplace(std::forward<Args>(args)...);
     }
@@ -60,10 +62,11 @@ struct Exception {
     Str summary() const;
 };
 
-struct TopLevelException: std::exception{
+struct TopLevelException : std::exception {
     VM* vm;
     Exception* ptr;
-    TopLevelException(VM* vm, Exception* ptr): vm(vm), ptr(ptr) {}
+
+    TopLevelException(VM* vm, Exception* ptr) : vm(vm), ptr(ptr) {}
 
     Str summary() const { return ptr->summary(); }
 
@@ -74,4 +77,4 @@ struct TopLevelException: std::exception{
     }
 };
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 15 - 13
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<PyVar>;
 using NameDict_ = std::shared_ptr<NameDict>;
 using NameDictInt = NameDictImpl<int>;
 
 static_assert(sizeof(NameDict) <= 128);
 
-struct PyObject final{
-    bool gc_marked;     // whether this object is marked
-    Type type;          // we have a duplicated type here for convenience
-    NameDict* _attr;    // gc will delete this on destruction
+struct PyObject final {
+    bool gc_marked;   // whether this object is marked
+    Type type;        // we have a duplicated type here for convenience
+    NameDict* _attr;  // gc will delete this on destruction
 
     bool is_attr_valid() const noexcept { return _attr != nullptr; }
+
     void* _value_ptr() noexcept { return (char*)this + 16; }
 
-    template<typename T> T& as() noexcept {
+    template <typename T>
+    T& as() noexcept {
         static_assert(std::is_same_v<T, std::decay_t<T>>);
         return *reinterpret_cast<T*>(_value_ptr());
     }
@@ -35,17 +37,17 @@ struct PyObject final{
 
     PyObject(Type type) : gc_marked(false), type(type), _attr(nullptr) {}
 
-    template<typename T, typename ...Args>
-    void placement_new(Args&&... args){
+    template <typename T, typename... Args>
+    void placement_new(Args&&... args) {
         static_assert(std::is_same_v<T, std::decay_t<T>>);
-        new(_value_ptr()) T(std::forward<Args>(args)...);
+        new (_value_ptr()) T(std::forward<Args>(args)...);
 
         // backdoor for important builtin types
-        if constexpr(std::is_same_v<T, DummyInstance>){
+        if constexpr(std::is_same_v<T, DummyInstance>) {
             _attr = new NameDict();
-        }else if constexpr(std::is_same_v<T, Type>){
+        } else if constexpr(std::is_same_v<T, Type>) {
             _attr = new NameDict(PK_TYPE_ATTR_LOAD_FACTOR);
-        }else if constexpr(std::is_same_v<T, DummyModule>){
+        } else if constexpr(std::is_same_v<T, DummyModule>) {
             _attr = new NameDict(PK_TYPE_ATTR_LOAD_FACTOR);
         }
     }
@@ -53,4 +55,4 @@ struct PyObject final{
 
 static_assert(sizeof(PyObject) <= 16);
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 6 - 12
include/pocketpy/objects/sourcedata.hpp

@@ -3,15 +3,9 @@
 #include "pocketpy/common/utils.hpp"
 #include "pocketpy/common/str.hpp"
 
-namespace pkpy{
-
-enum CompileMode {
-    EXEC_MODE,
-    EVAL_MODE,
-    REPL_MODE,
-    JSON_MODE,
-    CELL_MODE
-};
+namespace pkpy {
+
+enum CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, JSON_MODE, CELL_MODE };
 
 struct SourceData {
     PK_ALWAYS_PASS_BY_POINTER(SourceData)
@@ -24,12 +18,12 @@ struct SourceData {
 
     bool is_precompiled;
     vector<Str> _precompiled_tokens;
-    
+
     SourceData(std::string_view source, const Str& filename, CompileMode mode);
     SourceData(const Str& filename, CompileMode mode);
-    std::pair<const char*,const char*> _get_line(int lineno) const;
+    std::pair<const char*, const char*> _get_line(int lineno) const;
     std::string_view get_line(int lineno) const;
     Str snapshot(int lineno, const char* cursor, std::string_view name) const;
 };
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 7 - 6
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<StackMemory> = true;
+template <>
+constexpr inline bool is_sso_v<StackMemory> = true;
 
-inline const int kTpStackMemoryIndex = 27;
+const inline int kTpStackMemoryIndex = 27;
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 22 - 11
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<PyVar>{
+struct List : public vector<PyVar> {
     using vector<PyVar>::vector;
     void _gc_mark(VM*) const;
 
-    Tuple to_tuple() const{
+    Tuple to_tuple() const {
         Tuple ret(size());
-        for(int i=0; i<size(); i++) ret[i] = (*this)[i];
+        for(int i = 0; i < size(); i++)
+            ret[i] = (*this)[i];
         return ret;
     }
 };
 
 // a lightweight view for function args, it does not own the memory
-struct ArgsView{
+struct ArgsView {
     PyVar* _begin;
     PyVar* _end;
 
     ArgsView(PyVar* begin, PyVar* end) : _begin(begin), _end(end) {}
+
     ArgsView(const Tuple& t) : _begin(t.begin()), _end(t.end()) {}
 
     PyVar* begin() const { return _begin; }
+
     PyVar* end() const { return _end; }
+
     int size() const { return _end - _begin; }
+
     bool empty() const { return _begin == _end; }
-    PyVar operator[](int i) const { return _begin[i]; }
+
+    PyVar operator[] (int i) const { return _begin[i]; }
 
     List to_list() const;
     Tuple to_tuple() const;
 };
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 1 - 1
include/pocketpy/opcodes.h

@@ -153,4 +153,4 @@ OPCODE(INC_FAST)
 OPCODE(DEC_FAST)
 OPCODE(INC_GLOBAL)
 OPCODE(DEC_GLOBAL)
-#endif
+#endif

+ 14 - 14
include/pocketpy/pocketpy.hpp

@@ -9,17 +9,17 @@
 #include "pocketpy/modules/linalg.hpp"
 #include "pocketpy/tools/repl.hpp"
 
-namespace pkpy{
-    static_assert(py_sizeof<Str> <= 64);
-    static_assert(py_sizeof<Mat3x3> <= 64);
-    static_assert(py_sizeof<Struct> <= 64);
-    static_assert(py_sizeof<Tuple> <= 80);
-    static_assert(py_sizeof<List> <= 64);
-    static_assert(py_sizeof<Dict> <= 64);
-    static_assert(py_sizeof<RangeIter> <= 64);
-    static_assert(py_sizeof<RangeIterR> <= 64);
-    static_assert(py_sizeof<ArrayIter> <= 64);
-    static_assert(py_sizeof<StringIter> <= 64);
-    static_assert(py_sizeof<Generator> <= 64);
-    static_assert(py_sizeof<DictItemsIter> <= 64);
-}  // namespace pkpy
+namespace pkpy {
+static_assert(py_sizeof<Str> <= 64);
+static_assert(py_sizeof<Mat3x3> <= 64);
+static_assert(py_sizeof<Struct> <= 64);
+static_assert(py_sizeof<Tuple> <= 80);
+static_assert(py_sizeof<List> <= 64);
+static_assert(py_sizeof<Dict> <= 64);
+static_assert(py_sizeof<RangeIter> <= 64);
+static_assert(py_sizeof<RangeIterR> <= 64);
+static_assert(py_sizeof<ArrayIter> <= 64);
+static_assert(py_sizeof<StringIter> <= 64);
+static_assert(py_sizeof<Generator> <= 64);
+static_assert(py_sizeof<DictItemsIter> <= 64);
+}  // namespace pkpy

+ 89 - 90
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;
-
-/* Basic Functions */
-PK_EXPORT pkpy_vm* pkpy_new_vm(bool enable_os);
-PK_EXPORT void pkpy_delete_vm(pkpy_vm*);
-PK_EXPORT bool pkpy_exec(pkpy_vm*, const char* source);
-PK_EXPORT bool pkpy_exec_2(pkpy_vm*, const char* source, const char* filename, int mode, const char* module);
-PK_EXPORT void pkpy_set_main_argv(pkpy_vm*, int argc, char** argv);
-
-/* Stack Manipulation */
-PK_EXPORT bool pkpy_dup(pkpy_vm*, int i);
-PK_EXPORT bool pkpy_pop(pkpy_vm*, int n);
-PK_EXPORT bool pkpy_pop_top(pkpy_vm*);
-PK_EXPORT bool pkpy_dup_top(pkpy_vm*);
-PK_EXPORT bool pkpy_rot_two(pkpy_vm*);
-PK_EXPORT int pkpy_stack_size(pkpy_vm*);
-
-// int
-PK_EXPORT bool pkpy_push_int(pkpy_vm*, int val);
-PK_EXPORT bool pkpy_is_int(pkpy_vm*, int i);
-PK_EXPORT bool pkpy_to_int(pkpy_vm*, int i, int* out);
-
-// float
-PK_EXPORT bool pkpy_push_float(pkpy_vm*, double val);
-PK_EXPORT bool pkpy_is_float(pkpy_vm*, int i);
-PK_EXPORT bool pkpy_to_float(pkpy_vm*, int i, double* out);
-
-// bool
-PK_EXPORT bool pkpy_push_bool(pkpy_vm*, bool val);
-PK_EXPORT bool pkpy_is_bool(pkpy_vm*, int i);
-PK_EXPORT bool pkpy_to_bool(pkpy_vm*, int i, bool* out);
-
-// string
-PK_EXPORT bool pkpy_push_string(pkpy_vm*, pkpy_CString val);
-PK_EXPORT bool pkpy_is_string(pkpy_vm*, int i);
-PK_EXPORT bool pkpy_to_string(pkpy_vm*, int i, pkpy_CString* out);
-
-// void_p
-PK_EXPORT bool pkpy_push_voidp(pkpy_vm*, void* val);
-PK_EXPORT bool pkpy_is_voidp(pkpy_vm*, int i);
-PK_EXPORT bool pkpy_to_voidp(pkpy_vm*, int i, void** out);
-
-// none
-PK_EXPORT bool pkpy_push_none(pkpy_vm*);
-PK_EXPORT bool pkpy_is_none(pkpy_vm*, int i);
-
-// special push
-PK_EXPORT bool pkpy_push_null(pkpy_vm*);
-PK_EXPORT bool pkpy_push_function(pkpy_vm*, const char* sig, pkpy_CFunction val);
-PK_EXPORT bool pkpy_push_module(pkpy_vm*, const char* name);
-
-// some opt
-PK_EXPORT bool pkpy_getattr(pkpy_vm*, pkpy_CName name);
-PK_EXPORT bool pkpy_setattr(pkpy_vm*, pkpy_CName name);
-PK_EXPORT bool pkpy_getglobal(pkpy_vm*, pkpy_CName name);
-PK_EXPORT bool pkpy_setglobal(pkpy_vm*, pkpy_CName name);
-PK_EXPORT bool pkpy_eval(pkpy_vm*, const char* source);
-PK_EXPORT bool pkpy_unpack_sequence(pkpy_vm*, int size);
-PK_EXPORT bool pkpy_get_unbound_method(pkpy_vm*, pkpy_CName name);
-PK_EXPORT bool pkpy_py_repr(pkpy_vm*);
-PK_EXPORT bool pkpy_py_str(pkpy_vm*);
-PK_EXPORT bool pkpy_py_import(pkpy_vm*, pkpy_CString name);
-
-/* Error Handling */
-PK_EXPORT bool pkpy_error(pkpy_vm*, const char* name, pkpy_CString msg);
-PK_EXPORT bool pkpy_check_error(pkpy_vm*);
-PK_EXPORT bool pkpy_clear_error(pkpy_vm*, char** message);
-
-/* Callables */
-PK_EXPORT bool pkpy_vectorcall(pkpy_vm*, int argc);
-
-/* Special APIs */
-PK_EXPORT void pkpy_free(void* p);
+    typedef struct pkpy_vm_handle pkpy_vm;
+    typedef int (*pkpy_CFunction)(pkpy_vm*);
+    typedef void (*pkpy_COutputHandler)(const char*, int);
+    typedef unsigned char* (*pkpy_CImportHandler)(const char*, int*);
+    typedef int pkpy_CName;
+    typedef int pkpy_CType;
+    typedef const char* pkpy_CString;
+
+    /* Basic Functions */
+    PK_EXPORT pkpy_vm* pkpy_new_vm(bool enable_os);
+    PK_EXPORT void pkpy_delete_vm(pkpy_vm*);
+    PK_EXPORT bool pkpy_exec(pkpy_vm*, const char* source);
+    PK_EXPORT bool pkpy_exec_2(pkpy_vm*, const char* source, const char* filename, int mode, const char* module);
+    PK_EXPORT void pkpy_set_main_argv(pkpy_vm*, int argc, char** argv);
+
+    /* Stack Manipulation */
+    PK_EXPORT bool pkpy_dup(pkpy_vm*, int i);
+    PK_EXPORT bool pkpy_pop(pkpy_vm*, int n);
+    PK_EXPORT bool pkpy_pop_top(pkpy_vm*);
+    PK_EXPORT bool pkpy_dup_top(pkpy_vm*);
+    PK_EXPORT bool pkpy_rot_two(pkpy_vm*);
+    PK_EXPORT int pkpy_stack_size(pkpy_vm*);
+
+    // int
+    PK_EXPORT bool pkpy_push_int(pkpy_vm*, int val);
+    PK_EXPORT bool pkpy_is_int(pkpy_vm*, int i);
+    PK_EXPORT bool pkpy_to_int(pkpy_vm*, int i, int* out);
+
+    // float
+    PK_EXPORT bool pkpy_push_float(pkpy_vm*, double val);
+    PK_EXPORT bool pkpy_is_float(pkpy_vm*, int i);
+    PK_EXPORT bool pkpy_to_float(pkpy_vm*, int i, double* out);
+
+    // bool
+    PK_EXPORT bool pkpy_push_bool(pkpy_vm*, bool val);
+    PK_EXPORT bool pkpy_is_bool(pkpy_vm*, int i);
+    PK_EXPORT bool pkpy_to_bool(pkpy_vm*, int i, bool* out);
+
+    // string
+    PK_EXPORT bool pkpy_push_string(pkpy_vm*, pkpy_CString val);
+    PK_EXPORT bool pkpy_is_string(pkpy_vm*, int i);
+    PK_EXPORT bool pkpy_to_string(pkpy_vm*, int i, pkpy_CString* out);
+
+    // void_p
+    PK_EXPORT bool pkpy_push_voidp(pkpy_vm*, void* val);
+    PK_EXPORT bool pkpy_is_voidp(pkpy_vm*, int i);
+    PK_EXPORT bool pkpy_to_voidp(pkpy_vm*, int i, void** out);
+
+    // none
+    PK_EXPORT bool pkpy_push_none(pkpy_vm*);
+    PK_EXPORT bool pkpy_is_none(pkpy_vm*, int i);
+
+    // special push
+    PK_EXPORT bool pkpy_push_null(pkpy_vm*);
+    PK_EXPORT bool pkpy_push_function(pkpy_vm*, const char* sig, pkpy_CFunction val);
+    PK_EXPORT bool pkpy_push_module(pkpy_vm*, const char* name);
+
+    // some opt
+    PK_EXPORT bool pkpy_getattr(pkpy_vm*, pkpy_CName name);
+    PK_EXPORT bool pkpy_setattr(pkpy_vm*, pkpy_CName name);
+    PK_EXPORT bool pkpy_getglobal(pkpy_vm*, pkpy_CName name);
+    PK_EXPORT bool pkpy_setglobal(pkpy_vm*, pkpy_CName name);
+    PK_EXPORT bool pkpy_eval(pkpy_vm*, const char* source);
+    PK_EXPORT bool pkpy_unpack_sequence(pkpy_vm*, int size);
+    PK_EXPORT bool pkpy_get_unbound_method(pkpy_vm*, pkpy_CName name);
+    PK_EXPORT bool pkpy_py_repr(pkpy_vm*);
+    PK_EXPORT bool pkpy_py_str(pkpy_vm*);
+    PK_EXPORT bool pkpy_py_import(pkpy_vm*, pkpy_CString name);
+
+    /* Error Handling */
+    PK_EXPORT bool pkpy_error(pkpy_vm*, const char* name, pkpy_CString msg);
+    PK_EXPORT bool pkpy_check_error(pkpy_vm*);
+    PK_EXPORT bool pkpy_clear_error(pkpy_vm*, char** message);
+
+    /* Callables */
+    PK_EXPORT bool pkpy_vectorcall(pkpy_vm*, int argc);
+
+    /* Special APIs */
+    PK_EXPORT void pkpy_free(void* p);
 #define pkpy_string(__s) (__s)
-PK_EXPORT pkpy_CName pkpy_name(const char* s);
-PK_EXPORT pkpy_CString pkpy_name_to_string(pkpy_CName name);
-PK_EXPORT void pkpy_set_output_handler(pkpy_vm*, pkpy_COutputHandler handler);
-PK_EXPORT void pkpy_set_import_handler(pkpy_vm*, pkpy_CImportHandler handler);
-
-/* REPL */
-PK_EXPORT void* pkpy_new_repl(pkpy_vm*);
-PK_EXPORT bool pkpy_repl_input(void* r, const char* line);
-PK_EXPORT void pkpy_delete_repl(void* repl);
+    PK_EXPORT pkpy_CName pkpy_name(const char* s);
+    PK_EXPORT pkpy_CString pkpy_name_to_string(pkpy_CName name);
+    PK_EXPORT void pkpy_set_output_handler(pkpy_vm*, pkpy_COutputHandler handler);
+    PK_EXPORT void pkpy_set_import_handler(pkpy_vm*, pkpy_CImportHandler handler);
+
+    /* REPL */
+    PK_EXPORT void* pkpy_new_repl(pkpy_vm*);
+    PK_EXPORT bool pkpy_repl_input(void* r, const char* line);
+    PK_EXPORT void pkpy_delete_repl(void* repl);
 #ifdef __cplusplus
 }
 #endif
 
-
 #endif

+ 4 - 3
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
+}  // namespace pkpy

+ 1 - 1
include/pocketpy_c.h

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

+ 100 - 109
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());
-    }
-
-    // wrapper for builtin functions in Python
-    inline bool hasattr(const handle& obj, const handle& name) {
-        auto& key = _builtin_cast<pkpy::Str>(name);
-        return vm->getattr(obj.ptr(), key, false) != nullptr;
-    }
-
-    inline bool hasattr(const handle& obj, const char* name) {
-        return vm->getattr(obj.ptr(), name, false) != nullptr;
-    }
-
-    inline void delattr(const handle& obj, const handle& name) {
-        auto& key = _builtin_cast<pkpy::Str>(name);
-        vm->delattr(obj.ptr(), key);
-    }
-
-    inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
-
-    inline object getattr(const handle& obj, const handle& name) {
-        auto& key = _builtin_cast<pkpy::Str>(name);
-        return reinterpret_borrow<object>(vm->getattr(obj.ptr(), key));
-    }
-
-    inline object getattr(const handle& obj, const char* name) {
-        return reinterpret_borrow<object>(vm->getattr(obj.ptr(), name));
-    }
-
-    inline object getattr(const handle& obj, const handle& name, const handle& default_) {
-        if(!hasattr(obj, name)) {
-            return reinterpret_borrow<object>(default_);
-        }
-        return getattr(obj, name);
-    }
-
-    inline object getattr(const handle& obj, const char* name, const handle& default_) {
-        if(!hasattr(obj, name)) {
-            return reinterpret_borrow<object>(default_);
-        }
-        return getattr(obj, name);
-    }
-
-    inline void setattr(const handle& obj, const handle& name, const handle& value) {
-        auto& key = _builtin_cast<pkpy::Str>(name);
-        vm->setattr(obj.ptr(), key, value.ptr());
-    }
-
-    inline void setattr(const handle& obj, const char* name, const handle& value) {
-        vm->setattr(obj.ptr(), name, value.ptr());
-    }
-
-    template <typename T>
-    inline bool isinstance(const handle& obj) {
-        pkpy::Type cls = _builtin_cast<pkpy::Type>(type::handle_of<T>().ptr());
-        return vm->isinstance(obj.ptr(), cls);
-    }
-
-    template <>
-    inline bool isinstance<handle>(const handle&) = delete;
-
-    template <>
-    inline bool isinstance<iterable>(const handle& obj) {
-        return hasattr(obj, "__iter__");
-    }
-
-    template <>
-    inline bool isinstance<iterator>(const handle& obj) {
-        return hasattr(obj, "__iter__") && hasattr(obj, "__next__");
-    }
-
-    inline bool isinstance(const handle& obj, const handle& type) {
-        return vm->isinstance(obj.ptr(), _builtin_cast<pkpy::Type>(type));
-    }
-
-    inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
-
-    template <typename T, typename SFINAE = void>
-    struct type_caster;
-
-    template <typename T>
-    handle _cast(T&& value,
-                 return_value_policy policy = return_value_policy::automatic_reference,
-                 handle parent = handle()) {
-        using U = std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>;
-        return type_caster<U>::cast(std::forward<T>(value), policy, parent);
-    }
-
-    template <typename T>
-    object cast(T&& value,
-                return_value_policy policy = return_value_policy::automatic_reference,
-                handle parent = handle()) {
-        return reinterpret_borrow<object>(_cast(std::forward<T>(value), policy, parent));
-    }
-
-    template <typename T>
-    T cast(handle obj, bool convert = false) {
-        using Caster =
-            type_caster<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
-        Caster caster;
-
-        if(caster.load(obj, convert)) {
-            if constexpr(std::is_rvalue_reference_v<T>) {
-                return std::move(caster.value);
-            } else {
-                return caster.value;
-            }
+inline void exec(const char* code, handle global = {}, handle local = {}) {
+    vm->py_exec(code, global.ptr(), local.ptr());
+}
+
+// wrapper for builtin functions in Python
+inline bool hasattr(const handle& obj, const handle& name) {
+    auto& key = _builtin_cast<pkpy::Str>(name);
+    return vm->getattr(obj.ptr(), key, false) != nullptr;
+}
+
+inline bool hasattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name, false) != nullptr; }
+
+inline void delattr(const handle& obj, const handle& name) {
+    auto& key = _builtin_cast<pkpy::Str>(name);
+    vm->delattr(obj.ptr(), key);
+}
+
+inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
+
+inline object getattr(const handle& obj, const handle& name) {
+    auto& key = _builtin_cast<pkpy::Str>(name);
+    return reinterpret_borrow<object>(vm->getattr(obj.ptr(), key));
+}
+
+inline object getattr(const handle& obj, const char* name) {
+    return reinterpret_borrow<object>(vm->getattr(obj.ptr(), name));
+}
+
+inline object getattr(const handle& obj, const handle& name, const handle& default_) {
+    if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
+    return getattr(obj, name);
+}
+
+inline object getattr(const handle& obj, const char* name, const handle& default_) {
+    if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
+    return getattr(obj, name);
+}
+
+inline void setattr(const handle& obj, const handle& name, const handle& value) {
+    auto& key = _builtin_cast<pkpy::Str>(name);
+    vm->setattr(obj.ptr(), key, value.ptr());
+}
+
+inline void setattr(const handle& obj, const char* name, const handle& value) {
+    vm->setattr(obj.ptr(), name, value.ptr());
+}
+
+template <typename T>
+inline bool isinstance(const handle& obj) {
+    pkpy::Type cls = _builtin_cast<pkpy::Type>(type::handle_of<T>().ptr());
+    return vm->isinstance(obj.ptr(), cls);
+}
+
+template <>
+inline bool isinstance<handle>(const handle&) = delete;
+
+template <>
+inline bool isinstance<iterable>(const handle& obj) {
+    return hasattr(obj, "__iter__");
+}
+
+template <>
+inline bool isinstance<iterator>(const handle& obj) {
+    return hasattr(obj, "__iter__") && hasattr(obj, "__next__");
+}
+
+inline bool isinstance(const handle& obj, const handle& type) {
+    return vm->isinstance(obj.ptr(), _builtin_cast<pkpy::Type>(type));
+}
+
+inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
+
+template <typename T, typename SFINAE = void>
+struct type_caster;
+
+template <typename T>
+handle
+    _cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
+    using U = std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>;
+    return type_caster<U>::cast(std::forward<T>(value), policy, parent);
+}
+
+template <typename T>
+object
+    cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
+    return reinterpret_borrow<object>(_cast(std::forward<T>(value), policy, parent));
+}
+
+template <typename T>
+T cast(handle obj, bool convert = false) {
+    using Caster = type_caster<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
+    Caster caster;
+
+    if(caster.load(obj, convert)) {
+        if constexpr(std::is_rvalue_reference_v<T>) {
+            return std::move(caster.value);
+        } else {
+            return caster.value;
         }
-        throw std::runtime_error("Unable to cast Python instance to C++ type");
     }
+    throw std::runtime_error("Unable to cast Python instance to C++ type");
+}
 }  // namespace pybind11

+ 116 - 123
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 <typename T>
-    constexpr inline bool is_string_v =
-        std::is_same_v<T, char*> || std::is_same_v<T, const char*> ||
-        std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
+template <typename T>
+constexpr inline bool is_string_v = std::is_same_v<T, char*> || std::is_same_v<T, const char*> ||
+                                    std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
 
-    template <typename T>
-    constexpr bool is_pyobject_v = std::is_base_of_v<handle, T>;
+template <typename T>
+constexpr bool is_pyobject_v = std::is_base_of_v<handle, T>;
 
-    template <typename T, typename>
-    struct type_caster;
+template <typename T, typename>
+struct type_caster;
 
-    template <>
-    struct type_caster<bool> {
-        bool value;
+template <>
+struct type_caster<bool> {
+    bool value;
 
-        bool load(const handle& src, bool) {
-            if(isinstance<pybind11::bool_>(src)) {
-                value = pkpy::_py_cast<bool>(vm, src.ptr());
-                return true;
-            }
-
-            return false;
+    bool load(const handle& src, bool) {
+        if(isinstance<pybind11::bool_>(src)) {
+            value = pkpy::_py_cast<bool>(vm, src.ptr());
+            return true;
         }
 
-        static handle cast(bool src, return_value_policy, handle) {
-            return src ? vm->True : vm->False;
-        }
-    };
+        return false;
+    }
 
-    template <typename T>
-    struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
-        T value;
+    static handle cast(bool src, return_value_policy, handle) { return src ? vm->True : vm->False; }
+};
 
-        bool load(const handle& src, bool convert) {
-            if(isinstance<pybind11::int_>(src)) {
-                value = pkpy::_py_cast<T>(vm, src.ptr());
-                return true;
-            }
+template <typename T>
+struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
+    T value;
 
-            return false;
+    bool load(const handle& src, bool convert) {
+        if(isinstance<pybind11::int_>(src)) {
+            value = pkpy::_py_cast<T>(vm, src.ptr());
+            return true;
         }
 
-        static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
-    };
+        return false;
+    }
 
-    template <typename T>
-    struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
-        T value;
+    static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
+};
 
-        bool load(const handle& src, bool convert) {
-            if(isinstance<pybind11::float_>(src)) {
-                value = pkpy::_py_cast<T>(vm, src.ptr());
-                return true;
-            }
+template <typename T>
+struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
+    T value;
 
-            if(convert && isinstance<pybind11::int_>(src)) {
-                value = pkpy::_py_cast<int64_t>(vm, src.ptr());
-                return true;
-            }
+    bool load(const handle& src, bool convert) {
+        if(isinstance<pybind11::float_>(src)) {
+            value = pkpy::_py_cast<T>(vm, src.ptr());
+            return true;
+        }
 
-            return false;
+        if(convert && isinstance<pybind11::int_>(src)) {
+            value = pkpy::_py_cast<int64_t>(vm, src.ptr());
+            return true;
         }
 
-        static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
-    };
+        return false;
+    }
 
-    template <typename T>
-    struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
-        T value;
+    static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
+};
 
-        bool load(const handle& src, bool) {
-            if(isinstance<pybind11::str>(src)) {
-                // FIXME: support other kinds of string
-                value = pkpy::_py_cast<std::string>(vm, src.ptr());
-                return true;
-            }
+template <typename T>
+struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
+    T value;
 
-            return false;
+    bool load(const handle& src, bool) {
+        if(isinstance<pybind11::str>(src)) {
+            // FIXME: support other kinds of string
+            value = pkpy::_py_cast<std::string>(vm, src.ptr());
+            return true;
         }
 
-        static handle cast(const std::string& src, return_value_policy, handle) {
-            return pkpy::py_var(vm, src);
-        }
-    };
+        return false;
+    }
 
-    template <typename T>
-    struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
-        T value;
+    static handle cast(const std::string& src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
+};
 
-        bool load(const handle& src, bool) {
-            if(isinstance<T>(src)) {
-                value = reinterpret_borrow<T>(src);
-                return true;
-            }
+template <typename T>
+struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
+    T value;
 
-            return false;
+    bool load(const handle& src, bool) {
+        if(isinstance<T>(src)) {
+            value = reinterpret_borrow<T>(src);
+            return true;
         }
 
-        template <typename U>
-        static handle cast(U&& src, return_value_policy, handle) {
-            return std::forward<U>(src);
-        }
-    };
+        return false;
+    }
 
-    template <typename T, typename>
-    struct type_caster {
-        value_wrapper<T> value;
+    template <typename U>
+    static handle cast(U&& src, return_value_policy, handle) {
+        return std::forward<U>(src);
+    }
+};
 
-        using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
+template <typename T, typename>
+struct type_caster {
+    value_wrapper<T> value;
 
-        bool load(handle src, bool convert) {
-            if(isinstance<underlying_type>(src)) {
-                auto& i = _builtin_cast<instance>(src);
-                value.pointer = &i.cast<underlying_type>();
-                return true;
-            }
+    using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
 
-            return false;
+    bool load(handle src, bool convert) {
+        if(isinstance<underlying_type>(src)) {
+            auto& i = _builtin_cast<instance>(src);
+            value.pointer = &i.cast<underlying_type>();
+            return true;
         }
 
-        template <typename U>
-        static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
-            // TODO: support implicit cast
-            const auto& info = typeid(underlying_type);
-            bool existed = vm->_cxx_typeid_map.find(info) != vm->_cxx_typeid_map.end();
-            if(existed) {
-                auto type = vm->_cxx_typeid_map[info];
-                return instance::create(std::forward<U>(value), type, policy, parent.ptr());
+        return false;
+    }
+
+    template <typename U>
+    static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
+        // TODO: support implicit cast
+        const auto& info = typeid(underlying_type);
+        bool existed = vm->_cxx_typeid_map.find(info) != vm->_cxx_typeid_map.end();
+        if(existed) {
+            auto type = vm->_cxx_typeid_map[info];
+            return instance::create(std::forward<U>(value), type, policy, parent.ptr());
+        }
+        vm->TypeError("type not registered");
+    }
+};
+
+template <typename T>
+struct type_caster<T, std::enable_if_t<std::is_pointer_v<T> || std::is_reference_v<T>>> {
+    using underlying = std::conditional_t<std::is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<T>>;
+
+    struct wrapper {
+        type_caster<underlying> caster;
+
+        operator T () {
+            if constexpr(std::is_pointer_v<T>) {
+                return caster.value.pointer;
+            } else {
+                return caster.value;
             }
-            vm->TypeError("type not registered");
         }
     };
 
-    template <typename T>
-    struct type_caster<T, std::enable_if_t<std::is_pointer_v<T> || std::is_reference_v<T>>> {
-        using underlying = std::conditional_t<std::is_pointer_v<T>,
-                                              std::remove_pointer_t<T>,
-                                              std::remove_reference_t<T>>;
-
-        struct wrapper {
-            type_caster<underlying> caster;
-
-            operator T () {
-                if constexpr(std::is_pointer_v<T>) {
-                    return caster.value.pointer;
-                } else {
-                    return caster.value;
-                }
-            }
-        };
-
-        wrapper value;
+    wrapper value;
 
-        bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
+    bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
 
-        template <typename U>
-        static handle cast(U&& value, return_value_policy policy, const handle& parent) {
-            return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
-        }
-    };
+    template <typename U>
+    static handle cast(U&& value, return_value_policy policy, const handle& parent) {
+        return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
+    }
+};
 }  // namespace pybind11
 

+ 160 - 167
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
-
-    template <typename T, typename... Others>
-    class class_ : public type {
-    public:
-        using type::type;
-
-        template <typename... Args>
-        class_(const handle& scope, const char* name, Args&&... args) :
-            type(vm->new_type_object(scope.ptr(),
-                                     name,
-                                     vm->tp_object,
-                                     false,
-                                     pkpy::PyTypeInfo::Vt::get<instance>()),
-                 true) {
-            pkpy::PyVar mod = scope.ptr();
-            mod->attr().set(name, m_ptr);
-            vm->_cxx_typeid_map[typeid(T)] = _builtin_cast<pkpy::Type>(m_ptr);
-            vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
-                auto cls = _builtin_cast<pkpy::Type>(args[0]);
-                return instance::create<T>(cls);
-            });
-        }
-
-        /// bind constructor
-        template <typename... Args, typename... Extra>
-        class_& def(init<Args...>, const Extra&... extra) {
-            if constexpr(!std::is_constructible_v<T, Args...>) {
-                static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
-            } else {
-                bind_function(
-                    *this,
-                    "__init__",
-                    [](T* self, Args... args) { new (self) T(args...); },
-                    pkpy::BindType::DEFAULT,
-                    extra...);
-                return *this;
-            }
-        }
-
-        /// bind member function
-        template <typename Fn, typename... Extra>
-        class_& def(const char* name, Fn&& f, const Extra&... extra) {
-            using first = std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>;
-            constexpr bool is_first_base_of_v =
-                std::is_reference_v<first> && std::is_base_of_v<T, remove_cvref_t<first>>;
-
-            if constexpr(!is_first_base_of_v) {
-                static_assert(
-                    is_first_base_of_v,
-                    "If you want to bind member function, the first argument must be the base class");
-            } else {
-                bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
-            }
-
+    }
+};
+
+// TODO:
+// 1. inheritance
+// 2. virtual function
+// 3. factory function
+
+template <typename T, typename... Others>
+class class_ : public type {
+public:
+    using type::type;
+
+    template <typename... Args>
+    class_(const handle& scope, const char* name, Args&&... args) :
+        type(vm->new_type_object(scope.ptr(), name, vm->tp_object, false, pkpy::PyTypeInfo::Vt::get<instance>()),
+             true) {
+        pkpy::PyVar mod = scope.ptr();
+        mod->attr().set(name, m_ptr);
+        vm->_cxx_typeid_map[typeid(T)] = _builtin_cast<pkpy::Type>(m_ptr);
+        vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
+            auto cls = _builtin_cast<pkpy::Type>(args[0]);
+            return instance::create<T>(cls);
+        });
+    }
+
+    /// bind constructor
+    template <typename... Args, typename... Extra>
+    class_& def(init<Args...>, const Extra&... extra) {
+        if constexpr(!std::is_constructible_v<T, Args...>) {
+            static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
+        } else {
+            bind_function(
+                *this,
+                "__init__",
+                [](T* self, Args... args) {
+                    new (self) T(args...);
+                },
+                pkpy::BindType::DEFAULT,
+                extra...);
             return *this;
         }
-
-        /// bind operators
-        template <typename Operator, typename... Extras>
-        class_& def(Operator op, const Extras&... extras) {
-            op.execute(*this, extras...);
-            return *this;
+    }
+
+    /// bind member function
+    template <typename Fn, typename... Extra>
+    class_& def(const char* name, Fn&& f, const Extra&... extra) {
+        using first = std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>;
+        constexpr bool is_first_base_of_v = std::is_reference_v<first> && std::is_base_of_v<T, remove_cvref_t<first>>;
+
+        if constexpr(!is_first_base_of_v) {
+            static_assert(is_first_base_of_v,
+                          "If you want to bind member function, the first argument must be the base class");
+        } else {
+            bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
         }
 
-        // TODO: factory function
-
-        /// bind static function
-        template <typename Fn, typename... Extra>
-        class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
-            bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
-            return *this;
+        return *this;
+    }
+
+    /// bind operators
+    template <typename Operator, typename... Extras>
+    class_& def(Operator op, const Extras&... extras) {
+        op.execute(*this, extras...);
+        return *this;
+    }
+
+    // TODO: factory function
+
+    /// bind static function
+    template <typename Fn, typename... Extra>
+    class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
+        bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
+        return *this;
+    }
+
+    template <typename MP, typename... Extras>
+    class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
+        if constexpr(!std::is_member_object_pointer_v<MP>) {
+            static_assert(std::is_member_object_pointer_v<MP>, "def_readwrite only supports pointer to data member");
+        } else {
+            bind_property(*this, name, mp, mp, extras...);
         }
-
-        template <typename MP, typename... Extras>
-        class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
-            if constexpr(!std::is_member_object_pointer_v<MP>) {
-                static_assert(std::is_member_object_pointer_v<MP>,
-                              "def_readwrite only supports pointer to data member");
-            } else {
-                bind_property(*this, name, mp, mp, extras...);
-            }
-            return *this;
+        return *this;
+    }
+
+    template <typename MP, typename... Extras>
+    class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
+        if constexpr(!std::is_member_object_pointer_v<MP>) {
+            static_assert(std::is_member_object_pointer_v<MP>, "def_readonly only supports pointer to data member");
+        } else {
+            bind_property(*this, name, mp, nullptr, extras...);
         }
-
-        template <typename MP, typename... Extras>
-        class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
-            if constexpr(!std::is_member_object_pointer_v<MP>) {
-                static_assert(std::is_member_object_pointer_v<MP>,
-                              "def_readonly only supports pointer to data member");
-            } else {
-                bind_property(*this, name, mp, nullptr, extras...);
-            }
-            return *this;
-        }
-
-        template <typename Getter, typename Setter, typename... Extras>
-        class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
-            bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
-            return *this;
-        }
-
-        template <typename Getter, typename... Extras>
-        class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
-            bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
-            return *this;
-        }
-
-        template <typename Var, typename... Extras>
-        class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) {
-            static_assert(
-                dependent_false<Var>,
-                "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
-            return *this;
-        }
-
-        template <typename Var, typename... Extras>
-        class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) {
-            static_assert(
-                dependent_false<Var>,
-                "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
-            return *this;
-        }
-
-        template <typename Getter, typename Setter, typename... Extras>
-        class_&
-            def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
-            static_assert(
-                dependent_false<Getter>,
-                "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
-            return *this;
-        }
-    };
-
-    template <typename T, typename... Others>
-    class enum_ : public class_<T, Others...> {
-        std::map<const char*, pkpy::PyVar> m_values;
-
-    public:
-        using class_<T, Others...>::class_;
-
-        template <typename... Args>
-        enum_(const handle& scope, const char* name, Args&&... args) :
-            class_<T, Others...>(scope, name, std::forward<Args>(args)...) {}
-
-        enum_& value(const char* name, T value) {
-            handle var = type_caster<T>::cast(value, return_value_policy::copy);
-            this->m_ptr->attr().set(name, var.ptr());
-            m_values[name] = var.ptr();
-            return *this;
-        }
-
-        enum_& export_values() {
-            pkpy::PyVar mod = this->m_ptr->attr("__module__");
-            for(auto& [name, value]: m_values) {
-                mod->attr().set(name, value);
-            }
-            return *this;
+        return *this;
+    }
+
+    template <typename Getter, typename Setter, typename... Extras>
+    class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
+        bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
+        return *this;
+    }
+
+    template <typename Getter, typename... Extras>
+    class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
+        bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
+        return *this;
+    }
+
+    template <typename Var, typename... Extras>
+    class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) {
+        static_assert(
+            dependent_false<Var>,
+            "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
+        return *this;
+    }
+
+    template <typename Var, typename... Extras>
+    class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) {
+        static_assert(
+            dependent_false<Var>,
+            "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
+        return *this;
+    }
+
+    template <typename Getter, typename Setter, typename... Extras>
+    class_& def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
+        static_assert(
+            dependent_false<Getter>,
+            "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
+        return *this;
+    }
+};
+
+template <typename T, typename... Others>
+class enum_ : public class_<T, Others...> {
+    std::map<const char*, pkpy::PyVar> m_values;
+
+public:
+    using class_<T, Others...>::class_;
+
+    template <typename... Args>
+    enum_(const handle& scope, const char* name, Args&&... args) :
+        class_<T, Others...>(scope, name, std::forward<Args>(args)...) {}
+
+    enum_& value(const char* name, T value) {
+        handle var = type_caster<T>::cast(value, return_value_policy::copy);
+        this->m_ptr->attr().set(name, var.ptr());
+        m_values[name] = var.ptr();
+        return *this;
+    }
+
+    enum_& export_values() {
+        pkpy::PyVar mod = this->m_ptr->attr("__module__");
+        for(auto& [name, value]: m_values) {
+            mod->attr().set(name, value);
         }
-    };
+        return *this;
+    }
+};
 }  // namespace pybind11

+ 292 - 317
include/pybind11/internal/cpp_function.h

@@ -5,379 +5,354 @@
 
 namespace pybind11 {
 
-    template <std::size_t Nurse, std::size_t... Patients>
-    struct keep_alive {};
-
-    template <typename T>
-    struct call_guard {
-        static_assert(std::is_default_constructible_v<T>,
-                      "call_guard must be default constructible");
+template <std::size_t Nurse, std::size_t... Patients>
+struct keep_alive {};
+
+template <typename T>
+struct call_guard {
+    static_assert(std::is_default_constructible_v<T>, "call_guard must be default constructible");
+};
+
+// append the overload to the beginning of the overload list
+struct prepend {};
+
+template <typename... Args>
+struct init {};
+
+// TODO: support more customized tags
+// struct kw_only {};
+//
+// struct pos_only {};
+//
+// struct default_arg {};
+//
+// struct arg {
+//    const char* name;
+//    const char* description;
+// };
+//
+// struct default_arg {
+//    const char* name;
+//    const char* description;
+//    const char* value;
+// };
+
+template <typename Fn,
+          typename Extra,
+          typename Args = callable_args_t<std::decay_t<Fn>>,
+          typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
+struct generator;
+
+class function_record {
+    union {
+        void* data;
+        char buffer[16];
     };
 
-    // append the overload to the beginning of the overload list
-    struct prepend {};
-
-    template <typename... Args>
-    struct init {};
-
-    // TODO: support more customized tags
-    // struct kw_only {};
-    //
-    // struct pos_only {};
-    //
-    // struct default_arg {};
-    //
-    // struct arg {
-    //    const char* name;
-    //    const char* description;
-    // };
-    //
-    // struct default_arg {
-    //    const char* name;
-    //    const char* description;
-    //    const char* value;
-    // };
-
-    template <typename Fn,
-              typename Extra,
-              typename Args = callable_args_t<std::decay_t<Fn>>,
-              typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
-    struct generator;
-
-    class function_record {
-        union {
-            void* data;
-            char buffer[16];
-        };
+    // 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);
 
-        // TODO: optimize the function_record size to reduce memory usage
-        const char* name;
-        function_record* next;
-        void (*destructor)(function_record*);
-        return_value_policy policy = return_value_policy::automatic;
-        handle (*wrapper)(function_record&, pkpy::ArgsView, bool convert, handle parent);
-
-        template <typename Fn, typename Extra, typename Args, typename IndexSequence>
-        friend struct generator;
-
-    public:
-        template <typename Fn, typename... Extras>
-        function_record(Fn&& f, const char* name, const Extras&... extras) :
-            name(name), next(nullptr) {
-
-            if constexpr(sizeof(f) <= sizeof(buffer)) {
-                new (buffer) auto(std::forward<Fn>(f));
-                destructor = [](function_record* self) {
-                    reinterpret_cast<Fn*>(self->buffer)->~Fn();
-                };
-            } else {
-                data = new auto(std::forward<Fn>(f));
-                destructor = [](function_record* self) { delete static_cast<Fn*>(self->data); };
-            }
+    template <typename Fn, typename Extra, typename Args, typename IndexSequence>
+    friend struct generator;
+
+public:
+    template <typename Fn, typename... Extras>
+    function_record(Fn&& f, const char* name, const Extras&... extras) : name(name), next(nullptr) {
 
-            using Generator = generator<std::decay_t<Fn>, std::tuple<Extras...>>;
-            Generator::initialize(*this, extras...);
-            wrapper = Generator::generate();
+        if constexpr(sizeof(f) <= sizeof(buffer)) {
+            new (buffer) auto(std::forward<Fn>(f));
+            destructor = [](function_record* self) {
+                reinterpret_cast<Fn*>(self->buffer)->~Fn();
+            };
+        } else {
+            data = new auto(std::forward<Fn>(f));
+            destructor = [](function_record* self) {
+                delete static_cast<Fn*>(self->data);
+            };
         }
 
-        ~function_record() { destructor(this); }
+        using Generator = generator<std::decay_t<Fn>, std::tuple<Extras...>>;
+        Generator::initialize(*this, extras...);
+        wrapper = Generator::generate();
+    }
 
-        template <typename Fn>
-        auto& cast() {
-            if constexpr(sizeof(Fn) <= sizeof(buffer)) {
-                return *reinterpret_cast<Fn*>(buffer);
-            } else {
-                return *static_cast<Fn*>(data);
-            }
-        }
+    ~function_record() { destructor(this); }
 
-        void append(function_record* record) {
-            function_record* p = this;
-            while(p->next != nullptr) {
-                p = p->next;
-            }
-            p->next = record;
+    template <typename Fn>
+    auto& cast() {
+        if constexpr(sizeof(Fn) <= sizeof(buffer)) {
+            return *reinterpret_cast<Fn*>(buffer);
+        } else {
+            return *static_cast<Fn*>(data);
         }
+    }
 
-        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;
-            }
+    void append(function_record* record) {
+        function_record* p = this;
+        while(p->next != nullptr) {
+            p = p->next;
+        }
+        p->next = record;
+    }
 
-            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;
-            }
+    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;
+        }
 
-            vm->TypeError("no matching function found");
+        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;
         }
-    };
 
-    template <typename Fn, std::size_t... Is, typename... Args>
-    handle invoke(Fn&& fn,
-                  std::index_sequence<Is...>,
-                  std::tuple<type_caster<Args>...>& casters,
-                  return_value_policy policy,
-                  handle parent) {
-        using underlying_type = std::decay_t<Fn>;
-        using ret = callable_return_t<underlying_type>;
-
-        // if the return type is void, return None
-        if constexpr(std::is_void_v<ret>) {
-            // resolve the member function pointer
-            if constexpr(std::is_member_function_pointer_v<underlying_type>) {
+        vm->TypeError("no matching function found");
+    }
+};
+
+template <typename Fn, std::size_t... Is, typename... Args>
+handle invoke(Fn&& fn,
+              std::index_sequence<Is...>,
+              std::tuple<type_caster<Args>...>& casters,
+              return_value_policy policy,
+              handle parent) {
+    using underlying_type = std::decay_t<Fn>;
+    using ret = callable_return_t<underlying_type>;
+
+    // if the return type is void, return None
+    if constexpr(std::is_void_v<ret>) {
+        // resolve the member function pointer
+        if constexpr(std::is_member_function_pointer_v<underlying_type>) {
+            [&](class_type_t<underlying_type>& self, auto&... args) {
+                (self.*fn)(args...);
+            }(std::get<Is>(casters).value...);
+        } else {
+            fn(std::get<Is>(casters).value...);
+        }
+        return vm->None;
+    } else {
+        // resolve the member function pointer
+        if constexpr(std::is_member_function_pointer_v<remove_cvref_t<Fn>>) {
+            return type_caster<ret>::cast(
                 [&](class_type_t<underlying_type>& self, auto&... args) {
-                    (self.*fn)(args...);
-                }(std::get<Is>(casters).value...);
-            } else {
-                fn(std::get<Is>(casters).value...);
-            }
-            return vm->None;
+                    return (self.*fn)(args...);
+                }(std::get<Is>(casters).value...),
+                policy,
+                parent);
         } else {
-            // resolve the member function pointer
-            if constexpr(std::is_member_function_pointer_v<remove_cvref_t<Fn>>) {
-                return type_caster<ret>::cast(
-                    [&](class_type_t<underlying_type>& self, auto&... args) {
-                        return (self.*fn)(args...);
-                    }(std::get<Is>(casters).value...),
-                    policy,
-                    parent);
-            } else {
-                return type_caster<ret>::cast(fn(std::get<Is>(casters).value...), policy, parent);
-            }
+            return type_caster<ret>::cast(fn(std::get<Is>(casters).value...), policy, parent);
         }
     }
+}
 
-    template <typename Fn, typename... Args, std::size_t... Is, typename... Extras>
-    struct generator<Fn, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
-        static void initialize(function_record& record, const Extras&... extras) {}
+template <typename Fn, typename... Args, std::size_t... Is, typename... Extras>
+struct generator<Fn, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
+    static void initialize(function_record& record, const Extras&... extras) {}
 
-        static auto generate() {
-            return +[](function_record& self, pkpy::ArgsView view, bool convert, handle parent) {
-                // FIXME:
-                // Temporarily, args and kwargs must be at the end of the arguments list
-                // Named arguments are not supported yet
-                constexpr bool has_args = types_count_v<args, remove_cvref_t<Args>...> != 0;
-                constexpr bool has_kwargs = types_count_v<kwargs, remove_cvref_t<Args>...> != 0;
-                constexpr std::size_t count = sizeof...(Args) - has_args - has_kwargs;
+    static auto generate() {
+        return +[](function_record& self, pkpy::ArgsView view, bool convert, handle parent) {
+            // FIXME:
+            // Temporarily, args and kwargs must be at the end of the arguments list
+            // Named arguments are not supported yet
+            constexpr bool has_args = types_count_v<args, remove_cvref_t<Args>...> != 0;
+            constexpr bool has_kwargs = types_count_v<kwargs, remove_cvref_t<Args>...> != 0;
+            constexpr std::size_t count = sizeof...(Args) - has_args - has_kwargs;
 
-                handle stack[sizeof...(Args)] = {};
+            handle stack[sizeof...(Args)] = {};
 
-                // initialize the stack
+            // initialize the stack
 
-                if(!has_args && (view.size() != count)) {
-                    return handle();
-                }
+            if(!has_args && (view.size() != count)) { return handle(); }
 
-                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];
-                }
+            for(std::size_t i = 0; i < count; ++i) {
+                stack[i] = view[i];
+            }
 
-                // pack the args and kwargs
-                if constexpr(has_args) {
-                    const auto n = view.size() - count;
-                    pkpy::PyVar var = vm->new_object<pkpy::Tuple>(vm->tp_tuple, n);
-                    auto& tuple = var.obj_get<pkpy::Tuple>();
-                    for(std::size_t i = 0; i < n; ++i) {
-                        tuple[i] = view[count + i];
-                    }
-                    stack[count] = var;
+            // pack the args and kwargs
+            if constexpr(has_args) {
+                const auto n = view.size() - count;
+                pkpy::PyVar var = vm->new_object<pkpy::Tuple>(vm->tp_tuple, n);
+                auto& tuple = var.obj_get<pkpy::Tuple>();
+                for(std::size_t i = 0; i < n; ++i) {
+                    tuple[i] = view[count + i];
                 }
+                stack[count] = var;
+            }
 
-                if constexpr(has_kwargs) {
-                    const auto n = vm->s_data._sp - view.end();
-                    pkpy::PyVar var = vm->new_object<pkpy::Dict>(vm->tp_dict);
-                    auto& dict = var.obj_get<pkpy::Dict>();
-
-                    for(std::size_t i = 0; i < n; i += 2) {
-                        pkpy::i64 index = pkpy::_py_cast<pkpy::i64>(vm, view[count + i]);
-                        pkpy::PyVar str =
-                            vm->new_object<pkpy::Str>(vm->tp_str, pkpy::StrName(index).sv());
-                        dict.set(vm, str, view[count + i + 1]);
-                    }
+            if constexpr(has_kwargs) {
+                const auto n = vm->s_data._sp - view.end();
+                pkpy::PyVar var = vm->new_object<pkpy::Dict>(vm->tp_dict);
+                auto& dict = var.obj_get<pkpy::Dict>();
 
-                    stack[count + 1] = var;
+                for(std::size_t i = 0; i < n; i += 2) {
+                    pkpy::i64 index = pkpy::_py_cast<pkpy::i64>(vm, view[count + i]);
+                    pkpy::PyVar str = vm->new_object<pkpy::Str>(vm->tp_str, pkpy::StrName(index).sv());
+                    dict.set(vm, str, view[count + i + 1]);
                 }
 
-                // check if all the arguments are not valid
-                for(std::size_t i = 0; i < sizeof...(Args); ++i) {
-                    if(!stack[i]) {
-                        return handle();
-                    }
-                }
+                stack[count + 1] = var;
+            }
 
-                // ok, all the arguments are valid, call the function
-                std::tuple<type_caster<Args>...> casters;
+            // check if all the arguments are not valid
+            for(std::size_t i = 0; i < sizeof...(Args); ++i) {
+                if(!stack[i]) { return handle(); }
+            }
 
-                // check type compatibility
-                if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
-                    return invoke(self.cast<Fn>(),
-                                  std::index_sequence<Is...>{},
-                                  casters,
-                                  self.policy,
-                                  parent);
-                }
+            // ok, all the arguments are valid, call the function
+            std::tuple<type_caster<Args>...> casters;
 
-                return handle();
-            };
-        }
-    };
+            // check type compatibility
+            if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
+                return invoke(self.cast<Fn>(), std::index_sequence<Is...>{}, casters, self.policy, parent);
+            }
 
-    constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) {
-        auto& record = pkpy::lambda_get_userdata<function_record>(view.begin());
-        return record(view).ptr();
-    };
+            return handle();
+        };
+    }
+};
 
-    class cpp_function : public function {
-    public:
-        template <typename Fn, typename... Extras>
-        cpp_function(Fn&& f, const Extras&... extras) {
-            pkpy::any userdata = function_record(std::forward<Fn>(f), "anonymous", extras...);
-            m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata));
-            inc_ref();
-        }
-    };
+constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) {
+    auto& record = pkpy::lambda_get_userdata<function_record>(view.begin());
+    return record(view).ptr();
+};
 
+class cpp_function : public function {
+public:
     template <typename Fn, typename... Extras>
-    handle bind_function(const handle& obj,
-                         const char* name,
-                         Fn&& fn,
-                         pkpy::BindType type,
-                         const Extras&... extras) {
-        // do not use cpp_function directly to avoid unnecessary reference count change
-        pkpy::PyVar var = obj.ptr();
-        pkpy::PyVar callable = var->attr().try_get(name);
-
-        // if the function is not bound yet, bind it
-        if(!callable) {
-            pkpy::any userdata = function_record(std::forward<Fn>(fn), name, extras...);
-            callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata));
+    cpp_function(Fn&& f, const Extras&... extras) {
+        pkpy::any userdata = function_record(std::forward<Fn>(f), "anonymous", extras...);
+        m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata));
+        inc_ref();
+    }
+};
+
+template <typename Fn, typename... Extras>
+handle bind_function(const handle& obj, const char* name, Fn&& fn, pkpy::BindType type, const Extras&... extras) {
+    // do not use cpp_function directly to avoid unnecessary reference count change
+    pkpy::PyVar var = obj.ptr();
+    pkpy::PyVar callable = var->attr().try_get(name);
+
+    // if the function is not bound yet, bind it
+    if(!callable) {
+        pkpy::any userdata = function_record(std::forward<Fn>(fn), name, extras...);
+        callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata));
+    } else {
+        auto& userdata = callable.obj_get<pkpy::NativeFunc>()._userdata;
+        function_record* record = new function_record(std::forward<Fn>(fn), name, extras...);
+
+        constexpr bool is_prepend = (types_count_v<prepend, Extras...> != 0);
+        if constexpr(is_prepend) {
+            // if prepend is specified, append the new record to the beginning of the list
+            function_record* last = (function_record*)userdata.data;
+            userdata.data = record;
+            record->append(last);
         } else {
-            auto& userdata = callable.obj_get<pkpy::NativeFunc>()._userdata;
-            function_record* record = new function_record(std::forward<Fn>(fn), name, extras...);
-
-            constexpr bool is_prepend = (types_count_v<prepend, Extras...> != 0);
-            if constexpr(is_prepend) {
-                // if prepend is specified, append the new record to the beginning of the list
-                function_record* last = (function_record*)userdata.data;
-                userdata.data = record;
-                record->append(last);
-            } else {
-                // otherwise, append the new record to the end of the list
-                function_record* last = (function_record*)userdata.data;
-                last->append(record);
-            }
+            // otherwise, append the new record to the end of the list
+            function_record* last = (function_record*)userdata.data;
+            last->append(record);
         }
-        return callable;
     }
+    return callable;
+}
+
+template <typename Getter_, typename Setter_, typename... Extras>
+handle
+    bind_property(const handle& obj, const char* name, Getter_&& getter_, Setter_&& setter_, const Extras&... extras) {
+    pkpy::PyVar var = obj.ptr();
+    pkpy::PyVar getter = vm->None;
+    pkpy::PyVar setter = vm->None;
+    using Getter = std::decay_t<Getter_>;
+    using Setter = std::decay_t<Setter_>;
+
+    getter = vm->new_object<pkpy::NativeFunc>(
+        vm->tp_native_func,
+        [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
+            auto& getter = pkpy::lambda_get_userdata<Getter>(view.begin());
+
+            if constexpr(std::is_member_pointer_v<Getter>) {
+                using Self = class_type_t<Getter>;
+                auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
+
+                if constexpr(std::is_member_object_pointer_v<Getter>) {
+                    return type_caster<member_type_t<Getter>>::cast(self.*getter,
+                                                                    return_value_policy::reference_internal,
+                                                                    view[0])
+                        .ptr();
+                } else {
+                    return type_caster<callable_return_t<Getter>>::cast((self.*getter)(),
+                                                                        return_value_policy::reference_internal,
+                                                                        view[0])
+                        .ptr();
+                }
 
-    template <typename Getter_, typename Setter_, typename... Extras>
-    handle bind_property(const handle& obj,
-                         const char* name,
-                         Getter_&& getter_,
-                         Setter_&& setter_,
-                         const Extras&... extras) {
-        pkpy::PyVar var = obj.ptr();
-        pkpy::PyVar getter = vm->None;
-        pkpy::PyVar setter = vm->None;
-        using Getter = std::decay_t<Getter_>;
-        using Setter = std::decay_t<Setter_>;
-
-        getter = vm->new_object<pkpy::NativeFunc>(
-            vm->tp_native_func,
-            [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
-                auto& getter = pkpy::lambda_get_userdata<Getter>(view.begin());
+            } else {
+                using Self = std::tuple_element_t<0, callable_args_t<Getter>>;
+                auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
 
-                if constexpr(std::is_member_pointer_v<Getter>) {
-                    using Self = class_type_t<Getter>;
-                    auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
+                return type_caster<callable_return_t<Getter>>::cast(getter(self),
+                                                                    return_value_policy::reference_internal,
+                                                                    view[0])
+                    .ptr();
+            }
+        },
+        1,
+        std::forward<Getter_>(getter_));
 
-                    if constexpr(std::is_member_object_pointer_v<Getter>) {
-                        return type_caster<member_type_t<Getter>>::cast(
-                                   self.*getter,
-                                   return_value_policy::reference_internal,
-                                   view[0])
-                            .ptr();
-                    } else {
-                        return type_caster<callable_return_t<Getter>>::cast(
-                                   (self.*getter)(),
-                                   return_value_policy::reference_internal,
-                                   view[0])
-                            .ptr();
-                    }
+    if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
+        setter = vm->new_object<pkpy::NativeFunc>(
+            vm->tp_native_func,
+            [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
+                auto& setter = pkpy::lambda_get_userdata<Setter>(view.begin());
 
-                } else {
-                    using Self = std::tuple_element_t<0, callable_args_t<Getter>>;
+                if constexpr(std::is_member_pointer_v<Setter>) {
+                    using Self = class_type_t<Setter>;
                     auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
 
-                    return type_caster<callable_return_t<Getter>>::cast(
-                               getter(self),
-                               return_value_policy::reference_internal,
-                               view[0])
-                        .ptr();
-                }
-            },
-            1,
-            std::forward<Getter_>(getter_));
-
-        if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
-            setter = vm->new_object<pkpy::NativeFunc>(
-                vm->tp_native_func,
-                [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
-                    auto& setter = pkpy::lambda_get_userdata<Setter>(view.begin());
-
-                    if constexpr(std::is_member_pointer_v<Setter>) {
-                        using Self = class_type_t<Setter>;
-                        auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
-
-                        if constexpr(std::is_member_object_pointer_v<Setter>) {
-                            type_caster<member_type_t<Setter>> caster;
-                            if(caster.load(view[1], true)) {
-                                self.*setter = caster.value;
-                                return vm->None;
-                            }
-                        } else {
-                            type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
-                            if(caster.load(view[1], true)) {
-                                (self.*setter)(caster.value);
-                                return vm->None;
-                            }
+                    if constexpr(std::is_member_object_pointer_v<Setter>) {
+                        type_caster<member_type_t<Setter>> caster;
+                        if(caster.load(view[1], true)) {
+                            self.*setter = caster.value;
+                            return vm->None;
                         }
                     } else {
-                        using Self = std::tuple_element_t<0, callable_args_t<Setter>>;
-                        auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
-
                         type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
                         if(caster.load(view[1], true)) {
-                            setter(self, caster.value);
+                            (self.*setter)(caster.value);
                             return vm->None;
                         }
                     }
+                } else {
+                    using Self = std::tuple_element_t<0, callable_args_t<Setter>>;
+                    auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
 
-                    vm->TypeError("invalid argument");
-                },
-                2,
-                std::forward<Setter_>(setter_));
-        }
+                    type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
+                    if(caster.load(view[1], true)) {
+                        setter(self, caster.value);
+                        return vm->None;
+                    }
+                }
 
-        pkpy::PyVar property = vm->new_object<pkpy::Property>(vm->tp_property, getter, setter);
-        var->attr().set(name, property);
-        return property;
+                vm->TypeError("invalid argument");
+            },
+            2,
+            std::forward<Setter_>(setter_));
     }
 
+    pkpy::PyVar property = vm->new_object<pkpy::Property>(vm->tp_property, getter, setter);
+    var->attr().set(name, property);
+    return property;
+}
+
 }  // namespace pybind11

+ 128 - 130
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;
-
-        template <typename T>
-        static type_info& of() {
-            static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
-                          "T must not be a reference type or const type.");
-            static type_info info = {
-                typeid(T).name(),
-                sizeof(T),
-                alignof(T),
-                [](void* ptr) {
-                    ((T*)ptr)->~T();
-                    operator delete (ptr);
-                },
-                [](void* dst, const void* src) { new (dst) T(*(const T*)src); },
-                [](void* dst, void* src) { new (dst) T(std::move(*(T*)src)); },
-                &typeid(T),
-            };
-            return info;
-        }
-    };
-
-    // all registered C++ class will be ensured as instance type.
-    class instance {
-    public:
-        // use to record the type information of C++ class.
-
-    private:
-        enum Flag {
-            None = 0,
-            Own = 1 << 0,  // if the instance is owned by C++ side.
-            Ref = 1 << 1,  // need to mark the parent object.
+struct type_info {
+    const char* name;
+    std::size_t size;
+    std::size_t alignment;
+    void (*destructor)(void*);
+    void (*copy)(void*, const void*);
+    void (*move)(void*, void*);
+    const std::type_info* type;
+
+    template <typename T>
+    static type_info& of() {
+        static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
+                      "T must not be a reference type or const type.");
+        static type_info info = {
+            typeid(T).name(),
+            sizeof(T),
+            alignof(T),
+            [](void* ptr) {
+                ((T*)ptr)->~T();
+                operator delete (ptr);
+            },
+            [](void* dst, const void* src) {
+                new (dst) T(*(const T*)src);
+            },
+            [](void* dst, void* src) {
+                new (dst) T(std::move(*(T*)src));
+            },
+            &typeid(T),
         };
+        return info;
+    }
+};
+
+// all registered C++ class will be ensured as instance type.
+class instance {
+public:
+    // use to record the type information of C++ class.
+
+private:
+    enum Flag {
+        None = 0,
+        Own = 1 << 0,  // if the instance is owned by C++ side.
+        Ref = 1 << 1,  // need to mark the parent object.
+    };
 
-        Flag flag;
-        void* data;
-        const type_info* type;
-        pkpy::PyVar parent;
-        // pkpy::PyVar
-
-    public:
-        instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
-
-        instance(const instance&) = delete;
-
-        instance(instance&& other) noexcept :
-            flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
-            other.flag = Flag::None;
-            other.data = nullptr;
-            other.type = nullptr;
-            other.parent = nullptr;
-        }
-
-        template <typename T>
-        static pkpy::PyVar create(pkpy::Type type) {
-            instance instance;
-            instance.type = &type_info::of<T>();
-            instance.data = operator new (sizeof(T));
-            instance.flag = Flag::Own;
-            return vm->new_object<pybind11::instance>(type, std::move(instance));
+    Flag flag;
+    void* data;
+    const type_info* type;
+    pkpy::PyVar parent;
+    // pkpy::PyVar
+
+public:
+    instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
+
+    instance(const instance&) = delete;
+
+    instance(instance&& other) noexcept : flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
+        other.flag = Flag::None;
+        other.data = nullptr;
+        other.type = nullptr;
+        other.parent = nullptr;
+    }
+
+    template <typename T>
+    static pkpy::PyVar create(pkpy::Type type) {
+        instance instance;
+        instance.type = &type_info::of<T>();
+        instance.data = operator new (sizeof(T));
+        instance.flag = Flag::Own;
+        return vm->new_object<pybind11::instance>(type, std::move(instance));
+    }
+
+    template <typename T>
+    static pkpy::PyVar create(T&& value,
+                              pkpy::Type type,
+                              return_value_policy policy = return_value_policy::automatic_reference,
+                              pkpy::PyVar parent = nullptr) noexcept {
+        using underlying_type = std::remove_cv_t<std::remove_reference_t<T>>;
+
+        // resolve for automatic policy.
+        if(policy == return_value_policy::automatic) {
+            policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
+                     : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
+                                                        : return_value_policy::move;
+        } else if(policy == return_value_policy::automatic_reference) {
+            policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
+                     : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
+                                                        : return_value_policy::move;
         }
 
-        template <typename T>
-        static pkpy::PyVar
-            create(T&& value,
-                   pkpy::Type type,
-                   return_value_policy policy = return_value_policy::automatic_reference,
-                   pkpy::PyVar parent = nullptr) noexcept {
-            using underlying_type = std::remove_cv_t<std::remove_reference_t<T>>;
-
-            // resolve for automatic policy.
-            if(policy == return_value_policy::automatic) {
-                policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
-                         : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
-                                                            : return_value_policy::move;
-            } else if(policy == return_value_policy::automatic_reference) {
-                policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
-                         : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
-                                                            : return_value_policy::move;
+        auto& _value = [&]() -> auto& {
+            /**
+             * note that, pybind11 will ignore the const qualifier.
+             * in fact, try to modify a const value will result in undefined behavior.
+             */
+            if constexpr(std::is_pointer_v<underlying_type>) {
+                return *reinterpret_cast<underlying_type*>(value);
+            } else {
+                return const_cast<underlying_type&>(value);
             }
+        }();
 
-            auto& _value = [&]() -> auto& {
-                /**
-                 * note that, pybind11 will ignore the const qualifier.
-                 * in fact, try to modify a const value will result in undefined behavior.
-                 */
-                if constexpr(std::is_pointer_v<underlying_type>) {
-                    return *reinterpret_cast<underlying_type*>(value);
-                } else {
-                    return const_cast<underlying_type&>(value);
-                }
-            }();
-
-            instance instance;
-            instance.type = &type_info::of<underlying_type>();
-
-            if(policy == return_value_policy::take_ownership) {
-                instance.data = &_value;
-                instance.flag = Flag::Own;
-            } else if(policy == return_value_policy::copy) {
-                instance.data = ::new auto(_value);
-                instance.flag = Flag::Own;
-            } else if(policy == return_value_policy::move) {
-                instance.data = ::new auto(std::move(_value));
-                instance.flag = Flag::Own;
-            } else if(policy == return_value_policy::reference) {
-                instance.data = &_value;
-                instance.flag = Flag::None;
-            } else if(policy == return_value_policy::reference_internal) {
-                instance.data = &_value;
-                instance.flag = Flag::Ref;
-                instance.parent = parent;
-            }
+        instance instance;
+        instance.type = &type_info::of<underlying_type>();
 
-            return vm->new_object<pybind11::instance>(type, std::move(instance));
+        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;
         }
 
-        ~instance() {
-            if(flag & Flag::Own) {
-                type->destructor(data);
-            }
-        }
+        return vm->new_object<pybind11::instance>(type, std::move(instance));
+    }
 
-        void _gc_mark(pkpy::VM* vm) const noexcept {
-            if(parent && (flag & Flag::Ref)) {
-                PK_OBJ_MARK(parent);
-            }
-        }
+    ~instance() {
+        if(flag & Flag::Own) { type->destructor(data); }
+    }
 
-        template <typename T>
-        T& cast() noexcept {
-            return *static_cast<T*>(data);
-        }
-    };
+    void _gc_mark(pkpy::VM* vm) const noexcept {
+        if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
+    }
+
+    template <typename T>
+    T& cast() noexcept {
+        return *static_cast<T*>(data);
+    }
+};
 }  // namespace pybind11

+ 83 - 92
include/pybind11/internal/kernel.h

@@ -2,107 +2,98 @@
 
 #include <pocketpy.h>
 
-namespace pybind11
-{
-    inline pkpy::VM* vm = nullptr;
-    inline std::map<pkpy::PyVar, int*>* _ref_counts_map = nullptr;
+namespace pybind11 {
+inline pkpy::VM* vm = nullptr;
+inline std::map<pkpy::PyVar, int*>* _ref_counts_map = nullptr;
 
-    inline void initialize(bool enable_os = true)
-    {
-        vm = new pkpy::VM(enable_os);
-        _ref_counts_map = new std::map<pkpy::PyVar, int*>();
+inline void initialize(bool enable_os = true) {
+    vm = new pkpy::VM(enable_os);
+    _ref_counts_map = new std::map<pkpy::PyVar, int*>();
 
-        // use to keep alive PyObject, when the object is hold by C++ side.
-        vm->heap._gc_marker_ex = [](pkpy::VM* vm)
-        {
-            for(auto iter = _ref_counts_map->begin(); iter != _ref_counts_map->end();)
-            {
-                auto ref_count = iter->second;
-                if(*ref_count != 0)
-                {
-                    // if ref count is not zero, then mark it.
-                    PK_OBJ_MARK(iter->first);
-                    ++iter;
-                }
-                else
-                {
-                    // if ref count is zero, then delete it.
-                    iter = _ref_counts_map->erase(iter);
-                    delete ref_count;
-                }
+    // use to keep alive PyObject, when the object is hold by C++ side.
+    vm->heap._gc_marker_ex = [](pkpy::VM* vm) {
+        for(auto iter = _ref_counts_map->begin(); iter != _ref_counts_map->end();) {
+            auto ref_count = iter->second;
+            if(*ref_count != 0) {
+                // if ref count is not zero, then mark it.
+                PK_OBJ_MARK(iter->first);
+                ++iter;
+            } else {
+                // if ref count is zero, then delete it.
+                iter = _ref_counts_map->erase(iter);
+                delete ref_count;
             }
-        };
-    }
+        }
+    };
+}
 
-    inline void finalize()
-    {
-        delete _ref_counts_map;
-        delete vm;
-    }
+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,
+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,
+    /**
+     *  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,
+    /**
+     *  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,
+    /**
+     *  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,
+    /**
+     *  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,
+    /**
+     *  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
-    };
+    /**
+     *  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

+ 214 - 218
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;
-
-    template <typename T>
-    T& _builtin_cast(const handle& obj);
-
-    template <typename T>
-    T reinterpret_borrow(const handle& h);
-
-    template <typename T>
-    T reinterpret_steal(const handle& h);
-
-    class handle {
-    protected:
-        pkpy::PyVar m_ptr = nullptr;
-        mutable int* ref_count = nullptr;
-
-    public:
-        handle() = default;
-        handle(const handle& h) = default;
-        handle& operator= (const handle& other) = default;
-
-        handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
-
-        pkpy::PyVar ptr() const { return m_ptr; }
-
-        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;
-                }
+class handle;
+class object;
+class attr_accessor;
+class item_accessor;
+class iterator;
+class str;
+class bytes;
+class iterable;
+class tuple;
+class dict;
+class list;
+class set;
+class function;
+class module;
+class type;
+class bool_;
+class int_;
+class float_;
+class str;
+class bytes;
+
+template <typename T>
+T& _builtin_cast(const handle& obj);
+
+template <typename T>
+T reinterpret_borrow(const handle& h);
+
+template <typename T>
+T reinterpret_steal(const handle& h);
+
+class handle {
+protected:
+    pkpy::PyVar m_ptr = nullptr;
+    mutable int* ref_count = nullptr;
+
+public:
+    handle() = default;
+    handle(const handle& h) = default;
+    handle& operator= (const handle& other) = default;
+
+    handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
+
+    pkpy::PyVar ptr() const { return m_ptr; }
+
+    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;
             }
-            return *this;
+        } else {
+            *ref_count += 1;
         }
+        return *this;
+    }
 
-        const handle& dec_ref() const {
-            assert(m_ptr != nullptr);
-            assert(ref_count != nullptr);
+    const handle& dec_ref() const {
+        assert(m_ptr != nullptr);
+        assert(ref_count != nullptr);
 
-            *ref_count -= 1;
-            try {
-                if(*ref_count == 0) {
-                    _ref_counts_map->erase(m_ptr);
-                    ::delete ref_count;
-                    ref_count = nullptr;
-                }
-            } catch(std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
+        *ref_count -= 1;
+        try {
+            if(*ref_count == 0) {
+                _ref_counts_map->erase(m_ptr);
+                ::delete ref_count;
+                ref_count = nullptr;
+            }
+        } catch(std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
 
-            return *this;
-        }
+        return *this;
+    }
 
-    public:
-        template <typename T>
-        T cast() const;
+public:
+    template <typename T>
+    T cast() const;
 
-        explicit operator bool () const { return m_ptr.operator bool (); }
+    explicit operator bool () const { return m_ptr.operator bool (); }
 
-        bool is(const handle& other) const { return m_ptr == other.m_ptr; }
+    bool is(const handle& other) const { return m_ptr == other.m_ptr; }
 
-        bool is_none() const { return m_ptr == vm->None; }
+    bool is_none() const { return m_ptr == vm->None; }
 
-        bool in(const handle& other) const {
-            return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr));
-        }
+    bool in(const handle& other) const {
+        return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr));
+    }
 
-        bool contains(const handle& other) const {
-            return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), m_ptr, other.m_ptr));
-        }
+    bool contains(const handle& other) const {
+        return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), m_ptr, other.m_ptr));
+    }
 
-        iterator begin() const;
-        iterator end() const;
-
-        str doc() const;
-
-        attr_accessor attr(const char* name) const;
-        attr_accessor attr(const handle& name) const;
-        attr_accessor attr(object&& name) const;
-
-        item_accessor operator[] (int64_t key) const;
-        item_accessor operator[] (const char* key) const;
-        item_accessor operator[] (const handle& key) const;
-        item_accessor operator[] (object&& key) const;
-
-        object operator- () const;
-        object operator~() const;
-
-        template <return_value_policy policy = return_value_policy::automatic, typename... Args>
-        object operator() (Args&&... args) const;
-
-    private:
-        friend object operator+ (const handle& lhs, const handle& rhs);
-        friend object operator- (const handle& lhs, const handle& rhs);
-        friend object operator* (const handle& lhs, const handle& rhs);
-        friend object operator% (const handle& lhs, const handle& rhs);
-        friend object operator/ (const handle& lhs, const handle& rhs);
-        friend object operator| (const handle& lhs, const handle& rhs);
-        friend object operator& (const handle& lhs, const handle& rhs);
-        friend object operator^ (const handle& lhs, const handle& rhs);
-        friend object operator<< (const handle& lhs, const handle& rhs);
-        friend object operator>> (const handle& lhs, const handle& rhs);
-
-        friend object operator+= (const handle& lhs, const handle& rhs);
-        friend object operator-= (const handle& lhs, const handle& rhs);
-        friend object operator*= (const handle& lhs, const handle& rhs);
-        friend object operator/= (const handle& lhs, const handle& rhs);
-        friend object operator%= (const handle& lhs, const handle& rhs);
-        friend object operator|= (const handle& lhs, const handle& rhs);
-        friend object operator&= (const handle& lhs, const handle& rhs);
-        friend object operator^= (const handle& lhs, const handle& rhs);
-        friend object operator<<= (const handle& lhs, const handle& rhs);
-        friend object operator>>= (const handle& lhs, const handle& rhs);
-
-        friend object operator== (const handle& lhs, const handle& rhs);
-        friend object operator!= (const handle& lhs, const handle& rhs);
-        friend object operator< (const handle& lhs, const handle& rhs);
-        friend object operator> (const handle& lhs, const handle& rhs);
-        friend object operator<= (const handle& lhs, const handle& rhs);
-        friend object operator>= (const handle& lhs, const handle& rhs);
-
-        template <typename T>
-        friend T& _builtin_cast(const handle& obj) {
-            // FIXME: 2.0 does not use Py_<T> anymore
-            static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
-            return obj.ptr().obj_get<T>();
-        }
-    };
+    iterator begin() const;
+    iterator end() const;
+
+    str doc() const;
+
+    attr_accessor attr(const char* name) const;
+    attr_accessor attr(const handle& name) const;
+    attr_accessor attr(object&& name) const;
+
+    item_accessor operator[] (int64_t key) const;
+    item_accessor operator[] (const char* key) const;
+    item_accessor operator[] (const handle& key) const;
+    item_accessor operator[] (object&& key) const;
+
+    object operator- () const;
+    object operator~() const;
+
+    template <return_value_policy policy = return_value_policy::automatic, typename... Args>
+    object operator() (Args&&... args) const;
+
+private:
+    friend object operator+ (const handle& lhs, const handle& rhs);
+    friend object operator- (const handle& lhs, const handle& rhs);
+    friend object operator* (const handle& lhs, const handle& rhs);
+    friend object operator% (const handle& lhs, const handle& rhs);
+    friend object operator/ (const handle& lhs, const handle& rhs);
+    friend object operator| (const handle& lhs, const handle& rhs);
+    friend object operator& (const handle& lhs, const handle& rhs);
+    friend object operator^ (const handle& lhs, const handle& rhs);
+    friend object operator<< (const handle& lhs, const handle& rhs);
+    friend object operator>> (const handle& lhs, const handle& rhs);
+
+    friend object operator+= (const handle& lhs, const handle& rhs);
+    friend object operator-= (const handle& lhs, const handle& rhs);
+    friend object operator*= (const handle& lhs, const handle& rhs);
+    friend object operator/= (const handle& lhs, const handle& rhs);
+    friend object operator%= (const handle& lhs, const handle& rhs);
+    friend object operator|= (const handle& lhs, const handle& rhs);
+    friend object operator&= (const handle& lhs, const handle& rhs);
+    friend object operator^= (const handle& lhs, const handle& rhs);
+    friend object operator<<= (const handle& lhs, const handle& rhs);
+    friend object operator>>= (const handle& lhs, const handle& rhs);
+
+    friend object operator== (const handle& lhs, const handle& rhs);
+    friend object operator!= (const handle& lhs, const handle& rhs);
+    friend object operator< (const handle& lhs, const handle& rhs);
+    friend object operator> (const handle& lhs, const handle& rhs);
+    friend object operator<= (const handle& lhs, const handle& rhs);
+    friend object operator>= (const handle& lhs, const handle& rhs);
 
-    static_assert(std::is_trivially_copyable_v<handle>);
+    template <typename T>
+    friend T& _builtin_cast(const handle& obj) {
+        // FIXME: 2.0 does not use Py_<T> anymore
+        static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
+        return obj.ptr().obj_get<T>();
+    }
+};
 
-    class object : public handle {
-    public:
-        object(const object& other) : handle(other) { inc_ref(); }
+static_assert(std::is_trivially_copyable_v<handle>);
 
-        object(object&& other) noexcept : handle(other) {
-            other.m_ptr = nullptr;
-            other.ref_count = nullptr;
-        }
+class object : public handle {
+public:
+    object(const object& other) : handle(other) { inc_ref(); }
 
-        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(object&& other) noexcept : handle(other) {
+        other.m_ptr = nullptr;
+        other.ref_count = nullptr;
+    }
 
-        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& operator= (const object& other) {
+        if(this != &other) {
+            dec_ref();
+            m_ptr = other.m_ptr;
+            ref_count = other.ref_count;
+            inc_ref();
         }
+        return *this;
+    }
 
-        ~object() {
-            if(m_ptr != nullptr) {
-                dec_ref();
-            }
+    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;
+    }
 
-    protected:
-        object(const handle& h, bool borrow) : handle(h) {
-            if(borrow) {
-                inc_ref();
-            }
-        }
+    ~object() {
+        if(m_ptr != nullptr) { dec_ref(); }
+    }
 
-        template <typename T>
-        friend T reinterpret_borrow(const handle& h) {
-            return {h, true};
-        }
+protected:
+    object(const handle& h, bool borrow) : handle(h) {
+        if(borrow) { inc_ref(); }
+    }
 
-        template <typename T>
-        friend T reinterpret_steal(const handle& h) {
-            return {h, false};
-        }
-    };
+    template <typename T>
+    friend T reinterpret_borrow(const handle& h) {
+        return {h, true};
+    }
+
+    template <typename T>
+    friend T reinterpret_steal(const handle& h) {
+        return {h, false};
+    }
+};
 
-    inline void setattr(const handle& obj, const handle& name, const handle& value);
-    inline void setitem(const handle& obj, const handle& key, const handle& value);
+inline void setattr(const handle& obj, const handle& name, const handle& value);
+inline void setitem(const handle& obj, const handle& key, const handle& value);
 
-#define PYBIND11_BINARY_OPERATOR(OP, NAME)                                                         \
-    inline object operator OP (const handle& lhs, const handle& rhs) {                             \
-        return reinterpret_borrow<object>(vm->call(vm->py_op(NAME), lhs.m_ptr, rhs.m_ptr));        \
+#define PYBIND11_BINARY_OPERATOR(OP, NAME)                                                                             \
+    inline object operator OP (const handle& lhs, const handle& rhs) {                                                 \
+        return reinterpret_borrow<object>(vm->call(vm->py_op(NAME), lhs.m_ptr, rhs.m_ptr));                            \
     }
 
-    PYBIND11_BINARY_OPERATOR(+, "add");
-    PYBIND11_BINARY_OPERATOR(-, "sub");
-    PYBIND11_BINARY_OPERATOR(*, "mul");
-    PYBIND11_BINARY_OPERATOR(/, "truediv");
-    PYBIND11_BINARY_OPERATOR(%, "mod");
-    PYBIND11_BINARY_OPERATOR(|, "or_");
-    PYBIND11_BINARY_OPERATOR(&, "and_");
-    PYBIND11_BINARY_OPERATOR(^, "xor");
-    PYBIND11_BINARY_OPERATOR(<<, "lshift");
-    PYBIND11_BINARY_OPERATOR(>>, "rshift");
-
-    PYBIND11_BINARY_OPERATOR(+=, "iadd");
-    PYBIND11_BINARY_OPERATOR(-=, "isub");
-    PYBIND11_BINARY_OPERATOR(*=, "imul");
-    PYBIND11_BINARY_OPERATOR(/=, "itruediv");
-    PYBIND11_BINARY_OPERATOR(%=, "imod");
-    PYBIND11_BINARY_OPERATOR(|=, "ior");
-    PYBIND11_BINARY_OPERATOR(&=, "iand");
-    PYBIND11_BINARY_OPERATOR(^=, "ixor");
-    PYBIND11_BINARY_OPERATOR(<<=, "ilshift");
-    PYBIND11_BINARY_OPERATOR(>>=, "irshift");
-
-    PYBIND11_BINARY_OPERATOR(==, "eq");
-    PYBIND11_BINARY_OPERATOR(!=, "ne");
-    PYBIND11_BINARY_OPERATOR(<, "lt");
-    PYBIND11_BINARY_OPERATOR(>, "gt");
-    PYBIND11_BINARY_OPERATOR(<=, "le");
-    PYBIND11_BINARY_OPERATOR(>=, "ge");
+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
 

+ 115 - 115
include/pybind11/internal/type_traits.h

@@ -4,154 +4,154 @@
 #include <type_traits>
 
 namespace pybind11 {
-    template <typename T>
-    constexpr bool dependent_false = false;
-
-    template <typename T, typename Tuple>
-    struct tuple_push_front;
-
-    template <typename T, typename... Ts>
-    struct tuple_push_front<T, std::tuple<Ts...>> {
-        using type = std::tuple<T, Ts...>;
-    };
-
-    template <typename T, typename Tuple>
-    using tuple_push_front_t = typename tuple_push_front<T, Tuple>::type;
-
-    // traits for function types
-    template <typename Fn>
-    struct function_traits {
-        static_assert(dependent_false<Fn>, "unsupported function type");
-    };
-
-#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers)                                            \
-    template <typename R, typename... Args>                                                        \
-    struct function_traits<R(Args...) qualifiers> {                                                \
-        using return_type = R;                                                                     \
-        using args_type = std::tuple<Args...>;                                                     \
-        constexpr static std::size_t args_count = sizeof...(Args);                                 \
+template <typename T>
+constexpr bool dependent_false = false;
+
+template <typename T, typename Tuple>
+struct tuple_push_front;
+
+template <typename T, typename... Ts>
+struct tuple_push_front<T, std::tuple<Ts...>> {
+    using type = std::tuple<T, Ts...>;
+};
+
+template <typename T, typename Tuple>
+using tuple_push_front_t = typename tuple_push_front<T, Tuple>::type;
+
+// traits for function types
+template <typename Fn>
+struct function_traits {
+    static_assert(dependent_false<Fn>, "unsupported function type");
+};
+
+#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers)                                                                \
+    template <typename R, typename... Args>                                                                            \
+    struct function_traits<R(Args...) qualifiers> {                                                                    \
+        using return_type = R;                                                                                         \
+        using args_type = std::tuple<Args...>;                                                                         \
+        constexpr static std::size_t args_count = sizeof...(Args);                                                     \
     };
 
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE()
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE(&)
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const)
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const&)
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE(noexcept)
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE(& noexcept)
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const noexcept)
-    PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const& noexcept)
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE()
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE(&)
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const)
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const&)
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE(noexcept)
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE(& noexcept)
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const noexcept)
+PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const& noexcept)
 
 #undef PYBIND11_FUNCTION_TRAITS_SPECIALIZE
 
-    template <typename T>
-    using function_return_t = typename function_traits<T>::return_type;
+template <typename T>
+using function_return_t = typename function_traits<T>::return_type;
 
-    template <typename T>
-    using function_args_t = typename function_traits<T>::args_type;
+template <typename T>
+using function_args_t = typename function_traits<T>::args_type;
 
-    template <typename T>
-    constexpr std::size_t function_args_count = function_traits<T>::args_count;
+template <typename T>
+constexpr std::size_t function_args_count = function_traits<T>::args_count;
 
-    // traits for member pointers
-    template <typename T>
-    struct member_traits;
+// traits for member pointers
+template <typename T>
+struct member_traits;
 
-    template <typename M, typename C>
-    struct member_traits<M C::*> {
-        using member_type = M;
-        using class_type = C;
-    };
+template <typename M, typename C>
+struct member_traits<M C::*> {
+    using member_type = M;
+    using class_type = C;
+};
 
-    template <typename T>
-    using member_type_t = typename member_traits<T>::member_type;
+template <typename T>
+using member_type_t = typename member_traits<T>::member_type;
 
-    template <typename T>
-    using class_type_t = typename member_traits<T>::class_type;
+template <typename T>
+using class_type_t = typename member_traits<T>::class_type;
 
-    // some traits for distinguishing between function pointers, member function pointers and
-    // functors
-    using std::is_member_function_pointer_v;
-    using std::is_member_object_pointer_v;
+// some traits for distinguishing between function pointers, member function pointers and
+// functors
+using std::is_member_function_pointer_v;
+using std::is_member_object_pointer_v;
 
-    template <typename T>
-    constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
+template <typename T>
+constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
 
-    template <typename T, typename U = void>
-    constexpr bool is_functor_v = false;
+template <typename T, typename U = void>
+constexpr bool is_functor_v = false;
 
-    template <typename T>
-    constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
+template <typename T>
+constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
 
-    template <typename T, typename SFINAE = void>
-    struct callable_traits;
+template <typename T, typename SFINAE = void>
+struct callable_traits;
 
-    template <typename T>
-    struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
-        using args_type = tuple_push_front_t<class_type_t<T>&, function_args_t<member_type_t<T>>>;
-        using return_type = function_return_t<member_type_t<T>>;
-    };
+template <typename T>
+struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
+    using args_type = tuple_push_front_t<class_type_t<T>&, function_args_t<member_type_t<T>>>;
+    using return_type = function_return_t<member_type_t<T>>;
+};
 
-    template <typename T>
-    struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
-        using args_type = function_args_t<std::remove_pointer<T>>;
-        using return_type = function_return_t<std::remove_pointer<T>>;
-    };
+template <typename T>
+struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
+    using args_type = function_args_t<std::remove_pointer<T>>;
+    using return_type = function_return_t<std::remove_pointer<T>>;
+};
 
-    template <typename T>
-    struct callable_traits<T, std::enable_if_t<is_functor_v<T>>> {
-        using args_type = function_args_t<member_type_t<decltype(&T::operator())>>;
-        using return_type = function_return_t<member_type_t<decltype(&T::operator())>>;
-    };
+template <typename T>
+struct callable_traits<T, std::enable_if_t<is_functor_v<T>>> {
+    using args_type = function_args_t<member_type_t<decltype(&T::operator())>>;
+    using return_type = function_return_t<member_type_t<decltype(&T::operator())>>;
+};
 
-    template <typename Callable>
-    using callable_args_t = typename callable_traits<Callable>::args_type;
+template <typename Callable>
+using callable_args_t = typename callable_traits<Callable>::args_type;
 
-    template <typename Callable>
-    using callable_return_t = typename callable_traits<Callable>::return_type;
+template <typename Callable>
+using callable_return_t = typename callable_traits<Callable>::return_type;
 
-    template <typename Callable>
-    constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
+template <typename Callable>
+constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
 
-    template <typename T>
-    struct type_identity {
-        using type = T;
-    };
+template <typename T>
+struct type_identity {
+    using type = T;
+};
 
-    template <typename T>
-    using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
+template <typename T>
+using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
 
-    template <typename T, typename... Ts>
-    constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
+template <typename T, typename... Ts>
+constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
 
-    template <typename T>
-    constexpr inline std::size_t types_count_v<T> = 0;
+template <typename T>
+constexpr inline std::size_t types_count_v<T> = 0;
 
-    template <typename T>
-    struct value_wrapper {
-        T* pointer;
+template <typename T>
+struct value_wrapper {
+    T* pointer;
 
-        operator T& () { return *pointer; }
-    };
+    operator T& () { return *pointer; }
+};
 
-    template <typename T>
-    struct value_wrapper<T*> {
-        T* pointer;
+template <typename T>
+struct value_wrapper<T*> {
+    T* pointer;
 
-        operator T* () { return pointer; }
-    };
+    operator T* () { return pointer; }
+};
 
-    template <typename T>
-    struct value_wrapper<T&> {
-        T* pointer;
+template <typename T>
+struct value_wrapper<T&> {
+    T* pointer;
 
-        operator T& () { return *pointer; }
-    };
+    operator T& () { return *pointer; }
+};
 
-    template <typename T>
-    struct value_wrapper<T&&> {
-        T* pointer;
+template <typename T>
+struct value_wrapper<T&&> {
+    T* pointer;
 
-        operator T&& () { return std::move(*pointer); }
-    };
+    operator T&& () { return std::move(*pointer); }
+};
 
 }  // namespace pybind11

+ 179 - 189
include/pybind11/internal/types.h

@@ -3,216 +3,206 @@
 #include "object.h"
 
 namespace pybind11 {
-    class type : public object {
-    public:
-        using object::object;
-        template <typename T>
-        static handle handle_of();
-    };
+class type : public object {
+public:
+    using object::object;
+    template <typename T>
+    static handle handle_of();
+};
 
-    class iterable : public object {
-    public:
-        using object::object;
-        iterable() = delete;
-    };
+class iterable : public object {
+public:
+    using object::object;
+    iterable() = delete;
+};
 
-    class iterator : public object {
-    public:
-        using object::object;
-        iterator() = delete;
-    };
+class iterator : public object {
+public:
+    using object::object;
+    iterator() = delete;
+};
 
-    class list : public object {
-    public:
-        using object::object;
+class list : public object {
+public:
+    using object::object;
 
-        list() : object(vm->new_object<pkpy::List>(pkpy::VM::tp_list), true) {}
-    };
-
-    class tuple : public object {
-    public:
-        using object::object;
-
-        tuple(int n) : object(vm->new_object<pkpy::Tuple>(pkpy::VM::tp_tuple, n), true) {}
-
-        //& operator[](int i){ return _args[i]; }
-        // PyVar operator[](int i) const { return _args[i]; }
-    };
-
-    class set : public object {
-    public:
-        using object::object;
-        // set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
-    };
-
-    class dict : public object {
-    public:
-        using object::object;
-
-        dict() : object(vm->new_object<pkpy::Dict>(pkpy::VM::tp_dict), true) {}
-    };
-
-    class str : public object {
-
-    public:
-        using object::object;
-        str(const char* c, int len) :
-            object(vm->new_object<pkpy::Str>(pkpy::VM::tp_str, c, len), true) {
-
-            };
-
-        str(const char* c = "") : str(c, strlen(c)) {}
-
-        str(const std::string& s) : str(s.data(), s.size()) {}
-
-        str(std::string_view sv) : str(sv.data(), sv.size()) {}
-
-        explicit str(const bytes& b);
-        explicit str(handle h);
-        operator std::string () const;
-
-        template <typename... Args>
-        str format(Args&&... args) const;
-    };
-
-    class int_ : public object {
-    public:
-        using object::object;
-
-        int_(int64_t value) : object(pkpy::py_var(vm, value), true) {}
-    };
-
-    class float_ : public object {
-    public:
-        using object::object;
-
-        float_(double value) : object(pkpy::py_var(vm, value), true) {}
-    };
-
-    class bool_ : public object {
-    public:
-        using object::object;
-
-        bool_(bool value) : object(pkpy::py_var(vm, value), true) {}
-    };
-
-    class function : public object {
-    public:
-        using object::object;
-    };
-
-    class attr_accessor : public object {
-    private:
-        object key;
-
-    public:
-        template <typename T>
-        attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
+    list() : object(vm->new_object<pkpy::List>(pkpy::VM::tp_list), true) {}
+};
 
-        template <typename T>
-        attr_accessor& operator= (T&& value) & {
-            static_assert(std::is_base_of_v<object, std::decay_t<T>>,
-                          "T must be derived from object");
-            m_ptr = std::forward<T>(value);
-            return *this;
-        }
+class tuple : public object {
+public:
+    using object::object;
 
-        template <typename T>
-        attr_accessor& operator= (T&& value) && {
-            static_assert(std::is_base_of_v<object, std::decay_t<T>>,
-                          "T must be derived from object");
-            setattr(*this, key, std::forward<T>(value));
-            return *this;
-        }
-    };
+    tuple(int n) : object(vm->new_object<pkpy::Tuple>(pkpy::VM::tp_tuple, n), true) {}
 
-    inline attr_accessor handle::attr(const char* name) const {
-        return attr_accessor(reinterpret_borrow<object>(*this), str(name));
-    }
+    //& operator[](int i){ return _args[i]; }
+    // PyVar operator[](int i) const { return _args[i]; }
+};
 
-    inline attr_accessor handle::attr(const handle& name) const {
-        return attr_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(name));
-    }
+class set : public object {
+public:
+    using object::object;
+    // set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
+};
 
-    inline attr_accessor handle::attr(object&& name) const {
-        return attr_accessor(reinterpret_borrow<object>(*this), std::move(name));
-    }
+class dict : public object {
+public:
+    using object::object;
 
-    class item_accessor : public object {
-    public:
-        object key;
-
-    public:
-        template <typename T>
-        item_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
-
-        template <typename T>
-        item_accessor& operator= (T&& value) & {
-            static_assert(std::is_base_of_v<object, std::decay_t<T>>,
-                          "T must be derived from object");
-            m_ptr = std::forward<T>(value);
-        }
-
-        template <typename T>
-        item_accessor& operator= (object&& value) && {
-            static_assert(std::is_base_of_v<object, std::decay_t<T>>,
-                          "T must be derived from object");
-            setitem(*this, key, std::forward<T>(value));
-        }
-    };
-
-    inline item_accessor handle::operator[] (int64_t key) const {
-        return item_accessor(reinterpret_borrow<object>(*this), int_(key));
-    }
+    dict() : object(vm->new_object<pkpy::Dict>(pkpy::VM::tp_dict), true) {}
+};
 
-    inline item_accessor handle::operator[] (const char* key) const {
-        return item_accessor(reinterpret_borrow<object>(*this), str(key));
-    }
+class str : public object {
+
+public:
+    using object::object;
+    str(const char* c, int len) :
+        object(vm->new_object<pkpy::Str>(pkpy::VM::tp_str, c, len), true) {
+
+        };
+
+    str(const char* c = "") : str(c, strlen(c)) {}
+
+    str(const std::string& s) : str(s.data(), s.size()) {}
+
+    str(std::string_view sv) : str(sv.data(), sv.size()) {}
+
+    explicit str(const bytes& b);
+    explicit str(handle h);
+    operator std::string () const;
+
+    template <typename... Args>
+    str format(Args&&... args) const;
+};
+
+class int_ : public object {
+public:
+    using object::object;
+
+    int_(int64_t value) : object(pkpy::py_var(vm, value), true) {}
+};
+
+class float_ : public object {
+public:
+    using object::object;
+
+    float_(double value) : object(pkpy::py_var(vm, value), true) {}
+};
 
-    inline item_accessor handle::operator[] (const handle& key) const {
-        return item_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(key));
+class bool_ : public object {
+public:
+    using object::object;
+
+    bool_(bool value) : object(pkpy::py_var(vm, value), true) {}
+};
+
+class function : public object {
+public:
+    using object::object;
+};
+
+class attr_accessor : public object {
+private:
+    object key;
+
+public:
+    template <typename T>
+    attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
+
+    template <typename T>
+    attr_accessor& operator= (T&& value) & {
+        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
+        m_ptr = std::forward<T>(value);
+        return *this;
     }
 
-    inline item_accessor handle::operator[] (object&& key) const {
-        return item_accessor(reinterpret_borrow<object>(*this), std::move(key));
+    template <typename T>
+    attr_accessor& operator= (T&& value) && {
+        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
+        setattr(*this, key, std::forward<T>(value));
+        return *this;
     }
+};
 
-    class args : public tuple {
-        using tuple::tuple;
-    };
+inline attr_accessor handle::attr(const char* name) const {
+    return attr_accessor(reinterpret_borrow<object>(*this), str(name));
+}
 
-    class kwargs : public dict {
-        using dict::dict;
-    };
+inline attr_accessor handle::attr(const handle& name) const {
+    return attr_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(name));
+}
+
+inline attr_accessor handle::attr(object&& name) const {
+    return attr_accessor(reinterpret_borrow<object>(*this), std::move(name));
+}
+
+class item_accessor : public object {
+public:
+    object key;
+
+public:
+    template <typename T>
+    item_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
 
     template <typename T>
-    handle type::handle_of() {
-        if constexpr(std::is_same_v<T, object>) {
-            return vm->_t(vm->tp_object);
-        }
-#define PYBIND11_TYPE_MAPPER(type, tp)                                                             \
-    else if constexpr(std::is_same_v<T, type>) {                                                   \
-        return vm->_t(vm->tp);                                                                     \
+    item_accessor& operator= (T&& value) & {
+        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
+        m_ptr = std::forward<T>(value);
     }
-        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)
+
+    template <typename T>
+    item_accessor& operator= (object&& value) && {
+        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
+        setitem(*this, key, std::forward<T>(value));
+    }
+};
+
+inline item_accessor handle::operator[] (int64_t key) const {
+    return item_accessor(reinterpret_borrow<object>(*this), int_(key));
+}
+
+inline item_accessor handle::operator[] (const char* key) const {
+    return item_accessor(reinterpret_borrow<object>(*this), str(key));
+}
+
+inline item_accessor handle::operator[] (const handle& key) const {
+    return item_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(key));
+}
+
+inline item_accessor handle::operator[] (object&& key) const {
+    return item_accessor(reinterpret_borrow<object>(*this), std::move(key));
+}
+
+class args : public tuple {
+    using tuple::tuple;
+};
+
+class kwargs : public dict {
+    using dict::dict;
+};
+
+template <typename T>
+handle type::handle_of() {
+    if constexpr(std::is_same_v<T, object>) { return vm->_t(vm->tp_object); }
+#define PYBIND11_TYPE_MAPPER(type, tp)                                                                                 \
+    else if constexpr(std::is_same_v<T, type>) { return vm->_t(vm->tp); }
+    PYBIND11_TYPE_MAPPER(type, tp_type)
+    PYBIND11_TYPE_MAPPER(str, tp_str)
+    PYBIND11_TYPE_MAPPER(int_, tp_int)
+    PYBIND11_TYPE_MAPPER(float_, tp_float)
+    PYBIND11_TYPE_MAPPER(bool_, tp_bool)
+    PYBIND11_TYPE_MAPPER(list, tp_list)
+    PYBIND11_TYPE_MAPPER(tuple, tp_tuple)
+    PYBIND11_TYPE_MAPPER(args, tp_tuple)
+    PYBIND11_TYPE_MAPPER(dict, tp_dict)
+    PYBIND11_TYPE_MAPPER(kwargs, tp_dict)
 #undef PYBIND11_TYPE_MAPPER
-        else {
-            auto result = vm->_cxx_typeid_map.find(typeid(T));
-            if(result != vm->_cxx_typeid_map.end()) {
-                return vm->_t(result->second);
-            }
-
-            vm->TypeError("Type not registered");
-        }
+    else {
+        auto result = vm->_cxx_typeid_map.find(typeid(T));
+        if(result != vm->_cxx_typeid_map.end()) { return vm->_t(result->second); }
+
+        vm->TypeError("Type not registered");
     }
+}
 
 }  // namespace pybind11

+ 1 - 1
include/pybind11/pybind11.h

@@ -1,3 +1,3 @@
 #pragma once
 
-#include "internal/class.h"
+#include "internal/class.h"

+ 2 - 2
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)

+ 5 - 5
src/common/any.cpp

@@ -3,20 +3,20 @@
 #include <stdexcept>
 #include <cstdio>
 
-namespace pkpy{
+namespace pkpy {
 
-void any::__bad_any_cast(const std::type_index expected, const std::type_index actual){
+void any::__bad_any_cast(const std::type_index expected, const std::type_index actual) {
     char error[256];
     snprintf(error, sizeof(error), "bad_any_cast: expected %s, got %s", expected.name(), actual.name());
     throw std::runtime_error(error);
 }
 
-any::any(any&& other) noexcept: data(other.data), _vt(other._vt){
+any::any(any&& other) noexcept : data(other.data), _vt(other._vt) {
     other.data = nullptr;
     other._vt = nullptr;
 }
 
-any& any::operator=(any&& other) noexcept{
+any& any::operator= (any&& other) noexcept {
     if(data) _vt->deleter(data);
     data = other.data;
     _vt = other._vt;
@@ -25,4 +25,4 @@ any& any::operator=(any&& other) noexcept{
     return *this;
 }
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 66 - 64
src/common/memorypool.cpp

@@ -6,28 +6,28 @@
 #include <cassert>
 #include <stdexcept>
 
-namespace pkpy{
+namespace pkpy {
 
-struct LinkedListNode{
+struct LinkedListNode {
     LinkedListNode* prev;
     LinkedListNode* next;
 };
 
-template<typename T>
-struct DoubleLinkedList{
+template <typename T>
+struct DoubleLinkedList {
     static_assert(std::is_base_of_v<LinkedListNode, T>);
     int _size;
     LinkedListNode head;
     LinkedListNode tail;
-    
-    DoubleLinkedList(): _size(0){
+
+    DoubleLinkedList() : _size(0) {
         head.prev = nullptr;
         head.next = &tail;
         tail.prev = &head;
         tail.next = nullptr;
     }
 
-    void push_back(T* node){
+    void push_back(T* node) {
         node->prev = tail.prev;
         node->next = &tail;
         tail.prev->next = node;
@@ -35,7 +35,7 @@ struct DoubleLinkedList{
         _size++;
     }
 
-    void push_front(T* node){
+    void push_front(T* node) {
         node->prev = &head;
         node->next = head.next;
         head.next->prev = node;
@@ -43,14 +43,14 @@ struct DoubleLinkedList{
         _size++;
     }
 
-    void pop_back(){
+    void pop_back() {
         assert(!empty());
         tail.prev->prev->next = &tail;
         tail.prev = tail.prev->prev;
         _size--;
     }
 
-    void pop_front(){
+    void pop_front() {
         assert(!empty());
         head.next->next->prev = &head;
         head.next = head.next->next;
@@ -67,22 +67,20 @@ struct DoubleLinkedList{
         return static_cast<T*>(head.next);
     }
 
-    void erase(T* node){
+    void erase(T* node) {
         node->prev->next = node->next;
         node->next->prev = node->prev;
         _size--;
     }
 
-    bool empty() const {
-        return _size == 0;
-    }
+    bool empty() const { return _size == 0; }
 
     int size() const { return _size; }
 
-    template<typename Func>
-    void apply(Func func){
+    template <typename Func>
+    void apply(Func func) {
         LinkedListNode* p = head.next;
-        while(p != &tail){
+        while(p != &tail) {
             LinkedListNode* next = p->next;
             func(static_cast<T*>(p));
             p = next;
@@ -90,42 +88,41 @@ struct DoubleLinkedList{
     }
 };
 
-template<int __BlockSize>
-struct MemoryPool{
-    static const int __MaxBlocks = 256*1024 / __BlockSize;
-    static const int __MinArenaCount = PK_GC_MIN_THRESHOLD*100 / (256*1024);
+template <int __BlockSize>
+struct MemoryPool {
+    const static int __MaxBlocks = 256 * 1024 / __BlockSize;
+    const static int __MinArenaCount = PK_GC_MIN_THRESHOLD * 100 / (256 * 1024);
 
-    struct Block{
+    struct Block {
         void* arena;
         char data[__BlockSize];
     };
 
-    struct Arena: LinkedListNode{
+    struct Arena : LinkedListNode {
         Block _blocks[__MaxBlocks];
         Block* _free_list[__MaxBlocks];
         int _free_list_size;
-        
-        Arena(): _free_list_size(__MaxBlocks) {
-            for(int i=0; i<__MaxBlocks; i++){
+
+        Arena() : _free_list_size(__MaxBlocks) {
+            for(int i = 0; i < __MaxBlocks; i++) {
                 _blocks[i].arena = this;
                 _free_list[i] = &_blocks[i];
             }
         }
 
         bool empty() const { return _free_list_size == 0; }
+
         bool full() const { return _free_list_size == __MaxBlocks; }
 
-        size_t allocated_size() const{
-            return __BlockSize * (__MaxBlocks - _free_list_size);
-        }
+        size_t allocated_size() const { return __BlockSize * (__MaxBlocks - _free_list_size); }
 
-        Block* alloc(){
+        Block* alloc() {
             assert(!empty());
             _free_list_size--;
             return _free_list[_free_list_size];
         }
 
-        void dealloc(Block* block){
+        void dealloc(Block* block) {
             assert(!full());
             _free_list[_free_list_size] = block;
             _free_list_size++;
@@ -134,71 +131,73 @@ struct MemoryPool{
 
     MemoryPool() = default;
     MemoryPool(const MemoryPool&) = delete;
-    MemoryPool& operator=(const MemoryPool&) = delete;
+    MemoryPool& operator= (const MemoryPool&) = delete;
     MemoryPool(MemoryPool&&) = delete;
-    MemoryPool& operator=(MemoryPool&&) = delete;
+    MemoryPool& operator= (MemoryPool&&) = delete;
 
     DoubleLinkedList<Arena> _arenas;
     DoubleLinkedList<Arena> _empty_arenas;
 
-    void* alloc(size_t size){
+    void* alloc(size_t size) {
         PK_GLOBAL_SCOPE_LOCK();
-        if(size > __BlockSize){
+        if(size > __BlockSize) {
             void* p = std::malloc(sizeof(void*) + size);
             std::memset(p, 0, sizeof(void*));
             return (char*)p + sizeof(void*);
         }
 
-        if(_arenas.empty()){
-            _arenas.push_back(new Arena());
-        }
+        if(_arenas.empty()) { _arenas.push_back(new Arena()); }
         Arena* arena = _arenas.back();
         void* p = arena->alloc()->data;
-        if(arena->empty()){
+        if(arena->empty()) {
             _arenas.pop_back();
             _empty_arenas.push_back(arena);
         }
         return p;
     }
 
-    void dealloc(void* p){
+    void dealloc(void* p) {
         PK_GLOBAL_SCOPE_LOCK();
         assert(p != nullptr);
         Block* block = (Block*)((char*)p - sizeof(void*));
-        if(block->arena == nullptr){
+        if(block->arena == nullptr) {
             std::free(block);
-        }else{
+        } else {
             Arena* arena = (Arena*)block->arena;
-            if(arena->empty()){
+            if(arena->empty()) {
                 _empty_arenas.erase(arena);
                 _arenas.push_front(arena);
                 arena->dealloc(block);
-            }else{
+            } else {
                 arena->dealloc(block);
             }
         }
     }
 
-    void shrink_to_fit(){
+    void shrink_to_fit() {
         PK_GLOBAL_SCOPE_LOCK();
         if(_arenas.size() < __MinArenaCount) return;
-        _arenas.apply([this](Arena* arena){
-            if(arena->full()){
+        _arenas.apply([this](Arena* arena) {
+            if(arena->full()) {
                 _arenas.erase(arena);
                 delete arena;
             }
         });
     }
 
-    ~MemoryPool(){
-        _arenas.apply([](Arena* arena){ delete arena; });
-        _empty_arenas.apply([](Arena* arena){ delete arena; });
+    ~MemoryPool() {
+        _arenas.apply([](Arena* arena) {
+            delete arena;
+        });
+        _empty_arenas.apply([](Arena* arena) {
+            delete arena;
+        });
     }
 };
 
-template<int BlockSize, int BlockCount>
-struct FixedMemoryPool{
-    struct Block{
+template <int BlockSize, int BlockCount>
+struct FixedMemoryPool {
+    struct Block {
         char data[BlockSize];
     };
 
@@ -211,31 +210,29 @@ struct FixedMemoryPool{
 
     FixedMemoryPool() {
         _free_list_end = _free_list + BlockCount;
-        for(int i = 0; i < BlockCount; ++i){
+        for(int i = 0; i < BlockCount; ++i) {
             _free_list[i] = _blocks + i;
         }
     }
 
-    bool is_valid(void* p){
-        return p >= _blocks && p < _blocks + BlockCount;
-    }
+    bool is_valid(void* p) { return p >= _blocks && p < _blocks + BlockCount; }
 
-    void* alloc(){
+    void* alloc() {
         PK_GLOBAL_SCOPE_LOCK()
-        if(_free_list_end != _free_list){
+        if(_free_list_end != _free_list) {
             --_free_list_end;
             return *_free_list_end;
-        }else{
+        } else {
             return std::malloc(BlockSize);
         }
     }
 
-    void dealloc(void* p){
+    void dealloc(void* p) {
         PK_GLOBAL_SCOPE_LOCK()
-        if(is_valid(p)){
+        if(is_valid(p)) {
             *_free_list_end = static_cast<Block*>(p);
             ++_free_list_end;
-        }else{
+        } else {
             std::free(p);
         }
     }
@@ -246,12 +243,17 @@ static FixedMemoryPool<kPoolFrameBlockSize, 128> PoolFrame;
 static MemoryPool<80> PoolObject;
 
 void* PoolExpr_alloc() noexcept { return PoolExpr.alloc(); }
+
 void PoolExpr_dealloc(void* p) noexcept { PoolExpr.dealloc(p); }
+
 void* PoolFrame_alloc() noexcept { return PoolFrame.alloc(); }
+
 void PoolFrame_dealloc(void* p) noexcept { PoolFrame.dealloc(p); }
 
 void* PoolObject_alloc(size_t size) noexcept { return PoolObject.alloc(size); }
+
 void PoolObject_dealloc(void* p) noexcept { PoolObject.dealloc(p); }
+
 void PoolObject_shrink_to_fit() noexcept { PoolObject.shrink_to_fit(); }
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 403 - 436
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';
+#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() : 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(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(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(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) :
+    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(const char* s, int len) :
+    size(len), is_ascii(true){PK_STR_ALLOCATE() PK_STR_COPY_INIT(s)}
 
-    Str::Str(std::pair<char *, int> detached): size(detached.second), is_ascii(true) {
-        this->data = detached.first;
-        for(int i=0; i<size; i++){
-            if(!isascii(data[i])){ is_ascii = false; break; }
+    Str::Str(std::pair<char*, int> detached) : size(detached.second), is_ascii(true) {
+    this->data = detached.first;
+    for(int i = 0; i < size; i++) {
+        if(!isascii(data[i])) {
+            is_ascii = false;
+            break;
         }
-        assert(data[size] == '\0');
     }
+    assert(data[size] == '\0');
+}
 
-    Str::Str(const Str& other): size(other.size), is_ascii(other.is_ascii) {
-        PK_STR_ALLOCATE()
-        std::memcpy(data, other.data, size);
+Str::Str(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(Str&& other) : size(other.size), is_ascii(other.is_ascii) {
+    if(other.is_inlined()) {
+        data = _inlined;
+        for(int i = 0; i < size; i++)
+            _inlined[i] = other._inlined[i];
         data[size] = '\0';
+    } else {
+        data = other.data;
+        // zero out `other`
+        other.data = other._inlined;
+        other.data[0] = '\0';
+        other.size = 0;
     }
+}
 
-    Str::Str(Str&& other): size(other.size), is_ascii(other.is_ascii) {
-        if(other.is_inlined()){
-            data = _inlined;
-            for(int i=0; i<size; i++) _inlined[i] = other._inlined[i];
-            data[size] = '\0';
-        }else{
-            data = other.data;
-            // zero out `other`
-            other.data = other._inlined;
-            other.data[0] = '\0';
-            other.size = 0;
-        }
-    }
+Str operator+ (const char* p, const Str& str) {
+    Str other(p);
+    return other + str;
+}
 
-    Str operator+(const char* p, const Str& str){
-        Str other(p);
-        return other + str;
-    }
+std::ostream& operator<< (std::ostream& os, const Str& str) { return os << str.sv(); }
 
-    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(); }
 
-    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){
-        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 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;
+}
 
-    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 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 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 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 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 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 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 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(); }
 
-    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(){
-        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, 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::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++;
+    }
+    if(right) {
+        while(L < R && chars.index(u8_getitem(R - 1)) != -1)
+            R--;
     }
+    return u8_slice(L, R, 1);
+}
 
-    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::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::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::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::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(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::escape(bool single_quote) const{
-        SStream ss;
-        escape_(ss, single_quote);
-        return ss.str();
-    }
+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();
+}
 
-    void Str::escape_(SStream& ss, bool single_quote) const {
-        ss << (single_quote ? '\'' : '"');
-        for (int i=0; i<length(); i++) {
-            char c = this->operator[](i);
-            switch (c) {
-                case '"':
-                    if(!single_quote) ss << '\\';
-                    ss << '"';
-                    break;
-                case '\'':
-                    if(single_quote) ss << '\\';
-                    ss << '\'';
-                    break;
-                case '\\': ss << '\\' << '\\'; break;
-                case '\n': ss << "\\n"; break;
-                case '\r': ss << "\\r"; break;
-                case '\t': ss << "\\t"; break;
-                case '\b': ss << "\\b"; break;
-                default:
-                    if ('\x00' <= c && c <= '\x1f') {
-                        ss << "\\x"; // << std::hex << std::setw(2) << std::setfill('0') << (int)c;
-                        ss << PK_HEX_TABLE[c >> 4];
-                        ss << PK_HEX_TABLE[c & 0xf];
-                    } else {
-                        ss << c;
-                    }
-            }
-        }
-        ss << (single_quote ? '\'' : '"');
+int Str::_unicode_index_to_byte(int i) const {
+    if(is_ascii) return i;
+    int j = 0;
+    while(i > 0) {
+        j += utf8len(data[j]);
+        i--;
     }
+    return j;
+}
 
-    int Str::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;
+int Str::_byte_index_to_unicode(int n) const {
+    if(is_ascii) return n;
+    int cnt = 0;
+    for(int i = 0; i < n; i++) {
+        if((data[i] & 0xC0) != 0x80) cnt++;
     }
+    return cnt;
+}
 
-    Str Str::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::u8_getitem(int i) const {
+    i = _unicode_index_to_byte(i);
+    return substr(i, utf8len(data[i]));
+}
 
-    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::u8_slice(int start, int stop, int step) const {
+    SStream ss;
+    if(is_ascii) {
+        PK_SLICE_LOOP(i, start, stop, step) ss << data[i];
+    } else {
+        PK_SLICE_LOOP(i, start, stop, step) ss << u8_getitem(i);
     }
+    return ss.str();
+}
 
+int Str::u8_length() const { return _byte_index_to_unicode(size); }
 
-    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;
+vector<std::string_view> Str::split(const Str& sep) const {
+    vector<std::string_view> result;
+    std::string_view tmp;
+    int start = 0;
+    while(true) {
+        int i = index(sep, start);
+        if(i == -1) break;
+        tmp = sv().substr(start, i - start);
+        if(!tmp.empty()) result.push_back(tmp);
+        start = i + sep.size;
     }
+    tmp = sv().substr(start, size - start);
+    if(!tmp.empty()) result.push_back(tmp);
+    return result;
+}
 
-    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++;
+vector<std::string_view> Str::split(char sep) const {
+    vector<std::string_view> result;
+    int i = 0;
+    for(int j = 0; j < size; j++) {
+        if(data[j] == sep) {
+            if(j > i) result.emplace_back(data + i, j - i);
+            i = j + 1;
+            continue;
         }
-        return cnt;
     }
+    if(size > i) result.emplace_back(data + i, size - i);
+    return result;
+}
 
-    Str Str::u8_getitem(int i) const{
-        i = _unicode_index_to_byte(i);
-        return substr(i, utf8len(data[i]));
-    }
+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;
+}
 
-    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();
-    }
+std::map<std::string_view, uint16_t>& StrName::_interned() {
+    static std::map<std::string_view, uint16_t> interned;
+    return interned;
+}
 
-    int Str::u8_length() const {
-        return _byte_index_to_unicode(size);
-    }
+std::map<uint16_t, std::string>& StrName::_r_interned() {
+    static std::map<uint16_t, std::string> r_interned;
+    return r_interned;
+}
 
-    vector<std::string_view> Str::split(const Str& sep) const{
-        vector<std::string_view> result;
-        std::string_view tmp;
-        int start = 0;
-        while(true){
-            int i = index(sep, start);
-            if(i == -1) break;
-            tmp = sv().substr(start, i - start);
-            if(!tmp.empty()) result.push_back(tmp);
-            start = i + sep.size;
-        }
-        tmp = sv().substr(start, size - start);
-        if(!tmp.empty()) result.push_back(tmp);
-        return result;
-    }
+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);
+}
 
-    vector<std::string_view> Str::split(char sep) const{
-        vector<std::string_view> result;
-        int i = 0;
-        for(int j = 0; j < size; j++){
-            if(data[j] == sep){
-                if(j > i) result.emplace_back(data+i, j-i);
-                i = j + 1;
-                continue;
-            }
-        }
-        if(size > i) result.emplace_back(data+i, size-i);
-        return result;
-    }
+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());
+}
 
-    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;
-    }
+SStream& SStream::operator<< (const Str& s) {
+    for(char c: s)
+        buffer.push_back(c);
+    return *this;
+}
 
-    std::map<std::string_view, uint16_t>& StrName::_interned(){
-        static std::map<std::string_view, uint16_t> interned;
-        return interned;
-    }
+SStream& SStream::operator<< (const char* s) {
+    while(*s)
+        buffer.push_back(*s++);
+    return *this;
+}
 
-    std::map<uint16_t, std::string>& StrName::_r_interned(){
-        static std::map<uint16_t, std::string> r_interned;
-        return r_interned;
-    }
+SStream& SStream::operator<< (const std::string& s) {
+    for(char c: s)
+        buffer.push_back(c);
+    return *this;
+}
 
-    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);
-    }
+SStream& SStream::operator<< (std::string_view s) {
+    for(char c: s)
+        buffer.push_back(c);
+    return *this;
+}
 
-    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<< (char c) {
+    buffer.push_back(c);
+    return *this;
+}
 
-    SStream& SStream::operator<<(const Str& s){
-        for(char c: s) buffer.push_back(c);
-        return *this;
-    }
+SStream& SStream::operator<< (StrName sn) { return *this << sn.sv(); }
 
-    SStream& SStream::operator<<(const char* s){
-        while(*s) buffer.push_back(*s++);
-        return *this;
-    }
+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<<(const std::string& s){
-        for(char c: s) buffer.push_back(c);
-        return *this;
-    }
+SStream& SStream::operator<< (int val) { return (*this) << static_cast<i64>(val); }
 
-    SStream& SStream::operator<<(std::string_view s){
-        for(char c: s) buffer.push_back(c);
+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<<(char c){
-        buffer.push_back(c);
-        return *this;
+    if(val < 0) {
+        buffer.push_back('-');
+        val = -val;
     }
-
-    SStream& SStream::operator<<(StrName sn){
-        return *this << sn.sv();
+    auto begin = buffer.end();
+    while(val) {
+        buffer.push_back('0' + val % 10);
+        val /= 10;
     }
+    std::reverse(begin, buffer.end());
+    return *this;
+}
 
-    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<< (f64 val) {
+    if(std::isinf(val)) { return (*this) << (val > 0 ? "inf" : "-inf"); }
+    if(std::isnan(val)) { return (*this) << "nan"; }
+    char b[32];
+    if(_precision == -1) {
+        int prec = std::numeric_limits<f64>::max_digits10 - 1;
+        snprintf(b, sizeof(b), "%.*g", prec, val);
+    } else {
+        int prec = _precision;
+        snprintf(b, sizeof(b), "%.*f", prec, val);
+    }
+    (*this) << b;
+    if(std::all_of(b + 1, b + strlen(b), isdigit)) (*this) << ".0";
+    return *this;
+}
 
-    SStream& SStream::operator<<(int val){
-        return (*this) << static_cast<i64>(val);
+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<<(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(void* p) {
+    if(p == nullptr) {
+        (*this) << "0x0";
+        return;
     }
-
-    SStream& SStream::operator<<(f64 val){
-        if(std::isinf(val)){
-            return (*this) << (val > 0 ? "inf" : "-inf");
-        }
-        if(std::isnan(val)){
-            return (*this) << "nan";
-        }
-        char b[32];
-        if(_precision == -1){
-            int prec = std::numeric_limits<f64>::max_digits10-1;
-            snprintf(b, sizeof(b), "%.*g", prec, val);
-        }else{
-            int prec = _precision;
-            snprintf(b, sizeof(b), "%.*f", prec, val);
-        }
-        (*this) << b;
-        if(std::all_of(b+1, b+strlen(b), isdigit)) (*this) << ".0";
-        return *this;
+    (*this) << "0x";
+    uintptr_t p_t = reinterpret_cast<uintptr_t>(p);
+    bool non_zero = true;
+    for(int i = sizeof(void*) - 1; i >= 0; i--) {
+        unsigned char cpnt = (p_t >> (i * 8)) & 0xff;
+        write_hex(cpnt, non_zero);
+        if(cpnt != 0) non_zero = false;
     }
+}
 
-    void SStream::write_hex(unsigned char c, bool non_zero){
-        unsigned char high = c >> 4;
-        unsigned char low = c & 0xf;
-        if(non_zero){
-            if(high) (*this) << PK_HEX_TABLE[high];
-            if(high || low) (*this) << PK_HEX_TABLE[low];
-        }else{
-            (*this) << PK_HEX_TABLE[high];
-            (*this) << PK_HEX_TABLE[low];
-        }
+void SStream::write_hex(i64 val) {
+    if(val == 0) {
+        (*this) << "0x0";
+        return;
     }
-
-    void SStream::write_hex(void* p){
-        if(p == nullptr){
-            (*this) << "0x0";
-            return;
-        }
-        (*this) << "0x";
-        uintptr_t p_t = reinterpret_cast<uintptr_t>(p);
-        bool non_zero = true;
-        for(int i=sizeof(void*)-1; i>=0; i--){
-            unsigned char cpnt = (p_t >> (i * 8)) & 0xff;
-            write_hex(cpnt, non_zero);
-            if(cpnt != 0) non_zero = false;
-        }
+    if(val < 0) {
+        (*this) << "-";
+        val = -val;
     }
-
-    void SStream::write_hex(i64 val){
-        if(val == 0){
-            (*this) << "0x0";
-            return;
-        }
-        if(val < 0){
-            (*this) << "-";
-            val = -val;
-        }
-        (*this) << "0x";
-        bool non_zero = true;
-        for(int i=56; i>=0; i-=8){
-            unsigned char cpnt = (val >> i) & 0xff;
-            write_hex(cpnt, non_zero);
-            if(cpnt != 0) non_zero = false;
-        }
+    (*this) << "0x";
+    bool non_zero = true;
+    for(int i = 56; i >= 0; i -= 8) {
+        unsigned char cpnt = (val >> i) & 0xff;
+        write_hex(cpnt, non_zero);
+        if(cpnt != 0) non_zero = false;
     }
+}
 
 #undef PK_STR_ALLOCATE
 #undef PK_STR_COPY_INIT
 
-
-
 // unary operators
 const StrName __repr__ = StrName::get("__repr__");
 const StrName __str__ = StrName::get("__str__");
@@ -601,5 +569,4 @@ const StrName pk_id_set = StrName::get("set");
 const StrName pk_id_long = StrName::get("long");
 const StrName pk_id_complex = StrName::get("complex");
 
-
-} // namespace pkpy
+}  // namespace pkpy

+ 1201 - 1236
src/compiler/compiler.cpp

@@ -4,112 +4,106 @@
 
 #include <stdexcept>
 
-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;
-    }
-
-    CodeObject_ Compiler::push_global_context(){
-        CodeObject_ co = std::make_shared<CodeObject>(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<FuncDecl>();
-        decl->code = std::make_shared<CodeObject>(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()");
+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;
+}
+
+CodeObject_ Compiler::push_global_context() {
+    CodeObject_ co = std::make_shared<CodeObject>(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<FuncDecl>();
+    decl->code = std::make_shared<CodeObject>(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);
         }
-        // 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);
-            }
-        }
-        // 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;
+        }
+        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; }
                 }
-                else func->type = FuncType::NORMAL;
-            }
-
-            assert(func->type != FuncType::UNSET);
+                if(is_empty) func->type = FuncType::EMPTY;
+            } else
+                func->type = FuncType::NORMAL;
         }
-        contexts.pop();
+
+        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; i<kTokenCount; i++) rules[i] = { nullptr, PK_NO_INFIX };
+        for(TokenIndex i = 0; i < kTokenCount; i++) rules[i] = { nullptr, PK_NO_INFIX };
         rules[TK(".")] =        { nullptr,                  PK_METHOD(exprAttrib),         PREC_PRIMARY };
         rules[TK("(")] =        { PK_METHOD(exprGroup),     PK_METHOD(exprCall),           PREC_PRIMARY };
         rules[TK("[")] =        { PK_METHOD(exprList),      PK_METHOD(exprSubscr),         PREC_PRIMARY };
@@ -158,1278 +152,1249 @@ namespace pkpy{
         
 #undef PK_METHOD
 #undef PK_NO_INFIX
-    }
+    // clang-format on
+}
 
-    bool Compiler::match(TokenIndex expected) {
-        if (curr().type != expected) return false;
-        advance();
-        return true;
-    }
+bool Compiler::match(TokenIndex expected) {
+    if(curr().type != expected) return false;
+    advance();
+    return true;
+}
 
-    void Compiler::consume(TokenIndex expected) {
-        if (!match(expected)){
-            SyntaxError(
-                _S("expected '", TK_STR(expected), "', got '", TK_STR(curr().type), "'")
-            );
-        }
-    }
+void Compiler::consume(TokenIndex expected) {
+    if(!match(expected)) { SyntaxError(_S("expected '", TK_STR(expected), "', got '", TK_STR(curr().type), "'")); }
+}
 
-    bool Compiler::match_newlines_repl(){
-        return match_newlines(mode()==REPL_MODE);
-    }
+bool Compiler::match_newlines_repl() { return match_newlines(mode() == REPL_MODE); }
 
-    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");
+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;
+}
 
-    void Compiler::EXPR() {
-        parse_expression(PREC_LOWEST+1);
+bool Compiler::match_end_stmt() {
+    if(match(TK(";"))) {
+        match_newlines();
+        return true;
     }
-
-    void Compiler::EXPR_TUPLE(bool allow_slice) {
-        parse_expression(PREC_LOWEST+1, allow_slice);
-        if(!match(TK(","))) return;
-        // tuple expression
-        Expr_vector items;
+    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;
+    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<TupleExpr>(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<NameExpr>(prev().str(), name_scope()));
-        } while(match(TK(",")));
-        if(items.size()==1) return std::move(items[0]);
-        return make_expr<TupleExpr>(std::move(items));
-    }
+        if(curr().brackets_level) match_newlines_repl();
+    } while(match(TK(",")));
+    ctx()->s_expr.push(make_expr<TupleExpr>(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<NameExpr>(prev().str(), name_scope()));
+    } while(match(TK(",")));
+    if(items.size() == 1) return std::move(items[0]);
+    return make_expr<TupleExpr>(std::move(items));
+}
 
-    void Compiler::exprLiteral(){
-        ctx()->s_expr.push(make_expr<LiteralExpr>(prev().value));
-    }
+void Compiler::exprLiteral() { ctx()->s_expr.push(make_expr<LiteralExpr>(prev().value)); }
 
-    void Compiler::exprLong(){
-        ctx()->s_expr.push(make_expr<LongExpr>(prev().str()));
-    }
+void Compiler::exprLong() { ctx()->s_expr.push(make_expr<LongExpr>(prev().str())); }
 
-    void Compiler::exprImag(){
-        ctx()->s_expr.push(make_expr<ImagExpr>(std::get<f64>(prev().value)));
-    }
+void Compiler::exprImag() { ctx()->s_expr.push(make_expr<ImagExpr>(std::get<f64>(prev().value))); }
 
-    void Compiler::exprBytes(){
-        ctx()->s_expr.push(make_expr<BytesExpr>(std::get<Str>(prev().value)));
-    }
+void Compiler::exprBytes() { ctx()->s_expr.push(make_expr<BytesExpr>(std::get<Str>(prev().value))); }
 
-    void Compiler::exprFString(){
-        ctx()->s_expr.push(make_expr<FStringExpr>(std::get<Str>(prev().value)));
-    }
-
-    void Compiler::exprLambda(){
-        FuncDecl_ decl = push_f_context("<lambda>");
-        auto e = make_expr<LambdaExpr>(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::exprFString() { ctx()->s_expr.push(make_expr<FStringExpr>(std::get<Str>(prev().value))); }
 
-    void Compiler::exprOr(){
-        auto e = make_expr<OrExpr>();
-        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<AndExpr>();
-        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::exprLambda() {
+    FuncDecl_ decl = push_f_context("<lambda>");
+    auto e = make_expr<LambdaExpr>(decl);
+    if(!match(TK(":"))) {
+        _compile_f_args(e->decl, false);
+        consume(TK(":"));
     }
-    
-    void Compiler::exprTernary(){
-        auto e = make_expr<TernaryExpr>();
-        e->true_expr = ctx()->s_expr.popx();
-        // cond
-        parse_expression(PREC_TERNARY + 1);
-        e->cond = ctx()->s_expr.popx();
-        consume(TK("else"));
-        // if false
+    // 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<OrExpr>();
+    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<AndExpr>();
+    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<TernaryExpr>();
+    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<BinaryExpr>();
+    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<NotExpr>(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<NegatedExpr>(ctx()->s_expr.popx())); break;
+        case TK("~"): ctx()->s_expr.push(make_expr<InvertExpr>(ctx()->s_expr.popx())); break;
+        case TK("*"): ctx()->s_expr.push(make_expr<StarredExpr>(1, ctx()->s_expr.popx())); break;
+        case TK("**"): ctx()->s_expr.push(make_expr<StarredExpr>(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<GroupedExpr>(ctx()->s_expr.popx());
+    ctx()->s_expr.push(std::move(g));
+}
+
+void Compiler::consume_comp(unique_ptr_128<CompExpr> 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);
-        e->false_expr = ctx()->s_expr.popx();
-        ctx()->s_expr.push(std::move(e));
-    }
-    
-    void Compiler::exprBinaryOp(){
-        auto e = make_expr<BinaryExpr>();
-        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));
+        ce->cond = ctx()->s_expr.popx();
     }
+    ctx()->s_expr.push(std::move(ce));
+    match_newlines_repl();
+}
 
-    void Compiler::exprNot() {
-        parse_expression(PREC_LOGICAL_NOT + 1);
-        ctx()->s_expr.push(make_expr<NotExpr>(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<NegatedExpr>(ctx()->s_expr.popx()));
-                break;
-            case TK("~"):
-                ctx()->s_expr.push(make_expr<InvertExpr>(ctx()->s_expr.popx()));
-                break;
-            case TK("*"):
-                ctx()->s_expr.push(make_expr<StarredExpr>(1, ctx()->s_expr.popx()));
-                break;
-            case TK("**"):
-                ctx()->s_expr.push(make_expr<StarredExpr>(2, ctx()->s_expr.popx()));
-                break;
-            default: assert(false);
+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<ListCompExpr>(), std::move(items[0]));
+            consume(TK("]"));
+            return;
         }
-    }
-
-    void Compiler::exprGroup(){
         match_newlines_repl();
-        EXPR_TUPLE();   // () is just for change precedence
+    } while(match(TK(",")));
+    consume(TK("]"));
+    auto e = make_expr<ListExpr>(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();
-        consume(TK(")"));
-        if(ctx()->s_expr.top()->is_tuple()) return;
-        Expr_ g = make_expr<GroupedExpr>(ctx()->s_expr.popx());
-        ctx()->s_expr.push(std::move(g));
-    }
-
-    void Compiler::consume_comp(unique_ptr_128<CompExpr> 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();
+        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<DictItemExpr>();
+            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(match(TK("if"))){
-            parse_expression(PREC_TERNARY + 1);
-            ce->cond = ctx()->s_expr.popx();
+        if(items.size() == 1 && match(TK("for"))) {
+            if(parsing_dict)
+                consume_comp(make_expr<DictCompExpr>(), std::move(items[0]));
+            else
+                consume_comp(make_expr<SetCompExpr>(), std::move(items[0]));
+            consume(TK("}"));
+            return;
         }
-        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<ListCompExpr>(), std::move(items[0]));
-                consume(TK("]"));
-                return;
-            }
-            match_newlines_repl();
-        } while (match(TK(",")));
-        consume(TK("]"));
-        auto e = make_expr<ListExpr>(std::move(items));
-        e->line = line;     // override line
+    } while(match(TK(",")));
+    consume(TK("}"));
+    if(items.size() == 0 || parsing_dict) {
+        auto e = make_expr<DictExpr>(std::move(items));
         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<DictItemExpr>();
-                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<DictCompExpr>(), std::move(items[0]));
-                else consume_comp(make_expr<SetCompExpr>(), 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<DictExpr>(std::move(items));
-            ctx()->s_expr.push(std::move(e));
-        }else{
-            auto e = make_expr<SetExpr>(std::move(items));
-            ctx()->s_expr.push(std::move(e));
-        }
-    }
-
-    void Compiler::exprCall() {
-        auto e = make_expr<CallExpr>();
-        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");
+    } else {
+        auto e = make_expr<SetExpr>(std::move(items));
         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<NameExpr>(name, scope));
-    }
-
-    void Compiler::exprAttrib() {
-        consume(TK("@id"));
-        ctx()->s_expr.push(
-            make_expr<AttribExpr>(ctx()->s_expr.popx(), StrName::get(prev().sv()))
-        );
-    }
-
-    void Compiler::exprSlice0() {
-        auto slice = make_expr<SliceExpr>();
-        if(is_expression()){        // :<stop>
+void Compiler::exprCall() {
+    auto e = make_expr<CallExpr>();
+    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();
-            slice->stop = ctx()->s_expr.popx();
-            // try optional step
-            if(match(TK(":"))){     // :<stop>:<step>
-                EXPR();
-                slice->step = ctx()->s_expr.popx();
-            }
-        }else if(match(TK(":"))){
-            if(is_expression()){    // ::<step>
-                EXPR();
-                slice->step = ctx()->s_expr.popx();
-            }   // else ::
-        }   // else :
-        ctx()->s_expr.push(std::move(slice));
-    }
-
-    void Compiler::exprSlice1() {
-        auto slice = make_expr<SliceExpr>();
-        slice->start = ctx()->s_expr.popx();
-        if(is_expression()){        // <start>:<stop>
+            e->kwargs.push_back({key, ctx()->s_expr.popx()});
+        } else {
             EXPR();
-            slice->stop = ctx()->s_expr.popx();
-            // try optional step
-            if(match(TK(":"))){     // <start>:<stop>:<step>
-                EXPR();
-                slice->step = ctx()->s_expr.popx();
+            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());
             }
-        }else if(match(TK(":"))){   // <start>::<step>
+        }
+        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<NameExpr>(name, scope));
+}
+
+void Compiler::exprAttrib() {
+    consume(TK("@id"));
+    ctx()->s_expr.push(make_expr<AttribExpr>(ctx()->s_expr.popx(), StrName::get(prev().sv())));
+}
+
+void Compiler::exprSlice0() {
+    auto slice = make_expr<SliceExpr>();
+    if(is_expression()) {  // :<stop>
+        EXPR();
+        slice->stop = ctx()->s_expr.popx();
+        // try optional step
+        if(match(TK(":"))) {  // :<stop>:<step>
             EXPR();
             slice->step = ctx()->s_expr.popx();
-        }   // else <start>:
-        ctx()->s_expr.push(std::move(slice));
-    }
-    
-    void Compiler::exprSubscr() {
-        auto e = make_expr<SubscrExpr>();
-        match_newlines_repl();
-        e->a = ctx()->s_expr.popx();        // a
-        EXPR_TUPLE(true);
-        e->b = ctx()->s_expr.popx();        // a[<expr>]
-        match_newlines_repl();
-        consume(TK("]"));
-        ctx()->s_expr.push(std::move(e));
-    }
-
-    void Compiler::exprLiteral0() {
-        ctx()->s_expr.push(make_expr<Literal0Expr>(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 ':'");
+    } else if(match(TK(":"))) {
+        if(is_expression()) {  // ::<step>
+            EXPR();
+            slice->step = ctx()->s_expr.popx();
+        }  // else ::
+    }  // else :
+    ctx()->s_expr.push(std::move(slice));
+}
+
+void Compiler::exprSlice1() {
+    auto slice = make_expr<SliceExpr>();
+    slice->start = ctx()->s_expr.popx();
+    if(is_expression()) {  // <start>:<stop>
+        EXPR();
+        slice->stop = ctx()->s_expr.popx();
+        // try optional step
+        if(match(TK(":"))) {  // <start>:<stop>:<step>
+            EXPR();
+            slice->step = ctx()->s_expr.popx();
         }
-        consume(TK("@indent"));
-        while (curr().type != TK("@dedent")) {
-            match_newlines();
-            (this->*callback)();
-            match_newlines();
+    } else if(match(TK(":"))) {  // <start>::<step>
+        EXPR();
+        slice->step = ctx()->s_expr.popx();
+    }  // else <start>:
+    ctx()->s_expr.push(std::move(slice));
+}
+
+void Compiler::exprSubscr() {
+    auto e = make_expr<SubscrExpr>();
+    match_newlines_repl();
+    e->a = ctx()->s_expr.popx();  // a
+    EXPR_TUPLE(true);
+    e->b = ctx()->s_expr.popx();  // a[<expr>]
+    match_newlines_repl();
+    consume(TK("]"));
+    ctx()->s_expr.push(std::move(e));
+}
+
+void Compiler::exprLiteral0() { ctx()->s_expr.push(make_expr<Literal0Expr>(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;
         }
-        consume(TK("@dedent"));
-    }
-
-    // import a [as b]
-    // import a [as b], c [as d]
-    void Compiler::compile_normal_import() {
-        do {
+        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"));
-            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();
+            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<dots; i++) ss << '.';
-
-        if(dots > 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"));
+    SStream ss;
+    for(int i = 0; i < dots; i++)
+        ss << '.';
+
+    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 <module> 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);
-        consume_end_stmt();
+            ss << "." << prev().sv();
+        }
     }
 
-    bool Compiler::is_expression(bool allow_slice){
-        PrattCallback prefix = rules[curr().type].prefix;
-        return prefix != nullptr && (allow_slice || curr().type!=TK(":"));
-    }
+    ctx()->emit_(OP_IMPORT_PATH, ctx()->add_const_string(ss.str().sv()), prev().line);
+    consume(TK("import"));
 
-    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)();
-        }
+    if(match(TK("*"))) {
+        if(name_scope() != NAME_GLOBAL) SyntaxError("from <module> 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;
     }
 
-    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);
+    do {
+        ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE);
+        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();
         }
-    }
-
-    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);
+        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();
+}
+
+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()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true);
+        ctx()->patch_jump(exit_patch);
+    } else {
         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);
+}
+
+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();
-        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();
-        }
+        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<int, 6> 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"));
-        }
+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<int, 6> patches;
+    patches.push_back(ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE));
+    ctx()->exit_block();
 
-        if(match(TK("finally"))){
-            int patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
-            finally_entry = ctx()->co->codes.size();
+    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();
-            ctx()->emit_(OP_JUMP_ABSOLUTE_TOP, BC_NOARG, BC_KEEPLINE);
+            patches.push_back(ctx()->emit_(OP_JUMP_FORWARD, 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);
-        }
+        } while(curr().type == TK("except"));
     }
 
-    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);
-        }
+    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);
 
-    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<BinaryExpr>(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;
-        }
+    // 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_stmt() {
-        if(match(TK("class"))){
-            compile_class();
-            return;
+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<BinaryExpr>(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();
         }
-        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;
+            return true;
+        case TK("="): {
+            int n = 0;
+            while(match(TK("="))) {
+                EXPR_TUPLE();
+                n += 1;
             }
-            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;
+            // 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();
             }
-            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"): {
+        }
+            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();
-                Expr_ e = ctx()->s_expr.popx();
-                bool ok = e->emit_del(ctx());
-                if(!ok) SyntaxError();
-                consume_end_stmt();
-            } break;
-            case TK("with"): {
-                EXPR();    // [ <expr> ]
                 ctx()->emit_expr();
-                ctx()->enter_block(CodeBlockType::CONTEXT_MANAGER);
-                Expr_ as_name;
-                if(match(TK("as"))){
-                    consume(TK("@id"));
-                    as_name = make_expr<NameExpr>(prev().str(), name_scope());
-                }
-                ctx()->emit_(OP_WITH_ENTER, BC_NOARG, prev().line);
-                // [ <expr> <expr>.__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<NameExpr*>(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();
+                ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, kw_line);
             }
-        }
-    }
-
-    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();
+            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(TK(")"));
-        }
-        if(base == nullptr){
-            ctx()->emit_(OP_LOAD_NONE, BC_NOARG, prev().line);
-        }else {
-            base->emit_(ctx());
+            consume_end_stmt();
+            break;
         }
-        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");
+        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;
         }
-        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);
+        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;
         }
-
-        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");
+        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();  // [ <expr> ]
+            ctx()->emit_expr();
+            ctx()->enter_block(CodeBlockType::CONTEXT_MANAGER);
+            Expr_ as_name;
+            if(match(TK("as"))) {
+                consume(TK("@id"));
+                as_name = make_expr<NameExpr>(prev().str(), name_scope());
             }
-            else if(match(TK("**"))){
-                state = 3;
+            ctx()->emit_(OP_WITH_ENTER, BC_NOARG, prev().line);
+            // [ <expr> <expr>.__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"));
-            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(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<NameExpr*>(ctx()->s_expr.top().get());
+                        ctx()->emit_(OP_ADD_CLASS_ANNOTATION, ne->name.index, BC_KEEPLINE);
+                    }
                 }
             }
-            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"));
+            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);
                     }
-                    decl->add_kwarg(index, name, value);
-                } break;
-                case 3:
-                    decl->starred_kwarg = index;
-                    state+=1;
-                    break;
+                } else {
+                    assert(ctx()->s_expr.size() == 1);
+                    ctx()->s_expr.pop();
+                }
             }
-        } while (match(TK(",")));
+            consume_end_stmt();
+        }
     }
-
-    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(")"));
+}
+
+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();
         }
-        if(match(TK("->"))) consume_type_hints();
-        compile_block_body();
-        pop_context();
+        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);
 
-        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);
+    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);
-
-        if(!ctx()->is_compiling_class){
-            auto e = make_expr<NameExpr>(decl_name, name_scope());
-            e->emit_store(ctx());
-        }else{
-            int index = StrName(decl_name).index;
-            ctx()->emit_(OP_STORE_CLASS_ATTR, index, prev().line);
+        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();
 
-    PyVar Compiler::to_object(const TokenValue& value){
-        PyVar obj = nullptr;
-        if(std::holds_alternative<i64>(value)){
-            obj = VAR(std::get<i64>(value));
+        // check duplicate argument name
+        for(int j: decl->args) {
+            if(decl->code->varnames[j] == name) { SyntaxError("duplicate argument name"); }
         }
-        if(std::holds_alternative<f64>(value)){
-            obj = VAR(std::get<f64>(value));
+        for(auto& kv: decl->kwargs) {
+            if(decl->code->varnames[kv.index] == name) { SyntaxError("duplicate argument name"); }
         }
-        if(std::holds_alternative<Str>(value)){
-            obj = VAR(std::get<Str>(value));
+        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");
         }
-        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;
+        // 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;
         }
-        return nullptr;
+    } 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(")"));
     }
-
-    Compiler::Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope)
-            :lexer(vm, std::make_shared<SourceData>(source, filename, mode)){
-        this->vm = vm;
-        this->unknown_global_scope = unknown_global_scope;
-        init_pratt_rules();
+    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();
+        }
     }
-
-    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<std::string_view, int> 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');
-                }
-            }
+    ctx()->emit_(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line);
+
+    _add_decorators(decorators);
+
+    if(!ctx()->is_compiling_class) {
+        auto e = make_expr<NameExpr>(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<i64>(value)) { obj = VAR(std::get<i64>(value)); }
+    if(std::holds_alternative<f64>(value)) { obj = VAR(std::get<f64>(value)); }
+    if(std::holds_alternative<Str>(value)) { obj = VAR(std::get<Str>(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);
         }
-        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++;
+        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());
         }
-        
-        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()] << ',';
+        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<SourceData>(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<std::string_view, int> 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');
             }
-            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){
+        }
+    }
+    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<decltype(arg)>;
-                if constexpr(std::is_same_v<T, i64>){
+                if constexpr(std::is_same_v<T, i64>) {
                     ss << 'I' << arg;
-                }else if constexpr(std::is_same_v<T, f64>){
+                } else if constexpr(std::is_same_v<T, f64>) {
                     ss << 'F' << arg;
-                }else if constexpr(std::is_same_v<T, Str>){
+                } else if constexpr(std::is_same_v<T, Str>) {
                     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');
+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");
-        }
+    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<Str>& precompiled_tokens = lexer.src->_precompiled_tokens;
-        for(int i=0; i<count; i++){
-            precompiled_tokens.push_back(deserializer.read_string('\n'));
-        }
+    int count = deserializer.read_count();
+    vector<Str>& precompiled_tokens = lexer.src->_precompiled_tokens;
+    for(int i = 0; i < count; i++) {
+        precompiled_tokens.push_back(deserializer.read_string('\n'));
+    }
 
-        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;
-            }
+    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;
+        }
 
-            if(deserializer.match_char(',')){
-                t.line = tokens.back().line;
-            }else{
-                t.line = (int)deserializer.read_uint(',');
-            }
+        if(deserializer.match_char(',')) {
+            t.line = tokens.back().line;
+        } else {
+            t.line = (int)deserializer.read_uint(',');
+        }
 
-            if(deserializer.match_char(',')){
-                t.brackets_level = tokens.back().brackets_level;
-            }else{
-                t.brackets_level = (int)deserializer.read_uint(',');
-            }
+        if(deserializer.match_char(',')) {
+            t.brackets_level = tokens.back().brackets_level;
+        } else {
+            t.brackets_level = (int)deserializer.read_uint(',');
+        }
 
-            char type = deserializer.read_char();
-            switch(type){
-                case 'I': t.value = deserializer.read_uint('\n'); break;
-                case 'F': t.value = deserializer.read_float('\n'); break;
-                case 'S':
-                    t.value = deserializer.read_string_from_hex('\n');
-                    break;
-                default: t.value = {}; break;
-            }
-            tokens.push_back(t);
+        char type = deserializer.read_char();
+        switch(type) {
+            case 'I': t.value = deserializer.read_uint('\n'); break;
+            case 'F': t.value = deserializer.read_float('\n'); break;
+            case 'S': t.value = deserializer.read_string_from_hex('\n'); break;
+            default: t.value = {}; break;
         }
+        tokens.push_back(t);
     }
+}
 
-    CodeObject_ Compiler::compile(){
-        assert(i == 0);       // make sure it is the first time to compile
+CodeObject_ Compiler::compile() {
+    assert(i == 0);  // make sure it is the first time to compile
 
-        if(lexer.src->is_precompiled){
-            from_precompiled(lexer.src->source.c_str());
-        }else{
-            this->tokens = lexer.run();
-        }
+    if(lexer.src->is_precompiled) {
+        from_precompiled(lexer.src->source.c_str());
+    } else {
+        this->tokens = lexer.run();
+    }
 
-        CodeObject_ code = push_global_context();
+    CodeObject_ code = push_global_context();
 
-        advance();          // skip @sof, so prev() is always valid
-        match_newlines();   // skip possible leading '\n'
+    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<Exception>();
-        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<Exception>();
+    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<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<char*, int>(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++;
+    std::string_view retval(start, curr - start);
+    curr++;  // skip the delimiter
+    return retval;
+}
+
+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<char*, int>(buffer, s.size() / 2);
+}
+
+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++;
-        return read_uint('\n');
-    }
-
-    i64 TokenDeserializer::read_uint(char c){
-        i64 out = 0;
-        while(*curr != c){
-            out = out*10 + (*curr-'0');
-            curr++;
-        }
-        curr++;     // skip the delimiter
-        return out;
     }
+    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
+f64 TokenDeserializer::read_float(char c) {
+    std::string_view sv = read_string(c);
+    return std::stod(std::string(sv));
+}
+}  // namespace pkpy

+ 714 - 708
src/compiler/expr.cpp

@@ -1,771 +1,777 @@
 #include "pocketpy/compiler/expr.hpp"
 #include "pocketpy/interpreter/vm.hpp"
 
-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_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;
-        }
+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_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;
+}
+
+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);
+    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<Str>().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<Str>().sv();
-            _co_consts_string_dedup_map[key] = index;
-            return 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_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;
-        }
-    }
-
-
-    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;
-        }
-        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);
+}
+
+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;
+    }
+}
+
+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;
+    }
+    return true;
+}
+
+bool NameExpr::emit_store(CodeEmitContext* ctx) {
+    if(ctx->is_compiling_class) {
+        ctx->emit_(OP_STORE_CLASS_ATTR, name.index, 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 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<i64>(value)){
-            i64 _val = std::get<i64>(value);
+    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 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<i64>(value)) {
+        i64 _val = std::get<i64>(value);
+        ctx->emit_int(_val, line);
+        return;
+    }
+    if(std::holds_alternative<f64>(value)) {
+        f64 _val = std::get<f64>(value);
+        ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
+        return;
+    }
+    if(std::holds_alternative<Str>(value)) {
+        std::string_view key = std::get<Str>(value).sv();
+        ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(key), line);
+        return;
+    }
+}
+
+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<LiteralExpr*>(child.get());
+        if(std::holds_alternative<i64>(lit->value)) {
+            i64 _val = -std::get<i64>(lit->value);
             ctx->emit_int(_val, line);
             return;
         }
-        if(std::holds_alternative<f64>(value)){
-            f64 _val = std::get<f64>(value);
+        if(std::holds_alternative<f64>(lit->value)) {
+            f64 _val = -std::get<f64>(lit->value);
             ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
             return;
         }
-        if(std::holds_alternative<Str>(value)){
-            std::string_view key = std::get<Str>(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 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<LiteralExpr*>(child.get());
-            if(std::holds_alternative<i64>(lit->value)){
-                i64 _val = -std::get<i64>(lit->value);
-                ctx->emit_int(_val, line);
-                return;
-            }
-            if(std::holds_alternative<f64>(lit->value)){
-                f64 _val = -std::get<f64>(lit->value);
-                ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), 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 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);
+    if(stop) {
+        stop->emit_(ctx);
+    } else {
+        ctx->emit_(OP_LOAD_NONE, 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);
-        }
+    if(step) {
+        step->emit_(ctx);
+    } else {
+        ctx->emit_(OP_LOAD_NONE, BC_NOARG, 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);
-                }
-            }
-        }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;
-    }
+    ctx->emit_(OP_BUILD_SLICE, BC_NOARG, line);
+}
 
-    bool TupleExpr::emit_del(CodeEmitContext* ctx){
-        for(auto& e: items){
-            bool ok = e->emit_del(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);
     }
+}
 
-    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();
+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
     }
 
-
-    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
+    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);
             }
         }
-        // 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;
-                }
+    } 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;
             }
         }
-        
-        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);
-        }
+    if(!is_fastpath) {
+        int index = ctx->add_const_string(expr.sv());
+        ctx->emit_(OP_FSTRING_EVAL, index, line);
     }
 
-    static bool is_fmt_valid_char(char c){
-        switch(c){
+    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;
-        }
-    }
-
-    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);
+            // 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
+
+    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;
+                } 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{
-                        // { -> }
-                        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);
+            } 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++;
-                    continue;   // skip j++
+                } 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;
     }
-
-    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<AttribExpr*>(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);
-                    }
+    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<AttribExpr*>(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);
                 }
-                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;
-        }
-    }
-
-    void BinaryExpr::_emit_compare(CodeEmitContext* ctx, small_vector_2<int, 6>& jmps){
-        if(lhs->is_compare()){
-            static_cast<BinaryExpr*>(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()
-        }
+            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;
+    }
+}
+
+void BinaryExpr::_emit_compare(CodeEmitContext* ctx, small_vector_2<int, 6>& jmps) {
+    if(lhs->is_compare()) {
+        static_cast<BinaryExpr*>(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<int, 6> jmps;
+    if(is_compare() && lhs->is_compare()) {
+        // (a < b) < c
+        static_cast<BinaryExpr*>(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<int, 6> jmps;
-        if(is_compare() && lhs->is_compare()){
-            // (a < b) < c
-            static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
-            // [b, RES]
-        }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);
-    }
-
-}   // namespace pkpy
+    } 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);
+}
+
+}  // namespace pkpy

Diff do ficheiro suprimidas por serem muito extensas
+ 2 - 1
src/compiler/lexer.cpp


Diff do ficheiro suprimidas por serem muito extensas
+ 1097 - 988
src/interpreter/ceval.cpp


+ 198 - 188
src/interpreter/cffi.cpp

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

+ 107 - 105
src/interpreter/frame.cpp

@@ -3,126 +3,128 @@
 
 #include <stdexcept>
 
-namespace pkpy{
-    PyVar* FastLocals::try_get_name(StrName name){
-        int index = co->varnames_inv.try_get(name);
-        if(index == -1) return nullptr;
-        return &a[index];
-    }
+namespace pkpy {
+PyVar* FastLocals::try_get_name(StrName name) {
+    int index = co->varnames_inv.try_get(name);
+    if(index == -1) return nullptr;
+    return &a[index];
+}
 
-    NameDict_ FastLocals::to_namedict(){
-        NameDict_ dict = std::make_shared<NameDict>();
-        co->varnames_inv.apply([&](StrName name, int index){
-            PyVar value = a[index];
-            if(value) dict->set(name, value);
-        });
-        return dict;
-    }
+NameDict_ FastLocals::to_namedict() {
+    NameDict_ dict = std::make_shared<NameDict>();
+    co->varnames_inv.apply([&](StrName name, int index) {
+        PyVar value = a[index];
+        if(value) dict->set(name, value);
+    });
+    return dict;
+}
 
-    PyVar* Frame::f_closure_try_get(StrName name){
-        if(_callable == nullptr) return nullptr;
-        Function& fn = _callable->as<Function>();
-        if(fn._closure == nullptr) return nullptr;
-        return fn._closure->try_get_2(name);
-    }
+PyVar* Frame::f_closure_try_get(StrName name) {
+    if(_callable == nullptr) return nullptr;
+    Function& fn = _callable->as<Function>();
+    if(fn._closure == nullptr) return nullptr;
+    return fn._closure->try_get_2(name);
+}
 
-    int Frame::prepare_jump_exception_handler(ValueStack* _s){
-        // try to find a parent try block
-        int i = co->lines[ip()].iblock;
-        while(i >= 0){
-            if(co->blocks[i].type == CodeBlockType::TRY_EXCEPT) break;
-            i = co->blocks[i].parent;
-        }
-        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::prepare_jump_exception_handler(ValueStack* _s) {
+    // try to find a parent try block
+    int i = co->lines[ip()].iblock;
+    while(i >= 0) {
+        if(co->blocks[i].type == CodeBlockType::TRY_EXCEPT) break;
+        i = co->blocks[i].parent;
     }
+    if(i < 0) return -1;
+    PyVar obj = _s->popx();  // pop exception object
+    UnwindTarget* uw = find_unwind_target(i);
+    _s->reset(actual_sp_base() + uw->offset);  // unwind the stack
+    _s->push(obj);                             // push it back
+    return co->blocks[i].end;
+}
 
-    int Frame::_exit_block(ValueStack* _s, int i){
-        auto type = co->blocks[i].type;
-        if(type == CodeBlockType::FOR_LOOP){
-            _s->pop();  // pop the iterator
-            // pop possible stack memory slots
-            if(_s->top().type == kTpStackMemoryIndex){
-                int count = _s->top().as<StackMemory>().count;
-                assert(count < 0);
-                _s->_sp += count;
-                _s->_sp -= 2;   // pop header and tail
-            }
-        }else if(type==CodeBlockType::CONTEXT_MANAGER){
-            _s->pop();
+int Frame::_exit_block(ValueStack* _s, int i) {
+    auto type = co->blocks[i].type;
+    if(type == CodeBlockType::FOR_LOOP) {
+        _s->pop();  // pop the iterator
+        // pop possible stack memory slots
+        if(_s->top().type == kTpStackMemoryIndex) {
+            int count = _s->top().as<StackMemory>().count;
+            assert(count < 0);
+            _s->_sp += count;
+            _s->_sp -= 2;  // pop header and tail
         }
-        return co->blocks[i].parent;
+    } else if(type == CodeBlockType::CONTEXT_MANAGER) {
+        _s->pop();
     }
+    return co->blocks[i].parent;
+}
 
-    void Frame::prepare_jump_break(ValueStack* _s, int target){
-        int i = co->lines[ip()].iblock;
-        if(target >= co->codes.size()){
-            while(i>=0) i = _exit_block(_s, i);
-        }else{
-            // BUG (solved)
-            // for i in range(4):
-            //     _ = 0
-            // # if there is no op here, the block check will fail
-            // while i: --i
-            int next_block = co->lines[target].iblock;
-            while(i>=0 && i!=next_block) i = _exit_block(_s, i);
-            if(i!=next_block) throw std::runtime_error("invalid jump");
-        }
+void Frame::prepare_jump_break(ValueStack* _s, int target) {
+    int i = co->lines[ip()].iblock;
+    if(target >= co->codes.size()) {
+        while(i >= 0)
+            i = _exit_block(_s, i);
+    } else {
+        // BUG (solved)
+        // for i in range(4):
+        //     _ = 0
+        // # if there is no op here, the block check will fail
+        // while i: --i
+        int next_block = co->lines[target].iblock;
+        while(i >= 0 && i != next_block)
+            i = _exit_block(_s, i);
+        if(i != next_block) throw std::runtime_error("invalid jump");
     }
+}
 
-    void Frame::set_unwind_target(PyVar* _sp){
-        int iblock = co->lines[ip()].iblock;
-        UnwindTarget* existing = find_unwind_target(iblock);
-        if(existing){
-            existing->offset = _sp - actual_sp_base();
-        }else{
-            UnwindTarget* prev = _uw_list;
-            _uw_list = new UnwindTarget(iblock, _sp - actual_sp_base());
-            _uw_list->next = prev;
-        }
+void Frame::set_unwind_target(PyVar* _sp) {
+    int iblock = co->lines[ip()].iblock;
+    UnwindTarget* existing = find_unwind_target(iblock);
+    if(existing) {
+        existing->offset = _sp - actual_sp_base();
+    } else {
+        UnwindTarget* prev = _uw_list;
+        _uw_list = new UnwindTarget(iblock, _sp - actual_sp_base());
+        _uw_list->next = prev;
     }
+}
 
-    UnwindTarget* Frame::find_unwind_target(int iblock){
-        UnwindTarget* p;
-        for(p=_uw_list; p!=nullptr; p=p->next){
-            if(p->iblock == iblock) return p;
-        }
-        return nullptr;
+UnwindTarget* Frame::find_unwind_target(int iblock) {
+    UnwindTarget* p;
+    for(p = _uw_list; p != nullptr; p = p->next) {
+        if(p->iblock == iblock) return p;
     }
+    return nullptr;
+}
 
-    Frame::~Frame(){
-        while(_uw_list != nullptr){
-            UnwindTarget* p = _uw_list;
-            _uw_list = p->next;
-            delete p;
-        }
+Frame::~Frame() {
+    while(_uw_list != nullptr) {
+        UnwindTarget* p = _uw_list;
+        _uw_list = p->next;
+        delete p;
     }
+}
 
-    void CallStack::pop(){
-        assert(!empty());
-        LinkedFrame* p = _tail;
-        _tail = p->f_back;
-        p->~LinkedFrame();
-        PoolFrame_dealloc(p);
-        --_size;
-    }
+void CallStack::pop() {
+    assert(!empty());
+    LinkedFrame* p = _tail;
+    _tail = p->f_back;
+    p->~LinkedFrame();
+    PoolFrame_dealloc(p);
+    --_size;
+}
 
-    LinkedFrame* CallStack::popx(){
-        assert(!empty());
-        LinkedFrame* p = _tail;
-        _tail = p->f_back;
-        --_size;
-        p->f_back = nullptr;        // unlink
-        return p;
-    }
+LinkedFrame* CallStack::popx() {
+    assert(!empty());
+    LinkedFrame* p = _tail;
+    _tail = p->f_back;
+    --_size;
+    p->f_back = nullptr;  // unlink
+    return p;
+}
 
-    void CallStack::pushx(LinkedFrame* p){
-        p->f_back = _tail;
-        _tail = p;
-        ++_size;
-    }
-}   // namespace pkpy
+void CallStack::pushx(LinkedFrame* p) {
+    p->f_back = _tail;
+    _tail = p;
+    ++_size;
+}
+}  // namespace pkpy

+ 41 - 40
src/interpreter/gc.cpp

@@ -1,55 +1,56 @@
 #include "pocketpy/interpreter/gc.hpp"
 
-namespace pkpy{
+namespace pkpy {
 
-    int ManagedHeap::sweep(){
-        vector<PyObject*> alive;
-        alive.reserve(gen.size() / 2);
-        for(PyObject* obj: gen){
-            if(obj->gc_marked){
-                obj->gc_marked = false;
-                alive.push_back(obj);
-            }else{
+int ManagedHeap::sweep() {
+    vector<PyObject*> alive;
+    alive.reserve(gen.size() / 2);
+    for(PyObject* obj: gen) {
+        if(obj->gc_marked) {
+            obj->gc_marked = false;
+            alive.push_back(obj);
+        } else {
 #if PK_DEBUG_GC_STATS
-                deleted[obj->type] += 1;
+            deleted[obj->type] += 1;
 #endif
-                if(_gc_on_delete) _gc_on_delete(vm, obj);
-                _delete(obj);
-            }
+            if(_gc_on_delete) _gc_on_delete(vm, obj);
+            _delete(obj);
         }
+    }
 
-        // clear _no_gc marked flag
-        for(PyObject* obj: _no_gc) obj->gc_marked = false;
+    // clear _no_gc marked flag
+    for(PyObject* obj: _no_gc)
+        obj->gc_marked = false;
 
-        int freed = gen.size() - alive.size();
+    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;
+    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(){
+void ManagedHeap::_auto_collect() {
 #if !PK_DEBUG_NO_AUTO_GC
-        if(_gc_lock_counter > 0) return;
-        gc_counter = 0;
-        collect();
-        gc_threshold = gen.size() * 2;
-        if(gc_threshold < PK_GC_MIN_THRESHOLD) gc_threshold = PK_GC_MIN_THRESHOLD;
+    if(_gc_lock_counter > 0) return;
+    gc_counter = 0;
+    collect();
+    gc_threshold = gen.size() * 2;
+    if(gc_threshold < PK_GC_MIN_THRESHOLD) gc_threshold = PK_GC_MIN_THRESHOLD;
 #endif
-    }
+}
 
-    int ManagedHeap::collect(){
-        assert(_gc_lock_counter == 0);
-        mark();
-        int freed = sweep();
-        return freed;
-    }
-}   // namespace pkpy
+int ManagedHeap::collect() {
+    assert(_gc_lock_counter == 0);
+    mark();
+    int freed = sweep();
+    return freed;
+}
+}  // namespace pkpy

+ 126 - 112
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<Type>(), [](VM* vm, PyVar _0){ return _0; });
-        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned{
-            RangeIter& self = PK_OBJ_GET(RangeIter, _0);
-            if(self.current >= self.r.stop) return 0;
-            vm->s_data.emplace(VM::tp_int, self.current);
-            self.current += self.r.step;
-            return 1;
-        });
-    }
+void RangeIter::_register(VM* vm, PyObject* mod, PyObject* type) {
+    vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
+        return _0;
+    });
+    vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
+        RangeIter& self = PK_OBJ_GET(RangeIter, _0);
+        if(self.current >= self.r.stop) return 0;
+        vm->s_data.emplace(VM::tp_int, self.current);
+        self.current += self.r.step;
+        return 1;
+    });
+}
 
-    void RangeIterR::_register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0){ return _0; });
-        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned{
-            RangeIterR& self = PK_OBJ_GET(RangeIterR, _0);
-            if(self.current <= self.r.stop) return 0;
-            vm->s_data.emplace(VM::tp_int, self.current);
-            self.current += self.r.step;
-            return 1;
-        });
-    }
+void RangeIterR::_register(VM* vm, PyObject* mod, PyObject* type) {
+    vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
+        return _0;
+    });
+    vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
+        RangeIterR& self = PK_OBJ_GET(RangeIterR, _0);
+        if(self.current <= self.r.stop) return 0;
+        vm->s_data.emplace(VM::tp_int, self.current);
+        self.current += self.r.step;
+        return 1;
+    });
+}
 
-    void ArrayIter::_register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0){ return _0; });
-        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned{
-            ArrayIter& self = _CAST(ArrayIter&, _0);
-            if(self.current == self.end) return 0;
-            vm->s_data.push(*self.current++);
-            return 1;
-        });
-    }
+void ArrayIter::_register(VM* vm, PyObject* mod, PyObject* type) {
+    vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
+        return _0;
+    });
+    vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
+        ArrayIter& self = _CAST(ArrayIter&, _0);
+        if(self.current == self.end) return 0;
+        vm->s_data.push(*self.current++);
+        return 1;
+    });
+}
 
-    void StringIter::_register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0){ return _0; });
-        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned{
-            StringIter& self = _CAST(StringIter&, _0);
-            Str& s = PK_OBJ_GET(Str, self.ref);
-            if(self.i == s.size) return 0;
-            int start = self.i;
-            int len = utf8len(s.data[self.i]);
-            self.i += len;
-            vm->s_data.push(VAR(s.substr(start, len)));
-            return 1;
-        });
-    }
+void StringIter::_register(VM* vm, PyObject* mod, PyObject* type) {
+    vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
+        return _0;
+    });
+    vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
+        StringIter& self = _CAST(StringIter&, _0);
+        Str& s = PK_OBJ_GET(Str, self.ref);
+        if(self.i == s.size) return 0;
+        int start = self.i;
+        int len = utf8len(s.data[self.i]);
+        self.i += len;
+        vm->s_data.push(VAR(s.substr(start, len)));
+        return 1;
+    });
+}
 
-    PyVar Generator::next(VM* vm){
-        if(state == 2) return vm->StopIteration;
-        // reset frame._sp_base
-        lf->frame._sp_base = vm->s_data._sp;
-        lf->frame._locals.a = vm->s_data._sp;
-        // restore the context
-        for(PyVar obj: s_backup) vm->s_data.push(obj);
-        // relocate stack objects (their addresses become invalid)
-        for(PyVar* p=lf->frame.actual_sp_base(); p!=vm->s_data.end(); p++){
-            if(p->type == VM::tp_stack_memory){
-                // TODO: refactor this
-                int count = p->as<StackMemory>().count;
-                if(count < 0){
-                    void* new_p = p + count;
-                    p[1]._1 = reinterpret_cast<i64>(new_p);
-                }
+PyVar Generator::next(VM* vm) {
+    if(state == 2) return vm->StopIteration;
+    // reset frame._sp_base
+    lf->frame._sp_base = vm->s_data._sp;
+    lf->frame._locals.a = vm->s_data._sp;
+    // restore the context
+    for(PyVar obj: s_backup)
+        vm->s_data.push(obj);
+    // relocate stack objects (their addresses become invalid)
+    for(PyVar* p = lf->frame.actual_sp_base(); p != vm->s_data.end(); p++) {
+        if(p->type == VM::tp_stack_memory) {
+            // TODO: refactor this
+            int count = p->as<StackMemory>().count;
+            if(count < 0) {
+                void* new_p = p + count;
+                p[1]._1 = reinterpret_cast<i64>(new_p);
             }
         }
-        s_backup.clear();
-        vm->callstack.pushx(lf);
-        lf = nullptr;
-
-        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;
-        }
     }
+    s_backup.clear();
+    vm->callstack.pushx(lf);
+    lf = nullptr;
 
-    void Generator::_register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0){ return _0; });
-        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned{
-            Generator& self = _CAST(Generator&, _0);
-            PyVar retval = self.next(vm);
-            if(retval == vm->StopIteration) return 0;
-            vm->s_data.push(retval);
-            return 1;
-        });
+    PyVar ret;
+    try {
+        ret = vm->__run_top_frame();
+    } catch(...) {
+        state = 2;  // end this generator immediately when an exception is thrown
+        throw;
     }
 
-    void DictItemsIter::_register(VM *vm, PyObject* mod, PyObject* type){
-        vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0){ return _0; });
-        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned{
-            DictItemsIter& self = _CAST(DictItemsIter&, _0);
-            Dict& d = PK_OBJ_GET(Dict, self.ref);
-            if(self.i == -1) return 0;
-            vm->s_data.push(d._items[self.i].first);
-            vm->s_data.push(d._items[self.i].second);
-            self.i = d._items[self.i].next;
-            return 2;
-        });
+    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 Generator::_register(VM* vm, PyObject* mod, PyObject* type) {
+    vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
+        return _0;
+    });
+    vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
+        Generator& self = _CAST(Generator&, _0);
+        PyVar retval = self.next(vm);
+        if(retval == vm->StopIteration) return 0;
+        vm->s_data.push(retval);
+        return 1;
+    });
+}
+
+void DictItemsIter::_register(VM* vm, PyObject* mod, PyObject* type) {
+    vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
+        return _0;
+    });
+    vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
+        DictItemsIter& self = _CAST(DictItemsIter&, _0);
+        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){
+PyVar VM::__py_generator(LinkedFrame* frame, ArgsView buffer) {
     return vm->new_user_object<Generator>(std::move(frame), buffer);
 }
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 24 - 26
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
+}  // namespace pkpy

Diff do ficheiro suprimidas por serem muito extensas
+ 416 - 421
src/interpreter/vm.cpp


+ 113 - 118
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<Array2d>(cls);
         });
 
-        vm->bind(type, "__init__(self, n_cols: int, n_rows: int, default=None)", [](VM* vm, ArgsView args){
+        vm->bind(type, "__init__(self, n_cols: int, n_rows: int, default=None)", [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int n_cols = CAST(int, args[1]);
             int n_rows = CAST(int, args[2]);
-            if(n_cols <= 0 || n_rows <= 0){
-                vm->ValueError("n_cols and n_rows must be positive integers");
-            }
+            if(n_cols <= 0 || n_rows <= 0) { vm->ValueError("n_cols and n_rows must be positive integers"); }
             self.init(n_cols, n_rows);
-            if(vm->py_callable(args[3])){
-                for(int i = 0; i < self.numel; i++) self.data[i] = vm->call(args[3]);
-            }else{
-                for(int i = 0; i < self.numel; i++) self.data[i] = args[3];
+            if(vm->py_callable(args[3])) {
+                for(int i = 0; i < self.numel; i++)
+                    self.data[i] = vm->call(args[3]);
+            } else {
+                for(int i = 0; i < self.numel; i++)
+                    self.data[i] = args[3];
             }
             return vm->None;
         });
@@ -71,7 +65,7 @@ struct Array2d{
         PY_READONLY_FIELD(Array2d, "numel", numel);
 
         // _get
-        vm->bind_func(type, "_get", 3, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "_get", 3, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
@@ -80,7 +74,7 @@ struct Array2d{
         });
 
         // _set
-        vm->bind_func(type, "_set", 4, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "_set", 4, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
@@ -89,14 +83,14 @@ struct Array2d{
             return vm->None;
         });
 
-        vm->bind_func(type, "is_valid", 3, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "is_valid", 3, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
             return VAR(self.is_valid(col, row));
         });
 
-        vm->bind(type, "get(self, col: int, row: int, default=None)", [](VM* vm, ArgsView args){
+        vm->bind(type, "get(self, col: int, row: int, default=None)", [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
@@ -104,34 +98,34 @@ struct Array2d{
             return self._get(col, row);
         });
 
-        #define HANDLE_SLICE()                              \
-                int start_col, stop_col, step_col;          \
-                int start_row, stop_row, step_row;          \
-                vm->parse_int_slice(PK_OBJ_GET(Slice, xy[0]), self.n_cols, start_col, stop_col, step_col);  \
-                vm->parse_int_slice(PK_OBJ_GET(Slice, xy[1]), self.n_rows, start_row, stop_row, step_row);  \
-                if(step_col != 1 || step_row != 1) vm->ValueError("slice step must be 1");  \
-                int slice_width = stop_col - start_col; \
-                int slice_height = stop_row - start_row;    \
-                if(slice_width <= 0 || slice_height <= 0) vm->ValueError("slice width and height must be positive");
-
-        vm->bind__getitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1){
+#define HANDLE_SLICE()                                                                                                 \
+    int start_col, stop_col, step_col;                                                                                 \
+    int start_row, stop_row, step_row;                                                                                 \
+    vm->parse_int_slice(PK_OBJ_GET(Slice, xy[0]), self.n_cols, start_col, stop_col, step_col);                         \
+    vm->parse_int_slice(PK_OBJ_GET(Slice, xy[1]), self.n_rows, start_row, stop_row, step_row);                         \
+    if(step_col != 1 || step_row != 1) vm->ValueError("slice step must be 1");                                         \
+    int slice_width = stop_col - start_col;                                                                            \
+    int slice_height = stop_row - start_row;                                                                           \
+    if(slice_width <= 0 || slice_height <= 0) vm->ValueError("slice width and height must be positive");
+
+        vm->bind__getitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             const Tuple& xy = CAST(Tuple&, _1);
 
-            if(is_int(xy[0]) && is_int(xy[1])){
+            if(is_int(xy[0]) && is_int(xy[1])) {
                 i64 col = xy[0].as<i64>();
                 i64 row = xy[1].as<i64>();
                 self.check_valid(vm, col, row);
                 return self._get(col, row);
             }
 
-            if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)){
+            if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)) {
                 HANDLE_SLICE();
                 PyVar new_array_obj = vm->new_user_object<Array2d>();
                 Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
                 new_array.init(stop_col - start_col, stop_row - start_row);
-                for(int j = start_row; j < stop_row; j++){
-                    for(int i = start_col; i < stop_col; i++){
+                for(int j = start_row; j < stop_row; j++) {
+                    for(int i = start_col; i < stop_col; i++) {
                         new_array._set(i - start_col, j - start_row, self._get(i, j));
                     }
                 }
@@ -140,10 +134,10 @@ struct Array2d{
             vm->TypeError("expected `tuple[int, int]` or `tuple[slice, slice]` as index");
         });
 
-        vm->bind__setitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1, PyVar _2){
+        vm->bind__setitem__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1, PyVar _2) {
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             const Tuple& xy = CAST(Tuple&, _1);
-            if(is_int(xy[0]) && is_int(xy[1])){
+            if(is_int(xy[0]) && is_int(xy[1])) {
                 i64 col = xy[0].as<i64>();
                 i64 row = xy[1].as<i64>();
                 self.check_valid(vm, col, row);
@@ -151,11 +145,11 @@ struct Array2d{
                 return;
             }
 
-            if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)){
+            if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)) {
                 HANDLE_SLICE();
 
                 bool is_basic_type = false;
-                switch(vm->_tp(_2).index){
+                switch(vm->_tp(_2).index) {
                     case VM::tp_int.index: is_basic_type = true; break;
                     case VM::tp_float.index: is_basic_type = true; break;
                     case VM::tp_str.index: is_basic_type = true; break;
@@ -163,19 +157,19 @@ struct Array2d{
                     default: is_basic_type = _2 == vm->None;
                 }
 
-                if(is_basic_type){
+                if(is_basic_type) {
                     for(int j = 0; j < slice_height; j++)
                         for(int i = 0; i < slice_width; i++)
                             self._set(i + start_col, j + start_row, _2);
                     return;
                 }
 
-                if(!vm->is_user_type<Array2d>(_2)){
+                if(!vm->is_user_type<Array2d>(_2)) {
                     vm->TypeError(_S("expected int/float/str/bool/None or an array2d instance"));
                 }
 
                 Array2d& other = PK_OBJ_GET(Array2d, _2);
-                if(slice_width != other.n_cols || slice_height != other.n_rows){
+                if(slice_width != other.n_cols || slice_height != other.n_rows) {
                     vm->ValueError("array2d size does not match the slice size");
                 }
                 for(int j = 0; j < slice_height; j++)
@@ -186,161 +180,163 @@ struct Array2d{
             vm->TypeError("expected `tuple[int, int]` or `tuple[slice, slice]` as index");
         });
 
-        #undef HANDLE_SLICE
+#undef HANDLE_SLICE
 
-        vm->bind_func(type, "tolist", 1, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "tolist", 1, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             List t(self.n_rows);
-            for(int j = 0; j < self.n_rows; j++){
+            for(int j = 0; j < self.n_rows; j++) {
                 List row(self.n_cols);
-                for(int i = 0; i < self.n_cols; i++) row[i] = self._get(i, j);
+                for(int i = 0; i < self.n_cols; i++)
+                    row[i] = self._get(i, j);
                 t[j] = VAR(std::move(row));
             }
             return VAR(std::move(t));
         });
 
-        vm->bind__len__(type->as<Type>(), [](VM* vm, PyVar _0){
+        vm->bind__len__(type->as<Type>(), [](VM* vm, PyVar _0) {
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             return (i64)self.numel;
         });
 
-        vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar _0) -> Str{
+        vm->bind__repr__(type->as<Type>(), [](VM* vm, PyVar _0) -> Str {
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             return _S("array2d(", self.n_cols, ", ", self.n_rows, ')');
         });
 
-        vm->bind_func(type, "map", 2, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "map", 2, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar f = args[1];
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
-            for(int i = 0; i < new_array.numel; i++){
+            for(int i = 0; i < new_array.numel; i++) {
                 new_array.data[i] = vm->call(f, self.data[i]);
             }
             return new_array_obj;
         });
 
-        vm->bind_func(type, "copy", 1, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "copy", 1, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
-            for(int i = 0; i < new_array.numel; i++){
+            for(int i = 0; i < new_array.numel; i++) {
                 new_array.data[i] = self.data[i];
             }
             return new_array_obj;
         });
 
-        vm->bind_func(type, "fill_", 2, [](VM* vm, ArgsView args){
-            Array2d& self = PK_OBJ_GET(Array2d, args[0]); 
-            for(int i = 0; i < self.numel; i++){
+        vm->bind_func(type, "fill_", 2, [](VM* vm, ArgsView args) {
+            Array2d& self = PK_OBJ_GET(Array2d, args[0]);
+            for(int i = 0; i < self.numel; i++) {
                 self.data[i] = args[1];
             }
             return vm->None;
         });
 
-        vm->bind_func(type, "apply_", 2, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "apply_", 2, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar f = args[1];
-            for(int i = 0; i < self.numel; i++){
+            for(int i = 0; i < self.numel; i++) {
                 self.data[i] = vm->call(f, self.data[i]);
             }
             return vm->None;
         });
 
-        vm->bind_func(type, "copy_", 2, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "copy_", 2, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
-            if(is_type(args[1], VM::tp_list)){
+            if(is_type(args[1], VM::tp_list)) {
                 const List& list = PK_OBJ_GET(List, args[1]);
-                if(list.size() != self.numel){
+                if(list.size() != self.numel) {
                     vm->ValueError("list size must be equal to the number of elements in the array2d");
                 }
-                for(int i = 0; i < self.numel; i++){
+                for(int i = 0; i < self.numel; i++) {
                     self.data[i] = list[i];
                 }
                 return vm->None;
             }
             Array2d& other = CAST(Array2d&, args[1]);
             // if self and other have different sizes, re-initialize self
-            if(self.n_cols != other.n_cols || self.n_rows != other.n_rows){
+            if(self.n_cols != other.n_cols || self.n_rows != other.n_rows) {
                 delete self.data;
                 self.init(other.n_cols, other.n_rows);
             }
-            for(int i = 0; i < self.numel; i++){
+            for(int i = 0; i < self.numel; i++) {
                 self.data[i] = other.data[i];
             }
             return vm->None;
         });
 
-        vm->bind__eq__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1){
+        vm->bind__eq__(type->as<Type>(), [](VM* vm, PyVar _0, PyVar _1) {
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             if(!vm->is_user_type<Array2d>(_1)) return vm->NotImplemented;
             Array2d& other = PK_OBJ_GET(Array2d, _1);
             if(self.n_cols != other.n_cols || self.n_rows != other.n_rows) return vm->False;
-            for(int i = 0; i < self.numel; i++){
+            for(int i = 0; i < self.numel; i++) {
                 if(vm->py_ne(self.data[i], other.data[i])) return vm->False;
             }
             return vm->True;
         });
 
-        vm->bind(type, "count_neighbors(self, value, neighborhood='Moore') -> array2d[int]", [](VM* vm, ArgsView args){
+        vm->bind(type, "count_neighbors(self, value, neighborhood='Moore') -> array2d[int]", [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
             PyVar value = args[1];
             const Str& neighborhood = CAST(Str&, args[2]);
-            if(neighborhood == "Moore"){
-                for(int j = 0; j < new_array.n_rows; j++){
-                    for(int i = 0; i < new_array.n_cols; i++){
+            if(neighborhood == "Moore") {
+                for(int j = 0; j < new_array.n_rows; j++) {
+                    for(int i = 0; i < new_array.n_cols; i++) {
                         int count = 0;
-                        count += self.is_valid(i-1, j-1) && vm->py_eq(self._get(i-1, j-1), value);
-                        count += self.is_valid(i, j-1) && vm->py_eq(self._get(i, j-1), value);
-                        count += self.is_valid(i+1, j-1) && vm->py_eq(self._get(i+1, j-1), value);
-                        count += self.is_valid(i-1, j) && vm->py_eq(self._get(i-1, j), value);
-                        count += self.is_valid(i+1, j) && vm->py_eq(self._get(i+1, j), value);
-                        count += self.is_valid(i-1, j+1) && vm->py_eq(self._get(i-1, j+1), value);
-                        count += self.is_valid(i, j+1) && vm->py_eq(self._get(i, j+1), value);
-                        count += self.is_valid(i+1, j+1) && vm->py_eq(self._get(i+1, j+1), value);
+                        count += self.is_valid(i - 1, j - 1) && vm->py_eq(self._get(i - 1, j - 1), value);
+                        count += self.is_valid(i, j - 1) && vm->py_eq(self._get(i, j - 1), value);
+                        count += self.is_valid(i + 1, j - 1) && vm->py_eq(self._get(i + 1, j - 1), value);
+                        count += self.is_valid(i - 1, j) && vm->py_eq(self._get(i - 1, j), value);
+                        count += self.is_valid(i + 1, j) && vm->py_eq(self._get(i + 1, j), value);
+                        count += self.is_valid(i - 1, j + 1) && vm->py_eq(self._get(i - 1, j + 1), value);
+                        count += self.is_valid(i, j + 1) && vm->py_eq(self._get(i, j + 1), value);
+                        count += self.is_valid(i + 1, j + 1) && vm->py_eq(self._get(i + 1, j + 1), value);
                         new_array._set(i, j, VAR(count));
                     }
                 }
-            }else if(neighborhood == "von Neumann"){
-                for(int j = 0; j < new_array.n_rows; j++){
-                    for(int i = 0; i < new_array.n_cols; i++){
+            } else if(neighborhood == "von Neumann") {
+                for(int j = 0; j < new_array.n_rows; j++) {
+                    for(int i = 0; i < new_array.n_cols; i++) {
                         int count = 0;
-                        count += self.is_valid(i, j-1) && vm->py_eq(self._get(i, j-1), value);
-                        count += self.is_valid(i-1, j) && vm->py_eq(self._get(i-1, j), value);
-                        count += self.is_valid(i+1, j) && vm->py_eq(self._get(i+1, j), value);
-                        count += self.is_valid(i, j+1) && vm->py_eq(self._get(i, j+1), value);
+                        count += self.is_valid(i, j - 1) && vm->py_eq(self._get(i, j - 1), value);
+                        count += self.is_valid(i - 1, j) && vm->py_eq(self._get(i - 1, j), value);
+                        count += self.is_valid(i + 1, j) && vm->py_eq(self._get(i + 1, j), value);
+                        count += self.is_valid(i, j + 1) && vm->py_eq(self._get(i, j + 1), value);
                         new_array._set(i, j, VAR(count));
                     }
                 }
-            }else{
+            } else {
                 vm->ValueError("neighborhood must be 'Moore' or 'von Neumann'");
             }
-            return new_array_obj; 
+            return new_array_obj;
         });
 
-        vm->bind_func(type, "count", 2, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "count", 2, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar value = args[1];
             int count = 0;
-            for(int i = 0; i < self.numel; i++) count += vm->py_eq(self.data[i], value);
+            for(int i = 0; i < self.numel; i++)
+                count += vm->py_eq(self.data[i], value);
             return VAR(count);
         });
 
-        vm->bind_func(type, "find_bounding_rect", 2, [](VM* vm, ArgsView args){
+        vm->bind_func(type, "find_bounding_rect", 2, [](VM* vm, ArgsView args) {
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar value = args[1];
             int left = self.n_cols;
             int top = self.n_rows;
             int right = 0;
             int bottom = 0;
-            for(int j = 0; j < self.n_rows; j++){
-                for(int i = 0; i < self.n_cols; i++){
-                    if(vm->py_eq(self._get(i, j), value)){
+            for(int j = 0; j < self.n_rows; j++) {
+                for(int i = 0; i < self.n_cols; i++) {
+                    if(vm->py_eq(self._get(i, j), value)) {
                         left = (std::min)(left, i);
                         top = (std::min)(top, j);
                         right = (std::max)(right, i);
@@ -360,30 +356,30 @@ struct Array2d{
         });
     }
 
-    void _gc_mark(VM* vm) const{
-        for(int i = 0; i < numel; i++) vm->obj_gc_mark(data[i]);
+    void _gc_mark(VM* vm) const {
+        for(int i = 0; i < numel; i++)
+            vm->obj_gc_mark(data[i]);
     }
 
-    ~Array2d(){
-        delete[] data;
-    }
+    ~Array2d() { delete[] data; }
 };
 
-
-struct Array2dIter{
+struct Array2dIter {
     PK_ALWAYS_PASS_BY_POINTER(Array2dIter)
 
     PyVar ref;
     Array2d* a;
     int i;
-    
-    Array2dIter(PyVar ref, Array2d* a): ref(ref), a(a), i(0){}
 
-    void _gc_mark(VM* vm) const{ vm->obj_gc_mark(ref); }
+    Array2dIter(PyVar ref, Array2d* a) : ref(ref), a(a), i(0) {}
+
+    void _gc_mark(VM* vm) const { vm->obj_gc_mark(ref); }
 
-    static void _register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) { return _0; });
-        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned{
+    static void _register(VM* vm, PyObject* mod, PyObject* type) {
+        vm->bind__iter__(type->as<Type>(), [](VM* vm, PyVar _0) {
+            return _0;
+        });
+        vm->bind__next__(type->as<Type>(), [](VM* vm, PyVar _0) -> unsigned {
             Array2dIter& self = PK_OBJ_GET(Array2dIter, _0);
             if(self.i == self.a->numel) return 0;
             std::div_t res = std::div(self.i, self.a->n_cols);
@@ -395,20 +391,19 @@ struct Array2dIter{
     }
 };
 
-void add_module_array2d(VM* vm){
+void add_module_array2d(VM* vm) {
     PyObject* mod = vm->new_module("array2d");
 
     vm->register_user_class<Array2d>(mod, "array2d", VM::tp_object, true);
     vm->register_user_class<Array2dIter>(mod, "_array2d_iter");
 
     Type array2d_iter_t = vm->_tp_user<Array2d>();
-    vm->bind__iter__(array2d_iter_t, [](VM* vm, PyVar _0){
+    vm->bind__iter__(array2d_iter_t, [](VM* vm, PyVar _0) {
         return vm->new_user_object<Array2dIter>(_0, &_0.obj_get<Array2d>());
     });
-    vm->_all_types[array2d_iter_t].op__iter__ = [](VM* vm, PyVar _0){
+    vm->_all_types[array2d_iter_t].op__iter__ = [](VM* vm, PyVar _0) {
         vm->new_stack_object<Array2dIter>(vm->_tp_user<Array2dIter>(), _0, &_0.obj_get<Array2d>());
     };
 }
 
-
-}   // namespace pkpy
+}  // namespace pkpy

+ 83 - 97
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
 };
-
-static unsigned int
-base64_encode(const unsigned char *in, unsigned int inlen, char *out)
-{
-	int s;
-	unsigned int i;
-	unsigned int j;
-	unsigned char c;
-	unsigned char l;
-
-	s = 0;
-	l = 0;
-	for (i = j = 0; i < inlen; i++) {
-		c = in[i];
-
-		switch (s) {
-		case 0:
-			s = 1;
-			out[j++] = base64en[(c >> 2) & 0x3F];
-			break;
-		case 1:
-			s = 2;
-			out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xF)];
-			break;
-		case 2:
-			s = 0;
-			out[j++] = base64en[((l & 0xF) << 2) | ((c >> 6) & 0x3)];
-			out[j++] = base64en[c & 0x3F];
-			break;
-		}
-		l = c;
-	}
-
-	switch (s) {
-	case 1:
-		out[j++] = base64en[(l & 0x3) << 4];
-		out[j++] = BASE64_PAD;
-		out[j++] = BASE64_PAD;
-		break;
-	case 2:
-		out[j++] = base64en[(l & 0xF) << 2];
-		out[j++] = BASE64_PAD;
-		break;
-	}
-
-	out[j] = 0;
-
-	return j;
+// clang-format on
+
+static unsigned int base64_encode(const unsigned char* in, unsigned int inlen, char* out) {
+    int s;
+    unsigned int i;
+    unsigned int j;
+    unsigned char c;
+    unsigned char l;
+
+    s = 0;
+    l = 0;
+    for(i = j = 0; i < inlen; i++) {
+        c = in[i];
+
+        switch(s) {
+            case 0:
+                s = 1;
+                out[j++] = base64en[(c >> 2) & 0x3F];
+                break;
+            case 1:
+                s = 2;
+                out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xF)];
+                break;
+            case 2:
+                s = 0;
+                out[j++] = base64en[((l & 0xF) << 2) | ((c >> 6) & 0x3)];
+                out[j++] = base64en[c & 0x3F];
+                break;
+        }
+        l = c;
+    }
+
+    switch(s) {
+        case 1:
+            out[j++] = base64en[(l & 0x3) << 4];
+            out[j++] = BASE64_PAD;
+            out[j++] = BASE64_PAD;
+            break;
+        case 2:
+            out[j++] = base64en[(l & 0xF) << 2];
+            out[j++] = BASE64_PAD;
+            break;
+    }
+
+    out[j] = 0;
+
+    return j;
 }
 
-static unsigned int
-base64_decode(const char *in, unsigned int inlen, unsigned char *out)
-{
-	unsigned int i;
-	unsigned int j;
-	unsigned char c;
-
-	if (inlen & 0x3) {
-		return 0;
-	}
-
-	for (i = j = 0; i < inlen; i++) {
-		if (in[i] == BASE64_PAD) {
-			break;
-		}
-		if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST) {
-			return 0;
-		}
-
-		c = base64de[(unsigned char)in[i]];
-		if (c == 255) {
-			return 0;
-		}
-
-		switch (i & 0x3) {
-		case 0:
-			out[j] = (c << 2) & 0xFF;
-			break;
-		case 1:
-			out[j++] |= (c >> 4) & 0x3;
-			out[j] = (c & 0xF) << 4; 
-			break;
-		case 2:
-			out[j++] |= (c >> 2) & 0xF;
-			out[j] = (c & 0x3) << 6;
-			break;
-		case 3:
-			out[j++] |= c;
-			break;
-		}
-	}
-
-	return j;
+static unsigned int base64_decode(const char* in, unsigned int inlen, unsigned char* out) {
+    unsigned int i;
+    unsigned int j;
+    unsigned char c;
+
+    if(inlen & 0x3) { return 0; }
+
+    for(i = j = 0; i < inlen; i++) {
+        if(in[i] == BASE64_PAD) { break; }
+        if(in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST) { return 0; }
+
+        c = base64de[(unsigned char)in[i]];
+        if(c == 255) { return 0; }
+
+        switch(i & 0x3) {
+            case 0: out[j] = (c << 2) & 0xFF; break;
+            case 1:
+                out[j++] |= (c >> 4) & 0x3;
+                out[j] = (c & 0xF) << 4;
+                break;
+            case 2:
+                out[j++] |= (c >> 2) & 0xF;
+                out[j] = (c & 0x3) << 6;
+                break;
+            case 3: out[j++] |= c; break;
+        }
+    }
+
+    return j;
 }
 
-void add_module_base64(VM* vm){
+void add_module_base64(VM* vm) {
     PyObject* mod = vm->new_module("base64");
 
     // b64encode
-    vm->bind_func(mod, "b64encode", 1, [](VM* vm, ArgsView args){
+    vm->bind_func(mod, "b64encode", 1, [](VM* vm, ArgsView args) {
         Bytes& b = CAST(Bytes&, args[0]);
-		unsigned char* p = (unsigned char*)std::malloc(b.size() * 2);
+        unsigned char* p = (unsigned char*)std::malloc(b.size() * 2);
         int size = base64_encode((const unsigned char*)b.data(), b.size(), (char*)p);
         return VAR(Bytes(p, size));
     });
 
     // b64decode
-    vm->bind_func(mod, "b64decode", 1, [](VM* vm, ArgsView args){
+    vm->bind_func(mod, "b64decode", 1, [](VM* vm, ArgsView args) {
         Bytes& b = CAST(Bytes&, args[0]);
         unsigned char* p = (unsigned char*)std::malloc(b.size());
         int size = base64_decode((const char*)b.data(), b.size(), p);
@@ -186,4 +172,4 @@ void add_module_base64(VM* vm){
     });
 }
 
-}	// namespace pkpy
+}  // namespace pkpy

+ 26 - 33
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; i<csvfile.size(); i++){
+        for(int i = 0; i < csvfile.size(); i++) {
             std::string_view line = CAST(Str&, csvfile[i]).sv();
-            if(i == 0){
+            if(i == 0) {
                 // Skip utf8 BOM if there is any.
-                if (strncmp(line.data(), "\xEF\xBB\xBF", 3) == 0) line = line.substr(3);
+                if(strncmp(line.data(), "\xEF\xBB\xBF", 3) == 0) line = line.substr(3);
             }
             List row;
             int j;
             bool in_quote = false;
             std::string buffer;
-__NEXT_LINE:
+        __NEXT_LINE:
             j = 0;
-            while(j < line.size()){
-                switch(line[j]){
+            while(j < line.size()) {
+                switch(line[j]) {
                     case '"':
-                        if(in_quote){
-                            if(j+1 < line.size() && line[j+1] == '"'){
+                        if(in_quote) {
+                            if(j + 1 < line.size() && line[j + 1] == '"') {
                                 buffer += '"';
                                 j++;
-                            }else{
+                            } else {
                                 in_quote = false;
                             }
-                        }else{
+                        } else {
                             in_quote = true;
                         }
                         break;
                     case ',':
-                        if(in_quote){
+                        if(in_quote) {
                             buffer += line[j];
-                        }else{
+                        } else {
                             row.push_back(VAR(buffer));
                             buffer.clear();
                         }
                         break;
-                    case '\r':
-                        break;  // ignore
-                    default:
-                        buffer += line[j];
-                        break;
+                    case '\r': break;  // ignore
+                    default: buffer += line[j]; break;
                 }
                 j++;
             }
-            if(in_quote){
-                if(i == csvfile.size()-1){
+            if(in_quote) {
+                if(i == csvfile.size() - 1) {
                     vm->ValueError("unterminated quote");
-                }else{
+                } else {
                     buffer += '\n';
                     i++;
                     line = CAST(Str&, csvfile[i]).sv();
@@ -67,22 +64,18 @@ __NEXT_LINE:
         return VAR(std::move(ret));
     });
 
-    vm->bind(mod, "DictReader(csvfile: list[str]) -> list[dict]", [](VM* vm, ArgsView args){
+    vm->bind(mod, "DictReader(csvfile: list[str]) -> list[dict]", [](VM* vm, ArgsView args) {
         PyVar csv_reader = vm->_modules["csv"]->attr("reader");
         PyVar ret_obj = vm->call(csv_reader, args[0]);
         const List& ret = CAST(List&, ret_obj);
-        if(ret.size() == 0){
-            vm->ValueError("empty csvfile");
-        }
+        if(ret.size() == 0) { vm->ValueError("empty csvfile"); }
         const List& header = CAST(List&, ret[0]);
         List new_ret;
-        for(int i=1; i<ret.size(); i++){
+        for(int i = 1; i < ret.size(); i++) {
             const List& row = CAST(List&, ret[i]);
-            if(row.size() != header.size()){
-                vm->ValueError("row.size() != header.size()");
-            }
+            if(row.size() != header.size()) { vm->ValueError("row.size() != header.size()"); }
             Dict row_dict;
-            for(int j=0; j<header.size(); j++){
+            for(int j = 0; j < header.size(); j++) {
                 row_dict.set(vm, header[j], row[j]);
             }
             new_ret.push_back(VAR(std::move(row_dict)));
@@ -91,4 +84,4 @@ __NEXT_LINE:
     });
 }
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 34 - 31
src/modules/dataclasses.cpp

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

+ 75 - 111
src/modules/easing.cpp

@@ -3,193 +3,161 @@
 
 #include <cmath>
 
-namespace pkpy{
+namespace pkpy {
 // https://easings.net/
 
 const double kPi = 3.1415926545;
 
-static double easeLinear( double x ) {
-    return x;
-}
+static double easeLinear(double x) { return x; }
 
-static double easeInSine( double x ) {
-    return 1.0 - std::cos( x * kPi / 2 );
-}
+static double easeInSine(double x) { return 1.0 - std::cos(x * kPi / 2); }
 
-static double easeOutSine( double x ) {
-	return std::sin( x * kPi / 2 );
-}
+static double easeOutSine(double x) { return std::sin(x * kPi / 2); }
 
-static double easeInOutSine( double x ) {
-	return -( std::cos( kPi * x ) - 1 ) / 2;
-}
+static double easeInOutSine(double x) { return -(std::cos(kPi * x) - 1) / 2; }
 
-static double easeInQuad( double x ) {
-    return x * x;
-}
+static double easeInQuad(double x) { return x * x; }
 
-static double easeOutQuad( double x ) {
-    return 1 - std::pow( 1 - x, 2 );
-}
+static double easeOutQuad(double x) { return 1 - std::pow(1 - x, 2); }
 
-static double easeInOutQuad( double x ) {
-    if( x < 0.5 ) {
+static double easeInOutQuad(double x) {
+    if(x < 0.5) {
         return 2 * x * x;
     } else {
-        return 1 - std::pow( -2 * x + 2, 2 ) / 2;
+        return 1 - std::pow(-2 * x + 2, 2) / 2;
     }
 }
 
-static double easeInCubic( double x ) {
-    return x * x * x;
-}
+static double easeInCubic(double x) { return x * x * x; }
 
-static double easeOutCubic( double x ) {
-    return 1 - std::pow( 1 - x, 3 );
-}
+static double easeOutCubic(double x) { return 1 - std::pow(1 - x, 3); }
 
-static double easeInOutCubic( double x ) {
-    if( x < 0.5 ) {
+static double easeInOutCubic(double x) {
+    if(x < 0.5) {
         return 4 * x * x * x;
     } else {
-        return 1 - std::pow( -2 * x + 2, 3 ) / 2;
+        return 1 - std::pow(-2 * x + 2, 3) / 2;
     }
 }
 
-static double easeInQuart( double x ) {
-    return std::pow( x, 4 );
-}
+static double easeInQuart(double x) { return std::pow(x, 4); }
 
-static double easeOutQuart( double x ) {
-    return 1 - std::pow( 1 - x, 4 );
-}
+static double easeOutQuart(double x) { return 1 - std::pow(1 - x, 4); }
 
-static double easeInOutQuart( double x ) {
-    if( x < 0.5 ) {
-        return 8 * std::pow( x, 4 );
+static double easeInOutQuart(double x) {
+    if(x < 0.5) {
+        return 8 * std::pow(x, 4);
     } else {
-        return 1 - std::pow( -2 * x + 2, 4 ) / 2;
+        return 1 - std::pow(-2 * x + 2, 4) / 2;
     }
 }
 
-static double easeInQuint( double x ) {
-    return std::pow( x, 5 );
-}
+static double easeInQuint(double x) { return std::pow(x, 5); }
 
-static double easeOutQuint( double x ) {
-    return 1 - std::pow( 1 - x, 5 );
-}
+static double easeOutQuint(double x) { return 1 - std::pow(1 - x, 5); }
 
-static double easeInOutQuint( double x ) {
-    if( x < 0.5 ) {
-        return 16 * std::pow( x, 5 );
+static double easeInOutQuint(double x) {
+    if(x < 0.5) {
+        return 16 * std::pow(x, 5);
     } else {
-        return 1 - std::pow( -2 * x + 2, 5 ) / 2;
+        return 1 - std::pow(-2 * x + 2, 5) / 2;
     }
 }
 
-static double easeInExpo( double x ) {
-    return x == 0 ? 0 : std::pow( 2, 10 * x - 10 );
-}
+static double easeInExpo(double x) { return x == 0 ? 0 : std::pow(2, 10 * x - 10); }
 
-static double easeOutExpo( double x ) {
-    return x == 1 ? 1 : 1 - std::pow( 2, -10 * x );
-}
+static double easeOutExpo(double x) { return x == 1 ? 1 : 1 - std::pow(2, -10 * x); }
 
-static double easeInOutExpo( double x ) {
-    if( x == 0 ) {
+static double easeInOutExpo(double x) {
+    if(x == 0) {
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 1;
-    } else if( x < 0.5 ) {
-        return std::pow( 2, 20 * x - 10 ) / 2;
+    } else if(x < 0.5) {
+        return std::pow(2, 20 * x - 10) / 2;
     } else {
-        return (2 - std::pow( 2, -20 * x + 10 )) / 2;
+        return (2 - std::pow(2, -20 * x + 10)) / 2;
     }
 }
 
-static double easeInCirc( double x ) {
-    return 1 - std::sqrt( 1 - std::pow( x, 2 ) );
-}
+static double easeInCirc(double x) { return 1 - std::sqrt(1 - std::pow(x, 2)); }
 
-static double easeOutCirc( double x ) {
-    return std::sqrt( 1 - std::pow( x - 1, 2 ) );
-}
+static double easeOutCirc(double x) { return std::sqrt(1 - std::pow(x - 1, 2)); }
 
-static double easeInOutCirc( double x ) {
-    if( x < 0.5 ) {
-        return (1 - std::sqrt( 1 - std::pow( 2 * x, 2 ) )) / 2;
+static double easeInOutCirc(double x) {
+    if(x < 0.5) {
+        return (1 - std::sqrt(1 - std::pow(2 * x, 2))) / 2;
     } else {
-        return (std::sqrt( 1 - std::pow( -2 * x + 2, 2 ) ) + 1) / 2;
+        return (std::sqrt(1 - std::pow(-2 * x + 2, 2)) + 1) / 2;
     }
 }
 
-static double easeInBack( double x ) {
+static double easeInBack(double x) {
     const double c1 = 1.70158;
     const double c3 = c1 + 1;
     return c3 * x * x * x - c1 * x * x;
 }
 
-static double easeOutBack( double x ) {
+static double easeOutBack(double x) {
     const double c1 = 1.70158;
     const double c3 = c1 + 1;
-    return 1 + c3 * std::pow( x - 1, 3 ) + c1 * std::pow( x - 1, 2 );
+    return 1 + c3 * std::pow(x - 1, 3) + c1 * std::pow(x - 1, 2);
 }
 
-static double easeInOutBack( double x ) {
+static double easeInOutBack(double x) {
     const double c1 = 1.70158;
     const double c2 = c1 * 1.525;
-    if( x < 0.5 ) {
-        return (std::pow( 2 * x, 2 ) * ((c2 + 1) * 2 * x - c2)) / 2;
+    if(x < 0.5) {
+        return (std::pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2;
     } else {
-        return (std::pow( 2 * x - 2, 2 ) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
+        return (std::pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
     }
 }
 
-static double easeInElastic( double x ) {
+static double easeInElastic(double x) {
     const double c4 = (2 * kPi) / 3;
-    if( x == 0 ) {
+    if(x == 0) {
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 1;
     } else {
-        return -std::pow( 2, 10 * x - 10 ) * std::sin( (x * 10 - 10.75) * c4 );
+        return -std::pow(2, 10 * x - 10) * std::sin((x * 10 - 10.75) * c4);
     }
 }
 
-static double easeOutElastic( double x ) {
+static double easeOutElastic(double x) {
     const double c4 = (2 * kPi) / 3;
-    if( x == 0 ) {
+    if(x == 0) {
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 1;
     } else {
-        return std::pow( 2, -10 * x ) * std::sin( (x * 10 - 0.75) * c4 ) + 1;
+        return std::pow(2, -10 * x) * std::sin((x * 10 - 0.75) * c4) + 1;
     }
 }
 
-static double easeInOutElastic( double x ) {
+static double easeInOutElastic(double x) {
     const double c5 = (2 * kPi) / 4.5;
-    if( x == 0 ) {
+    if(x == 0) {
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 1;
-    } else if( x < 0.5 ) {
-        return -(std::pow( 2, 20 * x - 10 ) * std::sin( (20 * x - 11.125) * c5 )) / 2;
+    } else if(x < 0.5) {
+        return -(std::pow(2, 20 * x - 10) * std::sin((20 * x - 11.125) * c5)) / 2;
     } else {
-        return (std::pow( 2, -20 * x + 10 ) * std::sin( (20 * x - 11.125) * c5 )) / 2 + 1;
+        return (std::pow(2, -20 * x + 10) * std::sin((20 * x - 11.125) * c5)) / 2 + 1;
     }
 }
 
-static double easeOutBounce( double x ) {
+static double easeOutBounce(double x) {
     const double n1 = 7.5625;
     const double d1 = 2.75;
-    if( x < 1 / d1 ) {
+    if(x < 1 / d1) {
         return n1 * x * x;
-    } else if( x < 2 / d1 ) {
+    } else if(x < 2 / d1) {
         x -= 1.5 / d1;
         return n1 * x * x + 0.75;
-    } else if( x < 2.5 / d1 ) {
+    } else if(x < 2.5 / d1) {
         x -= 2.25 / d1;
         return n1 * x * x + 0.9375;
     } else {
@@ -198,23 +166,19 @@ static double easeOutBounce( double x ) {
     }
 }
 
-static double easeInBounce( double x ) {
-    return 1 - easeOutBounce(1 - x);
-}
+static double easeInBounce(double x) { return 1 - easeOutBounce(1 - x); }
 
-static double easeInOutBounce( double x ) {
-    return x < 0.5
-    ? (1 - easeOutBounce(1 - 2 * x)) / 2
-    : (1 + easeOutBounce(2 * x - 1)) / 2;
+static double easeInOutBounce(double x) {
+    return x < 0.5 ? (1 - easeOutBounce(1 - 2 * x)) / 2 : (1 + easeOutBounce(2 * x - 1)) / 2;
 }
 
-void add_module_easing(VM* vm){
+void add_module_easing(VM* vm) {
     PyObject* mod = vm->new_module("easing");
 
-#define EASE(name)  \
-    vm->bind_func(mod, #name, 1, [](VM* vm, ArgsView args){  \
-        f64 t = CAST(f64, args[0]); \
-        return VAR(ease##name(t));   \
+#define EASE(name)                                                                                                     \
+    vm->bind_func(mod, #name, 1, [](VM* vm, ArgsView args) {                                                           \
+        f64 t = CAST(f64, args[0]);                                                                                    \
+        return VAR(ease##name(t));                                                                                     \
     });
 
     EASE(Linear)
@@ -251,4 +215,4 @@ void add_module_easing(VM* vm){
 
 #undef EASE
 }
-}   // namespace pkpy
+}  // namespace pkpy

+ 48 - 53
src/modules/io.cpp

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

Diff do ficheiro suprimidas por serem muito extensas
+ 489 - 464
src/modules/linalg.cpp


+ 52 - 56
src/modules/modules.cpp

@@ -8,9 +8,9 @@
 #include <chrono>
 #include <cmath>
 
-namespace pkpy{
+namespace pkpy {
 
-struct PyStructTime{
+struct PyStructTime {
     int tm_year;
     int tm_mon;
     int tm_mday;
@@ -21,7 +21,7 @@ struct PyStructTime{
     int tm_yday;
     int tm_isdst;
 
-    PyStructTime(std::time_t t){
+    PyStructTime(std::time_t t) {
         std::tm* tm = std::localtime(&t);
         tm_year = tm->tm_year + 1900;
         tm_mon = tm->tm_mon + 1;
@@ -34,7 +34,7 @@ struct PyStructTime{
         tm_isdst = tm->tm_isdst;
     }
 
-    static void _register(VM* vm, PyObject* mod, PyObject* type){
+    static void _register(VM* vm, PyObject* mod, PyObject* type) {
         PY_READONLY_FIELD(PyStructTime, "tm_year", tm_year);
         PY_READONLY_FIELD(PyStructTime, "tm_mon", tm_mon);
         PY_READONLY_FIELD(PyStructTime, "tm_mday", tm_mday);
@@ -47,7 +47,7 @@ struct PyStructTime{
     }
 };
 
-void add_module_time(VM* vm){
+void add_module_time(VM* vm) {
     PyObject* mod = vm->new_module("time");
     vm->register_user_class<PyStructTime>(mod, "struct_time");
 
@@ -59,7 +59,7 @@ void add_module_time(VM* vm){
     vm->bind_func(mod, "sleep", 1, [](VM* vm, ArgsView args) {
         f64 seconds = CAST_F(args[0]);
         auto begin = std::chrono::system_clock::now();
-        while(true){
+        while(true) {
             auto now = std::chrono::system_clock::now();
             f64 elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - begin).count() / 1000.0;
             if(elapsed >= seconds) break;
@@ -74,7 +74,7 @@ void add_module_time(VM* vm){
     });
 }
 
-void add_module_sys(VM* vm){
+void add_module_sys(VM* vm) {
     PyObject* mod = vm->new_module("sys");
     vm->setattr(mod, "version", VAR(PK_VERSION));
     vm->setattr(mod, "platform", VAR(kPlatformStrings[PK_SYS_PLATFORM]));
@@ -97,14 +97,14 @@ void add_module_sys(VM* vm){
     });
 }
 
-void add_module_json(VM* vm){
+void add_module_json(VM* vm) {
     PyObject* mod = vm->new_module("json");
     vm->bind_func(mod, "loads", 1, [](VM* vm, ArgsView args) {
         std::string_view sv;
-        if(is_type(args[0], vm->tp_bytes)){
+        if(is_type(args[0], vm->tp_bytes)) {
             const Bytes& b = PK_OBJ_GET(Bytes, args[0]);
             sv = std::string_view((char*)b.data(), b.size());
-        }else{
+        } else {
             sv = CAST(Str&, args[0]).sv();
         }
         CodeObject_ code = vm->compile(sv, "<json>", JSON_MODE);
@@ -117,10 +117,10 @@ void add_module_json(VM* vm){
 }
 
 // https://docs.python.org/3.5/library/math.html
-void add_module_math(VM* vm){
+void add_module_math(VM* vm) {
     PyObject* mod = vm->new_module("math");
     mod->attr().set("pi", VAR(3.1415926535897932384));
-    mod->attr().set("e" , VAR(2.7182818284590452354));
+    mod->attr().set("e", VAR(2.7182818284590452354));
     mod->attr().set("inf", VAR(std::numeric_limits<double>::infinity()));
     mod->attr().set("nan", VAR(std::numeric_limits<double>::quiet_NaN()));
 
@@ -131,7 +131,7 @@ void add_module_math(VM* vm){
         List& list = CAST(List&, args[0]);
         double sum = 0;
         double c = 0;
-        for(PyVar arg : list){
+        for(PyVar arg: list) {
             double x = CAST_F(arg);
             double y = x - c;
             double t = sum + y;
@@ -145,7 +145,7 @@ void add_module_math(VM* vm){
         i64 b = CAST(i64, args[1]);
         if(a < 0) a = -a;
         if(b < 0) b = -b;
-        while(b != 0){
+        while(b != 0) {
             i64 t = b;
             b = a % b;
             a = t;
@@ -165,7 +165,7 @@ void add_module_math(VM* vm){
 
     vm->bind_func(mod, "exp", 1, PK_LAMBDA(VAR(std::exp(CAST_F(args[0])))));
 
-    vm->bind(mod, "log(x, base=2.718281828459045)", [](VM* vm, ArgsView args){
+    vm->bind(mod, "log(x, base=2.718281828459045)", [](VM* vm, ArgsView args) {
         f64 x = CAST_F(args[0]);
         f64 base = CAST_F(args[1]);
         return VAR(std::log(x) / std::log(base));
@@ -185,7 +185,7 @@ void add_module_math(VM* vm){
     vm->bind_func(mod, "cos", 1, PK_LAMBDA(VAR(std::cos(CAST_F(args[0])))));
     vm->bind_func(mod, "sin", 1, PK_LAMBDA(VAR(std::sin(CAST_F(args[0])))));
     vm->bind_func(mod, "tan", 1, PK_LAMBDA(VAR(std::tan(CAST_F(args[0])))));
-    
+
     vm->bind_func(mod, "degrees", 1, PK_LAMBDA(VAR(CAST_F(args[0]) * 180 / 3.1415926535897932384)));
     vm->bind_func(mod, "radians", 1, PK_LAMBDA(VAR(CAST_F(args[0]) * 3.1415926535897932384 / 180)));
 
@@ -199,12 +199,13 @@ void add_module_math(VM* vm){
         i64 n = CAST(i64, args[0]);
         if(n < 0) vm->ValueError("factorial() not defined for negative values");
         i64 r = 1;
-        for(i64 i=2; i<=n; i++) r *= i;
+        for(i64 i = 2; i <= n; i++)
+            r *= i;
         return VAR(r);
     });
 }
 
-void add_module_traceback(VM* vm){
+void add_module_traceback(VM* vm) {
     PyObject* mod = vm->new_module("traceback");
     vm->bind_func(mod, "print_exc", 0, [](VM* vm, ArgsView args) {
         if(vm->__last_exception == nullptr) vm->ValueError("no exception");
@@ -220,13 +221,13 @@ void add_module_traceback(VM* vm){
     });
 }
 
-void add_module_dis(VM* vm){
+void add_module_dis(VM* vm) {
     PyObject* mod = vm->new_module("dis");
 
     vm->bind_func(mod, "dis", 1, [](VM* vm, ArgsView args) {
         CodeObject_ code;
         PyVar obj = args[0];
-        if(is_type(obj, vm->tp_str)){
+        if(is_type(obj, vm->tp_str)) {
             const Str& source = CAST(Str, obj);
             code = vm->compile(source, "<dis>", EXEC_MODE);
         }
@@ -238,37 +239,36 @@ void add_module_dis(VM* vm){
     });
 }
 
-void add_module_gc(VM* vm){
+void add_module_gc(VM* vm) {
     PyObject* mod = vm->new_module("gc");
     vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(vm->heap.collect())));
 }
 
-void add_module_enum(VM* vm){
+void add_module_enum(VM* vm) {
     PyObject* mod = vm->new_module("enum");
     CodeObject_ code = vm->compile(kPythonLibs__enum, "enum.py", EXEC_MODE);
     vm->_exec(code, mod);
     PyVar Enum = mod->attr("Enum");
-    vm->_all_types[PK_OBJ_GET(Type, Enum)].on_end_subclass = \
-        [](VM* vm, PyTypeInfo* new_ti){
-            new_ti->subclass_enabled = false;    // Enum class cannot be subclassed twice
-            NameDict& attr = new_ti->obj->attr();
-            for(auto [k, v]: attr.items()){
-                // wrap every attribute
-                std::string_view k_sv = k.sv();
-                if(k_sv.empty() || k_sv[0] == '_') continue;
-                attr.set(k, vm->call(new_ti->obj, VAR(k_sv), v));
-            }
-        };
+    vm->_all_types[PK_OBJ_GET(Type, Enum)].on_end_subclass = [](VM* vm, PyTypeInfo* new_ti) {
+        new_ti->subclass_enabled = false;  // Enum class cannot be subclassed twice
+        NameDict& attr = new_ti->obj->attr();
+        for(auto [k, v]: attr.items()) {
+            // wrap every attribute
+            std::string_view k_sv = k.sv();
+            if(k_sv.empty() || k_sv[0] == '_') continue;
+            attr.set(k, vm->call(new_ti->obj, VAR(k_sv), v));
+        }
+    };
 }
 
-void add_module___builtins(VM* vm){
+void add_module___builtins(VM* vm) {
     PyObject* mod = vm->new_module("__builtins");
 
-    vm->bind_func(mod, "next", 1, [](VM* vm, ArgsView args){
+    vm->bind_func(mod, "next", 1, [](VM* vm, ArgsView args) {
         return vm->py_next(args[0]);
     });
 
-    vm->bind_func(mod, "_enable_instance_dict", 1, [](VM* vm, ArgsView args){
+    vm->bind_func(mod, "_enable_instance_dict", 1, [](VM* vm, ArgsView args) {
         PyVar self = args[0];
         if(is_tagged(self)) vm->TypeError("object: tagged object cannot enable instance dict");
         if(self->is_attr_valid()) vm->RuntimeError("object: instance dict is already enabled");
@@ -277,11 +277,11 @@ void add_module___builtins(VM* vm){
     });
 }
 
-
 /************************************************/
 #if PK_ENABLE_PROFILER
 struct LineProfilerW;
-struct _LpGuard{
+
+struct _LpGuard {
     PK_ALWAYS_PASS_BY_POINTER(_LpGuard)
     LineProfilerW* lp;
     VM* vm;
@@ -290,16 +290,16 @@ struct _LpGuard{
 };
 
 // line_profiler wrapper
-struct LineProfilerW{
+struct LineProfilerW {
     LineProfiler profiler;
 
-    static void _register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind_func(type, __new__, 1, [](VM* vm, ArgsView args){
+    static void _register(VM* vm, PyObject* mod, PyObject* type) {
+        vm->bind_func(type, __new__, 1, [](VM* vm, ArgsView args) {
             Type cls = PK_OBJ_GET(Type, args[0]);
             return vm->new_object<LineProfilerW>(cls);
         });
 
-        vm->bind(type, "add_function(self, func)", [](VM* vm, ArgsView args){
+        vm->bind(type, "add_function(self, func)", [](VM* vm, ArgsView args) {
             LineProfilerW& self = PK_OBJ_GET(LineProfilerW, args[0]);
             vm->check_type(args[1], VM::tp_function);
             auto decl = PK_OBJ_GET(Function, args[1]).decl.get();
@@ -307,19 +307,20 @@ struct LineProfilerW{
             return vm->None;
         });
 
-        vm->bind(type, "runcall(self, func, *args)", [](VM* vm, ArgsView view){
+        vm->bind(type, "runcall(self, func, *args)", [](VM* vm, ArgsView view) {
             LineProfilerW& self = PK_OBJ_GET(LineProfilerW, view[0]);
             PyVar func = view[1];
             const Tuple& args = CAST(Tuple&, view[2]);
             vm->s_data.push(func);
             vm->s_data.push(PY_NULL);
-            for(PyVar arg : args) vm->s_data.push(arg);
+            for(PyVar arg: args)
+                vm->s_data.push(arg);
             _LpGuard guard(&self, vm);
             PyVar ret = vm->vectorcall(args.size());
             return ret;
         });
 
-        vm->bind(type, "print_stats(self)", [](VM* vm, ArgsView args){
+        vm->bind(type, "print_stats(self)", [](VM* vm, ArgsView args) {
             LineProfilerW& self = PK_OBJ_GET(LineProfilerW, args[0]);
             vm->stdout_write(self.profiler.stats());
             return vm->None;
@@ -327,28 +328,23 @@ struct LineProfilerW{
     }
 };
 
-
-_LpGuard::_LpGuard(LineProfilerW* lp, VM* vm): lp(lp), vm(vm) {
-    if(vm->_profiler){
-        vm->ValueError("only one profiler can be enabled at a time");
-    }
+_LpGuard::_LpGuard(LineProfilerW* lp, VM* vm) : lp(lp), vm(vm) {
+    if(vm->_profiler) { vm->ValueError("only one profiler can be enabled at a time"); }
     vm->_profiler = &lp->profiler;
     lp->profiler.begin();
 }
 
-_LpGuard::~_LpGuard(){
+_LpGuard::~_LpGuard() {
     vm->_profiler = nullptr;
     lp->profiler.end();
 }
 
-void add_module_line_profiler(VM *vm){
+void add_module_line_profiler(VM* vm) {
     PyObject* mod = vm->new_module("line_profiler");
     vm->register_user_class<LineProfilerW>(mod, "LineProfiler");
 }
 #else
-void add_module_line_profiler(VM* vm){
-    (void)vm;
-}
+void add_module_line_profiler(VM* vm) { (void)vm; }
 #endif
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 45 - 55
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<N; mti++) {
-            mt[mti] = 
-            (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); 
+    void seed(uint32_t s) {
+        mt[0] = s & 0xffffffffUL;
+        for(mti = 1; mti < N; mti++) {
+            mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
             /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
             /* In the previous versions, MSBs of the seed affect   */
             /* only MSBs of the array mt[].                        */
@@ -63,32 +61,31 @@ struct mt19937{
     }
 
     /* generates a random number on [0,0xffffffff]-interval */
-    uint32_t next_uint32(void)
-    {
+    uint32_t next_uint32(void) {
         uint32_t y;
-        static uint32_t mag01[2]={0x0UL, MATRIX_A};
+        static uint32_t mag01[2] = {0x0UL, MATRIX_A};
         /* mag01[x] = x * MATRIX_A  for x=0,1 */
 
-        if (mti >= N) { /* generate N words at one time */
+        if(mti >= N) { /* generate N words at one time */
             int kk;
 
-            if (mti == N+1)   /* if init_genrand() has not been called, */
+            if(mti == N + 1)  /* if init_genrand() has not been called, */
                 seed(5489UL); /* a default initial seed is used */
 
-            for (kk=0;kk<N-M;kk++) {
-                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
-                mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+            for(kk = 0; kk < N - M; kk++) {
+                y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
+                mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
             }
-            for (;kk<N-1;kk++) {
-                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
-                mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+            for(; kk < N - 1; kk++) {
+                y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
+                mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
             }
-            y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
-            mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+            y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
+            mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
 
             mti = 0;
         }
-    
+
         y = mt[mti++];
 
         /* Tempering */
@@ -100,44 +97,36 @@ struct mt19937{
         return y;
     }
 
-    uint64_t next_uint64(void){
-        return (uint64_t(next_uint32()) << 32) | next_uint32();
-    }
+    uint64_t next_uint64(void) { return (uint64_t(next_uint32()) << 32) | next_uint32(); }
 
     /* generates a random number on [0,1)-real-interval */
-    float random(void)
-    {
-        return next_uint32()*(1.0/4294967296.0); /* divided by 2^32 */
-    }
+    float random(void) { return next_uint32() * (1.0 / 4294967296.0); /* divided by 2^32 */ }
 
     /* generates a random number on [a, b]-interval */
-    int64_t randint(int64_t a, int64_t b){
+    int64_t randint(int64_t a, int64_t b) {
         uint64_t delta = b - a + 1;
-        if(delta < 0x80000000UL){
+        if(delta < 0x80000000UL) {
             return a + next_uint32() % (uint32_t)delta;
-        }else{
+        } else {
             return a + next_uint64() % delta;
         }
     }
 
-    float uniform(float a, float b){
-        return a + random() * (b - a);
-    }
+    float uniform(float a, float b) { return a + random() * (b - a); }
 };
 
+namespace pkpy {
 
-namespace pkpy{
-
-struct Random{
+struct Random {
     mt19937 gen;
 
-    Random(){
+    Random() {
         auto count = std::chrono::high_resolution_clock::now().time_since_epoch().count();
         gen.seed((uint32_t)count);
     }
 
-    static void _register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind_func(type, __new__, 1, [](VM* vm, ArgsView args){
+    static void _register(VM* vm, PyObject* mod, PyObject* type) {
+        vm->bind_func(type, __new__, 1, [](VM* vm, ArgsView args) {
             Type cls = PK_OBJ_GET(Type, args[0]);
             return vm->new_object<Random>(cls);
         });
@@ -152,7 +141,7 @@ struct Random{
             Random& self = PK_OBJ_GET(Random, args[0]);
             i64 a = CAST(i64, args[1]);
             i64 b = CAST(i64, args[2]);
-            if (a > b) vm->ValueError("randint(a, b): a must be less than or equal to b");
+            if(a > b) vm->ValueError("randint(a, b): a must be less than or equal to b");
             return VAR(self.gen.randint(a, b));
         });
 
@@ -165,14 +154,14 @@ struct Random{
             Random& self = PK_OBJ_GET(Random, args[0]);
             f64 a = CAST(f64, args[1]);
             f64 b = CAST(f64, args[2]);
-            if (a > b) std::swap(a, b);
+            if(a > b) std::swap(a, b);
             return VAR(self.gen.uniform(a, b));
         });
 
         vm->bind_func(type, "shuffle", 2, [](VM* vm, ArgsView args) {
             Random& self = PK_OBJ_GET(Random, args[0]);
             List& L = CAST(List&, args[1]);
-            for(int i = L.size() - 1; i > 0; i--){
+            for(int i = L.size() - 1; i > 0; i--) {
                 int j = self.gen.randint(0, i);
                 std::swap(L[i], L[j]);
             }
@@ -183,7 +172,7 @@ struct Random{
             Random& self = PK_OBJ_GET(Random, args[0]);
             ArgsView view = vm->cast_array_view(args[1]);
             if(view.empty()) vm->IndexError("cannot choose from an empty sequence");
-            int index = self.gen.randint(0, view.size()-1);
+            int index = self.gen.randint(0, view.size() - 1);
             return view[index];
         });
 
@@ -194,20 +183,21 @@ struct Random{
             int size = view.size();
             if(size == 0) vm->IndexError("cannot choose from an empty sequence");
             array<f64> cum_weights(size);
-            if(args[2] == vm->None){
-                for(int i = 0; i < size; i++) cum_weights[i] = i + 1;
-            }else{
+            if(args[2] == vm->None) {
+                for(int i = 0; i < size; i++)
+                    cum_weights[i] = i + 1;
+            } else {
                 ArgsView weights = vm->cast_array_view(args[2]);
                 if(weights.size() != size) vm->ValueError(_S("len(weights) != ", size));
                 cum_weights[0] = CAST(f64, weights[0]);
-                for(int i = 1; i < size; i++){
+                for(int i = 1; i < size; i++) {
                     cum_weights[i] = cum_weights[i - 1] + CAST(f64, weights[i]);
                 }
             }
             if(cum_weights[size - 1] <= 0) vm->ValueError("total of weights must be greater than zero");
             int k = CAST(int, args[3]);
             List result(k);
-            for(int i = 0; i < k; i++){
+            for(int i = 0; i < k; i++) {
                 f64 r = self.gen.uniform(0.0, cum_weights[size - 1]);
                 int idx = std::lower_bound(cum_weights.begin(), cum_weights.end(), r) - cum_weights.begin();
                 result[i] = data[idx];
@@ -217,7 +207,7 @@ struct Random{
     }
 };
 
-void add_module_random(VM* vm){
+void add_module_random(VM* vm) {
     PyObject* mod = vm->new_module("random");
     vm->register_user_class<Random>(mod, "Random");
     PyVar instance = vm->new_user_object<Random>();
@@ -230,4 +220,4 @@ void add_module_random(VM* vm){
     mod->attr().set("choices", vm->getattr(instance, "choices"));
 }
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 4 - 4
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
+namespace pkpy {
+const PyVar PY_OP_CALL(Type(), new PyObject(Type()));
+const PyVar PY_OP_YIELD(Type(), new PyObject(Type()));
+}  // namespace pkpy

+ 6 - 6
src/objects/codeobject.cpp

@@ -1,9 +1,9 @@
 #include "pocketpy/objects/codeobject.hpp"
 
-namespace pkpy{
+namespace pkpy {
 
-    CodeObject::CodeObject(std::shared_ptr<SourceData> src, const Str& name):
-        src(src), name(name), nlocals(0), start_line(-1), end_line(-1) {
-            blocks.push_back(CodeBlock(CodeBlockType::NO_BLOCK, -1, 0));
-        }
-}   // namespace pkpy
+CodeObject::CodeObject(std::shared_ptr<SourceData> src, const Str& name) :
+    src(src), name(name), nlocals(0), start_line(-1), end_line(-1) {
+    blocks.push_back(CodeBlock(CodeBlockType::NO_BLOCK, -1, 0));
+}
+}  // namespace pkpy

+ 161 - 155
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();
-    }
-
-    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() :
+    _capacity(__Capacity), _mask(__Capacity - 1), _size(0), _critical_size(__Capacity * __LoadFactor + 0.5f),
+    _head_idx(-1), _tail_idx(-1) {
+    __alloc_items();
+}
 
-    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::__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;
     }
-
-    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;
-            }
+}
+
+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;
+    }
+    _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;
     }
 
-    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;
+    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;
+    _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;
-        
-        __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;
+    } 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;
         }
-
-        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;
+    _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);
+    });
+}
+
+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;
     }
-
-    bool Dict::contains(VM* vm, PyVar key) const{
-        bool ok; int i;
-        _probe_0(vm, key, ok, i);
-        return ok;
+    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;
     }
-
-    bool Dict::del(VM* vm, PyVar key){
-        bool ok; int i;
-        _probe_0(vm, key, ok, i);
-        if(!ok) return false;
+    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 = 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); });
-    }
-
-    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;
-        }
-        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
+Dict::~Dict() {
+    if(_items) std::free(_items);
+}
+}  // namespace pkpy

+ 18 - 16
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
+}  // namespace pkpy

+ 3 - 3
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
+namespace pkpy {
+PyVar::PyVar(PyObject* p) : PyVar(p->type, p) {}
+}  // namespace pkpy

+ 59 - 57
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++;
-        }
-        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());
+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++;
     }
-
-    SourceData::SourceData(const Str& filename, CompileMode mode): filename(filename), mode(mode) {
-        line_starts.push_back(this->source.c_str());
+    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());
+}
 
-    std::pair<const char*,const char*> SourceData::_get_line(int lineno) const {
-        if(is_precompiled || lineno == -1) return {nullptr, nullptr};
-        lineno -= 1;
-        if(lineno < 0) lineno = 0;
-        const char* _start = line_starts[lineno];
-        const char* i = _start;
-        // max 300 chars
-        while(*i != '\n' && *i != '\0' && i-_start < 300) i++;
-        return {_start, i};
-    }
+SourceData::SourceData(const Str& filename, CompileMode mode) : filename(filename), mode(mode) {
+    line_starts.push_back(this->source.c_str());
+}
 
-    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 "<?>";
-    }
+std::pair<const char*, const char*> SourceData::_get_line(int lineno) const {
+    if(is_precompiled || lineno == -1) return {nullptr, nullptr};
+    lineno -= 1;
+    if(lineno < 0) lineno = 0;
+    const char* _start = line_starts[lineno];
+    const char* i = _start;
+    // max 300 chars
+    while(*i != '\n' && *i != '\0' && i - _start < 300)
+        i++;
+    return {_start, i};
+}
+
+std::string_view SourceData::get_line(int lineno) const {
+    auto [_0, _1] = _get_line(lineno);
+    if(_0 && _1) return std::string_view(_0, _1 - _0);
+    return "<?>";
+}
 
-    Str SourceData::snapshot(int lineno, const char* cursor, std::string_view name) const{
-        SStream ss;
-        ss << "  " << "File \"" << filename << "\", line " << lineno;
-        if(!name.empty()) ss << ", in " << name;
-        if(!is_precompiled){
-            ss << '\n';
-            std::pair<const char*,const char*> pair = _get_line(lineno);
-            Str line = "<?>";
-            int removed_spaces = 0;
-            if(pair.first && pair.second){
-                line = Str(pair.first, pair.second-pair.first).lstrip();
-                removed_spaces = pair.second - pair.first - line.length();
-                if(line.empty()) line = "<?>";
-            }
-            ss << "    " << line;
-            if(cursor && line != "<?>" && cursor >= pair.first && cursor <= pair.second){
-                auto column = cursor - pair.first - removed_spaces;
-                if(column >= 0) ss << "\n    " << std::string(column, ' ') << "^";
-            }
+Str SourceData::snapshot(int lineno, const char* cursor, std::string_view name) const {
+    SStream ss;
+    ss << "  " << "File \"" << filename << "\", line " << lineno;
+    if(!name.empty()) ss << ", in " << name;
+    if(!is_precompiled) {
+        ss << '\n';
+        std::pair<const char*, const char*> pair = _get_line(lineno);
+        Str line = "<?>";
+        int removed_spaces = 0;
+        if(pair.first && pair.second) {
+            line = Str(pair.first, pair.second - pair.first).lstrip();
+            removed_spaces = pair.second - pair.first - line.length();
+            if(line.empty()) line = "<?>";
+        }
+        ss << "    " << line;
+        if(cursor && line != "<?>" && cursor >= pair.first && cursor <= pair.second) {
+            auto column = cursor - pair.first - removed_spaces;
+            if(column >= 0) ss << "\n    " << std::string(column, ' ') << "^";
         }
-        return ss.str();
     }
-}   // namespace pkpy
+    return ss.str();
+}
+}  // namespace pkpy

+ 19 - 14
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<size(); i++) ret[i] = _begin[i];
+    for(int i = 0; i < size(); i++)
+        ret[i] = _begin[i];
     return ret;
 }
 
-Tuple ArgsView::to_tuple() const{
+Tuple ArgsView::to_tuple() const {
     Tuple ret(size());
-    for(int i=0; i<size(); i++) ret[i] = _begin[i];
+    for(int i = 0; i < size(); i++)
+        ret[i] = _begin[i];
     return ret;
 }
 
-}   // namespace pkpy
+}  // namespace pkpy

Diff do ficheiro suprimidas por serem muito extensas
+ 223 - 199
src/pocketpy.cpp


+ 119 - 138
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>", EVAL_MODE);
@@ -434,7 +425,7 @@ bool pkpy_eval(pkpy_vm* vm_handle, const char* source) {
 }
 
 bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     auto _lock = vm->heap.gc_scope_lock();
@@ -450,8 +441,8 @@ bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
     return true;
 }
 
-bool pkpy_get_unbound_method(pkpy_vm* vm_handle, pkpy_CName name){
-    VM* vm = (VM*) vm_handle;
+bool pkpy_get_unbound_method(pkpy_vm* vm_handle, pkpy_CName name) {
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyVar o = vm->s_data.top();
@@ -466,7 +457,7 @@ bool pkpy_get_unbound_method(pkpy_vm* vm_handle, pkpy_CName name){
 }
 
 bool pkpy_py_repr(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyVar item = vm->s_data.top();
@@ -478,7 +469,7 @@ bool pkpy_py_repr(pkpy_vm* vm_handle) {
 }
 
 bool pkpy_py_str(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyVar item = vm->s_data.top();
@@ -490,7 +481,7 @@ bool pkpy_py_str(pkpy_vm* vm_handle) {
 }
 
 bool pkpy_py_import(pkpy_vm* vm_handle, pkpy_CString name) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_PROTECTED(
         PyVar module = vm->py_import(name);
@@ -501,14 +492,15 @@ bool pkpy_py_import(pkpy_vm* vm_handle, pkpy_CString name) {
 
 /* Error Handling */
 bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PyVar e_t = vm->_main->attr().try_get_likely_found(name);
-    if(e_t == nullptr){
+    if(e_t == nullptr) {
         e_t = vm->builtins->attr().try_get_likely_found(name);
-        if(e_t == nullptr){
+        if(e_t == nullptr) {
             e_t = vm->_t(vm->tp_exception);
-            std::cerr << "[warning] pkpy_error(): " << Str(name).escape() << " not found, fallback to 'Exception'" << std::endl;
+            std::cerr << "[warning] pkpy_error(): " << Str(name).escape() << " not found, fallback to 'Exception'"
+                      << std::endl;
         }
     }
     vm->__c.error = vm->call(e_t, VAR(message)).get();
@@ -516,23 +508,23 @@ bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) {
 }
 
 bool pkpy_check_error(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     return vm->__c.error != nullptr;
 }
 
 bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     // no error
-    if (vm->__c.error == nullptr) return false;
+    if(vm->__c.error == nullptr) return false;
     Exception& e = vm->__c.error->as<Exception>();
-    if (message != nullptr)
+    if(message != nullptr)
         *message = strdup(e.summary().c_str());
     else
         std::cout << e.summary() << std::endl;
     vm->__c.error = nullptr;
-    if(vm->callstack.empty()){
+    if(vm->callstack.empty()) {
         vm->s_data.clear();
-    }else{
+    } else {
         if(vm->__c.s_view.empty()) exit(127);
         vm->s_data.reset(vm->__c.s_view.top().end());
     }
@@ -540,7 +532,7 @@ bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
 }
 
 bool pkpy_vectorcall(pkpy_vm* vm_handle, int argc) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(argc + 2)
     PyVar res;
@@ -550,39 +542,28 @@ bool pkpy_vectorcall(pkpy_vm* vm_handle, int argc) {
     vm->s_data.push(res);
     return true;
 }
+
 /*****************************************************************/
-void pkpy_free(void* p){
-    std::free(p);
-}
+void pkpy_free(void* p) { std::free(p); }
 
-pkpy_CName pkpy_name(const char* name){
-    return StrName(name).index;
-}
+pkpy_CName pkpy_name(const char* name) { return StrName(name).index; }
 
-pkpy_CString pkpy_name_to_string(pkpy_CName name){
-    return StrName(name).c_str();
-}
+pkpy_CString pkpy_name_to_string(pkpy_CName name) { return StrName(name).c_str(); }
 
-void pkpy_set_output_handler(pkpy_vm* vm_handle, pkpy_COutputHandler handler){
-    VM* vm = (VM*) vm_handle;
+void pkpy_set_output_handler(pkpy_vm* vm_handle, pkpy_COutputHandler handler) {
+    VM* vm = (VM*)vm_handle;
     vm->_stdout = handler;
 }
 
-void pkpy_set_import_handler(pkpy_vm* vm_handle, pkpy_CImportHandler handler){
-    VM* vm = (VM*) vm_handle;
+void pkpy_set_import_handler(pkpy_vm* vm_handle, pkpy_CImportHandler handler) {
+    VM* vm = (VM*)vm_handle;
     vm->_import_handler = handler;
 }
 
-void* pkpy_new_repl(pkpy_vm* vm_handle){
-    return new REPL((VM*)vm_handle);
-}
+void* pkpy_new_repl(pkpy_vm* vm_handle) { return new REPL((VM*)vm_handle); }
 
-bool pkpy_repl_input(void* r, const char* line){
-    return ((REPL*)r)->input(line);
-}
+bool pkpy_repl_input(void* r, const char* line) { return ((REPL*)r)->input(line); }
 
-void pkpy_delete_repl(void* repl){
-    delete (REPL*)repl;
-}
+void pkpy_delete_repl(void* repl) { delete (REPL*)repl; }
 
-#endif // PK_NO_EXPORT_C_API
+#endif  // PK_NO_EXPORT_C_API

+ 34 - 34
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<buffer.size(); i++){
-                    // no enough lines
-                    if(buffer[i] != '\n') return true;
-                }
-                need_more_lines = 0;
-                line = buffer;
-                buffer.clear();
-                mode = CELL_MODE;
-            }else{
-                return true;
+bool REPL::input(std::string line) {
+    CompileMode mode = REPL_MODE;
+    if(need_more_lines) {
+        buffer += line;
+        buffer += '\n';
+        int n = buffer.size();
+        if(n >= need_more_lines) {
+            for(int i = buffer.size() - need_more_lines; i < buffer.size(); i++) {
+                // no enough lines
+                if(buffer[i] != '\n') return true;
             }
+            need_more_lines = 0;
+            line = buffer;
+            buffer.clear();
+            mode = CELL_MODE;
+        } else {
+            return true;
         }
-        
-        try{
-            vm->exec(line, "<stdin>", mode);
-        }catch(NeedMoreLines ne){
-            buffer += line;
-            buffer += '\n';
-            need_more_lines = ne.is_compiling_class ? 3 : 2;
-            if (need_more_lines) return true;
-        }
-        return false;
     }
 
-}
+    try {
+        vm->exec(line, "<stdin>", mode);
+    } catch(NeedMoreLines ne) {
+        buffer += line;
+        buffer += '\n';
+        need_more_lines = ne.is_compiling_class ? 3 : 2;
+        if(need_more_lines) return true;
+    }
+    return false;
+}
+
+}  // namespace pkpy

+ 23 - 28
src2/main.cpp

@@ -4,34 +4,31 @@
 #include <sstream>
 
 #if __has_include("pocketpy_c.h")
-    #include "pocketpy_c.h"
+#include "pocketpy_c.h"
 #else
-    // for amalgamated build
-    #include "pocketpy.h"
+// for amalgamated build
+#include "pocketpy.h"
 #endif
 
 #ifdef _WIN32
 
-    #include <windows.h>
+#include <windows.h>
 
 std::string pkpy_platform_getline(bool* eof) {
     HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
     std::wstringstream wss;
     WCHAR buf;
     DWORD read;
-    while (ReadConsoleW(hStdin, &buf, 1, &read, NULL) && buf != L'\n') {
-        if (eof && buf == L'\x1A') *eof = true;  // Ctrl+Z
+    while(ReadConsoleW(hStdin, &buf, 1, &read, NULL) && buf != L'\n') {
+        if(eof && buf == L'\x1A') *eof = true;  // Ctrl+Z
         wss << buf;
     }
     std::wstring wideInput = wss.str();
-    int length =
-        WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(),
-                            (int)wideInput.length(), NULL, 0, NULL, NULL);
+    int length = WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), NULL, 0, NULL, NULL);
     std::string output;
     output.resize(length);
-    WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(),
-                        &output[0], length, NULL, NULL);
-    if (!output.empty() && output.back() == '\r') output.pop_back();
+    WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), &output[0], length, NULL, NULL);
+    if(!output.empty() && output.back() == '\r') output.pop_back();
     return output;
 }
 
@@ -39,8 +36,8 @@ std::string pkpy_platform_getline(bool* eof) {
 
 std::string pkpy_platform_getline(bool* eof) {
     std::string output;
-    if (!std::getline(std::cin, output)) {
-        if (eof) *eof = true;
+    if(!std::getline(std::cin, output)) {
+        if(eof) *eof = true;
     }
     return output;
 }
@@ -48,10 +45,10 @@ std::string pkpy_platform_getline(bool* eof) {
 #endif
 
 static int f_input(pkpy_vm* vm) {
-    if (!pkpy_is_none(vm, -1)) {
+    if(!pkpy_is_none(vm, -1)) {
         pkpy_CString prompt;
         bool ok = pkpy_to_string(vm, -1, &prompt);
-        if (!ok) return 0;
+        if(!ok) return 0;
         std::cout << prompt << std::flush;
     }
     bool eof;
@@ -71,44 +68,42 @@ int main(int argc, char** argv) {
     pkpy_py_import(vm, "builtins");
     pkpy_setattr(vm, pkpy_name("input"));
 
-    if (argc == 1) {
+    if(argc == 1) {
         void* repl = pkpy_new_repl(vm);
         bool need_more_lines = false;
-        while (true) {
+        while(true) {
             std::cout << (need_more_lines ? "... " : ">>> ");
             bool eof = false;
             std::string line = pkpy_platform_getline(&eof);
-            if (eof) break;
+            if(eof) break;
             need_more_lines = pkpy_repl_input(repl, line.c_str());
         }
         pkpy_delete_vm(vm);
         return 0;
     }
 
-    if (argc == 2) {
+    if(argc == 2) {
         std::string argv_1 = argv[1];
-        if (argv_1 == "-h" || argv_1 == "--help") goto __HELP;
+        if(argv_1 == "-h" || argv_1 == "--help") goto __HELP;
 
         std::filesystem::path filepath(argv[1]);
         filepath = std::filesystem::absolute(filepath);
-        if (!std::filesystem::exists(filepath)) {
+        if(!std::filesystem::exists(filepath)) {
             std::cerr << "File not found: " << argv_1 << std::endl;
             return 2;
         }
         std::ifstream file(filepath);
-        if (!file.is_open()) {
+        if(!file.is_open()) {
             std::cerr << "Failed to open file: " << argv_1 << std::endl;
             return 3;
         }
-        std::string src((std::istreambuf_iterator<char>(file)),
-                        std::istreambuf_iterator<char>());
+        std::string src((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
         file.close();
 
         pkpy_set_main_argv(vm, argc, argv);
 
-        bool ok = pkpy_exec_2(vm, src.c_str(),
-                              filepath.filename().string().c_str(), 0, NULL);
-        if (!ok) pkpy_clear_error(vm, NULL);
+        bool ok = pkpy_exec_2(vm, src.c_str(), filepath.filename().string().c_str(), 0, NULL);
+        if(!ok) pkpy_clear_error(vm, NULL);
         pkpy_delete_vm(vm);
         return ok ? 0 : 1;
     }

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff