Просмотр исходного кода

Format the world. (#259)

* add some clang-format offs.

* add formats.

* format the world.

* AllowShortIfStatementsOnASingleLine

* off REGION.

* Rollback vm.hpp

* Disable insert.
ykiko 1 год назад
Родитель
Сommit
1c82060daf
93 измененных файлов с 9208 добавлено и 8698 удалено
  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
 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
 #pragma once
 
 
-#include "pocketpy/pocketpy.hpp"
+#include "pocketpy/pocketpy.hpp"

+ 1 - 1
include/pocketpy.hpp

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

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

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

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

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

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

@@ -1,4 +1,5 @@
 #pragma once
 #pragma once
+// clang-format off
 
 
 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
     //define something for Windows (32-bit and 64-bit, this part is common)
     //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>
 #include <mutex>
 
 
 struct GIL {
 struct GIL {
-	inline static std::mutex _mutex;
+    inline static std::mutex _mutex;
+
     explicit GIL() { _mutex.lock(); }
     explicit GIL() { _mutex.lock(); }
+
     ~GIL() { _mutex.unlock(); }
     ~GIL() { _mutex.unlock(); }
 };
 };
+
 #define PK_GLOBAL_SCOPE_LOCK() GIL _lock;
 #define PK_GLOBAL_SCOPE_LOCK() GIL _lock;
 
 
 #else
 #else

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

@@ -6,10 +6,10 @@
 #include <cassert>
 #include <cassert>
 #include <string>
 #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_alloc() noexcept;
 void PoolExpr_dealloc(void*) noexcept;
 void PoolExpr_dealloc(void*) noexcept;

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

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

+ 76 - 65
include/pocketpy/common/str.hpp

@@ -9,10 +9,10 @@
 
 
 namespace pkpy {
 namespace pkpy {
 
 
-int utf8len(unsigned char c, bool suppress=false);
+int utf8len(unsigned char c, bool suppress = false);
 struct SStream;
 struct SStream;
 
 
-struct Str{
+struct Str {
     int size;
     int size;
     bool is_ascii;
     bool is_ascii;
     char* data;
     char* data;
@@ -26,60 +26,70 @@ struct Str{
     Str(std::string_view s);
     Str(std::string_view s);
     Str(const char* s);
     Str(const char* s);
     Str(const char* s, int len);
     Str(const char* s, int len);
-    Str(std::pair<char *, int>);
+    Str(std::pair<char*, int>);
     Str(const Str& other);
     Str(const Str& other);
     Str(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* begin() const { return data; }
+
     const char* end() const { return data + size; }
     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; }
     int length() const { return size; }
+
     bool empty() const { return size == 0; }
     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();
     ~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; }
     const char* c_str() const { return data; }
+
     std::string_view sv() const { return std::string_view(data, size); }
     std::string_view sv() const { return std::string_view(data, size); }
+
     std::string str() const { return std::string(data, size); }
     std::string str() const { return std::string(data, size); }
 
 
     Str substr(int start, int len) const;
     Str substr(int start, int len) const;
     Str substr(int start) const;
     Str substr(int start) const;
     Str strip(bool left, bool right, const Str& chars) 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 lstrip() const { return strip(true, false); }
+
     Str rstrip() const { return strip(false, true); }
     Str rstrip() const { return strip(false, true); }
+
     Str lower() const;
     Str lower() const;
     Str upper() 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(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(const Str& sep) const;
     vector<std::string_view> split(char sep) const;
     vector<std::string_view> split(char sep) const;
     int count(const Str& sub) const;
     int count(const Str& sub) const;
@@ -95,32 +105,29 @@ struct Str{
 struct StrName {
 struct StrName {
     uint16_t index;
     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(); }
     const char* c_str() const { return _r_interned()[index].c_str(); }
 
 
     bool empty() const { return index == 0; }
     bool empty() const { return index == 0; }
+
     Str escape() const { return Str(sv()).escape(); }
     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 StrName get(std::string_view s);
     static std::map<std::string_view, uint16_t>& _interned();
     static std::map<std::string_view, uint16_t>& _interned();
@@ -128,32 +135,34 @@ struct StrName {
     static uint32_t _pesudo_random_index;
     static uint32_t _pesudo_random_index;
 };
 };
 
 
-struct SStream{
+struct SStream {
     PK_ALWAYS_PASS_BY_POINTER(SStream)
     PK_ALWAYS_PASS_BY_POINTER(SStream)
 
 
     vector<char> buffer;
     vector<char> buffer;
     int _precision = -1;
     int _precision = -1;
 
 
     bool empty() const { return buffer.empty(); }
     bool empty() const { return buffer.empty(); }
+
     void setprecision(int precision) { _precision = precision; }
     void setprecision(int precision) { _precision = precision; }
 
 
     SStream() {}
     SStream() {}
-    SStream(int guess_size) { buffer.reserve(guess_size);}
+
+    SStream(int guess_size) { buffer.reserve(guess_size); }
 
 
     Str str();
     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(void*);
     void write_hex(i64);
     void write_hex(i64);
 };
 };
@@ -162,17 +171,19 @@ struct SStream{
 #undef _S
 #undef _S
 #endif
 #endif
 
 
-template<typename... Args>
+template <typename... Args>
 Str _S(Args&&... args) {
 Str _S(Args&&... args) {
     SStream ss;
     SStream ss;
     (ss << ... << args);
     (ss << ... << args);
     return ss.str();
     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
 // unary operators
@@ -234,4 +245,4 @@ extern const StrName pk_id_complex;
 
 
 #define DEF_SNAME(name) const static StrName name(#name)
 #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>
 #include <type_traits>
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 // is_pod_v<> for c++17 and c++20
 // 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
 // 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
 // by default, only `int` and `float` enable SSO
 // users can specialize this template to enable SSO for other types
 // users can specialize this template to enable SSO for other types
 // SSO types cannot have instance dict
 // 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&
 // 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&>;
 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>
 #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(i64) == 8);
 static_assert(sizeof(f64) == 8);
 static_assert(sizeof(f64) == 8);
@@ -16,13 +16,15 @@ struct explicit_copy_t {
 };
 };
 
 
 // Dummy types
 // Dummy types
-struct DummyInstance { };
-struct DummyModule { };
-struct NoReturn { };
+struct DummyInstance {};
+
+struct DummyModule {};
+
+struct NoReturn {};
 
 
 // Forward declarations
 // Forward declarations
 struct PyObject;
 struct PyObject;
 struct Frame;
 struct Frame;
 class VM;
 class VM;
 
 
-};  // namespace pkpy
+};  // namespace pkpy

+ 22 - 22
include/pocketpy/common/utils.hpp

@@ -1,34 +1,34 @@
 #pragma once
 #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
 // 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
 #ifdef _MSC_VER
-#define PK_UNREACHABLE()			__assume(0);
+#define PK_UNREACHABLE() __assume(0);
 #else
 #else
-#define PK_UNREACHABLE()			__builtin_unreachable();
+#define PK_UNREACHABLE() __builtin_unreachable();
 #endif
 #endif
 
 
-} // namespace pkpy
+}  // namespace pkpy

+ 120 - 89
include/pocketpy/common/vector.hpp

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

+ 42 - 26
include/pocketpy/compiler/compiler.hpp

@@ -2,12 +2,12 @@
 
 
 #include "pocketpy/compiler/expr.hpp"
 #include "pocketpy/compiler/expr.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 class Compiler;
 class Compiler;
 typedef void (Compiler::*PrattCallback)();
 typedef void (Compiler::*PrattCallback)();
 
 
-struct PrattRule{
+struct PrattRule {
     PrattCallback prefix;
     PrattCallback prefix;
     PrattCallback infix;
     PrattCallback infix;
     Precedence precedence;
     Precedence precedence;
@@ -21,22 +21,28 @@ class Compiler {
     Lexer lexer;
     Lexer lexer;
     stack_no_copy<CodeEmitContext> contexts;
     stack_no_copy<CodeEmitContext> contexts;
     VM* vm;
     VM* vm;
-    bool unknown_global_scope;     // for eval/exec() call
+    bool unknown_global_scope;  // for eval/exec() call
     // for parsing token stream
     // for parsing token stream
     int i = 0;
     int i = 0;
     vector<Token> tokens;
     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();
         if(i >= tokens.size()) return prev();
         return curr();
         return curr();
     }
     }
-    void advance(int delta=1) { i += delta; }
+
+    void advance(int delta = 1) { i += delta; }
 
 
     CodeEmitContext* ctx() { return &contexts.top(); }
     CodeEmitContext* ctx() { return &contexts.top(); }
-    CompileMode mode() const{ return lexer.src->mode; }
+
+    CompileMode mode() const { return lexer.src->mode; }
+
     NameScope name_scope() const;
     NameScope name_scope() const;
     CodeObject_ push_global_context();
     CodeObject_ push_global_context();
     FuncDecl_ push_f_context(Str name);
     FuncDecl_ push_f_context(Str name);
@@ -48,13 +54,13 @@ class Compiler {
     void consume(TokenIndex expected);
     void consume(TokenIndex expected);
     bool match_newlines_repl();
     bool match_newlines_repl();
 
 
-    bool match_newlines(bool repl_throw=false);
+    bool match_newlines(bool repl_throw = false);
     bool match_end_stmt();
     bool match_end_stmt();
     void consume_end_stmt();
     void consume_end_stmt();
 
 
     /*************************************************/
     /*************************************************/
     void EXPR();
     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`
     Expr_ EXPR_VARS();  // special case for `for loop` and `comp`
 
 
     template <typename T, typename... Args>
     template <typename T, typename... Args>
@@ -91,11 +97,11 @@ class Compiler {
     void exprSubscr();
     void exprSubscr();
     void exprLiteral0();
     void exprLiteral0();
 
 
-    void compile_block_body(void (Compiler::*callback)()=nullptr);
+    void compile_block_body(void (Compiler::*callback)() = nullptr);
     void compile_normal_import();
     void compile_normal_import();
     void compile_from_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_if_stmt();
     void compile_while_loop();
     void compile_while_loop();
     void compile_for_loop();
     void compile_for_loop();
@@ -106,32 +112,42 @@ class Compiler {
     void compile_stmt();
     void compile_stmt();
     void consume_type_hints();
     void consume_type_hints();
     void _add_decorators(const Expr_vector& decorators);
     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_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 to_object(const TokenValue& value);
     PyVar read_literal();
     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:
 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();
     Str precompile();
     void from_precompiled(const char* source);
     void from_precompiled(const char* source);
     CodeObject_ compile();
     CodeObject_ compile();
 };
 };
 
 
-struct TokenDeserializer{
+struct TokenDeserializer {
     const char* curr;
     const char* curr;
     const char* source;
     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);
     std::string_view read_string(char c);
     Str read_string_from_hex(char c);
     Str read_string_from_hex(char c);
     int read_count();
     int read_count();
@@ -139,4 +155,4 @@ struct TokenDeserializer{
     f64 read_float(char c);
     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/objects/codeobject.hpp"
 #include "pocketpy/compiler/lexer.hpp"
 #include "pocketpy/compiler/lexer.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 struct CodeEmitContext;
 struct CodeEmitContext;
 struct Expr;
 struct Expr;
 
 
-template<typename T>
-class unique_ptr_128{
+template <typename T>
+class unique_ptr_128 {
     T* ptr;
     T* ptr;
+
 public:
 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* operator->() const { return ptr; }
+
     T* get() 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(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();
         ptr = other.detach();
         return *this;
         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;
         ptr = nullptr;
         return *this;
         return *this;
     }
     }
@@ -48,52 +69,54 @@ public:
 typedef unique_ptr_128<Expr> Expr_;
 typedef unique_ptr_128<Expr> Expr_;
 typedef small_vector<Expr_, 4> Expr_vector;
 typedef small_vector<Expr_, 4> Expr_vector;
 
 
-template<>
+template <>
 constexpr inline bool is_trivially_relocatable_v<Expr_> = true;
 constexpr inline bool is_trivially_relocatable_v<Expr_> = true;
 
 
-struct Expr{
+struct Expr {
     int line = 0;
     int line = 0;
     virtual ~Expr() = default;
     virtual ~Expr() = default;
     virtual void emit_(CodeEmitContext* ctx) = 0;
     virtual void emit_(CodeEmitContext* ctx) = 0;
+
     virtual bool is_literal() const { return false; }
     virtual bool is_literal() const { return false; }
+
     virtual bool is_json_object() const { return false; }
     virtual bool is_json_object() const { return false; }
+
     virtual bool is_attrib() const { return false; }
     virtual bool is_attrib() const { return false; }
+
     virtual bool is_subscr() const { return false; }
     virtual bool is_subscr() const { return false; }
+
     virtual bool is_compare() const { return false; }
     virtual bool is_compare() const { return false; }
+
     virtual int star_level() const { return 0; }
     virtual int star_level() const { return 0; }
+
     virtual bool is_tuple() const { return false; }
     virtual bool is_tuple() const { return false; }
+
     virtual bool is_name() const { return false; }
     virtual bool is_name() const { return false; }
+
     bool is_starred() const { return star_level() > 0; }
     bool is_starred() const { return star_level() > 0; }
 
 
     // for OP_DELETE_XXX
     // 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
     // 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;
     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
     // some bugs on MSVC (error C2280) when using Expr_vector
     // so we use stack_no_copy instead
     // so we use stack_no_copy instead
     stack_no_copy<Expr_> s_expr;
     stack_no_copy<Expr_> s_expr;
     int level;
     int level;
     vector<Str> global_names;
     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;
     int curr_iblock = 0;
     bool is_compiling_class = false;
     bool is_compiling_class = false;
@@ -104,8 +127,8 @@ struct CodeEmitContext{
     int get_loop() const;
     int get_loop() const;
     CodeBlock* enter_block(CodeBlockType type);
     CodeBlock* enter_block(CodeBlockType type);
     void exit_block();
     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_();
     void revert_last_emit_();
     int emit_int(i64 value, int line);
     int emit_int(i64 value, int line);
     void patch_jump(int index);
     void patch_jump(int index);
@@ -118,150 +141,189 @@ struct CodeEmitContext{
     void try_merge_for_iter_store(int);
     void try_merge_for_iter_store(int);
 };
 };
 
 
-struct NameExpr: Expr{
+struct NameExpr : Expr {
     StrName name;
     StrName name;
     NameScope scope;
     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;
     void emit_(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
+
     bool is_name() const override { return true; }
     bool is_name() const override { return true; }
 };
 };
 
 
-struct InvertExpr: Expr{
+struct InvertExpr : Expr {
     Expr_ child;
     Expr_ child;
-    InvertExpr(Expr_&& child): child(std::move(child)) {}
+
+    InvertExpr(Expr_&& child) : child(std::move(child)) {}
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct StarredExpr: Expr{
+struct StarredExpr : Expr {
     int level;
     int level;
     Expr_ child;
     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; }
     int star_level() const override { return level; }
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct NotExpr: Expr{
+struct NotExpr : Expr {
     Expr_ child;
     Expr_ child;
-    NotExpr(Expr_&& child): child(std::move(child)) {}
+
+    NotExpr(Expr_&& child) : child(std::move(child)) {}
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct AndExpr: Expr{
+struct AndExpr : Expr {
     Expr_ lhs;
     Expr_ lhs;
     Expr_ rhs;
     Expr_ rhs;
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct OrExpr: Expr{
+struct OrExpr : Expr {
     Expr_ lhs;
     Expr_ lhs;
     Expr_ rhs;
     Expr_ rhs;
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
 // [None, True, False, ...]
 // [None, True, False, ...]
-struct Literal0Expr: Expr{
+struct Literal0Expr : Expr {
     TokenIndex token;
     TokenIndex token;
-    Literal0Expr(TokenIndex token): token(token) {}
+
+    Literal0Expr(TokenIndex token) : token(token) {}
+
     bool is_json_object() const override { return true; }
     bool is_json_object() const override { return true; }
 
 
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct LongExpr: Expr{
+struct LongExpr : Expr {
     Str s;
     Str s;
-    LongExpr(const Str& s): s(s) {}
+
+    LongExpr(const Str& s) : s(s) {}
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct BytesExpr: Expr{
+struct BytesExpr : Expr {
     Str s;
     Str s;
-    BytesExpr(const Str& s): s(s) {}
+
+    BytesExpr(const Str& s) : s(s) {}
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct ImagExpr: Expr{
+struct ImagExpr : Expr {
     f64 value;
     f64 value;
-    ImagExpr(f64 value): value(value) {}
+
+    ImagExpr(f64 value) : value(value) {}
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
 // @num, @str which needs to invoke OP_LOAD_CONST
 // @num, @str which needs to invoke OP_LOAD_CONST
-struct LiteralExpr: Expr{
+struct LiteralExpr : Expr {
     TokenValue value;
     TokenValue value;
-    LiteralExpr(TokenValue value): value(value) {}
+
+    LiteralExpr(TokenValue value) : value(value) {}
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
+
     bool is_literal() const override { return true; }
     bool is_literal() const override { return true; }
+
     bool is_json_object() const override { return true; }
     bool is_json_object() const override { return true; }
 };
 };
 
 
-struct NegatedExpr: Expr{
+struct NegatedExpr : Expr {
     Expr_ child;
     Expr_ child;
-    NegatedExpr(Expr_&& child): child(std::move(child)) {}
+
+    NegatedExpr(Expr_&& child) : child(std::move(child)) {}
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
+
     bool is_json_object() const override { return child->is_literal(); }
     bool is_json_object() const override { return child->is_literal(); }
 };
 };
 
 
-struct SliceExpr: Expr{
+struct SliceExpr : Expr {
     Expr_ start;
     Expr_ start;
     Expr_ stop;
     Expr_ stop;
     Expr_ step;
     Expr_ step;
     void emit_(CodeEmitContext* ctx) override;
     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;
     Expr_ value;
+
     int star_level() const override { return value->star_level(); }
     int star_level() const override { return value->star_level(); }
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct SequenceExpr: Expr{
+struct SequenceExpr : Expr {
     Expr_vector items;
     Expr_vector items;
-    SequenceExpr(Expr_vector&& items): items(std::move(items)) {}
+
+    SequenceExpr(Expr_vector&& items) : items(std::move(items)) {}
+
     virtual Opcode opcode() const = 0;
     virtual Opcode opcode() const = 0;
 
 
     void emit_(CodeEmitContext* ctx) override {
     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);
         ctx->emit_(opcode(), items.size(), line);
     }
     }
 };
 };
 
 
-struct ListExpr: SequenceExpr{
+struct ListExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
     using SequenceExpr::SequenceExpr;
+
     Opcode opcode() const override {
     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;
         return OP_BUILD_LIST;
     }
     }
 
 
     bool is_json_object() const override { return true; }
     bool is_json_object() const override { return true; }
 };
 };
 
 
-struct DictExpr: SequenceExpr{
+struct DictExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
     using SequenceExpr::SequenceExpr;
+
     Opcode opcode() const override {
     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;
         return OP_BUILD_DICT;
     }
     }
 
 
     bool is_json_object() const override { return true; }
     bool is_json_object() const override { return true; }
 };
 };
 
 
-struct SetExpr: SequenceExpr{
+struct SetExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
     using SequenceExpr::SequenceExpr;
+
     Opcode opcode() const override {
     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;
         return OP_BUILD_SET;
     }
     }
 };
 };
 
 
-struct TupleExpr: SequenceExpr{
+struct TupleExpr : SequenceExpr {
     using SequenceExpr::SequenceExpr;
     using SequenceExpr::SequenceExpr;
+
     bool is_tuple() const override { return true; }
     bool is_tuple() const override { return true; }
+
     Opcode opcode() const override {
     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;
         return OP_BUILD_TUPLE;
     }
     }
 
 
@@ -269,11 +331,11 @@ struct TupleExpr: SequenceExpr{
     bool emit_del(CodeEmitContext* ctx) override;
     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 op0() = 0;
     virtual Opcode op1() = 0;
     virtual Opcode op1() = 0;
@@ -281,25 +343,28 @@ struct CompExpr: Expr{
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct ListCompExpr: CompExpr{
+struct ListCompExpr : CompExpr {
     Opcode op0() override { return OP_BUILD_LIST; }
     Opcode op0() override { return OP_BUILD_LIST; }
+
     Opcode op1() override { return OP_LIST_APPEND; }
     Opcode op1() override { return OP_LIST_APPEND; }
 };
 };
 
 
-struct DictCompExpr: CompExpr{
+struct DictCompExpr : CompExpr {
     Opcode op0() override { return OP_BUILD_DICT; }
     Opcode op0() override { return OP_BUILD_DICT; }
+
     Opcode op1() override { return OP_DICT_ADD; }
     Opcode op1() override { return OP_DICT_ADD; }
 };
 };
 
 
-struct SetCompExpr: CompExpr{
+struct SetCompExpr : CompExpr {
     Opcode op0() override { return OP_BUILD_SET; }
     Opcode op0() override { return OP_BUILD_SET; }
+
     Opcode op1() override { return OP_SET_ADD; }
     Opcode op1() override { return OP_SET_ADD; }
 };
 };
 
 
-struct LambdaExpr: Expr{
+struct LambdaExpr : Expr {
     FuncDecl_ decl;
     FuncDecl_ decl;
 
 
-    LambdaExpr(FuncDecl_ decl): decl(decl) {}
+    LambdaExpr(FuncDecl_ decl) : decl(decl) {}
 
 
     void emit_(CodeEmitContext* ctx) override {
     void emit_(CodeEmitContext* ctx) override {
         int index = ctx->add_func_decl(decl);
         int index = ctx->add_func_decl(decl);
@@ -307,17 +372,21 @@ struct LambdaExpr: Expr{
     }
     }
 };
 };
 
 
-struct FStringExpr: Expr{
+struct FStringExpr : Expr {
     Str src;
     Str src;
-    FStringExpr(const Str& src): src(src) {}
+
+    FStringExpr(const Str& src) : src(src) {}
+
     void _load_simple_expr(CodeEmitContext* ctx, Str expr);
     void _load_simple_expr(CodeEmitContext* ctx, Str expr);
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct SubscrExpr: Expr{
+struct SubscrExpr : Expr {
     Expr_ a;
     Expr_ a;
     Expr_ b;
     Expr_ b;
+
     bool is_subscr() const override { return true; }
     bool is_subscr() const override { return true; }
+
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
     bool emit_store(CodeEmitContext* ctx) override;
@@ -326,10 +395,11 @@ struct SubscrExpr: Expr{
     bool emit_store_inplace(CodeEmitContext* ctx) override;
     bool emit_store_inplace(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct AttribExpr: Expr{
+struct AttribExpr : Expr {
     Expr_ a;
     Expr_ a;
     StrName b;
     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;
     void emit_(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
     bool emit_del(CodeEmitContext* ctx) override;
@@ -337,11 +407,12 @@ struct AttribExpr: Expr{
     void emit_method(CodeEmitContext* ctx);
     void emit_method(CodeEmitContext* ctx);
 
 
     bool is_attrib() const override { return true; }
     bool is_attrib() const override { return true; }
+
     void emit_inplace(CodeEmitContext* ctx) override;
     void emit_inplace(CodeEmitContext* ctx) override;
     bool emit_store_inplace(CodeEmitContext* ctx) override;
     bool emit_store_inplace(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct CallExpr: Expr{
+struct CallExpr : Expr {
     Expr_ callable;
     Expr_ callable;
     Expr_vector args;
     Expr_vector args;
     // **a will be interpreted as a special keyword argument: {"**": a}
     // **a will be interpreted as a special keyword argument: {"**": a}
@@ -349,42 +420,36 @@ struct CallExpr: Expr{
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-struct GroupedExpr: Expr{
+struct GroupedExpr : Expr {
     Expr_ a;
     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;
     TokenIndex op;
     Expr_ lhs;
     Expr_ lhs;
     Expr_ rhs;
     Expr_ rhs;
     bool inplace;
     bool inplace;
 
 
-    BinaryExpr(bool inplace=false): inplace(inplace) {}
+    BinaryExpr(bool inplace = false) : inplace(inplace) {}
+
     bool is_compare() const override;
     bool is_compare() const override;
     void _emit_compare(CodeEmitContext*, small_vector_2<int, 6>&);
     void _emit_compare(CodeEmitContext*, small_vector_2<int, 6>&);
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-
-struct TernaryExpr: Expr{
+struct TernaryExpr : Expr {
     Expr_ cond;
     Expr_ cond;
     Expr_ true_expr;
     Expr_ true_expr;
     Expr_ false_expr;
     Expr_ false_expr;
     void emit_(CodeEmitContext* ctx) override;
     void emit_(CodeEmitContext* ctx) override;
 };
 };
 
 
-
-} // namespace pkpy
+}  // namespace pkpy

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

@@ -5,10 +5,11 @@
 
 
 #include <variant>
 #include <variant>
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 typedef uint8_t TokenIndex;
 typedef uint8_t TokenIndex;
 
 
+// clang-format off
 constexpr const char* kTokens[] = {
 constexpr const char* kTokens[] = {
     "is not", "not in", "yield from",
     "is not", "not in", "yield from",
     "@eof", "@eol", "@sof",
     "@eof", "@eol", "@sof",
@@ -28,67 +29,71 @@ constexpr const char* kTokens[] = {
     "None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally",
     "None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally",
     "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise"
     "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise"
 };
 };
+// clang-format on
 
 
 using TokenValue = std::variant<std::monostate, i64, f64, Str>;
 using TokenValue = std::variant<std::monostate, i64, f64, Str>;
 const int kTokenCount = sizeof(kTokens) / sizeof(kTokens[0]);
 const int kTokenCount = sizeof(kTokens) / sizeof(kTokens[0]);
 
 
 constexpr TokenIndex TK(const char token[]) {
 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* i = kTokens[k];
         const char* j = token;
         const char* j = token;
-        while(*i && *j && *i == *j) { i++; j++;}
+        while(*i && *j && *i == *j) {
+            i++;
+            j++;
+        }
         if(*i == *j) return k;
         if(*i == *j) return k;
     }
     }
     return 255;
     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]
 #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;
     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;
     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
 // https://docs.python.org/3/reference/expressions.html#operator-precedence
 enum 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 };
 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;
     stack_no_copy<int, small_vector_2<int, 8>> indents;
     int brackets_level = 0;
     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_n_chars(int n, char c0);
     bool match_string(const char* s);
     bool match_string(const char* s);
     int eat_spaces();
     int eat_spaces();
@@ -114,7 +120,7 @@ struct Lexer {
     int eat_name();
     int eat_name();
     void skip_line_comment();
     void skip_line_comment();
     bool matchchar(char c);
     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);
     void add_token_2(char c, TokenIndex one, TokenIndex two);
     Str eat_string_until(char quote, bool raw);
     Str eat_string_until(char quote, bool raw);
     void eat_string(char quote, StringType type);
     void eat_string(char quote, StringType type);
@@ -125,16 +131,18 @@ struct Lexer {
     /***** Error Reporter *****/
     /***** Error Reporter *****/
     [[noreturn]] void throw_err(StrName type, Str msg);
     [[noreturn]] void throw_err(StrName type, Str msg);
     [[noreturn]] void throw_err(StrName type, Str msg, int lineno, const char* cursor);
     [[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);
     Lexer(VM* vm, std::shared_ptr<SourceData> src);
     vector<Token> run();
     vector<Token> run();
 };
 };
 
 
-
-enum class IntParsingResult{
+enum class IntParsingResult {
     Success,
     Success,
     Failure,
     Failure,
     Overflow,
     Overflow,
@@ -142,4 +150,4 @@ enum class IntParsingResult{
 
 
 IntParsingResult parse_uint(std::string_view text, i64* out, int base);
 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"
 #include "pocketpy/interpreter/cffi.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 struct NativeProxyFuncCBase {
 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;
     _Fp func;
+
     NativeProxyFuncC(_Fp func) : func(func) {}
     NativeProxyFuncC(_Fp func) : func(func) {}
 
 
-    PyVar operator()(VM* vm, ArgsView args) override {
+    PyVar operator() (VM* vm, ArgsView args) override {
         assert(args.size() == N);
         assert(args.size() == N);
         return call<Ret>(vm, args, std::make_index_sequence<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])...);
             func(py_cast<Params>(vm, args[Is])...);
             return vm->None;
             return vm->None;
-        }else{
+        } else {
             __Ret ret = func(py_cast<Params>(vm, args[Is])...);
             __Ret ret = func(py_cast<Params>(vm, args[Is])...);
             return VAR(std::move(ret));
             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;
     _Fp func;
+
     NativeProxyMethodC(_Fp func) : func(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>());
         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;
             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));
             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());
     NativeProxyFuncCBase* pf = lambda_get_userdata<NativeProxyFuncCBase*>(args.begin());
     return (*pf)(vm, args);
     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);
     NativeProxyFuncCBase* proxy = new NativeProxyFuncC<Ret, Params...>(func);
     return vm->bind(obj, sig, __proxy_wrapper, proxy, bt);
     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);
     NativeProxyFuncCBase* proxy = new NativeProxyMethodC<Ret, T, Params...>(func);
     return vm->bind(obj, sig, __proxy_wrapper, proxy, bt);
     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);
     NativeProxyFuncCBase* proxy = new NativeProxyFuncC<Ret, Params...>(func);
     return vm->bind(obj, sig, docstring, __proxy_wrapper, proxy, bt);
     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);
     NativeProxyFuncCBase* proxy = new NativeProxyMethodC<Ret, T, Params...>(func);
     return vm->bind(obj, sig, docstring, __proxy_wrapper, proxy, bt);
     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>);
     static_assert(!std::is_reference_v<F>);
     assert(is_type(obj, tp_type));
     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);
     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]);
         obj_get_t<T> self = PK_OBJ_GET(T, args[0]);
         F T::*field = lambda_get_userdata<F T::*>(args.begin());
         F T::*field = lambda_get_userdata<F T::*>(args.begin());
         return VAR(self.*field);
         return VAR(self.*field);
     };
     };
     PyVar _0 = new_object<NativeFunc>(tp_native_func, fget, 1, field);
     PyVar _0 = new_object<NativeFunc>(tp_native_func, fget, 1, field);
     PyVar _1 = vm->None;
     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]);
             obj_get_t<T> self = PK_OBJ_GET(T, args[0]);
             F T::*field = lambda_get_userdata<F T::*>(args.begin());
             F T::*field = lambda_get_userdata<F T::*>(args.begin());
             self.*field = py_cast<F>(vm, args[1]);
             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
 #pragma once
 
 
 #include "pocketpy/interpreter/vm.hpp"
 #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 {
 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;
     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;
         SStream ss;
         ss.write_hex(ptr);
         ss.write_hex(ptr);
         return ss.str();
         return ss.str();
@@ -34,11 +36,11 @@ struct VoidP{
     static void _register(VM* vm, PyObject* mod, PyObject* type);
     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")
 POINTER_VAR(char*, "char_p")
@@ -58,43 +60,46 @@ POINTER_VAR(const bool*, "bool_p")
 
 
 #undef POINTER_VAR
 #undef POINTER_VAR
 
 
-
-struct Struct{
-    static constexpr int INLINE_SIZE = 24;
+struct Struct {
+    constexpr static int INLINE_SIZE = 24;
 
 
     char _inlined[INLINE_SIZE];
     char _inlined[INLINE_SIZE];
     char* p;
     char* p;
     int size;
     int size;
 
 
-    Struct(int new_size, bool zero_init=true){
+    Struct(int new_size, bool zero_init = true) {
         this->size = new_size;
         this->size = new_size;
-        if(size <= INLINE_SIZE){
+        if(size <= INLINE_SIZE) {
             p = _inlined;
             p = _inlined;
-        }else{
+        } else {
             p = (char*)std::malloc(size);
             p = (char*)std::malloc(size);
         }
         }
         if(zero_init) std::memset(p, 0, 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);
         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);
     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>);
     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);
     VoidP& p = CAST(VoidP&, var);
     return reinterpret_cast<Tp>(p.ptr);
     return reinterpret_cast<Tp>(p.ptr);
 }
 }
+
 /*****************************************************************/
 /*****************************************************************/
 void add_module_c(VM* vm);
 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"
 #include "pocketpy/objects/codeobject.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 // weak reference fast locals
 // weak reference fast locals
-struct FastLocals{
+struct FastLocals {
     // this is a weak reference
     // this is a weak reference
     const CodeObject* co;
     const CodeObject* co;
     PyVar* a;
     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);
     PyVar* try_get_name(StrName name);
     NameDict_ to_namedict();
     NameDict_ to_namedict();
 
 
     PyVar* begin() const { return a; }
     PyVar* begin() const { return a; }
+
     PyVar* end() const { return a + size(); }
     PyVar* end() const { return a + size(); }
 };
 };
 
 
@@ -28,49 +30,72 @@ struct ValueStack {
     PK_ALWAYS_PASS_BY_POINTER(ValueStack)
     PK_ALWAYS_PASS_BY_POINTER(ValueStack)
 
 
     // We allocate extra PK_VM_STACK_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`.
     // 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* _sp;
     PyVar* _max_end;
     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 top() const { return _sp[-1]; }
-    PyVar& second(){ return _sp[-2]; }
+
+    PyVar& second() { return _sp[-2]; }
+
     PyVar second() const { 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 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]; }
     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 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; }
     int size() const { return _sp - _begin; }
+
     bool empty() const { return _sp == _begin; }
     bool empty() const { return _sp == _begin; }
+
     PyVar* begin() { return _begin; }
     PyVar* begin() { return _begin; }
+
     PyVar* end() { return _sp; }
     PyVar* end() { return _sp; }
+
     void reset(PyVar* sp) { _sp = sp; }
     void reset(PyVar* sp) { _sp = sp; }
+
     void clear() { _sp = _begin; }
     void clear() { _sp = _begin; }
+
     bool is_overflow() const { return _sp >= _max_end; }
     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;
         ++_sp;
     }
     }
 };
 };
 
 
-struct UnwindTarget{
+struct UnwindTarget {
     UnwindTarget* next;
     UnwindTarget* next;
     int iblock;
     int iblock;
     int offset;
     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 {
 struct Frame {
@@ -82,36 +107,42 @@ struct Frame {
 
 
     const CodeObject* co;
     const CodeObject* co;
     PyObject* _module;
     PyObject* _module;
-    PyObject* _callable;    // a function object or nullptr (global scope)
+    PyObject* _callable;  // a function object or nullptr (global scope)
     FastLocals _locals;
     FastLocals _locals;
 
 
     // This list will be freed in __pop_frame
     // This list will be freed in __pop_frame
     UnwindTarget* _uw_list;
     UnwindTarget* _uw_list;
 
 
     NameDict& f_globals() { return _module->attr(); }
     NameDict& f_globals() { return _module->attr(); }
+
     PyVar* f_closure_try_get(StrName name);
     PyVar* f_closure_try_get(StrName name);
+
     int ip() const { return _ip - co->codes.data(); }
     int ip() const { return _ip - co->codes.data(); }
 
 
     // function scope
     // 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
     // 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
     // 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; }
     PyVar* actual_sp_base() const { return _locals.a; }
+
     ArgsView stack_view(ValueStack* _s) const { return ArgsView(actual_sp_base(), _s->_sp); }
     ArgsView stack_view(ValueStack* _s) const { return ArgsView(actual_sp_base(), _s->_sp); }
 
 
     [[nodiscard]] int prepare_jump_exception_handler(ValueStack*);
     [[nodiscard]] int prepare_jump_exception_handler(ValueStack*);
     void prepare_jump_break(ValueStack*, int);
     void prepare_jump_break(ValueStack*, int);
     int _exit_block(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;
         int target = co->_get_block_codei(ip()).end;
         prepare_jump_break(s_data, target);
         prepare_jump_break(s_data, target);
         return target;
         return target;
@@ -126,29 +157,35 @@ struct Frame {
     ~Frame();
     ~Frame();
 };
 };
 
 
-struct LinkedFrame{
+struct LinkedFrame {
     LinkedFrame* f_back;
     LinkedFrame* f_back;
     Frame frame;
     Frame frame;
-    template<typename... Args>
+
+    template <typename... Args>
     LinkedFrame(LinkedFrame* f_back, Args&&... args) : f_back(f_back), frame(std::forward<Args>(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);
     static_assert(sizeof(LinkedFrame) <= 128);
 
 
     LinkedFrame* _tail;
     LinkedFrame* _tail;
     int _size;
     int _size;
-    CallStack(): _tail(nullptr), _size(0) {}
+
+    CallStack() : _tail(nullptr), _size(0) {}
 
 
     int size() const { return _size; }
     int size() const { return _size; }
+
     bool empty() const { return _size == 0; }
     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);
         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;
         ++_size;
     }
     }
 
 
@@ -161,12 +198,13 @@ struct CallStack{
         return _tail->frame;
         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"
 #include "pocketpy/objects/object.hpp"
 
 
 namespace pkpy {
 namespace pkpy {
-struct ManagedHeap{
+struct ManagedHeap {
     vector<PyObject*> _no_gc;
     vector<PyObject*> _no_gc;
     vector<PyObject*> gen;
     vector<PyObject*> gen;
     VM* vm;
     VM* vm;
     void (*_gc_on_delete)(VM*, PyObject*) = nullptr;
     void (*_gc_on_delete)(VM*, PyObject*) = nullptr;
     void (*_gc_marker_ex)(VM*) = 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_threshold = PK_GC_MIN_THRESHOLD;
     int gc_counter = 0;
     int gc_counter = 0;
 
 
     /********************/
     /********************/
     int _gc_lock_counter = 0;
     int _gc_lock_counter = 0;
-    struct ScopeLock{
+
+    struct ScopeLock {
         PK_ALWAYS_PASS_BY_POINTER(ScopeLock)
         PK_ALWAYS_PASS_BY_POINTER(ScopeLock)
-        
+
         ManagedHeap* heap;
         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>;
         using __T = std::decay_t<T>;
         static_assert(!is_sso_v<__T>, "gcnew cannot be used with SSO types");
         static_assert(!is_sso_v<__T>, "gcnew cannot be used with SSO types");
         // https://github.com/pocketpy/pocketpy/issues/94#issuecomment-1594784476
         // 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)...);
         p->placement_new<__T>(std::forward<Args>(args)...);
         gen.push_back(p);
         gen.push_back(p);
         gc_counter++;
         gc_counter++;
         return p;
         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>;
         using __T = std::decay_t<T>;
         static_assert(!is_sso_v<__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)...);
         p->placement_new<__T>(std::forward<Args>(args)...);
         _no_gc.push_back(p);
         _no_gc.push_back(p);
         return p;
         return p;
@@ -67,9 +65,11 @@ struct ManagedHeap{
 
 
     int sweep();
     int sweep();
     void _auto_collect();
     void _auto_collect();
+
     bool _should_auto_collect() const { return gc_counter >= gc_threshold; }
     bool _should_auto_collect() const { return gc_counter >= gc_threshold; }
+
     int collect();
     int collect();
     void mark();
     void mark();
 };
 };
 
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 29 - 22
include/pocketpy/interpreter/iter.hpp

@@ -2,51 +2,57 @@
 
 
 #include "pocketpy/interpreter/bindings.hpp"
 #include "pocketpy/interpreter/bindings.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
-struct RangeIter{       // step > 0
+struct RangeIter {  // step > 0
     Range r;
     Range r;
     i64 current;
     i64 current;
+
     RangeIter(Range r) : r(r), current(r.start) {}
     RangeIter(Range r) : r(r), current(r.start) {}
 
 
     static void _register(VM* vm, PyObject* mod, PyObject* type);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 };
 };
 
 
-struct RangeIterR{      // step < 0
+struct RangeIterR {  // step < 0
     Range r;
     Range r;
     i64 current;
     i64 current;
+
     RangeIterR(Range r) : r(r), current(r.start) {}
     RangeIterR(Range r) : r(r), current(r.start) {}
 
 
     static void _register(VM* vm, PyObject* mod, PyObject* type);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 };
 };
 
 
-struct ArrayIter{
+struct ArrayIter {
     PyObject* ref;
     PyObject* ref;
     PyVar* end;
     PyVar* end;
     PyVar* current;
     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);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 };
 };
 
 
-struct StringIter{
+struct StringIter {
     PyVar ref;
     PyVar ref;
-    int i;      // byte index
+    int i;  // byte index
+
     StringIter(PyVar ref) : ref(ref), i(0) {}
     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);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 };
 };
 
 
-struct Generator{
+struct Generator {
     LinkedFrame* lf;
     LinkedFrame* lf;
-    int state;      // 0,1,2
+    int state;  // 0,1,2
     List s_backup;
     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) {
     void _gc_mark(VM* vm) {
@@ -58,22 +64,23 @@ struct Generator{
     PyVar next(VM* vm);
     PyVar next(VM* vm);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
 
-    ~Generator(){
-        if(lf){
+    ~Generator() {
+        if(lf) {
             lf->~LinkedFrame();
             lf->~LinkedFrame();
             PoolFrame_dealloc(lf);
             PoolFrame_dealloc(lf);
         }
         }
     }
     }
 };
 };
 
 
-struct DictItemsIter{
+struct DictItemsIter {
     PyVar ref;
     PyVar ref;
     int i;
     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);
     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 {
 namespace pkpy {
 
 
-struct _LineRecord{
+struct _LineRecord {
     int line;
     int line;
     i64 hits;
     i64 hits;
     clock_t time;
     clock_t time;
 
 
-    _LineRecord(): line(-1), hits(0), time(0) {}
+    _LineRecord() : line(-1), hits(0), time(0) {}
+
     bool is_valid() const { return line != -1; }
     bool is_valid() const { return line != -1; }
 };
 };
 
 
-struct _FrameRecord{
+struct _FrameRecord {
     int callstack_size;
     int callstack_size;
     Frame* frame;
     Frame* frame;
     clock_t prev_time;
     clock_t prev_time;
     _LineRecord* prev_record;
     _LineRecord* prev_record;
 };
 };
 
 
-struct LineProfiler{
+struct LineProfiler {
     // filename -> records
     // filename -> records
     std::map<std::string_view, vector<_LineRecord>> records;
     std::map<std::string_view, vector<_LineRecord>> records;
     stack_no_copy<_FrameRecord> frames;
     stack_no_copy<_FrameRecord> frames;
@@ -35,4 +36,4 @@ struct LineProfiler{
     Str stats();
     Str stats();
 };
 };
 
 
-} // namespace pkpy
+}  // namespace pkpy

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

@@ -11,67 +11,76 @@
 
 
 #include <stdexcept>
 #include <stdexcept>
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 /* Stack manipulation macros */
 /* Stack manipulation macros */
 // https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
 // https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
-#define TOP()             (s_data.top())
-#define 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 PyVar (*BinaryFuncC)(VM*, PyVar, PyVar);
 typedef void (*RegisterFunc)(VM*, PyObject*, PyObject*);
 typedef void (*RegisterFunc)(VM*, PyObject*, PyObject*);
 
 
 #if PK_ENABLE_PROFILER
 #if PK_ENABLE_PROFILER
-struct NextBreakpoint{
+struct NextBreakpoint {
     int callstack_size;
     int callstack_size;
     int lineno;
     int lineno;
     bool should_step_into;
     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);
     void _step(VM* vm);
+
     bool empty() const { return callstack_size == 0; }
     bool empty() const { return callstack_size == 0; }
 };
 };
 #endif
 #endif
 
 
-struct PyTypeInfo{
-    struct Vt{
+struct PyTypeInfo {
+    struct Vt {
         void (*_dtor)(void*);
         void (*_dtor)(void*);
         void (*_gc_mark)(void*, VM*);
         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>>);
             static_assert(std::is_same_v<T, std::decay_t<T>>);
             Vt vt;
             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;
             return vt;
         }
         }
     };
     };
 
 
-    PyObject* obj;      // never be garbage collected
+    PyObject* obj;  // never be garbage collected
     Type base;
     Type base;
-    PyObject* mod;      // never be garbage collected
+    PyObject* mod;  // never be garbage collected
     StrName name;
     StrName name;
     bool subclass_enabled;
     bool subclass_enabled;
     Vt vt;
     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) {}
         obj(obj), base(base), mod(mod), name(name), subclass_enabled(subclass_enabled), vt(vt) {}
-    
+
     vector<StrName> annotated_fields = {};
     vector<StrName> annotated_fields = {};
 
 
     // unary operators
     // unary operators
@@ -122,52 +131,53 @@ struct PyTypeInfo{
     void (*on_end_subclass)(VM* vm, PyTypeInfo*) = nullptr;
     void (*on_end_subclass)(VM* vm, PyTypeInfo*) = nullptr;
 };
 };
 
 
-struct ImportContext{
+struct ImportContext {
     PK_ALWAYS_PASS_BY_POINTER(ImportContext)
     PK_ALWAYS_PASS_BY_POINTER(ImportContext)
 
 
     vector<Str> pending;
     vector<Str> pending;
-    vector<bool> pending_is_init;   // a.k.a __init__.py
+    vector<bool> pending_is_init;  // a.k.a __init__.py
 
 
     ImportContext() {}
     ImportContext() {}
 
 
-    struct Temp{
+    struct Temp {
         PK_ALWAYS_PASS_BY_POINTER(Temp)
         PK_ALWAYS_PASS_BY_POINTER(Temp)
 
 
         ImportContext* ctx;
         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.push_back(name);
             ctx->pending_is_init.push_back(is_init);
             ctx->pending_is_init.push_back(is_init);
         }
         }
-        ~Temp(){
+
+        ~Temp() {
             ctx->pending.pop_back();
             ctx->pending.pop_back();
             ctx->pending_is_init.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 {
 class VM {
     PK_ALWAYS_PASS_BY_POINTER(VM)
     PK_ALWAYS_PASS_BY_POINTER(VM)
-    
-    VM* vm;     // self reference to simplify code
+
+    VM* vm;  // self reference to simplify code
+
 public:
 public:
     ManagedHeap heap;
     ManagedHeap heap;
     ValueStack s_data;
     ValueStack s_data;
     CallStack callstack;
     CallStack callstack;
     vector<PyTypeInfo> _all_types;
     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;
         PyObject* error;
         stack_no_copy<ArgsView> s_view;
         stack_no_copy<ArgsView> s_view;
     } __c;
     } __c;
 
 
-    PyVar StopIteration;        // a special Exception class
+    PyVar StopIteration;  // a special Exception class
     PyObject* builtins;
     PyObject* builtins;
     PyObject* _main;
     PyObject* _main;
 
 
@@ -191,34 +201,36 @@ public:
 #endif
 #endif
 
 
     void (*_ceval_on_step)(VM*, Frame*, Bytecode bc);
     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*);
     unsigned char* (*_import_handler)(const char*, int*);
     // function<void(const char*, int)> _stdout;
     // function<void(const char*, int)> _stdout;
     // function<void(const char*, int)> _stderr;
     // function<void(const char*, int)> _stderr;
     // function<unsigned char*(const char*, int*)> _import_handler;
     // function<unsigned char*(const char*, int*)> _import_handler;
-    
+
     // for quick access
     // 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;
     const bool enable_os;
-    VM(bool enable_os=true);
+    VM(bool enable_os = true);
 
 
+    // clang-format off
 #if PK_REGION("Python Equivalents")
 #if PK_REGION("Python Equivalents")
     Str py_str(PyVar obj);                              // x -> str(x)
     Str py_str(PyVar obj);                              // x -> str(x)
     Str py_repr(PyVar obj);                             // x -> repr(x)
     Str py_repr(PyVar obj);                             // x -> repr(x)
@@ -453,18 +465,19 @@ public:
         vm->s_data.emplace(p->type, p);
         vm->s_data.emplace(p->type, p);
     }
     }
 #endif
 #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));
         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"));
             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"));
             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");
             throw std::runtime_error("_find_type_in_cxx_typeid_map() failed: T not found");
-    #endif
+#endif
         }
         }
         return it->second;
         return it->second;
     }
     }
@@ -485,17 +498,35 @@ public:
     void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl_&);
     void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl_&);
     void __unpack_as_list(ArgsView args, List& list);
     void __unpack_as_list(ArgsView args, List& list);
     void __unpack_as_dict(ArgsView args, Dict& dict);
     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);
     [[noreturn]] void __builtin_error(StrName type, PyVar arg);
     [[noreturn]] void __builtin_error(StrName type, PyVar arg);
     [[noreturn]] void __builtin_error(StrName type, const Str& msg);
     [[noreturn]] void __builtin_error(StrName type, const Str& msg);
     void __init_builtin_types();
     void __init_builtin_types();
     void __post_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 __pack_next_retval(unsigned);
     PyVar __minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key);
     PyVar __minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key);
     bool __py_bool_non_trivial(PyVar);
     bool __py_bool_non_trivial(PyVar);
@@ -504,128 +535,201 @@ public:
     void* __stack_alloc(int size);
     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>;
     using T = std::decay_t<__T>;
 
 
     static_assert(!std::is_same_v<T, PyVar>, "py_var(VM*, PyVar) is not allowed");
     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)
         // str (shortcuts)
         return VAR(Str(std::forward<__T>(value)));
         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
         // NoneType
         return vm->None;
         return vm->None;
-    }else if constexpr(std::is_same_v<T, bool>){
+    } else if constexpr(std::is_same_v<T, bool>) {
         // bool
         // bool
         return value ? vm->True : vm->False;
         return value ? vm->True : vm->False;
-    }else if constexpr(is_integral_v<T>){
+    } else if constexpr(is_integral_v<T>) {
         // int
         // int
         return PyVar(VM::tp_int, static_cast<i64>(value));
         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
         // float
         return PyVar(VM::tp_float, static_cast<f64>(value));
         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);
         return from_void_p(vm, (void*)value);
-    }else{
+    } else {
         constexpr Type const_type = _find_type_in_const_cxx_typeid_map<T>();
         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>();
             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
 // 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) {
 __T _py_cast__internal(VM* vm, PyVar obj) {
     static_assert(!std::is_rvalue_reference_v<__T>, "rvalue reference is not allowed");
     static_assert(!std::is_rvalue_reference_v<__T>, "rvalue reference is not allowed");
     using T = std::decay_t<__T>;
     using T = std::decay_t<__T>;
     static_assert(!(is_sso_v<T> && std::is_reference_v<__T>), "SSO types cannot be reference");
     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>);
         static_assert(!std::is_reference_v<__T>);
         // str (shortcuts)
         // str (shortcuts)
         if(obj == vm->None) return nullptr;
         if(obj == vm->None) return nullptr;
         if constexpr(with_check) vm->check_type(obj, vm->tp_str);
         if constexpr(with_check) vm->check_type(obj, vm->tp_str);
         return PK_OBJ_GET(Str, obj).c_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>);
         static_assert(!std::is_reference_v<__T>);
         // bool
         // bool
-        if constexpr(with_check){
+        if constexpr(with_check) {
             if(obj == vm->True) return true;
             if(obj == vm->True) return true;
             if(obj == vm->False) return false;
             if(obj == vm->False) return false;
             vm->TypeError("expected 'bool', got " + _type_name(vm, vm->_tp(obj)).escape());
             vm->TypeError("expected 'bool', got " + _type_name(vm, vm->_tp(obj)).escape());
         }
         }
         return obj == vm->True;
         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>);
         static_assert(!std::is_reference_v<__T>);
         // int
         // int
-        if constexpr(with_check){
+        if constexpr(with_check) {
             if(is_int(obj)) return (T)obj.as<i64>();
             if(is_int(obj)) return (T)obj.as<i64>();
             vm->TypeError("expected 'int', got " + _type_name(vm, vm->_tp(obj)).escape());
             vm->TypeError("expected 'int', got " + _type_name(vm, vm->_tp(obj)).escape());
         }
         }
         return (T)obj.as<i64>();
         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>);
         static_assert(!std::is_reference_v<__T>);
         if(is_float(obj)) return (T)obj.as<f64>();
         if(is_float(obj)) return (T)obj.as<f64>();
         if(is_int(obj)) return (T)obj.as<i64>();
         if(is_int(obj)) return (T)obj.as<i64>();
         vm->TypeError("expected 'int' or 'float', got " + _type_name(vm, vm->_tp(obj)).escape());
         vm->TypeError("expected 'int' or 'float', got " + _type_name(vm, vm->_tp(obj)).escape());
         return 0.0f;
         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>);
         static_assert(!std::is_reference_v<__T>);
         return (__T)_py_cast__internal<i64, with_check>(vm, obj);
         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>);
         static_assert(!std::is_reference_v<__T>);
         return to_void_p<T>(vm, obj);
         return to_void_p<T>(vm, obj);
-    }else{
+    } else {
         constexpr Type const_type = _find_type_in_const_cxx_typeid_map<T>();
         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`
                     // Exception is `subclass_enabled`
                     vm->check_compatible_type(obj, const_type);
                     vm->check_compatible_type(obj, const_type);
-                }else{
+                } else {
                     vm->check_type(obj, const_type);
                     vm->check_type(obj, const_type);
                 }
                 }
             }
             }
             return PK_OBJ_GET(T, obj);
             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>();
                 Type type = vm->_find_type_in_cxx_typeid_map<T>();
                 vm->check_compatible_type(obj, type);
                 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>());
     PyObject* type = new_type_object(mod, name, base, subclass_enabled, PyTypeInfo::Vt::get<T>());
     mod->attr().set(name, type);
     mod->attr().set(name, type);
     _cxx_typeid_map[typeid(T)] = type->as<Type>();
     _cxx_typeid_map[typeid(T)] = type->as<Type>();
     _register(this, mod, type);
     _register(this, mod, type);
-    if(!type->attr().contains(__new__)){
+    if(!type->attr().contains(__new__)) {
         if constexpr(std::is_default_constructible_v<T>) {
         if constexpr(std::is_default_constructible_v<T>) {
-            bind_func(type, __new__, -1, [](VM* vm, ArgsView args){
+            bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
                 Type cls_t = args[0]->as<Type>();
                 Type cls_t = args[0]->as<Type>();
                 return vm->new_object<T>(cls_t);
                 return vm->new_object<T>(cls_t);
             });
             });
-        }else{
-            bind_func(type, __new__, -1, [](VM* vm, ArgsView args){
+        } else {
+            bind_func(type, __new__, -1, [](VM* vm, ArgsView args) {
                 vm->NotImplementedError();
                 vm->NotImplementedError();
                 return vm->None;
                 return vm->None;
             });
             });
@@ -661,9 +771,9 @@ PyObject* VM::register_user_class(PyObject* mod, StrName name, RegisterFunc _reg
     return type;
     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);
     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);
 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);
 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);
 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"
 #include "pocketpy/common/types.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 void add_module_dataclasses(VM* vm);
 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"
 #include "pocketpy/common/types.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 void add_module_easing(VM* vm);
 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"
 #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>
 #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);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
 
     float x, y;
     float x, y;
+
     Vec2() : x(0.0f), y(0.0f) {}
     Vec2() : x(0.0f), y(0.0f) {}
+
     Vec2(float x, float y) : x(x), y(y) {}
     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 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 cross(const Vec2& v) const { return x * v.y - y * v.x; }
+
     float length() const { return sqrtf(x * x + y * y); }
     float length() const { return sqrtf(x * x + y * y); }
+
     float length_squared() const { return 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);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
 
     float x, y, z;
     float x, y, z;
+
     Vec3() : x(0.0f), y(0.0f), z(0.0f) {}
     Vec3() : x(0.0f), y(0.0f), z(0.0f) {}
+
     Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
     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; }
     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); }
     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() const { return sqrtf(x * x + y * y + z * z); }
+
     float length_squared() const { return 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);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
 
     float x, y, z, w;
     float x, y, z, w;
+
     Vec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {}
     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(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 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() const { return sqrtf(x * x + y * y + z * z + w * w); }
+
     float length_squared() const { return 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);
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
 
     union {
     union {
         struct {
         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 m[3][3];
         float v[9];
         float v[9];
     };
     };
@@ -100,14 +177,14 @@ struct Mat3x3{
     static Mat3x3 ones();
     static Mat3x3 ones();
     static Mat3x3 identity();
     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;
     Mat3x3 matmul(const Mat3x3& other) const;
     Vec3 matmul(const Vec3& 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<Vec4>);
 static_assert(is_pod_v<Mat3x3>);
 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"
 #include "pocketpy/common/types.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 void add_module_time(VM* vm);
 void add_module_time(VM* vm);
 void add_module_sys(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_enum(VM* vm);
 void add_module___builtins(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"
 #include "pocketpy/common/types.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 void add_module_random(VM* vm);
 void add_module_random(VM* vm);
 
 
-} // namespace pkpy
+}  // namespace pkpy

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

@@ -8,25 +8,31 @@
 #include <cstdlib>
 #include <cstdlib>
 #include <cstring>
 #include <cstring>
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 struct Type {
 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 const_sso_var {};
 
 
-struct PyVar final{
+struct PyVar final {
     Type type;
     Type type;
     bool is_ptr;
     bool is_ptr;
     uint8_t flags;
     uint8_t flags;
     // 12 bytes SSO
     // 12 bytes SSO
-    int _0; i64 _1;
+    int _0;
+    i64 _1;
 
 
     // uninitialized
     // uninitialized
     PyVar() = default;
     PyVar() = default;
@@ -36,45 +42,50 @@ struct PyVar final{
 
 
     /* We must initialize all members to allow == operator to work correctly */
     /* We must initialize all members to allow == operator to work correctly */
     // constexpr initialized
     // 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
     // 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)
     // 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)
     // 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");
         static_assert(sizeof(T) <= 12, "SSO size exceeded");
         as<T>() = value;
         as<T>() = value;
     }
     }
 
 
-    template<typename T>
-    T& as(){
+    template <typename T>
+    T& as() {
         static_assert(!std::is_reference_v<T>);
         static_assert(!std::is_reference_v<T>);
-        if constexpr(sizeof(T) <= 8){
+        if constexpr(sizeof(T) <= 8) {
             return reinterpret_cast<T&>(_1);
             return reinterpret_cast<T&>(_1);
-        }else{
+        } else {
             return reinterpret_cast<T&>(_0);
             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) const { return ((const i64*)this)[i]; }
+
     i64& _qword(int i) { return ((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 {
     PyObject* get() const {
         assert(is_ptr);
         assert(is_ptr);
@@ -88,14 +99,12 @@ struct PyVar final{
 
 
     i64 hash() const { return _0 + _1; }
     i64 hash() const { return _0 + _1; }
 
 
-    template<typename T>
+    template <typename T>
     obj_get_t<T> obj_get();
     obj_get_t<T> obj_get();
 
 
     // for std::map<>
     // 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>);
 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/common/vector.hpp"
 #include "pocketpy/objects/object.hpp"
 #include "pocketpy/objects/object.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 struct BoundMethod {
 struct BoundMethod {
     PyVar self;
     PyVar self;
     PyVar func;
     PyVar func;
+
     BoundMethod(PyVar self, PyVar func) : self(self), func(func) {}
     BoundMethod(PyVar self, PyVar func) : self(self), func(func) {}
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
-struct StaticMethod{
+struct StaticMethod {
     PyVar func;
     PyVar func;
+
     StaticMethod(PyVar func) : func(func) {}
     StaticMethod(PyVar func) : func(func) {}
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
-struct ClassMethod{
+struct ClassMethod {
     PyVar func;
     PyVar func;
+
     ClassMethod(PyVar func) : func(func) {}
     ClassMethod(PyVar func) : func(func) {}
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
-struct Property{
+struct Property {
     PyVar getter;
     PyVar getter;
     PyVar setter;
     PyVar setter;
+
     Property(PyVar getter, PyVar setter) : getter(getter), setter(setter) {}
     Property(PyVar getter, PyVar setter) : getter(getter), setter(setter) {}
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
@@ -37,20 +45,23 @@ struct Range {
     i64 step = 1;
     i64 step = 1;
 };
 };
 
 
-
-struct StarWrapper{
-    int level;      // either 1 or 2
+struct StarWrapper {
+    int level;  // either 1 or 2
     PyVar obj;
     PyVar obj;
+
     StarWrapper(int level, PyVar obj) : level(level), obj(obj) {}
     StarWrapper(int level, PyVar obj) : level(level), obj(obj) {}
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
 using Bytes = array<unsigned char>;
 using Bytes = array<unsigned char>;
 
 
-struct Super{
+struct Super {
     PyVar first;
     PyVar first;
     Type second;
     Type second;
+
     Super(PyVar first, Type second) : first(first), second(second) {}
     Super(PyVar first, Type second) : first(first), second(second) {}
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
@@ -58,16 +69,19 @@ struct Slice {
     PyVar start;
     PyVar start;
     PyVar stop;
     PyVar stop;
     PyVar step;
     PyVar step;
+
     Slice(PyVar start, PyVar stop, PyVar step) : start(start), stop(stop), step(step) {}
     Slice(PyVar start, PyVar stop, PyVar step) : start(start), stop(stop), step(step) {}
+
     void _gc_mark(VM*) const;
     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_tagged(PyVar p) noexcept { return !p.is_ptr; }
+
 inline bool is_float(PyVar p) noexcept { return p.type.index == kTpFloatIndex; }
 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_int(PyVar p) noexcept { return p.type.index == kTpIntIndex; }
 
 
 inline bool is_type(PyVar obj, Type type) {
 inline bool is_type(PyVar obj, Type type) {
@@ -80,23 +94,26 @@ inline bool is_type(PyObject* p, Type type) {
     return p->type == type;
     return p->type == type;
 }
 }
 
 
-struct MappingProxy{
+struct MappingProxy {
     PyObject* obj;
     PyObject* obj;
+
     MappingProxy(PyObject* obj) : obj(obj) {}
     MappingProxy(PyObject* obj) : obj(obj) {}
+
     NameDict& attr() { return obj->attr(); }
     NameDict& attr() { return obj->attr(); }
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
 StrName _type_name(VM* vm, Type type);
 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*);
 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>();
         return as<T>();
-    }else{
+    } else {
         assert(is_ptr);
         assert(is_ptr);
         void* v = ((PyObject*)_1)->_value_ptr();
         void* v = ((PyObject*)_1)->_value_ptr();
         return *reinterpret_cast<T*>(v);
         return *reinterpret_cast<T*>(v);
@@ -119,6 +136,6 @@ obj_get_t<T> PyVar::obj_get(){
 
 
 #define PY_NULL nullptr
 #define PY_NULL nullptr
 extern PyVar const PY_OP_CALL;
 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/object.hpp"
 #include "pocketpy/objects/sourcedata.hpp"
 #include "pocketpy/objects/sourcedata.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 #if PK_ENABLE_STD_FUNCTION
 #if PK_ENABLE_STD_FUNCTION
 using NativeFuncC = function<PyVar(VM*, ArgsView)>;
 using NativeFuncC = function<PyVar(VM*, ArgsView)>;
@@ -13,7 +13,7 @@ using NativeFuncC = function<PyVar(VM*, ArgsView)>;
 typedef PyVar (*NativeFuncC)(VM*, ArgsView);
 typedef PyVar (*NativeFuncC)(VM*, ArgsView);
 #endif
 #endif
 
 
-enum class BindType{
+enum class BindType {
     DEFAULT,
     DEFAULT,
     STATICMETHOD,
     STATICMETHOD,
     CLASSMETHOD,
     CLASSMETHOD,
@@ -21,24 +21,23 @@ enum class BindType{
 
 
 enum NameScope { NAME_LOCAL, NAME_GLOBAL, NAME_GLOBAL_UNKNOWN };
 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;
     uint8_t op;
     uint16_t arg;
     uint16_t arg;
 
 
-    void set_signed_arg(int arg){
+    void set_signed_arg(int arg) {
         assert(arg >= INT16_MIN && arg <= INT16_MAX);
         assert(arg >= INT16_MIN && arg <= INT16_MAX);
         this->arg = (int16_t)arg;
         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 {
 enum class CodeBlockType {
@@ -49,20 +48,20 @@ enum class CodeBlockType {
     TRY_EXCEPT,
     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 {
 struct CodeBlock {
     CodeBlockType type;
     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) {}
         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;
         if(end2 != -1) return end2;
         return end;
         return end;
     }
     }
@@ -74,10 +73,10 @@ using CodeObject_ = std::shared_ptr<CodeObject>;
 using FuncDecl_ = std::shared_ptr<FuncDecl>;
 using FuncDecl_ = std::shared_ptr<FuncDecl>;
 
 
 struct CodeObject {
 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;
     std::shared_ptr<SourceData> src;
@@ -85,10 +84,10 @@ struct CodeObject {
 
 
     vector<Bytecode> codes;
     vector<Bytecode> codes;
     vector<LineInfo> lines;
     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;
     NameDictInt varnames_inv;
     vector<CodeBlock> blocks;
     vector<CodeBlock> blocks;
@@ -98,15 +97,13 @@ struct CodeObject {
     int start_line;
     int start_line;
     int end_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);
     CodeObject(std::shared_ptr<SourceData> src, const Str& name);
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
-enum class FuncType{
+enum class FuncType {
     UNSET,
     UNSET,
     NORMAL,
     NORMAL,
     SIMPLE,
     SIMPLE,
@@ -116,63 +113,68 @@ enum class FuncType{
 
 
 struct FuncDecl {
 struct FuncDecl {
     struct KwArg {
     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<int, 6> args;      // indices in co->varnames
     small_vector_2<KwArg, 6> kwargs;  // 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;
     FuncType type = FuncType::UNSET;
 
 
     NameDictInt kw_to_index;
     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);
         kw_to_index.set(key, index);
         kwargs.push_back(KwArg{index, key, value});
         kwargs.push_back(KwArg{index, key, value});
     }
     }
-    
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
 struct NativeFunc {
 struct NativeFunc {
     NativeFuncC f;
     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;
     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); }
     PyVar call(VM* vm, ArgsView args) const { return f(vm, args); }
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
-struct Function{
+struct Function {
     FuncDecl_ decl;
     FuncDecl_ decl;
     PyObject* _module;  // weak ref
     PyObject* _module;  // weak ref
     PyObject* _class;   // weak ref
     PyObject* _class;   // weak ref
     NameDict_ _closure;
     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) {}
         decl(decl), _module(_module), _class(_class), _closure(_closure) {}
 
 
     void _gc_mark(VM*) const;
     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>>);
     static_assert(std::is_same_v<T, std::decay_t<T>>);
     int offset = p[-1] != nullptr ? -1 : -2;
     int offset = p[-1] != nullptr ? -1 : -2;
     return p[offset].obj_get<NativeFunc>()._userdata.cast<T>();
     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/base.hpp"
 #include "pocketpy/objects/tuplelist.hpp"
 #include "pocketpy/objects/tuplelist.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
-struct Dict{
-    struct Item{
+struct Dict {
+    struct Item {
         PyVar first;
         PyVar first;
         PyVar second;
         PyVar second;
         int prev;
         int prev;
         int next;
         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 _capacity;
     int _mask;
     int _mask;
     int _size;
     int _size;
     int _critical_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;
     Item* _items;
 
 
     Dict();
     Dict();
     Dict(Dict&& other);
     Dict(Dict&& other);
     Dict(const 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; }
     int size() const { return _size; }
 
 
@@ -44,10 +44,10 @@ struct Dict{
     bool del(VM* vm, PyVar key);
     bool del(VM* vm, PyVar key);
     void update(VM* vm, const Dict& other);
     void update(VM* vm, const Dict& other);
 
 
-    template<typename __Func>
+    template <typename __Func>
     void apply(__Func f) const {
     void apply(__Func f) const {
         int i = _head_idx;
         int i = _head_idx;
-        while(i != -1){
+        while(i != -1) {
             f(_items[i].first, _items[i].second);
             f(_items[i].first, _items[i].second);
             i = _items[i].next;
             i = _items[i].next;
         }
         }
@@ -63,4 +63,4 @@ struct Dict{
     void _gc_mark(VM*) const;
     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/common/str.hpp"
 #include "pocketpy/objects/sourcedata.hpp"
 #include "pocketpy/objects/sourcedata.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 struct NeedMoreLines {
 struct NeedMoreLines {
     NeedMoreLines(bool is_compiling_class) : is_compiling_class(is_compiling_class) {}
     NeedMoreLines(bool is_compiling_class) : is_compiling_class(is_compiling_class) {}
+
     bool 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;
     InternalExceptionType type;
     int arg;
     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 {
 struct Exception {
@@ -29,9 +30,9 @@ struct Exception {
     int _ip_on_error;
     int _ip_on_error;
     void* _code_on_error;
     void* _code_on_error;
 
 
-    PyObject* _self;    // weak reference
+    PyObject* _self;  // weak reference
 
 
-    struct Frame{
+    struct Frame {
         std::shared_ptr<SourceData> src;
         std::shared_ptr<SourceData> src;
         int lineno;
         int lineno;
         const char* cursor;
         const char* cursor;
@@ -39,20 +40,21 @@ struct Exception {
 
 
         Str snapshot() const { return src->snapshot(lineno, cursor, name); }
         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) {}
             src(src), lineno(lineno), cursor(cursor), name(name) {}
     };
     };
 
 
     stack<Frame> stacktrace;
     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);
         assert(_self != nullptr);
         return _self;
         return _self;
     }
     }
 
 
-    template<typename... Args>
-    void st_push(Args&&... args){
+    template <typename... Args>
+    void st_push(Args&&... args) {
         if(stacktrace.size() >= 7) return;
         if(stacktrace.size() >= 7) return;
         stacktrace.emplace(std::forward<Args>(args)...);
         stacktrace.emplace(std::forward<Args>(args)...);
     }
     }
@@ -60,10 +62,11 @@ struct Exception {
     Str summary() const;
     Str summary() const;
 };
 };
 
 
-struct TopLevelException: std::exception{
+struct TopLevelException : std::exception {
     VM* vm;
     VM* vm;
     Exception* ptr;
     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(); }
     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/common/namedict.hpp"
 #include "pocketpy/objects/base.hpp"
 #include "pocketpy/objects/base.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 using NameDict = NameDictImpl<PyVar>;
 using NameDict = NameDictImpl<PyVar>;
 using NameDict_ = std::shared_ptr<NameDict>;
 using NameDict_ = std::shared_ptr<NameDict>;
 using NameDictInt = NameDictImpl<int>;
 using NameDictInt = NameDictImpl<int>;
 
 
 static_assert(sizeof(NameDict) <= 128);
 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; }
     bool is_attr_valid() const noexcept { return _attr != nullptr; }
+
     void* _value_ptr() noexcept { return (char*)this + 16; }
     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>>);
         static_assert(std::is_same_v<T, std::decay_t<T>>);
         return *reinterpret_cast<T*>(_value_ptr());
         return *reinterpret_cast<T*>(_value_ptr());
     }
     }
@@ -35,17 +37,17 @@ struct PyObject final{
 
 
     PyObject(Type type) : gc_marked(false), type(type), _attr(nullptr) {}
     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>>);
         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
         // backdoor for important builtin types
-        if constexpr(std::is_same_v<T, DummyInstance>){
+        if constexpr(std::is_same_v<T, DummyInstance>) {
             _attr = new NameDict();
             _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);
             _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);
             _attr = new NameDict(PK_TYPE_ATTR_LOAD_FACTOR);
         }
         }
     }
     }
@@ -53,4 +55,4 @@ struct PyObject final{
 
 
 static_assert(sizeof(PyObject) <= 16);
 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/utils.hpp"
 #include "pocketpy/common/str.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 {
 struct SourceData {
     PK_ALWAYS_PASS_BY_POINTER(SourceData)
     PK_ALWAYS_PASS_BY_POINTER(SourceData)
@@ -24,12 +18,12 @@ struct SourceData {
 
 
     bool is_precompiled;
     bool is_precompiled;
     vector<Str> _precompiled_tokens;
     vector<Str> _precompiled_tokens;
-    
+
     SourceData(std::string_view source, const Str& filename, CompileMode mode);
     SourceData(std::string_view source, const Str& filename, CompileMode mode);
     SourceData(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;
     std::string_view get_line(int lineno) const;
     Str snapshot(int lineno, const char* cursor, std::string_view name) 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"
 #include "pocketpy/common/traits.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
-struct StackMemory{
+struct StackMemory {
     int count;
     int count;
+
     StackMemory(int count) : count(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 {
 namespace pkpy {
 
 
 struct Tuple {
 struct Tuple {
-    static const int INLINED_SIZE = 3;
+    const static int INLINED_SIZE = 3;
 
 
     PyVar* _args;
     PyVar* _args;
     PyVar _inlined[INLINED_SIZE];
     PyVar _inlined[INLINED_SIZE];
@@ -15,52 +15,63 @@ struct Tuple {
     Tuple(int n);
     Tuple(int n);
     Tuple(Tuple&& other) noexcept;
     Tuple(Tuple&& other) noexcept;
     Tuple(const Tuple& other) = delete;
     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();
 
 
     Tuple(PyVar, PyVar);
     Tuple(PyVar, PyVar);
     Tuple(PyVar, PyVar, PyVar);
     Tuple(PyVar, PyVar, PyVar);
 
 
     bool is_inlined() const { return _args == _inlined; }
     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; }
     int size() const { return _size; }
 
 
     PyVar* begin() const { return _args; }
     PyVar* begin() const { return _args; }
+
     PyVar* end() const { return _args + _size; }
     PyVar* end() const { return _args + _size; }
+
     PyVar* data() const { return _args; }
     PyVar* data() const { return _args; }
+
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 };
 };
 
 
-struct List: public vector<PyVar>{
+struct List : public vector<PyVar> {
     using vector<PyVar>::vector;
     using vector<PyVar>::vector;
     void _gc_mark(VM*) const;
     void _gc_mark(VM*) const;
 
 
-    Tuple to_tuple() const{
+    Tuple to_tuple() const {
         Tuple ret(size());
         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;
         return ret;
     }
     }
 };
 };
 
 
 // a lightweight view for function args, it does not own the memory
 // a lightweight view for function args, it does not own the memory
-struct ArgsView{
+struct ArgsView {
     PyVar* _begin;
     PyVar* _begin;
     PyVar* _end;
     PyVar* _end;
 
 
     ArgsView(PyVar* begin, PyVar* end) : _begin(begin), _end(end) {}
     ArgsView(PyVar* begin, PyVar* end) : _begin(begin), _end(end) {}
+
     ArgsView(const Tuple& t) : _begin(t.begin()), _end(t.end()) {}
     ArgsView(const Tuple& t) : _begin(t.begin()), _end(t.end()) {}
 
 
     PyVar* begin() const { return _begin; }
     PyVar* begin() const { return _begin; }
+
     PyVar* end() const { return _end; }
     PyVar* end() const { return _end; }
+
     int size() const { return _end - _begin; }
     int size() const { return _end - _begin; }
+
     bool empty() const { return _begin == _end; }
     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;
     List to_list() const;
     Tuple to_tuple() 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(DEC_FAST)
 OPCODE(INC_GLOBAL)
 OPCODE(INC_GLOBAL)
 OPCODE(DEC_GLOBAL)
 OPCODE(DEC_GLOBAL)
-#endif
+#endif

+ 14 - 14
include/pocketpy/pocketpy.hpp

@@ -9,17 +9,17 @@
 #include "pocketpy/modules/linalg.hpp"
 #include "pocketpy/modules/linalg.hpp"
 #include "pocketpy/tools/repl.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
 #define POCKETPY_C_H
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
@@ -10,98 +10,97 @@ extern "C" {
 
 
 #include "pocketpy/common/export.h"
 #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)
 #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
 #ifdef __cplusplus
 }
 }
 #endif
 #endif
 
 
-
 #endif
 #endif

+ 4 - 3
include/pocketpy/tools/repl.hpp

@@ -2,16 +2,17 @@
 
 
 #include "pocketpy/interpreter/vm.hpp"
 #include "pocketpy/interpreter/vm.hpp"
 
 
-namespace pkpy{
-    
+namespace pkpy {
+
 class REPL {
 class REPL {
 protected:
 protected:
     int need_more_lines = 0;
     int need_more_lines = 0;
     std::string buffer;
     std::string buffer;
     VM* vm;
     VM* vm;
+
 public:
 public:
     REPL(VM* vm);
     REPL(VM* vm);
     bool input(std::string line);
     bool input(std::string line);
 };
 };
 
 
-} // namespace pkpy
+}  // namespace pkpy

+ 1 - 1
include/pocketpy_c.h

@@ -1,3 +1,3 @@
 #pragma once
 #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"
 #include "types.h"
 
 
 namespace pybind11 {
 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
 }  // namespace pybind11

+ 116 - 123
include/pybind11/internal/cast.h

@@ -6,168 +6,161 @@
 
 
 namespace pybind11 {
 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
 }  // namespace pybind11
 
 

+ 160 - 167
include/pybind11/internal/class.h

@@ -4,181 +4,174 @@
 
 
 namespace pybind11 {
 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;
             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
 }  // namespace pybind11

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

@@ -5,379 +5,354 @@
 
 
 namespace pybind11 {
 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) {
                 [&](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 {
         } 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>
     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 {
         } 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>();
                     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 {
                     } 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;
                         type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
                         if(caster.load(view[1], true)) {
                         if(caster.load(view[1], true)) {
-                            setter(self, caster.value);
+                            (self.*setter)(caster.value);
                             return vm->None;
                             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
 }  // namespace pybind11

+ 128 - 130
include/pybind11/internal/instance.h

@@ -3,145 +3,143 @@
 #include "kernel.h"
 #include "kernel.h"
 
 
 namespace pybind11 {
 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
 }  // namespace pybind11

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

@@ -2,107 +2,98 @@
 
 
 #include <pocketpy.h>
 #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
 }  // namespace pybind11

+ 214 - 218
include/pybind11/internal/object.h

@@ -3,253 +3,249 @@
 #include "kernel.h"
 #include "kernel.h"
 
 
 namespace pybind11 {
 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 {
             } else {
+                ref_count = iter->second;
                 *ref_count += 1;
                 *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
 #undef PYBIND11_BINARY_OPERATOR
 
 

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

@@ -4,154 +4,154 @@
 #include <type_traits>
 #include <type_traits>
 
 
 namespace pybind11 {
 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
 #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
 }  // namespace pybind11

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

@@ -3,216 +3,206 @@
 #include "object.h"
 #include "object.h"
 
 
 namespace pybind11 {
 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>
     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
 #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
 }  // namespace pybind11

+ 1 - 1
include/pybind11/pybind11.h

@@ -1,3 +1,3 @@
 #pragma once
 #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__':
 if __name__ == '__main__':
     files = []
     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'))
     files.extend(get_all_files('src2'))
     subprocess.run(['clang-format', '-i'] + files, check=True)
     subprocess.run(['clang-format', '-i'] + files, check=True)

+ 5 - 5
src/common/any.cpp

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

+ 66 - 64
src/common/memorypool.cpp

@@ -6,28 +6,28 @@
 #include <cassert>
 #include <cassert>
 #include <stdexcept>
 #include <stdexcept>
 
 
-namespace pkpy{
+namespace pkpy {
 
 
-struct LinkedListNode{
+struct LinkedListNode {
     LinkedListNode* prev;
     LinkedListNode* prev;
     LinkedListNode* next;
     LinkedListNode* next;
 };
 };
 
 
-template<typename T>
-struct DoubleLinkedList{
+template <typename T>
+struct DoubleLinkedList {
     static_assert(std::is_base_of_v<LinkedListNode, T>);
     static_assert(std::is_base_of_v<LinkedListNode, T>);
     int _size;
     int _size;
     LinkedListNode head;
     LinkedListNode head;
     LinkedListNode tail;
     LinkedListNode tail;
-    
-    DoubleLinkedList(): _size(0){
+
+    DoubleLinkedList() : _size(0) {
         head.prev = nullptr;
         head.prev = nullptr;
         head.next = &tail;
         head.next = &tail;
         tail.prev = &head;
         tail.prev = &head;
         tail.next = nullptr;
         tail.next = nullptr;
     }
     }
 
 
-    void push_back(T* node){
+    void push_back(T* node) {
         node->prev = tail.prev;
         node->prev = tail.prev;
         node->next = &tail;
         node->next = &tail;
         tail.prev->next = node;
         tail.prev->next = node;
@@ -35,7 +35,7 @@ struct DoubleLinkedList{
         _size++;
         _size++;
     }
     }
 
 
-    void push_front(T* node){
+    void push_front(T* node) {
         node->prev = &head;
         node->prev = &head;
         node->next = head.next;
         node->next = head.next;
         head.next->prev = node;
         head.next->prev = node;
@@ -43,14 +43,14 @@ struct DoubleLinkedList{
         _size++;
         _size++;
     }
     }
 
 
-    void pop_back(){
+    void pop_back() {
         assert(!empty());
         assert(!empty());
         tail.prev->prev->next = &tail;
         tail.prev->prev->next = &tail;
         tail.prev = tail.prev->prev;
         tail.prev = tail.prev->prev;
         _size--;
         _size--;
     }
     }
 
 
-    void pop_front(){
+    void pop_front() {
         assert(!empty());
         assert(!empty());
         head.next->next->prev = &head;
         head.next->next->prev = &head;
         head.next = head.next->next;
         head.next = head.next->next;
@@ -67,22 +67,20 @@ struct DoubleLinkedList{
         return static_cast<T*>(head.next);
         return static_cast<T*>(head.next);
     }
     }
 
 
-    void erase(T* node){
+    void erase(T* node) {
         node->prev->next = node->next;
         node->prev->next = node->next;
         node->next->prev = node->prev;
         node->next->prev = node->prev;
         _size--;
         _size--;
     }
     }
 
 
-    bool empty() const {
-        return _size == 0;
-    }
+    bool empty() const { return _size == 0; }
 
 
     int size() const { return _size; }
     int size() const { return _size; }
 
 
-    template<typename Func>
-    void apply(Func func){
+    template <typename Func>
+    void apply(Func func) {
         LinkedListNode* p = head.next;
         LinkedListNode* p = head.next;
-        while(p != &tail){
+        while(p != &tail) {
             LinkedListNode* next = p->next;
             LinkedListNode* next = p->next;
             func(static_cast<T*>(p));
             func(static_cast<T*>(p));
             p = next;
             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;
         void* arena;
         char data[__BlockSize];
         char data[__BlockSize];
     };
     };
 
 
-    struct Arena: LinkedListNode{
+    struct Arena : LinkedListNode {
         Block _blocks[__MaxBlocks];
         Block _blocks[__MaxBlocks];
         Block* _free_list[__MaxBlocks];
         Block* _free_list[__MaxBlocks];
         int _free_list_size;
         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;
                 _blocks[i].arena = this;
                 _free_list[i] = &_blocks[i];
                 _free_list[i] = &_blocks[i];
             }
             }
         }
         }
 
 
         bool empty() const { return _free_list_size == 0; }
         bool empty() const { return _free_list_size == 0; }
+
         bool full() const { return _free_list_size == __MaxBlocks; }
         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());
             assert(!empty());
             _free_list_size--;
             _free_list_size--;
             return _free_list[_free_list_size];
             return _free_list[_free_list_size];
         }
         }
 
 
-        void dealloc(Block* block){
+        void dealloc(Block* block) {
             assert(!full());
             assert(!full());
             _free_list[_free_list_size] = block;
             _free_list[_free_list_size] = block;
             _free_list_size++;
             _free_list_size++;
@@ -134,71 +131,73 @@ struct MemoryPool{
 
 
     MemoryPool() = default;
     MemoryPool() = default;
     MemoryPool(const MemoryPool&) = delete;
     MemoryPool(const MemoryPool&) = delete;
-    MemoryPool& operator=(const MemoryPool&) = delete;
+    MemoryPool& operator= (const MemoryPool&) = delete;
     MemoryPool(MemoryPool&&) = delete;
     MemoryPool(MemoryPool&&) = delete;
-    MemoryPool& operator=(MemoryPool&&) = delete;
+    MemoryPool& operator= (MemoryPool&&) = delete;
 
 
     DoubleLinkedList<Arena> _arenas;
     DoubleLinkedList<Arena> _arenas;
     DoubleLinkedList<Arena> _empty_arenas;
     DoubleLinkedList<Arena> _empty_arenas;
 
 
-    void* alloc(size_t size){
+    void* alloc(size_t size) {
         PK_GLOBAL_SCOPE_LOCK();
         PK_GLOBAL_SCOPE_LOCK();
-        if(size > __BlockSize){
+        if(size > __BlockSize) {
             void* p = std::malloc(sizeof(void*) + size);
             void* p = std::malloc(sizeof(void*) + size);
             std::memset(p, 0, sizeof(void*));
             std::memset(p, 0, sizeof(void*));
             return (char*)p + 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();
         Arena* arena = _arenas.back();
         void* p = arena->alloc()->data;
         void* p = arena->alloc()->data;
-        if(arena->empty()){
+        if(arena->empty()) {
             _arenas.pop_back();
             _arenas.pop_back();
             _empty_arenas.push_back(arena);
             _empty_arenas.push_back(arena);
         }
         }
         return p;
         return p;
     }
     }
 
 
-    void dealloc(void* p){
+    void dealloc(void* p) {
         PK_GLOBAL_SCOPE_LOCK();
         PK_GLOBAL_SCOPE_LOCK();
         assert(p != nullptr);
         assert(p != nullptr);
         Block* block = (Block*)((char*)p - sizeof(void*));
         Block* block = (Block*)((char*)p - sizeof(void*));
-        if(block->arena == nullptr){
+        if(block->arena == nullptr) {
             std::free(block);
             std::free(block);
-        }else{
+        } else {
             Arena* arena = (Arena*)block->arena;
             Arena* arena = (Arena*)block->arena;
-            if(arena->empty()){
+            if(arena->empty()) {
                 _empty_arenas.erase(arena);
                 _empty_arenas.erase(arena);
                 _arenas.push_front(arena);
                 _arenas.push_front(arena);
                 arena->dealloc(block);
                 arena->dealloc(block);
-            }else{
+            } else {
                 arena->dealloc(block);
                 arena->dealloc(block);
             }
             }
         }
         }
     }
     }
 
 
-    void shrink_to_fit(){
+    void shrink_to_fit() {
         PK_GLOBAL_SCOPE_LOCK();
         PK_GLOBAL_SCOPE_LOCK();
         if(_arenas.size() < __MinArenaCount) return;
         if(_arenas.size() < __MinArenaCount) return;
-        _arenas.apply([this](Arena* arena){
-            if(arena->full()){
+        _arenas.apply([this](Arena* arena) {
+            if(arena->full()) {
                 _arenas.erase(arena);
                 _arenas.erase(arena);
                 delete 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];
         char data[BlockSize];
     };
     };
 
 
@@ -211,31 +210,29 @@ struct FixedMemoryPool{
 
 
     FixedMemoryPool() {
     FixedMemoryPool() {
         _free_list_end = _free_list + BlockCount;
         _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;
             _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()
         PK_GLOBAL_SCOPE_LOCK()
-        if(_free_list_end != _free_list){
+        if(_free_list_end != _free_list) {
             --_free_list_end;
             --_free_list_end;
             return *_free_list_end;
             return *_free_list_end;
-        }else{
+        } else {
             return std::malloc(BlockSize);
             return std::malloc(BlockSize);
         }
         }
     }
     }
 
 
-    void dealloc(void* p){
+    void dealloc(void* p) {
         PK_GLOBAL_SCOPE_LOCK()
         PK_GLOBAL_SCOPE_LOCK()
-        if(is_valid(p)){
+        if(is_valid(p)) {
             *_free_list_end = static_cast<Block*>(p);
             *_free_list_end = static_cast<Block*>(p);
             ++_free_list_end;
             ++_free_list_end;
-        }else{
+        } else {
             std::free(p);
             std::free(p);
         }
         }
     }
     }
@@ -246,12 +243,17 @@ static FixedMemoryPool<kPoolFrameBlockSize, 128> PoolFrame;
 static MemoryPool<80> PoolObject;
 static MemoryPool<80> PoolObject;
 
 
 void* PoolExpr_alloc() noexcept { return PoolExpr.alloc(); }
 void* PoolExpr_alloc() noexcept { return PoolExpr.alloc(); }
+
 void PoolExpr_dealloc(void* p) noexcept { PoolExpr.dealloc(p); }
 void PoolExpr_dealloc(void* p) noexcept { PoolExpr.dealloc(p); }
+
 void* PoolFrame_alloc() noexcept { return PoolFrame.alloc(); }
 void* PoolFrame_alloc() noexcept { return PoolFrame.alloc(); }
+
 void PoolFrame_dealloc(void* p) noexcept { PoolFrame.dealloc(p); }
 void PoolFrame_dealloc(void* p) noexcept { PoolFrame.dealloc(p); }
 
 
 void* PoolObject_alloc(size_t size) noexcept { return PoolObject.alloc(size); }
 void* PoolObject_alloc(size_t size) noexcept { return PoolObject.alloc(size); }
+
 void PoolObject_dealloc(void* p) noexcept { PoolObject.dealloc(p); }
 void PoolObject_dealloc(void* p) noexcept { PoolObject.dealloc(p); }
+
 void PoolObject_shrink_to_fit() noexcept { PoolObject.shrink_to_fit(); }
 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 {
 namespace pkpy {
 
 
-int utf8len(unsigned char c, bool suppress){
+int utf8len(unsigned char c, bool suppress) {
     if((c & 0b10000000) == 0) return 1;
     if((c & 0b10000000) == 0) return 1;
     if((c & 0b11100000) == 0b11000000) return 2;
     if((c & 0b11100000) == 0b11000000) return 2;
     if((c & 0b11110000) == 0b11100000) return 3;
     if((c & 0b11110000) == 0b11100000) return 3;
@@ -19,531 +19,499 @@ int utf8len(unsigned char c, bool suppress){
     return 0;
     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';
         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 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;
         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_ALLOCATE
 #undef PK_STR_COPY_INIT
 #undef PK_STR_COPY_INIT
 
 
-
-
 // unary operators
 // unary operators
 const StrName __repr__ = StrName::get("__repr__");
 const StrName __repr__ = StrName::get("__repr__");
 const StrName __str__ = StrName::get("__str__");
 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_long = StrName::get("long");
 const StrName pk_id_complex = StrName::get("complex");
 const StrName pk_id_complex = StrName::get("complex");
 
 
-
-} // namespace pkpy
+}  // namespace pkpy

+ 1201 - 1236
src/compiler/compiler.cpp

@@ -4,112 +4,106 @@
 
 
 #include <stdexcept>
 #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/
 // http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
 #define PK_METHOD(name) &Compiler::name
 #define PK_METHOD(name) &Compiler::name
 #define PK_NO_INFIX nullptr, PREC_LOWEST
 #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(".")] =        { nullptr,                  PK_METHOD(exprAttrib),         PREC_PRIMARY };
         rules[TK("(")] =        { PK_METHOD(exprGroup),     PK_METHOD(exprCall),           PREC_PRIMARY };
         rules[TK("(")] =        { PK_METHOD(exprGroup),     PK_METHOD(exprCall),           PREC_PRIMARY };
         rules[TK("[")] =        { PK_METHOD(exprList),      PK_METHOD(exprSubscr),         PREC_PRIMARY };
         rules[TK("[")] =        { PK_METHOD(exprList),      PK_METHOD(exprSubscr),         PREC_PRIMARY };
@@ -158,1278 +152,1249 @@ namespace pkpy{
         
         
 #undef PK_METHOD
 #undef PK_METHOD
 #undef PK_NO_INFIX
 #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());
         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);
         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();
         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();
         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();
         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();
         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));
         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));
         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();
             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();
             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();
             EXPR();
             slice->step = ctx()->s_expr.popx();
             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"));
             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:
 __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();
             ss << prev().sv();
-            while (match(TK("."))) {
+            while(match(TK("."))) {
                 consume(TK("@id"));
                 consume(TK("@id"));
                 ss << "." << prev().sv();
                 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"));
             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();
         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()->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();
         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();
             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);
             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_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()->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();
                 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"));
             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);
         _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)>;
                 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;
                     ss << 'I' << arg;
-                }else if constexpr(std::is_same_v<T, f64>){
+                } else if constexpr(std::is_same_v<T, f64>) {
                     ss << 'F' << arg;
                     ss << 'F' << arg;
-                }else if constexpr(std::is_same_v<T, Str>){
+                } else if constexpr(std::is_same_v<T, Str>) {
                     ss << 'S';
                     ss << 'S';
-                    for(char c: arg) ss.write_hex((unsigned char)c);
+                    for(char c: arg)
+                        ss.write_hex((unsigned char)c);
                 }
                 }
                 ss << '\n';
                 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();
         pop_context();
         return code;
         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++;
         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/compiler/expr.hpp"
 #include "pocketpy/interpreter/vm.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;
         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;
         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;
         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);
             ctx->emit_int(_val, line);
             return;
             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);
             ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
             return;
             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 '@': case '!': case '~':
             case '<': case '>': case '^':
             case '<': case '>': case '^':
             case '.': case 'f': case 'd': case 's':
             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':
             case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
             return true;
             return true;
             default: return false;
             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);
                         _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++;
                     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++;
                     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);
         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);
         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]
         // [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

Разница между файлами не показана из-за своего большого размера
+ 2 - 1
src/compiler/lexer.cpp


Разница между файлами не показана из-за своего большого размера
+ 1097 - 988
src/interpreter/ceval.cpp


+ 198 - 188
src/interpreter/cffi.cpp

@@ -1,159 +1,173 @@
 #include "pocketpy/interpreter/cffi.hpp"
 #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]);
             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;
                 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;
                 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));
             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
 #undef BIND_SETGET
-    }
+}
 
 
-void add_module_c(VM* vm){
+void add_module_c(VM* vm) {
     PyObject* mod = vm->new_module("c");
     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]);
         i64 size = CAST(i64, args[0]);
         return VAR(std::malloc(size));
         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]);
         void* p = CAST(void*, args[0]);
         std::free(p);
         std::free(p);
         return vm->None;
         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]);
         void* p = CAST(void*, args[0]);
         std::memset(p, CAST(int, args[1]), CAST(size_t, args[2]));
         std::memset(p, CAST(int, args[1]), CAST(size_t, args[2]));
         return vm->None;
         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* dst = CAST(void*, args[0]);
         void* src = CAST(void*, args[1]);
         void* src = CAST(void*, args[1]);
         i64 size = CAST(i64, args[2]);
         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<VoidP>(mod, "void_p", VM::tp_object, true);
     vm->register_user_class<Struct>(mod, "struct", 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));
     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]);
         VoidP& ptr = CAST(VoidP&, args[0]);
         vm->check_type(args[1], vm->tp_type);
         vm->check_type(args[1], vm->tp_type);
         Type cls = PK_OBJ_GET(Type, args[1]);
         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);
         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]);
         VoidP& ptr = CAST(VoidP&, args[0]);
         return VAR(reinterpret_cast<i64>(ptr.ptr));
         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]);
         VoidP& ptr = CAST(VoidP&, args[0]);
         void* value = *reinterpret_cast<void**>(ptr.ptr);
         void* value = *reinterpret_cast<void**>(ptr.ptr);
         return vm->new_object<VoidP>(args[0].type, value);
         return vm->new_object<VoidP>(args[0].type, value);
@@ -190,54 +202,54 @@ void add_module_c(VM* vm){
     PyObject* type;
     PyObject* type;
     Type type_t;
     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(char, "char")
     BIND_PRIMITIVE(unsigned char, "uchar")
     BIND_PRIMITIVE(unsigned char, "uchar")
@@ -256,13 +268,13 @@ void add_module_c(VM* vm){
 #undef BIND_PRIMITIVE
 #undef BIND_PRIMITIVE
 
 
     PyObject* char_p_t = mod->attr("char_p").get();
     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]);
         obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]);
         const char* target = (const char*)voidp.ptr;
         const char* target = (const char*)voidp.ptr;
         return VAR(target);
         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]);
         obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]);
         std::string_view sv = CAST(Str&, args[1]).sv();
         std::string_view sv = CAST(Str&, args[1]).sv();
         char* target = (char*)voidp.ptr;
         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>
 #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"
 #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
 #if PK_DEBUG_GC_STATS
-                deleted[obj->type] += 1;
+            deleted[obj->type] += 1;
 #endif
 #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
 #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 !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
 #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"
 #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);
     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"
 #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();
     int n = width - s.size();
     if(n <= 0) return s;
     if(n <= 0) return s;
     return std::string(n, ' ') + 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];
     char buf[32];
     snprintf(buf, 32, "%.1f", x);
     snprintf(buf, 32, "%.1f", x);
     return buf;
     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()];
     auto line_info = frame->co->lines[frame->ip()];
     if(line_info.is_virtual) return;
     if(line_info.is_virtual) return;
     std::string_view filename = frame->co->src->filename.sv();
     std::string_view filename = frame->co->src->filename.sv();
     int line = line_info.lineno;
     int line = line_info.lineno;
 
 
-    if(frames.empty()){
+    if(frames.empty()) {
         frames.push({callstack_size, frame, clock(), nullptr});
         frames.push({callstack_size, frame, clock(), nullptr});
-    }else{
+    } else {
         _step_end(callstack_size, frame, line);
         _step_end(callstack_size, frame, line);
     }
     }
 
 
     auto& file_records = records[filename];
     auto& file_records = records[filename];
-    if(file_records.empty()){
+    if(file_records.empty()) {
         // initialize file_records
         // initialize file_records
         int total_lines = frame->co->src->line_starts.size();
         int total_lines = frame->co->src->line_starts.size();
         file_records.resize(total_lines + 1);
         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;
             file_records[i].line = i;
         }
         }
     }
     }
@@ -43,7 +41,7 @@ void LineProfiler::_step(int callstack_size, Frame* frame){
     frames.top().prev_record = &file_records[line];
     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();
     clock_t now = clock();
     _FrameRecord& top_frame_record = frames.top();
     _FrameRecord& top_frame_record = frames.top();
     _LineRecord* prev_record = top_frame_record.prev_record;
     _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);
     assert(abs(id_delta) <= 1);
 
 
     // current line is about to change
     // 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;
         clock_t delta = now - top_frame_record.prev_time;
         top_frame_record.prev_time = now;
         top_frame_record.prev_time = now;
         if(id_delta != 1) prev_record->hits++;
         if(id_delta != 1) prev_record->hits++;
         prev_record->time += delta;
         prev_record->time += delta;
     }
     }
-    
-    if(id_delta == 1){
+
+    if(id_delta == 1) {
         frames.push({callstack_size, frame, now, nullptr});
         frames.push({callstack_size, frame, now, nullptr});
-    }else{
+    } else {
         if(id_delta == -1) frames.pop();
         if(id_delta == -1) frames.pop();
     }
     }
 }
 }
 
 
-void LineProfiler::end(){
+void LineProfiler::end() {
     clock_t now = clock();
     clock_t now = clock();
     _FrameRecord& top_frame_record = frames.top();
     _FrameRecord& top_frame_record = frames.top();
     _LineRecord* prev_record = top_frame_record.prev_record;
     _LineRecord* prev_record = top_frame_record.prev_record;
@@ -80,9 +78,9 @@ void LineProfiler::end(){
     assert(frames.empty());
     assert(frames.empty());
 }
 }
 
 
-Str LineProfiler::stats(){
+Str LineProfiler::stats() {
     SStream ss;
     SStream ss;
-    for(FuncDecl* decl: functions){
+    for(FuncDecl* decl: functions) {
         int start_line = decl->code->start_line;
         int start_line = decl->code->start_line;
         int end_line = decl->code->end_line;
         int end_line = decl->code->end_line;
         if(start_line == -1 || end_line == -1) continue;
         if(start_line == -1 || end_line == -1) continue;
@@ -90,7 +88,7 @@ Str LineProfiler::stats(){
         vector<_LineRecord>& file_records = records[filename];
         vector<_LineRecord>& file_records = records[filename];
         if(file_records.empty()) continue;
         if(file_records.empty()) continue;
         clock_t total_time = 0;
         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;
             total_time += file_records[line].time;
         }
         }
         ss << "Total time: " << (f64)total_time / CLOCKS_PER_SEC << "s\n";
         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 << "Function: " << decl->code->name << " at line " << start_line << "\n";
         ss << "Line #      Hits         Time  Per Hit   % Time  Line Contents\n";
         ss << "Line #      Hits         Time  Per Hit   % Time  Line Contents\n";
         ss << "==============================================================\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];
             const _LineRecord& record = file_records[line];
             if(!record.is_valid()) continue;
             if(!record.is_valid()) continue;
             ss << left_pad(std::to_string(line), 6);
             ss << left_pad(std::to_string(line), 6);
-            if(record.hits == 0){
+            if(record.hits == 0) {
                 ss << std::string(10 + 13 + 9 + 9, ' ');
                 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.hits), 10);
                 ss << left_pad(std::to_string(record.time), 13);
                 ss << left_pad(std::to_string(record.time), 13);
                 ss << left_pad(std::to_string(record.time / record.hits), 9);
                 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);
                     ss << left_pad("0.0", 9);
-                }else{
+                } else {
                     ss << left_pad(to_string_1f(record.time * (f64)100 / total_time), 9);
                     ss << left_pad(to_string_1f(record.time * (f64)100 / total_time), 9);
                 }
                 }
             }
             }
@@ -122,4 +120,4 @@ Str LineProfiler::stats(){
     return ss.str();
     return ss.str();
 }
 }
 
 
-}   // namespace pkpy
+}  // namespace pkpy

Разница между файлами не показана из-за своего большого размера
+ 416 - 421
src/interpreter/vm.cpp


+ 113 - 118
src/modules/array2d.cpp

@@ -1,9 +1,9 @@
 #include "pocketpy/modules/array2d.hpp"
 #include "pocketpy/modules/array2d.hpp"
 #include "pocketpy/interpreter/bindings.hpp"
 #include "pocketpy/interpreter/bindings.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
-struct Array2d{
+struct Array2d {
     PK_ALWAYS_PASS_BY_POINTER(Array2d)
     PK_ALWAYS_PASS_BY_POINTER(Array2d)
 
 
     PyVar* data;
     PyVar* data;
@@ -11,55 +11,49 @@ struct Array2d{
     int n_rows;
     int n_rows;
     int numel;
     int numel;
 
 
-    Array2d(){
+    Array2d() {
         data = nullptr;
         data = nullptr;
         n_cols = 0;
         n_cols = 0;
         n_rows = 0;
         n_rows = 0;
         numel = 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_cols = n_cols;
         this->n_rows = n_rows;
         this->n_rows = n_rows;
         this->numel = n_cols * n_rows;
         this->numel = n_cols * n_rows;
         this->data = new PyVar[numel];
         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;
         if(is_valid(col, row)) return;
         vm->IndexError(_S('(', col, ", ", row, ')', " is not a valid index for array2d(", n_cols, ", ", n_rows, ')'));
         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]);
             Type cls = PK_OBJ_GET(Type, args[0]);
             return vm->new_object<Array2d>(cls);
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int n_cols = CAST(int, args[1]);
             int n_cols = CAST(int, args[1]);
             int n_rows = CAST(int, args[2]);
             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);
             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;
             return vm->None;
         });
         });
@@ -71,7 +65,7 @@ struct Array2d{
         PY_READONLY_FIELD(Array2d, "numel", numel);
         PY_READONLY_FIELD(Array2d, "numel", numel);
 
 
         // _get
         // _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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
             int row = CAST(int, args[2]);
@@ -80,7 +74,7 @@ struct Array2d{
         });
         });
 
 
         // _set
         // _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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
             int row = CAST(int, args[2]);
@@ -89,14 +83,14 @@ struct Array2d{
             return vm->None;
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
             int row = CAST(int, args[2]);
             return VAR(self.is_valid(col, row));
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             int col = CAST(int, args[1]);
             int col = CAST(int, args[1]);
             int row = CAST(int, args[2]);
             int row = CAST(int, args[2]);
@@ -104,34 +98,34 @@ struct Array2d{
             return self._get(col, row);
             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);
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             const Tuple& xy = CAST(Tuple&, _1);
             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 col = xy[0].as<i64>();
                 i64 row = xy[1].as<i64>();
                 i64 row = xy[1].as<i64>();
                 self.check_valid(vm, col, row);
                 self.check_valid(vm, col, row);
                 return self._get(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();
                 HANDLE_SLICE();
                 PyVar new_array_obj = vm->new_user_object<Array2d>();
                 PyVar new_array_obj = vm->new_user_object<Array2d>();
                 Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
                 Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
                 new_array.init(stop_col - start_col, stop_row - start_row);
                 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));
                         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->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);
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             const Tuple& xy = CAST(Tuple&, _1);
             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 col = xy[0].as<i64>();
                 i64 row = xy[1].as<i64>();
                 i64 row = xy[1].as<i64>();
                 self.check_valid(vm, col, row);
                 self.check_valid(vm, col, row);
@@ -151,11 +145,11 @@ struct Array2d{
                 return;
                 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();
                 HANDLE_SLICE();
 
 
                 bool is_basic_type = false;
                 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_int.index: is_basic_type = true; break;
                     case VM::tp_float.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;
                     case VM::tp_str.index: is_basic_type = true; break;
@@ -163,19 +157,19 @@ struct Array2d{
                     default: is_basic_type = _2 == vm->None;
                     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 j = 0; j < slice_height; j++)
                         for(int i = 0; i < slice_width; i++)
                         for(int i = 0; i < slice_width; i++)
                             self._set(i + start_col, j + start_row, _2);
                             self._set(i + start_col, j + start_row, _2);
                     return;
                     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"));
                     vm->TypeError(_S("expected int/float/str/bool/None or an array2d instance"));
                 }
                 }
 
 
                 Array2d& other = PK_OBJ_GET(Array2d, _2);
                 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");
                     vm->ValueError("array2d size does not match the slice size");
                 }
                 }
                 for(int j = 0; j < slice_height; j++)
                 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");
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             List t(self.n_rows);
             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);
                 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));
                 t[j] = VAR(std::move(row));
             }
             }
             return VAR(std::move(t));
             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);
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             return (i64)self.numel;
             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);
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             return _S("array2d(", self.n_cols, ", ", self.n_rows, ')');
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar f = args[1];
             PyVar f = args[1];
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
             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]);
                 new_array.data[i] = vm->call(f, self.data[i]);
             }
             }
             return new_array_obj;
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
             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];
                 new_array.data[i] = self.data[i];
             }
             }
             return new_array_obj;
             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];
                 self.data[i] = args[1];
             }
             }
             return vm->None;
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar f = args[1];
             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]);
                 self.data[i] = vm->call(f, self.data[i]);
             }
             }
             return vm->None;
             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]);
             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]);
                 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");
                     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];
                     self.data[i] = list[i];
                 }
                 }
                 return vm->None;
                 return vm->None;
             }
             }
             Array2d& other = CAST(Array2d&, args[1]);
             Array2d& other = CAST(Array2d&, args[1]);
             // if self and other have different sizes, re-initialize self
             // 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;
                 delete self.data;
                 self.init(other.n_cols, other.n_rows);
                 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];
                 self.data[i] = other.data[i];
             }
             }
             return vm->None;
             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);
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             if(!vm->is_user_type<Array2d>(_1)) return vm->NotImplemented;
             if(!vm->is_user_type<Array2d>(_1)) return vm->NotImplemented;
             Array2d& other = PK_OBJ_GET(Array2d, _1);
             Array2d& other = PK_OBJ_GET(Array2d, _1);
             if(self.n_cols != other.n_cols || self.n_rows != other.n_rows) return vm->False;
             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;
                 if(vm->py_ne(self.data[i], other.data[i])) return vm->False;
             }
             }
             return vm->True;
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             PyVar new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
             new_array.init(self.n_cols, self.n_rows);
             PyVar value = args[1];
             PyVar value = args[1];
             const Str& neighborhood = CAST(Str&, args[2]);
             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;
                         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));
                         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;
                         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));
                         new_array._set(i, j, VAR(count));
                     }
                     }
                 }
                 }
-            }else{
+            } else {
                 vm->ValueError("neighborhood must be 'Moore' or 'von Neumann'");
                 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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar value = args[1];
             PyVar value = args[1];
             int count = 0;
             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);
             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]);
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyVar value = args[1];
             PyVar value = args[1];
             int left = self.n_cols;
             int left = self.n_cols;
             int top = self.n_rows;
             int top = self.n_rows;
             int right = 0;
             int right = 0;
             int bottom = 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);
                         left = (std::min)(left, i);
                         top = (std::min)(top, j);
                         top = (std::min)(top, j);
                         right = (std::max)(right, i);
                         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)
     PK_ALWAYS_PASS_BY_POINTER(Array2dIter)
 
 
     PyVar ref;
     PyVar ref;
     Array2d* a;
     Array2d* a;
     int i;
     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);
             Array2dIter& self = PK_OBJ_GET(Array2dIter, _0);
             if(self.i == self.a->numel) return 0;
             if(self.i == self.a->numel) return 0;
             std::div_t res = std::div(self.i, self.a->n_cols);
             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");
     PyObject* mod = vm->new_module("array2d");
 
 
     vm->register_user_class<Array2d>(mod, "array2d", VM::tp_object, true);
     vm->register_user_class<Array2d>(mod, "array2d", VM::tp_object, true);
     vm->register_user_class<Array2dIter>(mod, "_array2d_iter");
     vm->register_user_class<Array2dIter>(mod, "_array2d_iter");
 
 
     Type array2d_iter_t = vm->_tp_user<Array2d>();
     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>());
         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>());
         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/modules/base64.hpp"
 #include "pocketpy/interpreter/bindings.hpp"
 #include "pocketpy/interpreter/bindings.hpp"
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 // https://github.com/zhicheng/base64/blob/master/base64.c
 // https://github.com/zhicheng/base64/blob/master/base64.c
 
 
@@ -9,6 +9,7 @@ const char BASE64_PAD = '=';
 const char BASE64DE_FIRST = '+';
 const char BASE64DE_FIRST = '+';
 const char BASE64DE_LAST = 'z';
 const char BASE64DE_LAST = 'z';
 
 
+// clang-format off
 /* BASE 64 encode table */
 /* BASE 64 encode table */
 const char base64en[] = {
 const char base64en[] = {
 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
@@ -71,114 +72,99 @@ const unsigned char base64de[] = {
 	/* 'x', 'y', 'z', '{', '|', '}', '~', del, */
 	/* 'x', 'y', 'z', '{', '|', '}', '~', del, */
 	    49,  50,  51, 255, 255, 255, 255, 255
 	    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");
     PyObject* mod = vm->new_module("base64");
 
 
     // b64encode
     // 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]);
         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);
         int size = base64_encode((const unsigned char*)b.data(), b.size(), (char*)p);
         return VAR(Bytes(p, size));
         return VAR(Bytes(p, size));
     });
     });
 
 
     // b64decode
     // 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]);
         Bytes& b = CAST(Bytes&, args[0]);
         unsigned char* p = (unsigned char*)std::malloc(b.size());
         unsigned char* p = (unsigned char*)std::malloc(b.size());
         int size = base64_decode((const char*)b.data(), b.size(), p);
         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/modules/csv.hpp"
 #include "pocketpy/interpreter/bindings.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");
     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]);
         const List& csvfile = CAST(List&, args[0]);
         List ret;
         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();
             std::string_view line = CAST(Str&, csvfile[i]).sv();
-            if(i == 0){
+            if(i == 0) {
                 // Skip utf8 BOM if there is any.
                 // 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;
             List row;
             int j;
             int j;
             bool in_quote = false;
             bool in_quote = false;
             std::string buffer;
             std::string buffer;
-__NEXT_LINE:
+        __NEXT_LINE:
             j = 0;
             j = 0;
-            while(j < line.size()){
-                switch(line[j]){
+            while(j < line.size()) {
+                switch(line[j]) {
                     case '"':
                     case '"':
-                        if(in_quote){
-                            if(j+1 < line.size() && line[j+1] == '"'){
+                        if(in_quote) {
+                            if(j + 1 < line.size() && line[j + 1] == '"') {
                                 buffer += '"';
                                 buffer += '"';
                                 j++;
                                 j++;
-                            }else{
+                            } else {
                                 in_quote = false;
                                 in_quote = false;
                             }
                             }
-                        }else{
+                        } else {
                             in_quote = true;
                             in_quote = true;
                         }
                         }
                         break;
                         break;
                     case ',':
                     case ',':
-                        if(in_quote){
+                        if(in_quote) {
                             buffer += line[j];
                             buffer += line[j];
-                        }else{
+                        } else {
                             row.push_back(VAR(buffer));
                             row.push_back(VAR(buffer));
                             buffer.clear();
                             buffer.clear();
                         }
                         }
                         break;
                         break;
-                    case '\r':
-                        break;  // ignore
-                    default:
-                        buffer += line[j];
-                        break;
+                    case '\r': break;  // ignore
+                    default: buffer += line[j]; break;
                 }
                 }
                 j++;
                 j++;
             }
             }
-            if(in_quote){
-                if(i == csvfile.size()-1){
+            if(in_quote) {
+                if(i == csvfile.size() - 1) {
                     vm->ValueError("unterminated quote");
                     vm->ValueError("unterminated quote");
-                }else{
+                } else {
                     buffer += '\n';
                     buffer += '\n';
                     i++;
                     i++;
                     line = CAST(Str&, csvfile[i]).sv();
                     line = CAST(Str&, csvfile[i]).sv();
@@ -67,22 +64,18 @@ __NEXT_LINE:
         return VAR(std::move(ret));
         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 csv_reader = vm->_modules["csv"]->attr("reader");
         PyVar ret_obj = vm->call(csv_reader, args[0]);
         PyVar ret_obj = vm->call(csv_reader, args[0]);
         const List& ret = CAST(List&, ret_obj);
         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]);
         const List& header = CAST(List&, ret[0]);
         List new_ret;
         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]);
             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;
             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]);
                 row_dict.set(vm, header[j], row[j]);
             }
             }
             new_ret.push_back(VAR(std::move(row_dict)));
             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/modules/dataclasses.hpp"
 #include "pocketpy/interpreter/bindings.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];
         PyVar self = _view[0];
         const Tuple& args = CAST(Tuple&, _view[1]);
         const Tuple& args = CAST(Tuple&, _view[1]);
         const Dict& kwargs_ = CAST(Dict&, _view[2]);
         const Dict& kwargs_ = CAST(Dict&, _view[2]);
         NameDict kwargs;
         NameDict kwargs;
-        kwargs_.apply([&](PyVar k, PyVar v){
+        kwargs_.apply([&](PyVar k, PyVar v) {
             kwargs.set(CAST(Str&, k), 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();
         NameDict& cls_d = cls_info->obj->attr();
         const auto& fields = cls_info->annotated_fields;
         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]);
                 self->attr().set(field, kwargs[field]);
                 kwargs.del(field);
                 kwargs.del(field);
-            }else{
-                if(i < args.size()){
+            } else {
+                if(i < args.size()) {
                     self->attr().set(field, args[i]);
                     self->attr().set(field, args[i]);
                     ++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]);
                     self->attr().set(field, cls_d[field]);
-                }else{
+                } else {
                     vm->TypeError(_S(cls_info->name, " missing required argument ", field.escape()));
                     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;
             StrName unexpected_key = kwargs.items()[0].first;
             vm->TypeError(_S(cls_info->name, " got an unexpected keyword argument ", unexpected_key.escape()));
             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 PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
         const auto& fields = cls_info->annotated_fields;
         const auto& fields = cls_info->annotated_fields;
         const NameDict& obj_d = _0->attr();
         const NameDict& obj_d = _0->attr();
         SStream ss;
         SStream ss;
         ss << cls_info->name << "(";
         ss << cls_info->name << "(";
         bool first = true;
         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 << field << "=" << vm->py_repr(obj_d[field]);
         }
         }
         ss << ")";
         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;
         if(vm->_tp(_0) != vm->_tp(_1)) return vm->NotImplemented;
         const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
         const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
         const auto& fields = cls_info->annotated_fields;
         const auto& fields = cls_info->annotated_fields;
-        for(StrName field: fields){
+        for(StrName field: fields) {
             PyVar lhs = _0->attr(field);
             PyVar lhs = _0->attr(field);
             PyVar rhs = _1->attr(field);
             PyVar rhs = _1->attr(field);
             if(vm->py_ne(lhs, rhs)) return vm->False;
             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");
     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);
         vm->check_type(args[0], VM::tp_type);
         Type cls = PK_OBJ_GET(Type, args[0]);
         Type cls = PK_OBJ_GET(Type, args[0]);
         NameDict& cls_d = args[0]->attr();
         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;
         const auto& fields = vm->_all_types[cls].annotated_fields;
         bool has_default = false;
         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;
                 has_default = true;
-            }else{
-                if(has_default){
+            } else {
+                if(has_default) {
                     vm->TypeError(_S("non-default argument ", field.escape(), " follows default argument"));
                     vm->TypeError(_S("non-default argument ", field.escape(), " follows default argument"));
                 }
                 }
             }
             }
@@ -103,15 +106,15 @@ void add_module_dataclasses(VM* vm){
         return args[0];
         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 auto& fields = vm->_tp_info(args[0])->annotated_fields;
         const NameDict& obj_d = args[0]->attr();
         const NameDict& obj_d = args[0]->attr();
         Dict d;
         Dict d;
-        for(StrName field: fields){
+        for(StrName field: fields) {
             d.set(vm, VAR(field.sv()), obj_d[field]);
             d.set(vm, VAR(field.sv()), obj_d[field]);
         }
         }
         return VAR(std::move(d));
         return VAR(std::move(d));
     });
     });
 }
 }
 
 
-}   // namespace pkpy
+}  // namespace pkpy

+ 75 - 111
src/modules/easing.cpp

@@ -3,193 +3,161 @@
 
 
 #include <cmath>
 #include <cmath>
 
 
-namespace pkpy{
+namespace pkpy {
 // https://easings.net/
 // https://easings.net/
 
 
 const double kPi = 3.1415926545;
 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;
         return 2 * x * x;
     } else {
     } 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;
         return 4 * x * x * x;
     } else {
     } 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 {
     } 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 {
     } 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;
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 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 {
     } 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 {
     } 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 c1 = 1.70158;
     const double c3 = c1 + 1;
     const double c3 = c1 + 1;
     return c3 * x * x * x - c1 * x * x;
     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 c1 = 1.70158;
     const double c3 = c1 + 1;
     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 c1 = 1.70158;
     const double c2 = c1 * 1.525;
     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 {
     } 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;
     const double c4 = (2 * kPi) / 3;
-    if( x == 0 ) {
+    if(x == 0) {
         return 0;
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 1;
         return 1;
     } else {
     } 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;
     const double c4 = (2 * kPi) / 3;
-    if( x == 0 ) {
+    if(x == 0) {
         return 0;
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 1;
         return 1;
     } else {
     } 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;
     const double c5 = (2 * kPi) / 4.5;
-    if( x == 0 ) {
+    if(x == 0) {
         return 0;
         return 0;
-    } else if( x == 1 ) {
+    } else if(x == 1) {
         return 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 {
     } 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 n1 = 7.5625;
     const double d1 = 2.75;
     const double d1 = 2.75;
-    if( x < 1 / d1 ) {
+    if(x < 1 / d1) {
         return n1 * x * x;
         return n1 * x * x;
-    } else if( x < 2 / d1 ) {
+    } else if(x < 2 / d1) {
         x -= 1.5 / d1;
         x -= 1.5 / d1;
         return n1 * x * x + 0.75;
         return n1 * x * x + 0.75;
-    } else if( x < 2.5 / d1 ) {
+    } else if(x < 2.5 / d1) {
         x -= 2.25 / d1;
         x -= 2.25 / d1;
         return n1 * x * x + 0.9375;
         return n1 * x * x + 0.9375;
     } else {
     } 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");
     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)
     EASE(Linear)
@@ -251,4 +215,4 @@ void add_module_easing(VM* vm){
 
 
 #undef EASE
 #undef EASE
 }
 }
-}   // namespace pkpy
+}  // namespace pkpy

+ 48 - 53
src/modules/io.cpp

@@ -6,7 +6,7 @@
 #include <cstdio>
 #include <cstdio>
 #endif
 #endif
 
 
-namespace pkpy{
+namespace pkpy {
 
 
 #if PK_ENABLE_OS
 #if PK_ENABLE_OS
 
 
@@ -19,7 +19,7 @@ struct FileIO {
     static void _register(VM* vm, PyObject* mod, PyObject* type);
     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
 #if _MSC_VER
     FILE* fp;
     FILE* fp;
     errno_t err = fopen_s(&fp, name, mode);
     errno_t err = fopen_s(&fp, name, mode);
@@ -30,7 +30,7 @@ static FILE* io_fopen(const char* name, const char* mode){
 #endif
 #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
 #if _MSC_VER
     return fread_s(buffer, std::numeric_limits<size_t>::max(), size, count, fp);
     return fread_s(buffer, std::numeric_limits<size_t>::max(), size, count, fp);
 #else
 #else
@@ -38,7 +38,7 @@ static size_t io_fread(void* buffer, size_t size, size_t count, FILE* fp){
 #endif
 #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));
     bool exists = std::filesystem::exists(std::filesystem::path(name));
     if(!exists) return nullptr;
     if(!exists) return nullptr;
     FILE* fp = io_fopen(name, "rb");
     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];
     unsigned char* buffer = new unsigned char[buffer_size];
     fseek(fp, 0, SEEK_SET);
     fseek(fp, 0, SEEK_SET);
     size_t sz = io_fread(buffer, 1, buffer_size, fp);
     size_t sz = io_fread(buffer, 1, buffer_size, fp);
-    (void)sz;   // suppress warning
+    (void)sz;  // suppress warning
     fclose(fp);
     fclose(fp);
     *out_size = buffer_size;
     *out_size = buffer_size;
     return buffer;
     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]);
         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]);
         FileIO& io = PK_OBJ_GET(FileIO, args[0]);
         i64 size = CAST(i64, args[1]);
         i64 size = CAST(i64, args[1]);
         i64 buffer_size;
         i64 buffer_size;
-        if(size < 0){
+        if(size < 0) {
             long current = ftell(io.fp);
             long current = ftell(io.fp);
             fseek(io.fp, 0, SEEK_END);
             fseek(io.fp, 0, SEEK_END);
             buffer_size = ftell(io.fp);
             buffer_size = ftell(io.fp);
             fseek(io.fp, current, SEEK_SET);
             fseek(io.fp, current, SEEK_SET);
-        }else{
+        } else {
             buffer_size = size;
             buffer_size = size;
         }
         }
         unsigned char* buffer = (unsigned char*)std::malloc(buffer_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);
         assert(actual_size <= buffer_size);
         // in text mode, CR may be dropped, which may cause `actual_size < buffer_size`
         // in text mode, CR may be dropped, which may cause `actual_size < buffer_size`
         Bytes b(buffer, actual_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));
         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]);
         FileIO& io = PK_OBJ_GET(FileIO, args[0]);
-        if(io.is_text){
+        if(io.is_text) {
             Str& s = CAST(Str&, args[1]);
             Str& s = CAST(Str&, args[1]);
             fwrite(s.data, 1, s.length(), io.fp);
             fwrite(s.data, 1, s.length(), io.fp);
-        }else{
+        } else {
             Bytes& buffer = CAST(Bytes&, args[1]);
             Bytes& buffer = CAST(Bytes&, args[1]);
             fwrite(buffer.data(), 1, buffer.size(), io.fp);
             fwrite(buffer.data(), 1, buffer.size(), io.fp);
         }
         }
         return vm->None;
         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]);
         FileIO& io = PK_OBJ_GET(FileIO, args[0]);
         long pos = ftell(io.fp);
         long pos = ftell(io.fp);
         if(pos == -1) vm->IOError(strerror(errno));
         if(pos == -1) vm->IOError(strerror(errno));
         return VAR(pos);
         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]);
         FileIO& io = PK_OBJ_GET(FileIO, args[0]);
         long offset = CAST(long, args[1]);
         long offset = CAST(long, args[1]);
         int whence = CAST(int, args[2]);
         int whence = CAST(int, args[2]);
@@ -113,13 +109,13 @@ void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
         return vm->None;
         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]);
         FileIO& io = PK_OBJ_GET(FileIO, args[0]);
         io.close();
         io.close();
         return vm->None;
         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]);
         FileIO& io = PK_OBJ_GET(FileIO, args[0]);
         io.close();
         io.close();
         return vm->None;
         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]));
     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;
     this->is_text = mode.sv().find("b") == std::string::npos;
     fp = io_fopen(file.c_str(), mode.c_str());
     fp = io_fopen(file.c_str(), mode.c_str());
     if(!fp) vm->IOError(strerror(errno));
     if(!fp) vm->IOError(strerror(errno));
 }
 }
 
 
-void FileIO::close(){
+void FileIO::close() {
     if(fp == nullptr) return;
     if(fp == nullptr) return;
     fclose(fp);
     fclose(fp);
     fp = nullptr;
     fp = nullptr;
 }
 }
 
 
-void add_module_io(VM* vm){
+void add_module_io(VM* vm) {
     PyObject* mod = vm->new_module("io");
     PyObject* mod = vm->new_module("io");
     vm->register_user_class<FileIO>(mod, "FileIO");
     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_CUR", VAR(SEEK_CUR));
     mod->attr().set("SEEK_END", VAR(SEEK_END));
     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]);
         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* mod = vm->new_module("os");
     PyObject* path_obj = vm->heap.gcnew<DummyInstance>(VM::tp_object);
     PyObject* path_obj = vm->heap.gcnew<DummyInstance>(VM::tp_object);
     mod->attr().set("path", path_obj);
     mod->attr().set("path", path_obj);
-    
+
     // Working directory is shared by all VMs!!
     // 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());
         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::path path(CAST(Str&, args[0]).sv());
         std::filesystem::current_path(path);
         std::filesystem::current_path(path);
         return vm->None;
         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::path path(CAST(Str&, args[0]).sv());
         std::filesystem::directory_iterator di;
         std::filesystem::directory_iterator di;
-        try{
+        try {
             di = std::filesystem::directory_iterator(path);
             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;
         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));
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         bool ok = std::filesystem::remove(path);
         bool ok = std::filesystem::remove(path);
         if(!ok) vm->IOError("operation failed");
         if(!ok) vm->IOError("operation failed");
         return vm->None;
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         bool ok = std::filesystem::create_directory(path);
         bool ok = std::filesystem::create_directory(path);
         if(!ok) vm->IOError("operation failed");
         if(!ok) vm->IOError("operation failed");
         return vm->None;
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         bool ok = std::filesystem::remove(path);
         bool ok = std::filesystem::remove(path);
         if(!ok) vm->IOError("operation failed");
         if(!ok) vm->IOError("operation failed");
         return vm->None;
         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;
         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();
             path /= CAST(Str&, args[i]).sv();
         }
         }
         return VAR(path.string());
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         bool exists = std::filesystem::exists(path);
         bool exists = std::filesystem::exists(path);
         return VAR(exists);
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         return VAR(path.filename().string());
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         bool isdir = std::filesystem::is_directory(path);
         bool isdir = std::filesystem::is_directory(path);
         return VAR(isdir);
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         bool isfile = std::filesystem::is_regular_file(path);
         bool isfile = std::filesystem::is_regular_file(path);
         return VAR(isfile);
         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());
         std::filesystem::path path(CAST(Str&, args[0]).sv());
         return VAR(std::filesystem::absolute(path).string());
         return VAR(std::filesystem::absolute(path).string());
     });
     });
 }
 }
 #else
 #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
 #endif
 
 
-}   // namespace pkpy
+}  // namespace pkpy

Разница между файлами не показана из-за своего большого размера
+ 489 - 464
src/modules/linalg.cpp


+ 52 - 56
src/modules/modules.cpp

@@ -8,9 +8,9 @@
 #include <chrono>
 #include <chrono>
 #include <cmath>
 #include <cmath>
 
 
-namespace pkpy{
+namespace pkpy {
 
 
-struct PyStructTime{
+struct PyStructTime {
     int tm_year;
     int tm_year;
     int tm_mon;
     int tm_mon;
     int tm_mday;
     int tm_mday;
@@ -21,7 +21,7 @@ struct PyStructTime{
     int tm_yday;
     int tm_yday;
     int tm_isdst;
     int tm_isdst;
 
 
-    PyStructTime(std::time_t t){
+    PyStructTime(std::time_t t) {
         std::tm* tm = std::localtime(&t);
         std::tm* tm = std::localtime(&t);
         tm_year = tm->tm_year + 1900;
         tm_year = tm->tm_year + 1900;
         tm_mon = tm->tm_mon + 1;
         tm_mon = tm->tm_mon + 1;
@@ -34,7 +34,7 @@ struct PyStructTime{
         tm_isdst = tm->tm_isdst;
         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_year", tm_year);
         PY_READONLY_FIELD(PyStructTime, "tm_mon", tm_mon);
         PY_READONLY_FIELD(PyStructTime, "tm_mon", tm_mon);
         PY_READONLY_FIELD(PyStructTime, "tm_mday", tm_mday);
         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");
     PyObject* mod = vm->new_module("time");
     vm->register_user_class<PyStructTime>(mod, "struct_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) {
     vm->bind_func(mod, "sleep", 1, [](VM* vm, ArgsView args) {
         f64 seconds = CAST_F(args[0]);
         f64 seconds = CAST_F(args[0]);
         auto begin = std::chrono::system_clock::now();
         auto begin = std::chrono::system_clock::now();
-        while(true){
+        while(true) {
             auto now = std::chrono::system_clock::now();
             auto now = std::chrono::system_clock::now();
             f64 elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - begin).count() / 1000.0;
             f64 elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - begin).count() / 1000.0;
             if(elapsed >= seconds) break;
             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");
     PyObject* mod = vm->new_module("sys");
     vm->setattr(mod, "version", VAR(PK_VERSION));
     vm->setattr(mod, "version", VAR(PK_VERSION));
     vm->setattr(mod, "platform", VAR(kPlatformStrings[PK_SYS_PLATFORM]));
     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");
     PyObject* mod = vm->new_module("json");
     vm->bind_func(mod, "loads", 1, [](VM* vm, ArgsView args) {
     vm->bind_func(mod, "loads", 1, [](VM* vm, ArgsView args) {
         std::string_view sv;
         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]);
             const Bytes& b = PK_OBJ_GET(Bytes, args[0]);
             sv = std::string_view((char*)b.data(), b.size());
             sv = std::string_view((char*)b.data(), b.size());
-        }else{
+        } else {
             sv = CAST(Str&, args[0]).sv();
             sv = CAST(Str&, args[0]).sv();
         }
         }
         CodeObject_ code = vm->compile(sv, "<json>", JSON_MODE);
         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
 // 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");
     PyObject* mod = vm->new_module("math");
     mod->attr().set("pi", VAR(3.1415926535897932384));
     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("inf", VAR(std::numeric_limits<double>::infinity()));
     mod->attr().set("nan", VAR(std::numeric_limits<double>::quiet_NaN()));
     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]);
         List& list = CAST(List&, args[0]);
         double sum = 0;
         double sum = 0;
         double c = 0;
         double c = 0;
-        for(PyVar arg : list){
+        for(PyVar arg: list) {
             double x = CAST_F(arg);
             double x = CAST_F(arg);
             double y = x - c;
             double y = x - c;
             double t = sum + y;
             double t = sum + y;
@@ -145,7 +145,7 @@ void add_module_math(VM* vm){
         i64 b = CAST(i64, args[1]);
         i64 b = CAST(i64, args[1]);
         if(a < 0) a = -a;
         if(a < 0) a = -a;
         if(b < 0) b = -b;
         if(b < 0) b = -b;
-        while(b != 0){
+        while(b != 0) {
             i64 t = b;
             i64 t = b;
             b = a % b;
             b = a % b;
             a = t;
             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_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 x = CAST_F(args[0]);
         f64 base = CAST_F(args[1]);
         f64 base = CAST_F(args[1]);
         return VAR(std::log(x) / std::log(base));
         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, "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, "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, "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, "degrees", 1, PK_LAMBDA(VAR(CAST_F(args[0]) * 180 / 3.1415926535897932384)));
     vm->bind_func(mod, "radians", 1, PK_LAMBDA(VAR(CAST_F(args[0]) * 3.1415926535897932384 / 180)));
     vm->bind_func(mod, "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]);
         i64 n = CAST(i64, args[0]);
         if(n < 0) vm->ValueError("factorial() not defined for negative values");
         if(n < 0) vm->ValueError("factorial() not defined for negative values");
         i64 r = 1;
         i64 r = 1;
-        for(i64 i=2; i<=n; i++) r *= i;
+        for(i64 i = 2; i <= n; i++)
+            r *= i;
         return VAR(r);
         return VAR(r);
     });
     });
 }
 }
 
 
-void add_module_traceback(VM* vm){
+void add_module_traceback(VM* vm) {
     PyObject* mod = vm->new_module("traceback");
     PyObject* mod = vm->new_module("traceback");
     vm->bind_func(mod, "print_exc", 0, [](VM* vm, ArgsView args) {
     vm->bind_func(mod, "print_exc", 0, [](VM* vm, ArgsView args) {
         if(vm->__last_exception == nullptr) vm->ValueError("no exception");
         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");
     PyObject* mod = vm->new_module("dis");
 
 
     vm->bind_func(mod, "dis", 1, [](VM* vm, ArgsView args) {
     vm->bind_func(mod, "dis", 1, [](VM* vm, ArgsView args) {
         CodeObject_ code;
         CodeObject_ code;
         PyVar obj = args[0];
         PyVar obj = args[0];
-        if(is_type(obj, vm->tp_str)){
+        if(is_type(obj, vm->tp_str)) {
             const Str& source = CAST(Str, obj);
             const Str& source = CAST(Str, obj);
             code = vm->compile(source, "<dis>", EXEC_MODE);
             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");
     PyObject* mod = vm->new_module("gc");
     vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(vm->heap.collect())));
     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");
     PyObject* mod = vm->new_module("enum");
     CodeObject_ code = vm->compile(kPythonLibs__enum, "enum.py", EXEC_MODE);
     CodeObject_ code = vm->compile(kPythonLibs__enum, "enum.py", EXEC_MODE);
     vm->_exec(code, mod);
     vm->_exec(code, mod);
     PyVar Enum = mod->attr("Enum");
     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");
     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]);
         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];
         PyVar self = args[0];
         if(is_tagged(self)) vm->TypeError("object: tagged object cannot enable instance dict");
         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");
         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
 #if PK_ENABLE_PROFILER
 struct LineProfilerW;
 struct LineProfilerW;
-struct _LpGuard{
+
+struct _LpGuard {
     PK_ALWAYS_PASS_BY_POINTER(_LpGuard)
     PK_ALWAYS_PASS_BY_POINTER(_LpGuard)
     LineProfilerW* lp;
     LineProfilerW* lp;
     VM* vm;
     VM* vm;
@@ -290,16 +290,16 @@ struct _LpGuard{
 };
 };
 
 
 // line_profiler wrapper
 // line_profiler wrapper
-struct LineProfilerW{
+struct LineProfilerW {
     LineProfiler profiler;
     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]);
             Type cls = PK_OBJ_GET(Type, args[0]);
             return vm->new_object<LineProfilerW>(cls);
             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]);
             LineProfilerW& self = PK_OBJ_GET(LineProfilerW, args[0]);
             vm->check_type(args[1], VM::tp_function);
             vm->check_type(args[1], VM::tp_function);
             auto decl = PK_OBJ_GET(Function, args[1]).decl.get();
             auto decl = PK_OBJ_GET(Function, args[1]).decl.get();
@@ -307,19 +307,20 @@ struct LineProfilerW{
             return vm->None;
             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]);
             LineProfilerW& self = PK_OBJ_GET(LineProfilerW, view[0]);
             PyVar func = view[1];
             PyVar func = view[1];
             const Tuple& args = CAST(Tuple&, view[2]);
             const Tuple& args = CAST(Tuple&, view[2]);
             vm->s_data.push(func);
             vm->s_data.push(func);
             vm->s_data.push(PY_NULL);
             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);
             _LpGuard guard(&self, vm);
             PyVar ret = vm->vectorcall(args.size());
             PyVar ret = vm->vectorcall(args.size());
             return ret;
             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]);
             LineProfilerW& self = PK_OBJ_GET(LineProfilerW, args[0]);
             vm->stdout_write(self.profiler.stats());
             vm->stdout_write(self.profiler.stats());
             return vm->None;
             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;
     vm->_profiler = &lp->profiler;
     lp->profiler.begin();
     lp->profiler.begin();
 }
 }
 
 
-_LpGuard::~_LpGuard(){
+_LpGuard::~_LpGuard() {
     vm->_profiler = nullptr;
     vm->_profiler = nullptr;
     lp->profiler.end();
     lp->profiler.end();
 }
 }
 
 
-void add_module_line_profiler(VM *vm){
+void add_module_line_profiler(VM* vm) {
     PyObject* mod = vm->new_module("line_profiler");
     PyObject* mod = vm->new_module("line_profiler");
     vm->register_user_class<LineProfilerW>(mod, "LineProfiler");
     vm->register_user_class<LineProfilerW>(mod, "LineProfiler");
 }
 }
 #else
 #else
-void add_module_line_profiler(VM* vm){
-    (void)vm;
-}
+void add_module_line_profiler(VM* vm) { (void)vm; }
 #endif
 #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.
 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 MATRIX_A = 0x9908b0dfUL;   /* constant vector a */
     const uint32_t UPPER_MASK = 0x80000000UL; /* most significant w-r bits */
     const uint32_t UPPER_MASK = 0x80000000UL; /* most significant w-r bits */
     const uint32_t LOWER_MASK = 0x7fffffffUL; /* least significant 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 */
     /* 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. */
             /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
             /* In the previous versions, MSBs of the seed affect   */
             /* In the previous versions, MSBs of the seed affect   */
             /* only MSBs of the array mt[].                        */
             /* only MSBs of the array mt[].                        */
@@ -63,32 +61,31 @@ struct mt19937{
     }
     }
 
 
     /* generates a random number on [0,0xffffffff]-interval */
     /* generates a random number on [0,0xffffffff]-interval */
-    uint32_t next_uint32(void)
-    {
+    uint32_t next_uint32(void) {
         uint32_t y;
         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 */
         /* 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;
             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 */
                 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;
             mti = 0;
         }
         }
-    
+
         y = mt[mti++];
         y = mt[mti++];
 
 
         /* Tempering */
         /* Tempering */
@@ -100,44 +97,36 @@ struct mt19937{
         return y;
         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 */
     /* 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 */
     /* 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;
         uint64_t delta = b - a + 1;
-        if(delta < 0x80000000UL){
+        if(delta < 0x80000000UL) {
             return a + next_uint32() % (uint32_t)delta;
             return a + next_uint32() % (uint32_t)delta;
-        }else{
+        } else {
             return a + next_uint64() % delta;
             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;
     mt19937 gen;
 
 
-    Random(){
+    Random() {
         auto count = std::chrono::high_resolution_clock::now().time_since_epoch().count();
         auto count = std::chrono::high_resolution_clock::now().time_since_epoch().count();
         gen.seed((uint32_t)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]);
             Type cls = PK_OBJ_GET(Type, args[0]);
             return vm->new_object<Random>(cls);
             return vm->new_object<Random>(cls);
         });
         });
@@ -152,7 +141,7 @@ struct Random{
             Random& self = PK_OBJ_GET(Random, args[0]);
             Random& self = PK_OBJ_GET(Random, args[0]);
             i64 a = CAST(i64, args[1]);
             i64 a = CAST(i64, args[1]);
             i64 b = CAST(i64, args[2]);
             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));
             return VAR(self.gen.randint(a, b));
         });
         });
 
 
@@ -165,14 +154,14 @@ struct Random{
             Random& self = PK_OBJ_GET(Random, args[0]);
             Random& self = PK_OBJ_GET(Random, args[0]);
             f64 a = CAST(f64, args[1]);
             f64 a = CAST(f64, args[1]);
             f64 b = CAST(f64, args[2]);
             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));
             return VAR(self.gen.uniform(a, b));
         });
         });
 
 
         vm->bind_func(type, "shuffle", 2, [](VM* vm, ArgsView args) {
         vm->bind_func(type, "shuffle", 2, [](VM* vm, ArgsView args) {
             Random& self = PK_OBJ_GET(Random, args[0]);
             Random& self = PK_OBJ_GET(Random, args[0]);
             List& L = CAST(List&, args[1]);
             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);
                 int j = self.gen.randint(0, i);
                 std::swap(L[i], L[j]);
                 std::swap(L[i], L[j]);
             }
             }
@@ -183,7 +172,7 @@ struct Random{
             Random& self = PK_OBJ_GET(Random, args[0]);
             Random& self = PK_OBJ_GET(Random, args[0]);
             ArgsView view = vm->cast_array_view(args[1]);
             ArgsView view = vm->cast_array_view(args[1]);
             if(view.empty()) vm->IndexError("cannot choose from an empty sequence");
             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];
             return view[index];
         });
         });
 
 
@@ -194,20 +183,21 @@ struct Random{
             int size = view.size();
             int size = view.size();
             if(size == 0) vm->IndexError("cannot choose from an empty sequence");
             if(size == 0) vm->IndexError("cannot choose from an empty sequence");
             array<f64> cum_weights(size);
             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]);
                 ArgsView weights = vm->cast_array_view(args[2]);
                 if(weights.size() != size) vm->ValueError(_S("len(weights) != ", size));
                 if(weights.size() != size) vm->ValueError(_S("len(weights) != ", size));
                 cum_weights[0] = CAST(f64, weights[0]);
                 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]);
                     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");
             if(cum_weights[size - 1] <= 0) vm->ValueError("total of weights must be greater than zero");
             int k = CAST(int, args[3]);
             int k = CAST(int, args[3]);
             List result(k);
             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]);
                 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();
                 int idx = std::lower_bound(cum_weights.begin(), cum_weights.end(), r) - cum_weights.begin();
                 result[i] = data[idx];
                 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");
     PyObject* mod = vm->new_module("random");
     vm->register_user_class<Random>(mod, "Random");
     vm->register_user_class<Random>(mod, "Random");
     PyVar instance = vm->new_user_object<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"));
     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"
 #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"
 #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"
 #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;
         _head_idx = -1;
         _tail_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].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].prev = -1;
         _items[i].next = -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"
 #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"
 #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"
 #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 {
 namespace pkpy {
 
 
-Tuple::Tuple(int n){
-    if(n <= INLINED_SIZE){
+Tuple::Tuple(int n) {
+    if(n <= INLINED_SIZE) {
         this->_args = _inlined;
         this->_args = _inlined;
-    }else{
+    } else {
         this->_args = (PyVar*)std::malloc(n * sizeof(PyVar));
         this->_args = (PyVar*)std::malloc(n * sizeof(PyVar));
     }
     }
     this->_size = n;
     this->_size = n;
@@ -13,39 +13,44 @@ Tuple::Tuple(int n){
 
 
 Tuple::Tuple(Tuple&& other) noexcept {
 Tuple::Tuple(Tuple&& other) noexcept {
     _size = other._size;
     _size = other._size;
-    if(other.is_inlined()){
+    if(other.is_inlined()) {
         _args = _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;
         _args = other._args;
         other._args = other._inlined;
         other._args = other._inlined;
         other._size = 0;
         other._size = 0;
     }
     }
 }
 }
 
 
-Tuple::Tuple(PyVar _0, PyVar _1): Tuple(2){
+Tuple::Tuple(PyVar _0, PyVar _1) : Tuple(2) {
     _args[0] = _0;
     _args[0] = _0;
     _args[1] = _1;
     _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[0] = _0;
     _args[1] = _1;
     _args[1] = _1;
     _args[2] = _2;
     _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());
     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;
     return ret;
 }
 }
 
 
-Tuple ArgsView::to_tuple() const{
+Tuple ArgsView::to_tuple() const {
     Tuple ret(size());
     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;
     return ret;
 }
 }
 
 
-}   // namespace pkpy
+}  // namespace pkpy

Разница между файлами не показана из-за своего большого размера
+ 223 - 199
src/pocketpy.cpp


+ 119 - 138
src/pocketpy_c.cpp

@@ -7,32 +7,29 @@
 
 
 using namespace pkpy;
 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());
     assert(!vm->__c.s_view.empty());
     return vm->s_data._sp - vm->__c.s_view.top().end();
     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* begin;
     PyVar* end = vm->s_data.end();
     PyVar* end = vm->s_data.end();
-    if(vm->callstack.empty()){
+    if(vm->callstack.empty()) {
         begin = vm->s_data.begin();
         begin = vm->s_data.begin();
-    }else{
+    } else {
         assert(!vm->__c.s_view.empty());
         assert(!vm->__c.s_view.empty());
         begin = vm->__c.s_view.top().begin();
         begin = vm->__c.s_view.top().begin();
     }
     }
@@ -42,27 +39,24 @@ static PyVar stack_item(VM* vm, int index){
     return begin[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) {
 bool pkpy_exec(pkpy_vm* vm_handle, const char* source) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PyVar res;
     PyVar res;
     PK_PROTECTED(
     PK_PROTECTED(
@@ -72,16 +66,15 @@ bool pkpy_exec(pkpy_vm* vm_handle, const char* source) {
     return res != nullptr;
     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()
     PK_ASSERT_NO_ERROR()
     PyVar res;
     PyVar res;
     PyObject* mod;
     PyObject* mod;
     PK_PROTECTED(
     PK_PROTECTED(
-        if(module == nullptr){
-            mod = vm->_main;
+        if(module == nullptr){ mod = vm->_main;
         }else{
         }else{
-            mod = vm->_modules[module].get();     // may raise
+                        mod = vm->_modules[module].get();  // may raise
         }
         }
         CodeObject_ code = vm->compile(source, filename, (CompileMode)mode);
         CodeObject_ code = vm->compile(source, filename, (CompileMode)mode);
         res = vm->_exec(code, mod);
         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;
     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);
     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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, n);
         PyVar item = stack_item(vm, n);
@@ -104,15 +97,15 @@ bool pkpy_dup(pkpy_vm* vm_handle, int n){
     return true;
     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_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(n)
     PK_ASSERT_N_EXTRA_ELEMENTS(n)
     vm->s_data.shrink(n);
     vm->s_data.shrink(n);
     return true;
     return true;
 }
 }
 
 
-bool pkpy_pop_top(pkpy_vm* vm_handle){
+bool pkpy_pop_top(pkpy_vm* vm_handle) {
     VM* vm = (VM*)vm_handle;
     VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
@@ -120,7 +113,7 @@ bool pkpy_pop_top(pkpy_vm* vm_handle){
     return true;
     return true;
 }
 }
 
 
-bool pkpy_dup_top(pkpy_vm* vm_handle){
+bool pkpy_dup_top(pkpy_vm* vm_handle) {
     VM* vm = (VM*)vm_handle;
     VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
@@ -128,7 +121,7 @@ bool pkpy_dup_top(pkpy_vm* vm_handle){
     return true;
     return true;
 }
 }
 
 
-bool pkpy_rot_two(pkpy_vm* vm_handle){
+bool pkpy_rot_two(pkpy_vm* vm_handle) {
     VM* vm = (VM*)vm_handle;
     VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(2)
     PK_ASSERT_N_EXTRA_ELEMENTS(2)
@@ -136,19 +129,17 @@ bool pkpy_rot_two(pkpy_vm* vm_handle){
     return true;
     return true;
 }
 }
 
 
-int pkpy_stack_size(pkpy_vm* vm_handle){
+int pkpy_stack_size(pkpy_vm* vm_handle) {
     VM* vm = (VM*)vm_handle;
     VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     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);
     if(vm->__c.s_view.empty()) exit(127);
     return vm->s_data._sp - vm->__c.s_view.top().begin();
     return vm->s_data._sp - vm->__c.s_view.top().begin();
 }
 }
 
 
 // int
 // int
 bool pkpy_push_int(pkpy_vm* vm_handle, int value) {
 bool pkpy_push_int(pkpy_vm* vm_handle, int value) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PyVar res;
     PyVar res;
     PK_PROTECTED(
     PK_PROTECTED(
@@ -159,16 +150,16 @@ bool pkpy_push_int(pkpy_vm* vm_handle, int value) {
     return true;
     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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         return is_int(stack_item(vm, i));
         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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         PyVar item = stack_item(vm, i);
@@ -179,15 +170,15 @@ bool pkpy_to_int(pkpy_vm* vm_handle, int i, int* out){
 
 
 // float
 // float
 bool pkpy_push_float(pkpy_vm* vm_handle, double value) {
 bool pkpy_push_float(pkpy_vm* vm_handle, double value) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PyVar res = py_var(vm, value);
     PyVar res = py_var(vm, value);
     vm->s_data.push(res);
     vm->s_data.push(res);
     return true;
     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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         PyVar item = stack_item(vm, i);
@@ -207,14 +198,14 @@ bool pkpy_to_float(pkpy_vm* vm_handle, int i, double* out){
 
 
 // bool
 // bool
 bool pkpy_push_bool(pkpy_vm* vm_handle, bool value) {
 bool pkpy_push_bool(pkpy_vm* vm_handle, bool value) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     vm->s_data.push(value ? vm->True : vm->False);
     vm->s_data.push(value ? vm->True : vm->False);
     return true;
     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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         PyVar item = stack_item(vm, i);
@@ -234,15 +225,15 @@ bool pkpy_to_bool(pkpy_vm* vm_handle, int i, bool* out){
 
 
 // string
 // string
 bool pkpy_push_string(pkpy_vm* vm_handle, pkpy_CString value) {
 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()
     PK_ASSERT_NO_ERROR()
     PyVar res = py_var(vm, value);
     PyVar res = py_var(vm, value);
     vm->s_data.push(res);
     vm->s_data.push(res);
     return true;
     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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         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
 // void_p
 bool pkpy_push_voidp(pkpy_vm* vm_handle, void* value) {
 bool pkpy_push_voidp(pkpy_vm* vm_handle, void* value) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PyVar res = py_var(vm, value);
     PyVar res = py_var(vm, value);
     vm->s_data.push(res);
     vm->s_data.push(res);
     return true;
     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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         PyVar item = stack_item(vm, i);
@@ -292,14 +283,14 @@ bool pkpy_to_voidp(pkpy_vm* vm_handle, int i, void** out){
 
 
 // none
 // none
 bool pkpy_push_none(pkpy_vm* vm_handle) {
 bool pkpy_push_none(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     vm->s_data.push(vm->None);
     vm->s_data.push(vm->None);
     return true;
     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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar item = stack_item(vm, i);
         PyVar item = stack_item(vm, i);
@@ -309,25 +300,25 @@ bool pkpy_is_none(pkpy_vm* vm_handle, int i){
 
 
 // null
 // null
 bool pkpy_push_null(pkpy_vm* vm_handle) {
 bool pkpy_push_null(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     vm->s_data.push(PY_NULL);
     vm->s_data.push(PY_NULL);
     return true;
     return true;
 }
 }
 
 
-struct TempViewPopper{
+struct TempViewPopper {
     VM* vm;
     VM* vm;
     bool used;
     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;
         if(used) return;
         vm->__c.s_view.pop();
         vm->__c.s_view.pop();
         used = true;
         used = true;
     }
     }
 
 
-    ~TempViewPopper(){ restore(); }
+    ~TempViewPopper() { restore(); }
 };
 };
 
 
 // function
 // function
@@ -337,25 +328,25 @@ static PyVar c_function_wrapper(VM* vm, ArgsView args) {
 
 
     vm->__c.s_view.push(args);
     vm->__c.s_view.push(args);
     TempViewPopper _tvp(vm);
     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();
     _tvp.restore();
 
 
     // propagate_if_errored
     // 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->__c.error = nullptr;
         vm->_error(e_obj);
         vm->_error(e_obj);
         return nullptr;
         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 == 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);
     ArgsView ret_view(curr_sp, vm->s_data._sp);
     return py_var(vm, ret_view.to_tuple());
     return py_var(vm, ret_view.to_tuple());
 }
 }
 
 
 bool pkpy_push_function(pkpy_vm* vm_handle, const char* sig, pkpy_CFunction f) {
 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()
     PK_ASSERT_NO_ERROR()
     PyVar f_obj;
     PyVar f_obj;
     PK_PROTECTED(
     PK_PROTECTED(
@@ -367,7 +358,7 @@ bool pkpy_push_function(pkpy_vm* vm_handle, const char* sig, pkpy_CFunction f) {
 
 
 // special push
 // special push
 bool pkpy_push_module(pkpy_vm* vm_handle, const char* name) {
 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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyObject* module = vm->new_module(name);
         PyObject* module = vm->new_module(name);
@@ -378,7 +369,7 @@ bool pkpy_push_module(pkpy_vm* vm_handle, const char* name) {
 
 
 // some opt
 // some opt
 bool pkpy_getattr(pkpy_vm* vm_handle, pkpy_CName name) {
 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_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyVar o = vm->s_data.top();
     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) {
 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_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(2)
     PK_ASSERT_N_EXTRA_ELEMENTS(2)
     PyVar a = vm->s_data.top();
     PyVar a = vm->s_data.top();
@@ -401,21 +392,21 @@ bool pkpy_setattr(pkpy_vm* vm_handle, pkpy_CName name) {
     return true;
     return true;
 }
 }
 
 
-//get global will also get bulitins
+// get global will also get bulitins
 bool pkpy_getglobal(pkpy_vm* vm_handle, pkpy_CName name) {
 bool pkpy_getglobal(pkpy_vm* vm_handle, pkpy_CName name) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PyVar o = vm->_main->attr().try_get(StrName(name));
     PyVar o = vm->_main->attr().try_get(StrName(name));
-    if (o == nullptr) {
+    if(o == nullptr) {
         o = vm->builtins->attr().try_get(StrName(name));
         o = vm->builtins->attr().try_get(StrName(name));
-        if (o == nullptr) return false;
+        if(o == nullptr) return false;
     }
     }
     vm->s_data.push(o);
     vm->s_data.push(o);
     return true;
     return true;
 }
 }
 
 
 bool pkpy_setglobal(pkpy_vm* vm_handle, pkpy_CName name) {
 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_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     vm->_main->attr().set(StrName(name), vm->s_data.popx());
     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) {
 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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         CodeObject_ co = vm->compile(source, "<eval>", EVAL_MODE);
         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) {
 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_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     auto _lock = vm->heap.gc_scope_lock();
     auto _lock = vm->heap.gc_scope_lock();
@@ -450,8 +441,8 @@ bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
     return true;
     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_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyVar o = vm->s_data.top();
     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) {
 bool pkpy_py_repr(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyVar item = vm->s_data.top();
     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) {
 bool pkpy_py_str(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyVar item = vm->s_data.top();
     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) {
 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_ASSERT_NO_ERROR()
     PK_PROTECTED(
     PK_PROTECTED(
         PyVar module = vm->py_import(name);
         PyVar module = vm->py_import(name);
@@ -501,14 +492,15 @@ bool pkpy_py_import(pkpy_vm* vm_handle, pkpy_CString name) {
 
 
 /* Error Handling */
 /* Error Handling */
 bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) {
 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()
     PK_ASSERT_NO_ERROR()
     PyVar e_t = vm->_main->attr().try_get_likely_found(name);
     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);
         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);
             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();
     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) {
 bool pkpy_check_error(pkpy_vm* vm_handle) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     return vm->__c.error != nullptr;
     return vm->__c.error != nullptr;
 }
 }
 
 
 bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
 bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
-    VM* vm = (VM*) vm_handle;
+    VM* vm = (VM*)vm_handle;
     // no error
     // no error
-    if (vm->__c.error == nullptr) return false;
+    if(vm->__c.error == nullptr) return false;
     Exception& e = vm->__c.error->as<Exception>();
     Exception& e = vm->__c.error->as<Exception>();
-    if (message != nullptr)
+    if(message != nullptr)
         *message = strdup(e.summary().c_str());
         *message = strdup(e.summary().c_str());
     else
     else
         std::cout << e.summary() << std::endl;
         std::cout << e.summary() << std::endl;
     vm->__c.error = nullptr;
     vm->__c.error = nullptr;
-    if(vm->callstack.empty()){
+    if(vm->callstack.empty()) {
         vm->s_data.clear();
         vm->s_data.clear();
-    }else{
+    } else {
         if(vm->__c.s_view.empty()) exit(127);
         if(vm->__c.s_view.empty()) exit(127);
         vm->s_data.reset(vm->__c.s_view.top().end());
         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) {
 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_NO_ERROR()
     PK_ASSERT_N_EXTRA_ELEMENTS(argc + 2)
     PK_ASSERT_N_EXTRA_ELEMENTS(argc + 2)
     PyVar res;
     PyVar res;
@@ -550,39 +542,28 @@ bool pkpy_vectorcall(pkpy_vm* vm_handle, int argc) {
     vm->s_data.push(res);
     vm->s_data.push(res);
     return true;
     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;
     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;
     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"
 #include "pocketpy/common/export.h"
 
 
 namespace pkpy {
 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>
 #include <sstream>
 
 
 #if __has_include("pocketpy_c.h")
 #if __has_include("pocketpy_c.h")
-    #include "pocketpy_c.h"
+#include "pocketpy_c.h"
 #else
 #else
-    // for amalgamated build
-    #include "pocketpy.h"
+// for amalgamated build
+#include "pocketpy.h"
 #endif
 #endif
 
 
 #ifdef _WIN32
 #ifdef _WIN32
 
 
-    #include <windows.h>
+#include <windows.h>
 
 
 std::string pkpy_platform_getline(bool* eof) {
 std::string pkpy_platform_getline(bool* eof) {
     HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
     HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
     std::wstringstream wss;
     std::wstringstream wss;
     WCHAR buf;
     WCHAR buf;
     DWORD read;
     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;
         wss << buf;
     }
     }
     std::wstring wideInput = wss.str();
     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;
     std::string output;
     output.resize(length);
     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;
     return output;
 }
 }
 
 
@@ -39,8 +36,8 @@ std::string pkpy_platform_getline(bool* eof) {
 
 
 std::string pkpy_platform_getline(bool* eof) {
 std::string pkpy_platform_getline(bool* eof) {
     std::string output;
     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;
     return output;
 }
 }
@@ -48,10 +45,10 @@ std::string pkpy_platform_getline(bool* eof) {
 #endif
 #endif
 
 
 static int f_input(pkpy_vm* vm) {
 static int f_input(pkpy_vm* vm) {
-    if (!pkpy_is_none(vm, -1)) {
+    if(!pkpy_is_none(vm, -1)) {
         pkpy_CString prompt;
         pkpy_CString prompt;
         bool ok = pkpy_to_string(vm, -1, &prompt);
         bool ok = pkpy_to_string(vm, -1, &prompt);
-        if (!ok) return 0;
+        if(!ok) return 0;
         std::cout << prompt << std::flush;
         std::cout << prompt << std::flush;
     }
     }
     bool eof;
     bool eof;
@@ -71,44 +68,42 @@ int main(int argc, char** argv) {
     pkpy_py_import(vm, "builtins");
     pkpy_py_import(vm, "builtins");
     pkpy_setattr(vm, pkpy_name("input"));
     pkpy_setattr(vm, pkpy_name("input"));
 
 
-    if (argc == 1) {
+    if(argc == 1) {
         void* repl = pkpy_new_repl(vm);
         void* repl = pkpy_new_repl(vm);
         bool need_more_lines = false;
         bool need_more_lines = false;
-        while (true) {
+        while(true) {
             std::cout << (need_more_lines ? "... " : ">>> ");
             std::cout << (need_more_lines ? "... " : ">>> ");
             bool eof = false;
             bool eof = false;
             std::string line = pkpy_platform_getline(&eof);
             std::string line = pkpy_platform_getline(&eof);
-            if (eof) break;
+            if(eof) break;
             need_more_lines = pkpy_repl_input(repl, line.c_str());
             need_more_lines = pkpy_repl_input(repl, line.c_str());
         }
         }
         pkpy_delete_vm(vm);
         pkpy_delete_vm(vm);
         return 0;
         return 0;
     }
     }
 
 
-    if (argc == 2) {
+    if(argc == 2) {
         std::string argv_1 = argv[1];
         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]);
         std::filesystem::path filepath(argv[1]);
         filepath = std::filesystem::absolute(filepath);
         filepath = std::filesystem::absolute(filepath);
-        if (!std::filesystem::exists(filepath)) {
+        if(!std::filesystem::exists(filepath)) {
             std::cerr << "File not found: " << argv_1 << std::endl;
             std::cerr << "File not found: " << argv_1 << std::endl;
             return 2;
             return 2;
         }
         }
         std::ifstream file(filepath);
         std::ifstream file(filepath);
-        if (!file.is_open()) {
+        if(!file.is_open()) {
             std::cerr << "Failed to open file: " << argv_1 << std::endl;
             std::cerr << "Failed to open file: " << argv_1 << std::endl;
             return 3;
             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();
         file.close();
 
 
         pkpy_set_main_argv(vm, argc, argv);
         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);
         pkpy_delete_vm(vm);
         return ok ? 0 : 1;
         return ok ? 0 : 1;
     }
     }

Некоторые файлы не были показаны из-за большого количества измененных файлов