Răsfoiți Sursa

remove `*.cpp`

blueloveTH 1 an în urmă
părinte
comite
19ed0fdaf5
78 a modificat fișierele cu 816 adăugiri și 9165 ștergeri
  1. BIN
      .github/workflows.zip
  2. 0 204
      .github/workflows/main.yml
  3. 0 42
      .github/workflows/website.yml
  4. 3 3
      compile_flags.txt
  5. 1 1
      include/pocketpy.h
  6. 1 1
      include/pocketpy.hpp
  7. 1 0
      include/pocketpy/common/str.h
  8. 0 404
      include/pocketpy/common/str.hpp
  9. 6 6
      include/pocketpy/common/strname.h
  10. 0 46
      include/pocketpy/common/traits.hpp
  11. 0 37
      include/pocketpy/common/types.hpp
  12. 2 0
      include/pocketpy/common/utils.h
  13. 0 459
      include/pocketpy/common/vector.hpp
  14. 0 229
      include/pocketpy/interpreter/bindings.hpp
  15. 0 0
      include/pocketpy/interpreter/ceval.h
  16. 0 105
      include/pocketpy/interpreter/cffi.hpp
  17. 102 0
      include/pocketpy/interpreter/frame.h
  18. 0 214
      include/pocketpy/interpreter/frame.hpp
  19. 6 5
      include/pocketpy/interpreter/gc.h
  20. 0 86
      include/pocketpy/interpreter/iter.hpp
  21. 0 44
      include/pocketpy/interpreter/profiler.hpp
  22. 82 12
      include/pocketpy/interpreter/vm.h
  23. 71 69
      include/pocketpy/interpreter/vm.hpp
  24. 0 9
      include/pocketpy/modules/array2d.hpp
  25. 0 9
      include/pocketpy/modules/base64.hpp
  26. 0 9
      include/pocketpy/modules/csv.hpp
  27. 0 9
      include/pocketpy/modules/dataclasses.hpp
  28. 0 9
      include/pocketpy/modules/easing.hpp
  29. 0 9
      include/pocketpy/modules/io.hpp
  30. 0 215
      include/pocketpy/modules/linalg.hpp
  31. 0 18
      include/pocketpy/modules/modules.hpp
  32. 0 9
      include/pocketpy/modules/random.hpp
  33. 18 15
      include/pocketpy/objects/base.h
  34. 0 85
      include/pocketpy/objects/base.hpp
  35. 0 141
      include/pocketpy/objects/builtins.hpp
  36. 0 66
      include/pocketpy/objects/codeobject.hpp
  37. 0 98
      include/pocketpy/objects/dict.hpp
  38. 2 2
      include/pocketpy/objects/error.h
  39. 0 64
      include/pocketpy/objects/error.hpp
  40. 1 1
      include/pocketpy/objects/namedict.h
  41. 0 104
      include/pocketpy/objects/namedict.hpp
  42. 15 4
      include/pocketpy/objects/object.h
  43. 0 37
      include/pocketpy/objects/object.hpp
  44. 0 77
      include/pocketpy/objects/tuplelist.hpp
  45. 16 8
      include/pocketpy/pocketpy.h
  46. 0 25
      include/pocketpy/pocketpy.hpp
  47. 0 107
      include/pocketpy/pocketpy_c.h
  48. 13 0
      include/pocketpy/xmacros/smallmap.h
  49. 0 3
      include/pocketpy_c.h
  50. 56 56
      src/common/strname.c
  51. 2 2
      src/error.c
  52. 6 6
      src/interpreter/ceval.c
  53. 0 289
      src/interpreter/cffi.cpp
  54. 133 0
      src/interpreter/frame.c
  55. 0 122
      src/interpreter/frame.cpp
  56. 16 8
      src/interpreter/gc.c
  57. 0 135
      src/interpreter/iter.cpp
  58. 0 131
      src/interpreter/profiler.cpp
  59. 161 40
      src/interpreter/vm.c
  60. 41 34
      src/interpreter/vm.cpp
  61. 0 406
      src/modules/array2d.cpp
  62. 0 175
      src/modules/base64.cpp
  63. 0 87
      src/modules/csv.cpp
  64. 0 120
      src/modules/dataclasses.cpp
  65. 0 218
      src/modules/easing.cpp
  66. 0 247
      src/modules/io.cpp
  67. 0 717
      src/modules/linalg.cpp
  68. 0 355
      src/modules/modules.cpp
  69. 0 225
      src/modules/random.cpp
  70. 3 5
      src/objects/base.c
  71. 1 1
      src/objects/namedict.c
  72. 0 21
      src/objects/public.cpp
  73. 0 56
      src/objects/tuplelist.cpp
  74. 15 2
      src/pocketpy.c
  75. 0 1767
      src/pocketpy.cpp
  76. 0 575
      src/pocketpy_c.cpp
  77. 42 0
      src2/main.c
  78. 0 265
      src2/pocketpy_c.c

BIN
.github/workflows.zip


+ 0 - 204
.github/workflows/main.yml

@@ -1,204 +0,0 @@
-name: build
-
-on:
-  push:
-    paths-ignore:
-      - 'docs/**'
-      - 'web/**'
-      - '**.md'
-  pull_request:
-    paths-ignore:
-      - 'docs/**'
-      - 'web/**'
-      - '**.md'
-jobs:
-  # build_win32_amalgamated:
-  #   runs-on: windows-latest
-  #   steps:
-  #   - uses: actions/checkout@v4
-  #   - uses: ilammy/msvc-dev-cmd@v1
-  #   - name: Compile
-  #     shell: powershell
-  #     run: |
-  #       python amalgamate.py
-  #       cd amalgamated
-  #       cl.exe /std:c++17 /EHsc /utf-8 /Ox /I. /DPK_ENABLE_OS=1 main.cpp /link /out:pkpy.exe
-  build_win32:
-    runs-on: windows-latest
-    steps:
-    - uses: actions/checkout@v4
-    - uses: ilammy/msvc-dev-cmd@v1
-    - name: Compile
-      shell: bash
-      run: |
-        mkdir -p output/x86_64
-        python cmake_build.py
-        cp main.exe output/x86_64
-        cp pocketpy.dll output/x86_64
-    - uses: actions/upload-artifact@v4
-      with:
-        name: windows
-        path: output
-    - name: Unit Test
-      run: python scripts/run_tests.py
-    - name: Benchmark
-      run: python scripts/run_tests.py benchmark
-  build_linux:
-    runs-on: ubuntu-20.04
-    steps:
-    - uses: actions/checkout@v4
-    - name: Setup Clang
-      uses: egor-tensin/setup-clang@v1
-      with:
-        version: 15
-        platform: x64
-    - name: Install libc++
-      run: sudo apt-get install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15 libclang-rt-15-dev
-    # - name: Unit Test with Coverage
-    #   run: bash run_tests.sh
-    # - name: Upload coverage reports to Codecov
-    #   uses: codecov/codecov-action@v4
-    #   with:
-    #     token: ${{ secrets.CODECOV_TOKEN }}
-    #     directory: .coverage
-    #   if: github.ref == 'refs/heads/main'
-    - name: Compile and Test
-      run: |
-        mkdir -p output/x86_64
-        python cmake_build.py
-        python scripts/run_tests.py
-        cp main output/x86_64
-        cp libpocketpy.so output/x86_64
-      env:
-        CXX: clang++
-        CC: clang
-    - uses: actions/upload-artifact@v4
-      with:
-        name: linux
-        path: output
-    - name: Benchmark
-      run: python scripts/run_tests.py benchmark
-    - name: C Binding Test
-      run: bash run_c_binding_test.sh
-  build_linux_x86:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v4
-      - name: Setup Alpine Linux for aarch64
-        uses: jirutka/setup-alpine@v1
-        with:
-          arch: x86
-          packages: gcc g++ make cmake libc-dev linux-headers python3
-      - name: Build and Test
-        run: |
-          uname -m
-          python cmake_build.py
-          python scripts/run_tests.py
-          python scripts/run_tests.py benchmark
-        shell: alpine.sh --root {0}
-  build_darwin:
-      runs-on: macos-latest
-      steps:
-      - uses: actions/checkout@v4
-      - name: Compile and Test
-        run: |
-          python cmake_build.py
-          python scripts/run_tests.py
-      - name: Benchmark
-        run: python scripts/run_tests.py benchmark
-      # - run: |
-      #     python amalgamate.py
-      #     cd plugins/macos/pocketpy
-      #     mkdir output
-      #     xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
-      #     cp -r build/Release/pocketpy.bundle output
-      # - uses: actions/upload-artifact@v4
-      #   with:
-      #     name: macos
-      #     path: plugins/macos/pocketpy/output
-  build_android:
-      runs-on: ubuntu-latest
-      steps:
-      - uses: actions/checkout@v4
-      - uses: nttld/setup-ndk@v1
-        id: setup-ndk
-        with:
-          ndk-version: r23
-          local-cache: false
-          add-to-path: false
-      - name: Compile Shared Library
-        run: |
-          bash build_android.sh arm64-v8a
-          bash build_android.sh armeabi-v7a
-          bash build_android.sh x86_64
-
-          mkdir -p output/arm64-v8a
-          mkdir -p output/armeabi-v7a
-          mkdir -p output/x86_64
-
-          cp build/android/arm64-v8a/libpocketpy.so output/arm64-v8a
-          cp build/android/armeabi-v7a/libpocketpy.so output/armeabi-v7a
-          cp build/android/x86_64/libpocketpy.so output/x86_64
-        env:
-          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
-      - uses: actions/upload-artifact@v4
-        with:
-          name: android
-          path: output
-  build_ios:
-      runs-on: macos-latest
-      steps:
-      - uses: actions/checkout@v4
-      - name: Compile Frameworks
-        run: |
-          git clone https://github.com/leetal/ios-cmake --depth 1 ~/ios-cmake
-          bash build_ios.sh
-          mkdir -p output
-          cp -r build/pocketpy.xcframework output/pocketpy.xcframework
-      - uses: actions/upload-artifact@v4
-        with:
-          name: ios
-          path: output
-
-  merge:
-      runs-on: ubuntu-latest
-      needs: [ build_win32, build_linux, build_darwin, build_android, build_ios ]
-      steps:
-      - name: "Create output directory"
-        run: "mkdir $GITHUB_WORKSPACE/output"
-        
-      - name: "Merge win32"
-        uses: actions/download-artifact@v4.1.7
-        with:
-          name: windows
-          path: $GITHUB_WORKSPACE/output/windows
-
-      - name: "Merge linux"
-        uses: actions/download-artifact@v4.1.7
-        with:
-          name: linux
-          path: $GITHUB_WORKSPACE/output/linux
-
-      # - name: "Merge darwin"
-      #   uses: actions/download-artifact@v4.1.7
-      #   with:
-      #     name: macos
-      #     path: $GITHUB_WORKSPACE/output/macos
-
-      - name: "Merge android"
-        uses: actions/download-artifact@v4.1.7
-        with:
-          name: android
-          path: $GITHUB_WORKSPACE/output/android
-          
-      - name: "Merge ios"
-        uses: actions/download-artifact@v4.1.7
-        with:
-          name: ios
-          path: $GITHUB_WORKSPACE/output/ios
-
-      - name: "Upload merged artifact"
-        uses: actions/upload-artifact@v4.3.3
-        with:
-          name: all-in-one
-          path: $GITHUB_WORKSPACE/output

+ 0 - 42
.github/workflows/website.yml

@@ -1,42 +0,0 @@
-name: website
-
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
-
-permissions:
-  contents: write
-
-jobs:
-  deploy:
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v4
-    ###################################################
-    - uses: actions/setup-node@v3.1.1
-    - name: Retype build
-      run: |
-        python scripts/build_references.py
-        cd docs
-        npm install retypeapp --global
-        retype build
-    ###################################################
-    - name: Setup emsdk
-      uses: mymindstorm/setup-emsdk@v12
-      with:
-        version: 3.1.25
-        actions-cache-folder: 'emsdk-cache'
-    - name: Compile
-      run: |
-        bash build_web.sh
-        mv web docs/.retype/static
-    ###################################################
-    - uses: crazy-max/ghaction-github-pages@v3
-      with:
-        target_branch: gh-pages
-        build_dir: docs/.retype
-      env:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      if: github.ref == 'refs/heads/main'

+ 3 - 3
compile_flags.txt

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

+ 1 - 1
include/pocketpy.h

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

+ 1 - 1
include/pocketpy.hpp

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

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

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

+ 0 - 404
include/pocketpy/common/str.hpp

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

+ 6 - 6
include/pocketpy/common/strname.h

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

+ 0 - 46
include/pocketpy/common/traits.hpp

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

+ 0 - 37
include/pocketpy/common/types.hpp

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

+ 2 - 0
include/pocketpy/common/utils.h

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

+ 0 - 459
include/pocketpy/common/vector.hpp

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

+ 0 - 229
include/pocketpy/interpreter/bindings.hpp

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

+ 0 - 0
include/pocketpy/interpreter/ceval.hpp → include/pocketpy/interpreter/ceval.h


+ 0 - 105
include/pocketpy/interpreter/cffi.hpp

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

+ 102 - 0
include/pocketpy/interpreter/frame.h

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

+ 0 - 214
include/pocketpy/interpreter/frame.hpp

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

+ 6 - 5
include/pocketpy/interpreter/gc.h

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

+ 0 - 86
include/pocketpy/interpreter/iter.hpp

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

+ 0 - 44
include/pocketpy/interpreter/profiler.hpp

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

+ 82 - 12
include/pocketpy/interpreter/vm.h

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

+ 71 - 69
include/pocketpy/interpreter/vm.hpp

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

+ 0 - 9
include/pocketpy/modules/array2d.hpp

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

+ 0 - 9
include/pocketpy/modules/base64.hpp

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

+ 0 - 9
include/pocketpy/modules/csv.hpp

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

+ 0 - 9
include/pocketpy/modules/dataclasses.hpp

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

+ 0 - 9
include/pocketpy/modules/easing.hpp

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

+ 0 - 9
include/pocketpy/modules/io.hpp

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

+ 0 - 215
include/pocketpy/modules/linalg.hpp

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

+ 0 - 18
include/pocketpy/modules/modules.hpp

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

+ 0 - 9
include/pocketpy/modules/random.hpp

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

+ 18 - 15
include/pocketpy/objects/base.h

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

+ 0 - 85
include/pocketpy/objects/base.hpp

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

+ 0 - 141
include/pocketpy/objects/builtins.hpp

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

+ 0 - 66
include/pocketpy/objects/codeobject.hpp

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

+ 0 - 98
include/pocketpy/objects/dict.hpp

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

+ 2 - 2
include/pocketpy/objects/error.h

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

+ 0 - 64
include/pocketpy/objects/error.hpp

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

+ 1 - 1
include/pocketpy/objects/namedict.h

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

+ 0 - 104
include/pocketpy/objects/namedict.hpp

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

+ 15 - 4
include/pocketpy/objects/object.h

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

+ 0 - 37
include/pocketpy/objects/object.hpp

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

+ 0 - 77
include/pocketpy/objects/tuplelist.hpp

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

+ 16 - 8
include/pocketpy/objects/public.h → include/pocketpy/pocketpy.h

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

+ 0 - 25
include/pocketpy/pocketpy.hpp

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

+ 0 - 107
include/pocketpy/pocketpy_c.h

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

+ 13 - 0
include/pocketpy/xmacros/smallmap.h

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

+ 0 - 3
include/pocketpy_c.h

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

+ 56 - 56
src/common/strname.c

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

+ 2 - 2
src/error.c

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

+ 6 - 6
src/interpreter/ceval.cpp → src/interpreter/ceval.c

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

+ 0 - 289
src/interpreter/cffi.cpp

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

+ 133 - 0
src/interpreter/frame.c

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

+ 0 - 122
src/interpreter/frame.cpp

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

+ 16 - 8
src/interpreter/gc.c

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

+ 0 - 135
src/interpreter/iter.cpp

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

+ 0 - 131
src/interpreter/profiler.cpp

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

+ 161 - 40
src/interpreter/vm.c

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

+ 41 - 34
src/interpreter/vm.cpp

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

+ 0 - 406
src/modules/array2d.cpp

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

+ 0 - 175
src/modules/base64.cpp

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

+ 0 - 87
src/modules/csv.cpp

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

+ 0 - 120
src/modules/dataclasses.cpp

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

+ 0 - 218
src/modules/easing.cpp

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

+ 0 - 247
src/modules/io.cpp

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

+ 0 - 717
src/modules/linalg.cpp

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

+ 0 - 355
src/modules/modules.cpp

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

+ 0 - 225
src/modules/random.cpp

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

+ 3 - 5
src/objects/base.c

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

+ 1 - 1
src/objects/namedict.c

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

+ 0 - 21
src/objects/public.cpp

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

+ 0 - 56
src/objects/tuplelist.cpp

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

+ 15 - 2
src/public.c → src/pocketpy.c

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

+ 0 - 1767
src/pocketpy.cpp

@@ -1,1767 +0,0 @@
-#include "pocketpy/pocketpy.hpp"
-
-#include "pocketpy/common/_generated.h"
-
-#include "pocketpy/common/refcount.h"
-#include "pocketpy/modules/array2d.hpp"
-#include "pocketpy/modules/base64.hpp"
-#include "pocketpy/modules/csv.hpp"
-#include "pocketpy/modules/dataclasses.hpp"
-#include "pocketpy/modules/easing.hpp"
-#include "pocketpy/modules/io.hpp"
-#include "pocketpy/modules/linalg.hpp"
-#include "pocketpy/modules/random.hpp"
-#include "pocketpy/modules/modules.hpp"
-#include "pocketpy/objects/base.h"
-#include "pocketpy/objects/codeobject.h"
-
-#include <iostream>
-#include <algorithm>
-
-namespace pkpy {
-
-#ifdef PK_USE_CJSON
-void add_module_cjson(VM* vm);
-#endif
-
-template <typename T>
-PyVar PyArrayGetItem(VM* vm, PyVar _0, PyVar _1) {
-    static_assert(std::is_same_v<T, List> || std::is_same_v<T, Tuple>);
-    const T& self = _CAST(T&, _0);
-    if(is_int(_1)) {
-        i64 index = _1._i64;
-        index = vm->normalized_index(index, self.size());
-        return self[index];
-    }
-    if(is_type(_1, vm->tp_slice)) {
-        const Slice& s = _CAST(Slice&, _1);
-        int start, stop, step;
-        vm->parse_int_slice(s, self.size(), start, stop, step);
-        List new_list;
-        PK_SLICE_LOOP(i, start, stop, step) new_list.push_back(self[i]);
-
-        if constexpr(std::is_same_v<T, List>)
-            return VAR(std::move(new_list));
-        else
-            return VAR(new_list.to_tuple());
-    }
-    vm->TypeError("indices must be integers or slices");
-}
-
-void __init_builtins(VM* _vm) {
-#define BIND_NUM_ARITH_OPT(name, op)                                                                                   \
-    _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) {                                                     \
-        if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs));                                                \
-        if(is_float(rhs)) return VAR(_CAST(i64, lhs) op _CAST(f64, rhs));                                              \
-        return vm->NotImplemented;                                                                                     \
-    });                                                                                                                \
-    _vm->bind##name(VM::tp_float, [](VM* vm, PyVar lhs, PyVar rhs) {                                                   \
-        if(is_int(rhs)) return VAR(_CAST(f64, lhs) op _CAST(i64, rhs));                                                \
-        if(is_float(rhs)) return VAR(_CAST(f64, lhs) op _CAST(f64, rhs));                                              \
-        return vm->NotImplemented;                                                                                     \
-    });
-
-    BIND_NUM_ARITH_OPT(__add__, +)
-    BIND_NUM_ARITH_OPT(__sub__, -)
-    BIND_NUM_ARITH_OPT(__mul__, *)
-
-#undef BIND_NUM_ARITH_OPT
-
-#define BIND_NUM_LOGICAL_OPT(name, op)                                                                                 \
-    _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) {                                                     \
-        if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs));                                                \
-        if(is_float(rhs)) return VAR(_CAST(i64, lhs) op _CAST(f64, rhs));                                              \
-        return vm->NotImplemented;                                                                                     \
-    });                                                                                                                \
-    _vm->bind##name(VM::tp_float, [](VM* vm, PyVar lhs, PyVar rhs) {                                                   \
-        if(is_int(rhs)) return VAR(_CAST(f64, lhs) op _CAST(i64, rhs));                                                \
-        if(is_float(rhs)) return VAR(_CAST(f64, lhs) op _CAST(f64, rhs));                                              \
-        return vm->NotImplemented;                                                                                     \
-    });
-
-    BIND_NUM_LOGICAL_OPT(__eq__, ==)
-    BIND_NUM_LOGICAL_OPT(__lt__, <)
-    BIND_NUM_LOGICAL_OPT(__le__, <=)
-    BIND_NUM_LOGICAL_OPT(__gt__, >)
-    BIND_NUM_LOGICAL_OPT(__ge__, >=)
-
-#undef BIND_NUM_ARITH_OPT
-#undef BIND_NUM_LOGICAL_OPT
-
-    // builtin functions
-    _vm->bind_func(_vm->builtins, "breakpoint", 0, [](VM* vm, ArgsView args) {
-#if PK_ENABLE_PROFILER
-        vm->_next_breakpoint = NextBreakpoint(vm->callstack.size(), vm->callstack.top().curr_lineno(), false);
-#endif
-        return vm->None;
-    });
-
-    _vm->bind_func(_vm->builtins, "super", -1, [](VM* vm, ArgsView args)->PyVar  {
-        PyObject* class_arg = nullptr;
-        PyVar self_arg = nullptr;
-        if(args.size() == 2) {
-            class_arg = args[0].get();
-            self_arg = args[1];
-        } else if(args.size() == 0) {
-            Frame* frame = &vm->callstack.top();
-            if(frame->_callable != nullptr) {
-                class_arg = frame->_callable->as<Function>()._class;
-                if(frame->_locals.size() > 0) self_arg = frame->_locals[0];
-            }
-            if(class_arg == nullptr || self_arg == nullptr) {
-                vm->TypeError("super(): unable to determine the class context, use super(class, self) instead");
-            }
-        } else {
-            vm->TypeError("super() takes 0 or 2 arguments");
-        }
-        vm->check_type(class_arg, vm->tp_type);
-        Type type = class_arg->as<Type>();
-        if(!vm->isinstance(self_arg, type)) {
-            StrName _0 = _type_name(vm, vm->_tp(self_arg));
-            StrName _1 = _type_name(vm, type);
-            vm->TypeError("super(): " + _0.escape() + " is not an instance of " + _1.escape());
-        }
-        return vm->new_object<Super>(vm->tp_super, self_arg, vm->_all_types[type].base);
-    });
-
-    _vm->bind_func(_vm->builtins, "staticmethod", 1, [](VM* vm, ArgsView args)->PyVar {
-        PyVar func = args[0];
-        vm->check_type(func, vm->tp_function);
-        return vm->new_object<StaticMethod>(vm->tp_staticmethod, args[0]);
-    });
-
-    _vm->bind_func(_vm->builtins, "classmethod", 1, [](VM* vm, ArgsView args)->PyVar  {
-        PyVar func = args[0];
-        vm->check_type(func, vm->tp_function);
-        return vm->new_object<ClassMethod>(vm->tp_classmethod, args[0]);
-    });
-
-    _vm->bind_func(_vm->builtins, "isinstance", 2, [](VM* vm, ArgsView args) {
-        if(is_type(args[1], vm->tp_tuple)) {
-            Tuple& types = _CAST(Tuple&, args[1]);
-            for(PyVar type: types) {
-                vm->check_type(type, vm->tp_type);
-                if(vm->isinstance(args[0], type->as<Type>())) return vm->True;
-            }
-            return vm->False;
-        }
-        vm->check_type(args[1], vm->tp_type);
-        Type type = PK_OBJ_GET(Type, args[1]);
-        return VAR(vm->isinstance(args[0], type));
-    });
-
-    _vm->bind_func(_vm->builtins, "issubclass", 2, [](VM* vm, ArgsView args) {
-        vm->check_type(args[0], vm->tp_type);
-        vm->check_type(args[1], vm->tp_type);
-        return VAR(vm->issubclass(PK_OBJ_GET(Type, args[0]), PK_OBJ_GET(Type, args[1])));
-    });
-
-    _vm->bind_func(_vm->builtins, "globals", 0, [](VM* vm, ArgsView args) {
-        PyObject* mod = vm->callstack.top()._module;
-        return VAR(MappingProxy(mod));
-    });
-
-    _vm->bind(_vm->builtins, "round(x, ndigits=None)", [](VM* vm, ArgsView args) {
-        if(is_int(args[0])) return args[0];
-        f64 x = CAST(f64, args[0]);
-        f64 offset = x >= 0 ? 0.5 : -0.5;
-        if(is_none(args[1])) return VAR((i64)(x + offset));
-        int ndigits = CAST(int, args[1]);
-        if(ndigits < 0) vm->ValueError("ndigits should be non-negative");
-        // ndigits > 0
-        return VAR((i64)(x * std::pow(10, ndigits) + offset) / std::pow(10, ndigits));
-    });
-
-    _vm->bind_func(_vm->builtins, "abs", 1, [](VM* vm, ArgsView args) {
-        if(is_int(args[0])) return VAR(std::abs(_CAST(i64, args[0])));
-        if(is_float(args[0])) return VAR(std::abs(_CAST(f64, args[0])));
-        vm->TypeError("bad operand type for abs()");
-        return vm->None;
-    });
-
-    _vm->bind(_vm->builtins, "max(*args, key=None)", [](VM* vm, ArgsView args) {
-        return vm->__minmax_reduce(&VM::py_gt, args[0], args[1]);
-    });
-
-    _vm->bind(_vm->builtins, "min(*args, key=None)", [](VM* vm, ArgsView args) {
-        return vm->__minmax_reduce(&VM::py_lt, args[0], args[1]);
-    });
-
-    _vm->bind_func(_vm->builtins, "id", 1, [](VM* vm, ArgsView args) {
-        PyVar obj = args[0];
-        if(is_tagged(obj)) return vm->None;
-        return VAR(reinterpret_cast<i64>(obj.get()));
-    });
-
-    _vm->bind_func(_vm->builtins, "callable", 1, [](VM* vm, ArgsView args) {
-        return VAR(vm->py_callable(args[0]));
-    });
-
-    _vm->bind_func(_vm->builtins, "__import__", 1, [](VM* vm, ArgsView args) -> PyVar {
-        const Str& name = CAST(Str&, args[0]);
-        return vm->py_import(name);
-    });
-
-    _vm->bind_func(_vm->builtins, "divmod", 2, [](VM* vm, ArgsView args) {
-        if(is_int(args[0])) {
-            i64 lhs = _CAST(i64, args[0]);
-            i64 rhs = CAST(i64, args[1]);
-            if(rhs == 0) vm->ZeroDivisionError();
-            auto res = std::div(lhs, rhs);
-            return VAR(Tuple(VAR(res.quot), VAR(res.rem)));
-        } else {
-            return vm->call_method(args[0], __divmod__, args[1]);
-        }
-    });
-
-    // we use `_0`, `_1` and `_2` here to disable keyword arguments (but with default values)
-    _vm->bind(_vm->builtins, "eval(_0, _1=None, _2=None)", [](VM* vm, ArgsView args) {
-        return vm->py_eval(CAST(Str&, args[0]), args[1], args[2]);
-    });
-
-    _vm->bind(_vm->builtins, "exec(_0, _1=None, _2=None)", [](VM* vm, ArgsView args) {
-        vm->py_exec(CAST(Str&, args[0]), args[1], args[2]);
-        return vm->None;
-    });
-
-    _vm->bind(_vm->builtins, "compile(source: str, filename: str, mode: str) -> str", [](VM* vm, ArgsView args) {
-        const Str& source = CAST(Str&, args[0]);
-        const Str& filename = CAST(Str&, args[1]);
-        const Str& mode = CAST(Str&, args[2]);
-        if(mode == "exec") {
-            return VAR(vm->precompile(source, filename, EXEC_MODE));
-        } else if(mode == "eval") {
-            return VAR(vm->precompile(source, filename, EVAL_MODE));
-        } else if(mode == "single") {
-            return VAR(vm->precompile(source, filename, CELL_MODE));
-        } else {
-            vm->ValueError("compile() mode must be 'exec', 'eval' or 'single'");
-            return vm->None;
-        }
-    });
-
-    _vm->bind(_vm->builtins, "exit(code=0)", [](VM* vm, ArgsView args) {
-        std::exit(CAST(int, args[0]));
-        return vm->None;
-    });
-
-    _vm->bind_func(_vm->builtins, "repr", 1, [](VM* vm, ArgsView args) {
-        return VAR(vm->py_repr(args[0]));
-    });
-
-    _vm->bind_func(_vm->builtins, "len", 1, [](VM* vm, ArgsView args) {
-        const PyTypeInfo* ti = vm->_tp_info(args[0]);
-        if(ti->m__len__) return VAR(ti->m__len__(vm, args[0]));
-        return vm->call_method(args[0], __len__);
-    });
-
-    _vm->bind_func(_vm->builtins, "hash", 1, [](VM* vm, ArgsView args) {
-        i64 value = vm->py_hash(args[0]);
-        return VAR(value);
-    });
-
-    _vm->bind_func(_vm->builtins, "chr", 1, [](VM* vm, ArgsView args) {
-        i64 i = CAST(i64, args[0]);
-        if(i < 0 || i >= 128) vm->ValueError("chr() arg not in [0, 128)");
-        return VAR(std::string(1, (char)i));
-    });
-
-    _vm->bind_func(_vm->builtins, "ord", 1, [](VM* vm, ArgsView args) {
-        const Str& s = CAST(Str&, args[0]);
-        if(s.length() != 1) vm->TypeError("ord() expected an ASCII character");
-        return VAR((i64)(s[0]));
-    });
-
-    _vm->bind_func(_vm->builtins, "hasattr", 2, [](VM* vm, ArgsView args) {
-        return VAR(vm->getattr(args[0], CAST(Str&, args[1]), false) != nullptr);
-    });
-
-    _vm->bind_func(_vm->builtins, "setattr", 3, [](VM* vm, ArgsView args) {
-        vm->setattr(args[0], CAST(Str&, args[1]), args[2]);
-        return vm->None;
-    });
-
-    _vm->bind_func(_vm->builtins, "getattr", -1, [](VM* vm, ArgsView args) {
-        if(args.size() != 2 && args.size() != 3) vm->TypeError("getattr() takes 2 or 3 arguments");
-        StrName name = CAST(Str&, args[1]);
-        PyVar val = vm->getattr(args[0], name, false);
-        if(val == nullptr) {
-            if(args.size() == 2) vm->AttributeError(args[0], name);
-            return args[2];
-        }
-        return val;
-    });
-
-    _vm->bind_func(_vm->builtins, "delattr", 2, [](VM* vm, ArgsView args) {
-        vm->delattr(args[0], CAST(Str&, args[1]));
-        return vm->None;
-    });
-
-    _vm->bind_func(_vm->builtins, "hex", 1, [](VM* vm, ArgsView args) {
-        SStream ss;
-        i64 val = CAST(i64, args[0]);
-        if(val == 0) {
-            ss << "0x0";
-            return VAR(ss.str());
-        }
-        if(val < 0) {
-            ss << "-";
-            val = -val;
-        }
-        ss << "0x";
-        bool non_zero = true;
-        for(int i = 56; i >= 0; i -= 8) {
-            unsigned char cpnt = (val >> i) & 0xff;
-            ss.write_hex(cpnt, non_zero);
-            if(cpnt != 0) non_zero = false;
-        }
-        return VAR(ss.str());
-    });
-
-    _vm->bind_func(_vm->builtins, "iter", 1, [](VM* vm, ArgsView args) {
-        return vm->py_iter(args[0]);
-    });
-
-    _vm->bind_func(_vm->builtins, "next", 1, [](VM* vm, ArgsView args) {
-        PyVar retval = vm->py_next(args[0]);
-        if(retval == vm->StopIteration) vm->_error(vm->call(vm->StopIteration));
-        return retval;
-    });
-
-    _vm->bind_func(_vm->builtins, "bin", 1, [](VM* vm, ArgsView args) {
-        SStream ss;
-        i64 x = CAST(i64, args[0]);
-        if(x < 0) {
-            ss << "-";
-            x = -x;
-        }
-        ss << "0b";
-        std::string bits;
-        while(x) {
-            bits += (x & 1) ? '1' : '0';
-            x >>= 1;
-        }
-        std::reverse(bits.begin(), bits.end());
-        if(bits.empty()) bits = "0";
-        ss << bits;
-        return VAR(ss.str());
-    });
-
-    _vm->bind_func(_vm->builtins, "dir", 1, [](VM* vm, ArgsView args) {
-        vector<StrName> names;
-        if(!is_tagged(args[0]) && args[0]->is_attr_valid()) {
-            auto keys = args[0]->attr().keys();
-            names.extend(keys.begin(), keys.end());
-        }
-        const NameDict& t_attr = vm->_t(args[0])->attr();
-        auto keys = t_attr.keys();
-        names.extend(keys.begin(), keys.end());
-        std::sort(names.begin(), names.end());
-        List ret;
-        for(int i = 0; i < names.size(); i++) {
-            // remove duplicates
-            if(i > 0 && names[i] == names[i - 1]) continue;
-            ret.push_back(VAR(names[i].sv()));
-        }
-        return VAR(std::move(ret));
-    });
-
-    // tp_object
-    _vm->bind__repr__(VM::tp_object, [](VM* vm, PyVar obj) -> Str {
-        assert(!is_tagged(obj));
-        SStream ss;
-        ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at ";
-        ss.write_ptr(obj.get());
-        ss << ">";
-        return ss.str();
-    });
-
-    _vm->bind__eq__(VM::tp_object, [](VM* vm, PyVar _0, PyVar _1) {
-        if(!_0.is_ptr) vm->TypeError("cannot compare tagged object: _0");
-        if(!_1.is_ptr) vm->TypeError("cannot compare tagged object: _1");
-        return _0._obj == _1._obj ? vm->True : vm->False;
-    });
-
-    _vm->__cached_object_new = _vm->bind_func(VM::tp_object, __new__, 1, [](VM* vm, ArgsView args) ->PyVar {
-        vm->check_type(args[0], vm->tp_type);
-        Type t = PK_OBJ_GET(Type, args[0]);
-        return vm->new_object<DummyInstance>(t);
-    });
-
-    // tp_type
-    _vm->bind_func(VM::tp_type, __new__, 2, PK_LAMBDA(vm->_t(args[1])));
-
-    // tp_range
-    _vm->bind_func(VM::tp_range, __new__, -1, [](VM* vm, ArgsView args) {
-        args._begin += 1;  // skip cls
-        Range r;
-        switch(args.size()) {
-            case 1: r.stop = CAST(i64, args[0]); break;
-            case 2:
-                r.start = CAST(i64, args[0]);
-                r.stop = CAST(i64, args[1]);
-                break;
-            case 3:
-                r.start = CAST(i64, args[0]);
-                r.stop = CAST(i64, args[1]);
-                r.step = CAST(i64, args[2]);
-                break;
-            default: vm->TypeError("expected 1-3 arguments, got " + std::to_string(args.size()));
-        }
-        if(r.step == 0) vm->ValueError("range() arg 3 must not be zero");
-        return VAR(r);
-    });
-
-    _vm->bind__iter__(VM::tp_range, [](VM* vm, PyVar _0) {
-        const Range& r = PK_OBJ_GET(Range, _0);
-        if(r.step > 0) {
-            return vm->new_user_object<RangeIter>(r);
-        } else {
-            return vm->new_user_object<RangeIterR>(r);
-        }
-    });
-
-    // tp_nonetype
-    _vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyVar _0) -> Str {
-        return "None";
-    });
-
-    // tp_float / tp_float
-    _vm->bind__truediv__(VM::tp_float, [](VM* vm, PyVar _0, PyVar _1) {
-        f64 value = CAST_F(_1);
-        return VAR(_CAST(f64, _0) / value);
-    });
-    _vm->bind__truediv__(VM::tp_int, [](VM* vm, PyVar _0, PyVar _1) {
-        f64 value = CAST_F(_1);
-        return VAR(_CAST(i64, _0) / value);
-    });
-
-    auto py_number_pow = [](VM* vm, PyVar _0, PyVar _1) {
-        if(is_int(_0) && is_int(_1)) {
-            i64 lhs = _CAST(i64, _0);
-            i64 rhs = _CAST(i64, _1);
-            if(rhs < 0) {
-                if(lhs == 0) vm->ZeroDivisionError("0.0 cannot be raised to a negative power");
-                return VAR((f64)std::pow(lhs, rhs));
-            }
-            i64 ret = 1;
-            while(rhs) {
-                if(rhs & 1) ret *= lhs;
-                lhs *= lhs;
-                rhs >>= 1;
-            }
-            return VAR(ret);
-        } else {
-            return VAR((f64)std::pow(CAST_F(_0), CAST_F(_1)));
-        }
-    };
-
-    _vm->bind__pow__(VM::tp_int, py_number_pow);
-    _vm->bind__pow__(VM::tp_float, py_number_pow);
-
-    _vm->bind_func(VM::tp_int, __new__, -1, [](VM* vm, ArgsView args) {
-        if(args.size() == 1 + 0) return VAR(0);
-        // 1 arg
-        if(args.size() == 1 + 1) {
-            switch(vm->_tp(args[1])) {
-                case VM::tp_float: return VAR((i64)_CAST(f64, args[1]));
-                case VM::tp_int: return args[1];
-                case VM::tp_bool: return VAR(args[1].extra ? 1 : 0);
-                case VM::tp_str: break;
-                default: vm->TypeError("invalid arguments for int()");
-            }
-        }
-        // 2+ args -> error
-        if(args.size() > 1 + 2) vm->TypeError("int() takes at most 2 arguments");
-        // 1 or 2 args with str
-        int base = 10;
-        if(args.size() == 1 + 2) base = CAST(i64, args[2]);
-        const Str& s = CAST(Str&, args[1]);
-        std::string_view sv = s.sv();
-        bool negative = false;
-        if(!sv.empty() && (sv[0] == '+' || sv[0] == '-')) {
-            negative = sv[0] == '-';
-            sv.remove_prefix(1);
-        }
-        i64 val;
-        if(parse_uint(sv, &val, base) != IntParsingResult::Success) {
-            vm->ValueError(_S("invalid literal for int() with base ", base, ": ", s.escape()));
-        }
-        if(negative) val = -val;
-        return VAR(val);
-    });
-
-    _vm->bind__floordiv__(VM::tp_int, [](VM* vm, PyVar _0, PyVar _1) {
-        i64 rhs = CAST(i64, _1);
-        if(rhs == 0) vm->ZeroDivisionError();
-        return VAR(_CAST(i64, _0) / rhs);
-    });
-
-    _vm->bind__mod__(VM::tp_int, [](VM* vm, PyVar _0, PyVar _1) {
-        i64 rhs = CAST(i64, _1);
-        if(rhs == 0) vm->ZeroDivisionError();
-        return VAR(_CAST(i64, _0) % rhs);
-    });
-
-    _vm->bind_func(VM::tp_int, "bit_length", 1, [](VM* vm, ArgsView args) {
-        i64 x = _CAST(i64, args[0]);
-        if(x < 0) x = -x;
-        int bits = 0;
-        while(x) {
-            x >>= 1;
-            bits++;
-        }
-        return VAR(bits);
-    });
-
-    _vm->bind__repr__(VM::tp_int, [](VM* vm, PyVar obj) -> Str {
-        return std::to_string(_CAST(i64, obj));
-    });
-    _vm->bind__neg__(VM::tp_int, [](VM* vm, PyVar obj) {
-        return VAR(-_CAST(i64, obj));
-    });
-    _vm->bind__hash__(VM::tp_int, [](VM* vm, PyVar obj) {
-        return _CAST(i64, obj);
-    });
-    _vm->bind__invert__(VM::tp_int, [](VM* vm, PyVar obj) {
-        return VAR(~_CAST(i64, obj));
-    });
-
-#define INT_BITWISE_OP(name, op)                                                                                       \
-    _vm->bind##name(VM::tp_int, [](VM* vm, PyVar lhs, PyVar rhs) {                                                     \
-        return VAR(_CAST(i64, lhs) op CAST(i64, rhs));                                                                 \
-    });
-
-    INT_BITWISE_OP(__lshift__, <<)
-    INT_BITWISE_OP(__rshift__, >>)
-    INT_BITWISE_OP(__and__, &)
-    INT_BITWISE_OP(__or__, |)
-    INT_BITWISE_OP(__xor__, ^)
-
-#undef INT_BITWISE_OP
-
-    _vm->bind_func(VM::tp_float, __new__, -1, [](VM* vm, ArgsView args) {
-        if(args.size() == 1 + 0) return VAR(0.0);
-        if(args.size() > 1 + 1) vm->TypeError("float() takes at most 1 argument");
-        // 1 arg
-        switch(vm->_tp(args[1])) {
-            case VM::tp_int: return VAR((f64)CAST(i64, args[1]));
-            case VM::tp_float: return args[1];
-            case VM::tp_bool: return VAR(args[1].extra ? 1.0 : 0.0);
-            case VM::tp_str: break;
-            default: vm->TypeError("invalid arguments for float()");
-        }
-        // str to float
-        const Str& s = PK_OBJ_GET(Str, args[1]);
-        if(s == "inf") return VAR(INFINITY);
-        if(s == "-inf") return VAR(-INFINITY);
-
-        double float_out;
-        char* p_end;
-        try {
-            float_out = std::strtod(s.c_str(), &p_end);
-            if(p_end != s.end()) throw 1;
-        } catch(...) { vm->ValueError("invalid literal for float(): " + s.escape()); }
-        return VAR(float_out);
-    });
-
-    _vm->bind__hash__(VM::tp_float, [](VM* vm, PyVar _0) {
-        f64 val = _CAST(f64, _0);
-        return (i64)std::hash<f64>()(val);
-    });
-
-    _vm->bind__neg__(VM::tp_float, [](VM* vm, PyVar _0) {
-        return VAR(-_CAST(f64, _0));
-    });
-
-    _vm->bind__repr__(VM::tp_float, [](VM* vm, PyVar _0) -> Str {
-        f64 val = _CAST(f64, _0);
-        SStream ss;
-        ss << val;
-        return ss.str();
-    });
-
-    // tp_str
-    _vm->bind_func(VM::tp_str, __new__, -1, [](VM* vm, ArgsView args) {
-        if(args.size() == 1) return VAR(Str());
-        if(args.size() > 2) vm->TypeError("str() takes at most 1 argument");
-        return VAR(vm->py_str(args[1]));
-    });
-
-    _vm->bind__hash__(VM::tp_str, [](VM* vm, PyVar _0) {
-        return (i64)_CAST(Str&, _0).hash();
-    });
-
-    _vm->bind__add__(VM::tp_str, [](VM* vm, PyVar _0, PyVar _1) {
-        return VAR(_CAST(Str&, _0) + CAST(Str&, _1));
-    });
-    _vm->bind__len__(VM::tp_str, [](VM* vm, PyVar _0) {
-        return (i64)_CAST(Str&, _0).u8_length();
-    });
-    _vm->bind__mul__(VM::tp_str, [](VM* vm, PyVar _0, PyVar _1) {
-        const Str& self = _CAST(Str&, _0);
-        i64 n = CAST(i64, _1);
-        SStream ss;
-        for(i64 i = 0; i < n; i++)
-            ss << self.sv();
-        return VAR(ss.str());
-    });
-    _vm->bind_func(VM::tp_str, "__rmul__", 2, [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        i64 n = CAST(i64, args[1]);
-        SStream ss;
-        for(i64 i = 0; i < n; i++)
-            ss << self.sv();
-        return VAR(ss.str());
-    });
-    _vm->bind__contains__(VM::tp_str, [](VM* vm, PyVar _0, PyVar _1) {
-        const Str& self = _CAST(Str&, _0);
-        return VAR(self.index(CAST(Str&, _1)) != -1);
-    });
-
-    _vm->bind_func(VM::tp_str, __str__, 1, [](VM* vm, ArgsView args) {
-        return args[0];
-    });
-    _vm->bind__iter__(VM::tp_str, [](VM* vm, PyVar _0) {
-        return vm->new_user_object<StringIter>(_0);
-    });
-    _vm->bind__repr__(VM::tp_str, [](VM* vm, PyVar _0) -> Str {
-        const Str& self = _CAST(Str&, _0);
-        return self.escape();
-    });
-
-#define BIND_CMP_STR(name, op)                                                                                         \
-    _vm->bind##name(VM::tp_str, [](VM* vm, PyVar lhs, PyVar rhs) {                                                     \
-        if(!is_type(rhs, vm->tp_str)) return vm->NotImplemented;                                                       \
-        return VAR(_CAST(Str&, lhs) op _CAST(Str&, rhs));                                                              \
-    });
-
-    BIND_CMP_STR(__eq__, ==)
-    BIND_CMP_STR(__lt__, <)
-    BIND_CMP_STR(__le__, <=)
-    BIND_CMP_STR(__gt__, >)
-    BIND_CMP_STR(__ge__, >=)
-#undef BIND_CMP_STR
-
-    _vm->bind__getitem__(VM::tp_str, [](VM* vm, PyVar _0, PyVar _1) {
-        const Str& self = PK_OBJ_GET(Str, _0);
-        if(is_type(_1, vm->tp_slice)) {
-            const Slice& s = _CAST(Slice&, _1);
-            int start, stop, step;
-            vm->parse_int_slice(s, self.u8_length(), start, stop, step);
-            return VAR(self.u8_slice(start, stop, step));
-        }
-        i64 i = CAST(i64, _1);
-        i = vm->normalized_index(i, self.u8_length());
-        return VAR(self.u8_getitem(i));
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "replace(self, old, new)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& old = CAST(Str&, args[1]);
-        if(old.empty()) vm->ValueError("empty substring");
-        const Str& new_ = CAST(Str&, args[2]);
-        return VAR(self.replace(old, new_));
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "split(self, sep=' ')", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& sep = CAST(Str&, args[1]);
-        if(sep.empty()) vm->ValueError("empty separator");
-        vector<std::string_view> parts;
-        if(sep.size == 1) {
-            parts = self.split(sep[0]);
-        } else {
-            parts = self.split(sep);
-        }
-        List ret(parts.size());
-        for(int i = 0; i < parts.size(); i++)
-            ret[i] = VAR(Str(parts[i]));
-        return VAR(std::move(ret));
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "splitlines(self)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        vector<std::string_view> parts = self.split('\n');
-        List ret(parts.size());
-        for(int i = 0; i < parts.size(); i++)
-            ret[i] = VAR(Str(parts[i]));
-        return VAR(std::move(ret));
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "count(self, s: str)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& s = CAST(Str&, args[1]);
-        return VAR(self.count(s));
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "index(self, value, __start=0)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& value = CAST(Str&, args[1]);
-        int start = CAST(int, args[2]);
-        if(start < 0) vm->ValueError("argument 'start' can't be negative");
-        int index = self.index(value, start);
-        if(index < 0) vm->ValueError("substring not found");
-        return VAR(index);
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "find(self, value, __start=0)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& value = CAST(Str&, args[1]);
-        int start = CAST(int, args[2]);
-        if(start < 0) vm->ValueError("argument 'start' can't be negative");
-        return VAR(self.index(value, start));
-    });
-
-    _vm->bind_func(VM::tp_str, "startswith", 2, [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& prefix = CAST(Str&, args[1]);
-        return VAR(self.index(prefix) == 0);
-    });
-
-    _vm->bind_func(VM::tp_str, "endswith", 2, [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& suffix = CAST(Str&, args[1]);
-        int offset = self.length() - suffix.length();
-        if(offset < 0) return vm->False;
-        bool ok = memcmp(self.c_str() + offset, suffix.c_str(), suffix.length()) == 0;
-        return VAR(ok);
-    });
-
-    _vm->bind_func(VM::tp_str, "encode", 1, [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        Bytes retval(self.length());
-        std::memcpy(retval.data(), self.c_str(), self.length());
-        return VAR(std::move(retval));
-    });
-
-    _vm->bind_func(VM::tp_str, "join", 2, [](VM* vm, ArgsView args) {
-        auto _lock = vm->gc_scope_lock();
-        const Str& self = _CAST(Str&, args[0]);
-        SStream ss;
-        PyVar it = vm->py_iter(args[1]);  // strong ref
-        const PyTypeInfo* info = vm->_tp_info(args[1]);
-        PyVar obj = vm->_py_next(info, it);
-        while(obj != vm->StopIteration) {
-            if(!ss.empty()) ss << self;
-            ss << CAST(Str&, obj);
-            obj = vm->_py_next(info, it);
-        }
-        return VAR(ss.str());
-    });
-
-    _vm->bind_func(VM::tp_str, "lower", 1, [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        return VAR(self.lower());
-    });
-
-    _vm->bind_func(VM::tp_str, "upper", 1, [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        return VAR(self.upper());
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "strip(self, chars=None)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        if(is_none(args[1])) {
-            return VAR(self.strip());
-        } else {
-            const Str& chars = CAST(Str&, args[1]);
-            return VAR(self.strip(true, true, chars));
-        }
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "lstrip(self, chars=None)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        if(is_none(args[1])) {
-            return VAR(self.lstrip());
-        } else {
-            const Str& chars = CAST(Str&, args[1]);
-            return VAR(self.strip(true, false, chars));
-        }
-    });
-
-    _vm->bind(_vm->_t(VM::tp_str), "rstrip(self, chars=None)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        if(is_none(args[1])) {
-            return VAR(self.rstrip());
-        } else {
-            const Str& chars = CAST(Str&, args[1]);
-            return VAR(self.strip(false, true, chars));
-        }
-    });
-
-    // zfill
-    _vm->bind(_vm->_t(VM::tp_str), "zfill(self, width)", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        int width = CAST(int, args[1]);
-        int delta = width - self.u8_length();
-        if(delta <= 0) return args[0];
-        SStream ss;
-        for(int i = 0; i < delta; i++)
-            ss << '0';
-        ss << self;
-        return VAR(ss.str());
-    });
-
-    // ljust
-    _vm->bind(_vm->_t(VM::tp_str), "ljust(self, width, fillchar=' ')", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        int width = CAST(int, args[1]);
-        int delta = width - self.u8_length();
-        if(delta <= 0) return args[0];
-        const Str& fillchar = CAST(Str&, args[2]);
-        if(fillchar.u8_length() != 1) vm->TypeError("The fill character must be exactly one character long");
-        SStream ss;
-        ss << self;
-        for(int i = 0; i < delta; i++)
-            ss << fillchar;
-        return VAR(ss.str());
-    });
-
-    // rjust
-    _vm->bind(_vm->_t(VM::tp_str), "rjust(self, width, fillchar=' ')", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        int width = CAST(int, args[1]);
-        int delta = width - self.u8_length();
-        if(delta <= 0) return args[0];
-        const Str& fillchar = CAST(Str&, args[2]);
-        if(fillchar.u8_length() != 1) vm->TypeError("The fill character must be exactly one character long");
-        SStream ss;
-        for(int i = 0; i < delta; i++)
-            ss << fillchar;
-        ss << self;
-        return VAR(ss.str());
-    });
-
-    // tp_list / tp_tuple
-    _vm->bind(_vm->_t(VM::tp_list), "sort(self, key=None, reverse=False)", [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        PyVar key = args[1];
-        if(is_none(key)) {
-            std::stable_sort(self.begin(), self.end(), [vm](PyVar a, PyVar b) {
-                return vm->py_lt(a, b);
-            });
-        } else {
-            std::stable_sort(self.begin(), self.end(), [vm, key](PyVar a, PyVar b) {
-                return vm->py_lt(vm->call(key, a), vm->call(key, b));
-            });
-        }
-        bool reverse = CAST(bool, args[2]);
-        if(reverse) { std::reverse(self.begin(), self.end()); }
-        return vm->None;
-    });
-
-    _vm->bind__repr__(VM::tp_list, [](VM* vm, PyVar _0) -> Str {
-        if(vm->_repr_recursion_set.contains(_0.get())) return "[...]";
-        List& iterable = _CAST(List&, _0);
-        SStream ss;
-        ss << '[';
-        vm->_repr_recursion_set.push_back(_0.get());
-        for(int i = 0; i < iterable.size(); i++) {
-            ss << vm->py_repr(iterable[i]);
-            if(i != iterable.size() - 1) ss << ", ";
-        }
-        vm->_repr_recursion_set.pop_back();
-        ss << ']';
-        return ss.str();
-    });
-
-    _vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyVar _0) -> Str {
-        Tuple& iterable = _CAST(Tuple&, _0);
-        SStream ss;
-        ss << '(';
-        if(iterable.size() == 1) {
-            ss << vm->py_repr(iterable[0]);
-            ss << ',';
-        } else {
-            for(int i = 0; i < iterable.size(); i++) {
-                ss << vm->py_repr(iterable[i]);
-                if(i != iterable.size() - 1) ss << ", ";
-            }
-        }
-        ss << ')';
-        return ss.str();
-    });
-
-    _vm->bind_func(VM::tp_list, __new__, -1, [](VM* vm, ArgsView args) {
-        if(args.size() == 1 + 0) return VAR(List());
-        if(args.size() == 1 + 1) return VAR(vm->py_list(args[1]));
-        vm->TypeError("list() takes 0 or 1 arguments");
-        return vm->None;
-    });
-
-    _vm->bind__contains__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) {
-        List& self = _CAST(List&, _0);
-        for(PyVar i: self)
-            if(vm->py_eq(i, _1)) return vm->True;
-        return vm->False;
-    });
-
-    _vm->bind_func(VM::tp_list, "count", 2, [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        int count = 0;
-        for(PyVar i: self)
-            if(vm->py_eq(i, args[1])) count++;
-        return VAR(count);
-    });
-
-    _vm->bind__eq__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) {
-        List& a = _CAST(List&, _0);
-        if(!is_type(_1, vm->tp_list)) return vm->NotImplemented;
-        List& b = _CAST(List&, _1);
-        if(a.size() != b.size()) return vm->False;
-        for(int i = 0; i < a.size(); i++) {
-            if(!vm->py_eq(a[i], b[i])) return vm->False;
-        }
-        return vm->True;
-    });
-
-    _vm->bind(_vm->_t(VM::tp_list), "index(self, value, __start=0)", [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        PyVar obj = args[1];
-        int start = CAST(int, args[2]);
-        for(int i = start; i < self.size(); i++) {
-            if(vm->py_eq(self[i], obj)) return VAR(i);
-        }
-        vm->ValueError(vm->py_repr(obj) + " is not in list");
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_list, "remove", 2, [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        PyVar obj = args[1];
-        for(PyVar* it = self.begin(); it != self.end(); it++) {
-            if(vm->py_eq(*it, obj)) {
-                self.erase(it);
-                return vm->None;
-            }
-        }
-        vm->ValueError(vm->py_repr(obj) + " is not in list");
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_list, "pop", -1, [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        if(args.size() == 1 + 0) {
-            if(self.empty()) vm->IndexError("pop from empty list");
-            PyVar retval = self.back();
-            self.pop_back();
-            return retval;
-        }
-        if(args.size() == 1 + 1) {
-            i64 index = CAST(i64, args[1]);
-            index = vm->normalized_index(index, self.size());
-            PyVar ret = self[index];
-            self.erase(self.begin() + index);
-            return ret;
-        }
-        vm->TypeError("pop() takes at most 1 argument");
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_list, "append", 2, [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        self.push_back(args[1]);
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_list, "extend", 2, [](VM* vm, ArgsView args) {
-        auto _lock = vm->gc_scope_lock();
-        List& self = _CAST(List&, args[0]);
-        PyVar it = vm->py_iter(args[1]);  // strong ref
-        const PyTypeInfo* info = vm->_tp_info(args[1]);
-        PyVar obj = vm->_py_next(info, it);
-        while(obj != vm->StopIteration) {
-            self.push_back(obj);
-            obj = vm->_py_next(info, it);
-        }
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_list, "reverse", 1, [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        std::reverse(self.begin(), self.end());
-        return vm->None;
-    });
-
-    _vm->bind__mul__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) {
-        const List& self = _CAST(List&, _0);
-        if(!is_int(_1)) return vm->NotImplemented;
-        int n = _CAST(int, _1);
-        List result;
-        result.reserve(self.size() * n);
-        for(int i = 0; i < n; i++)
-            result.extend(self.begin(), self.end());
-        return VAR(std::move(result));
-    });
-    _vm->bind_func(VM::tp_list, "__rmul__", 2, [](VM* vm, ArgsView args) {
-        const List& self = _CAST(List&, args[0]);
-        if(!is_int(args[1])) return vm->NotImplemented;
-        int n = _CAST(int, args[1]);
-        List result;
-        result.reserve(self.size() * n);
-        for(int i = 0; i < n; i++)
-            result.extend(self.begin(), self.end());
-        return VAR(std::move(result));
-    });
-
-    _vm->bind_func(VM::tp_list, "insert", 3, [](VM* vm, ArgsView args) {
-        List& self = _CAST(List&, args[0]);
-        int index = CAST(int, args[1]);
-        if(index < 0) index += self.size();
-        if(index < 0) index = 0;
-        if(index > self.size()) index = self.size();
-        self.insert(self.begin() + index, args[2]);
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_list, "clear", 1, [](VM* vm, ArgsView args) {
-        _CAST(List&, args[0]).clear();
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_list, "copy", 1, [](VM* vm, ArgsView args) {
-        const List& self = _CAST(List&, args[0]);
-        return VAR(List(explicit_copy_t(), self));
-    });
-
-#define BIND_RICH_CMP(name, op, _t, _T)                                                                                \
-    _vm->bind__##name##__(_vm->_t, [](VM* vm, PyVar lhs, PyVar rhs) {                                                  \
-        if(!is_type(rhs, vm->_t)) return vm->NotImplemented;                                                           \
-        auto& a = _CAST(_T&, lhs);                                                                                     \
-        auto& b = _CAST(_T&, rhs);                                                                                     \
-        for(int i = 0; i < a.size() && i < b.size(); i++) {                                                            \
-            if(vm->py_eq(a[i], b[i])) continue;                                                                        \
-            return VAR(vm->py_##name(a[i], b[i]));                                                                     \
-        }                                                                                                              \
-        return VAR(a.size() op b.size());                                                                              \
-    });
-
-    BIND_RICH_CMP(lt, <, tp_list, List)
-    BIND_RICH_CMP(le, <=, tp_list, List)
-    BIND_RICH_CMP(gt, >, tp_list, List)
-    BIND_RICH_CMP(ge, >=, tp_list, List)
-
-    BIND_RICH_CMP(lt, <, tp_tuple, Tuple)
-    BIND_RICH_CMP(le, <=, tp_tuple, Tuple)
-    BIND_RICH_CMP(gt, >, tp_tuple, Tuple)
-    BIND_RICH_CMP(ge, >=, tp_tuple, Tuple)
-
-#undef BIND_RICH_CMP
-
-    _vm->bind__add__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) {
-        const List& self = _CAST(List&, _0);
-        const List& other = CAST(List&, _1);
-        List new_list(explicit_copy_t(), self);
-        new_list.extend(other.begin(), other.end());
-        return VAR(std::move(new_list));
-    });
-
-    _vm->bind__len__(VM::tp_list, [](VM* vm, PyVar _0) {
-        return (i64)_CAST(List&, _0).size();
-    });
-    _vm->bind__iter__(VM::tp_list, [](VM* vm, PyVar _0) {
-        List& self = _CAST(List&, _0);
-        return vm->new_user_object<ArrayIter>(_0.get(), self.begin(), self.end());
-    });
-
-    _vm->bind__getitem__(VM::tp_list, PyArrayGetItem<List>);
-    _vm->bind__setitem__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1, PyVar _2) {
-        List& self = _CAST(List&, _0);
-        i64 i = CAST(i64, _1);
-        i = vm->normalized_index(i, self.size());
-        self[i] = _2;
-    });
-    _vm->bind__delitem__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) {
-        List& self = _CAST(List&, _0);
-        i64 i = CAST(i64, _1);
-        i = vm->normalized_index(i, self.size());
-        self.erase(self.begin() + i);
-    });
-
-    _vm->bind_func(VM::tp_tuple, __new__, -1, [](VM* vm, ArgsView args) {
-        if(args.size() == 1 + 0) return VAR(Tuple(0));
-        if(args.size() == 1 + 1) {
-            List list = vm->py_list(args[1]);
-            return VAR(list.to_tuple());
-        }
-        vm->TypeError("tuple() takes at most 1 argument");
-        return vm->None;
-    });
-
-    _vm->bind__contains__(VM::tp_tuple, [](VM* vm, PyVar obj, PyVar item) {
-        Tuple& self = _CAST(Tuple&, obj);
-        for(PyVar i: self)
-            if(vm->py_eq(i, item)) return vm->True;
-        return vm->False;
-    });
-
-    _vm->bind_func(VM::tp_tuple, "count", 2, [](VM* vm, ArgsView args) {
-        Tuple& self = _CAST(Tuple&, args[0]);
-        int count = 0;
-        for(PyVar i: self)
-            if(vm->py_eq(i, args[1])) count++;
-        return VAR(count);
-    });
-
-    _vm->bind__eq__(VM::tp_tuple, [](VM* vm, PyVar _0, PyVar _1) {
-        const Tuple& self = _CAST(Tuple&, _0);
-        if(!is_type(_1, vm->tp_tuple)) return vm->NotImplemented;
-        const Tuple& other = _CAST(Tuple&, _1);
-        if(self.size() != other.size()) return vm->False;
-        for(int i = 0; i < self.size(); i++) {
-            if(!vm->py_eq(self[i], other[i])) return vm->False;
-        }
-        return vm->True;
-    });
-
-    _vm->bind__hash__(VM::tp_tuple, [](VM* vm, PyVar _0) {
-        i64 x = 1000003;
-        for(PyVar item: _CAST(Tuple&, _0)) {
-            i64 y = vm->py_hash(item);
-            // recommended by Github Copilot
-            x = x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2));
-        }
-        return x;
-    });
-
-    _vm->bind__iter__(VM::tp_tuple, [](VM* vm, PyVar _0) {
-        Tuple& self = _CAST(Tuple&, _0);
-        return vm->new_user_object<ArrayIter>(_0.get(), self.begin(), self.end());
-    });
-
-    _vm->bind__getitem__(VM::tp_tuple, PyArrayGetItem<Tuple>);
-    _vm->bind__len__(VM::tp_tuple, [](VM* vm, PyVar obj) {
-        return (i64)_CAST(Tuple&, obj).size();
-    });
-
-    // tp_bool
-    _vm->bind_func(VM::tp_bool, __new__, 2, PK_LAMBDA(VAR(vm->py_bool(args[1]))));
-    _vm->bind__hash__(VM::tp_bool, [](VM* vm, PyVar _0) {
-        return (i64)_CAST(bool, _0);
-    });
-    _vm->bind__repr__(VM::tp_bool, [](VM* vm, PyVar _0) -> Str {
-        bool val = _CAST(bool, _0);
-        return val ? "True" : "False";
-    });
-
-    _vm->bind__and__(VM::tp_bool, [](VM* vm, PyVar _0, PyVar _1) {
-        return VAR(_CAST(bool, _0) && CAST(bool, _1));
-    });
-    _vm->bind__or__(VM::tp_bool, [](VM* vm, PyVar _0, PyVar _1) {
-        return VAR(_CAST(bool, _0) || CAST(bool, _1));
-    });
-    _vm->bind__xor__(VM::tp_bool, [](VM* vm, PyVar _0, PyVar _1) {
-        return VAR(_CAST(bool, _0) != CAST(bool, _1));
-    });
-    _vm->bind__eq__(VM::tp_bool, [](VM* vm, PyVar _0, PyVar _1) {
-        if(is_type(_1, vm->tp_bool)) return VAR(_0.extra == _1.extra);
-        if(is_int(_1)) return VAR(_CAST(bool, _0) == (bool)CAST(i64, _1));
-        return vm->NotImplemented;
-    });
-
-    // tp_ellipsis / tp_NotImplementedType
-    _vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyVar _0) -> Str {
-        return "...";
-    });
-    _vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyVar _0) -> Str {
-        return "NotImplemented";
-    });
-
-    // tp_bytes
-    _vm->bind_func(VM::tp_bytes, __new__, 2, [](VM* vm, ArgsView args) {
-        List& list = CAST(List&, args[1]);
-        Bytes retval(list.size());
-        for(int i = 0; i < list.size(); i++) {
-            i64 b = CAST(i64, list[i]);
-            if(b < 0 || b > 255) vm->ValueError("byte must be in range[0, 256)");
-            retval[i] = (char)b;
-        }
-        return VAR(std::move(retval));
-    });
-
-    _vm->bind__getitem__(VM::tp_bytes, [](VM* vm, PyVar _0, PyVar _1) {
-        const Bytes& self = PK_OBJ_GET(Bytes, _0);
-        if(is_type(_1, vm->tp_slice)) {
-            const Slice& s = _CAST(Slice&, _1);
-            int start, stop, step;
-            vm->parse_int_slice(s, self.size(), start, stop, step);
-            int guess_max_size = abs(stop - start) / abs(step) + 1;
-            if(guess_max_size > self.size()) guess_max_size = self.size();
-            unsigned char* buffer = (unsigned char*)std::malloc(guess_max_size);
-            int j = 0;  // actual size
-            PK_SLICE_LOOP(i, start, stop, step) buffer[j++] = self[i];
-            return VAR(Bytes(buffer, j));
-        }
-        i64 i = CAST(i64, _1);
-        i = vm->normalized_index(i, self.size());
-        return VAR(self[i]);
-    });
-
-    _vm->bind__add__(VM::tp_bytes, [](VM* vm, PyVar _0, PyVar _1) {
-        const Bytes& a = _CAST(Bytes&, _0);
-        const Bytes& b = CAST(Bytes&, _1);
-        Bytes retval(a.size() + b.size());
-        std::memcpy(retval.data(), a.data(), a.size());
-        std::memcpy(retval.data() + a.size(), b.data(), b.size());
-        return VAR(std::move(retval));
-    });
-
-    _vm->bind__hash__(VM::tp_bytes, [](VM* vm, PyVar _0) {
-        const Bytes& self = _CAST(Bytes&, _0);
-        std::string_view view((char*)self.data(), self.size());
-        return (i64)std::hash<std::string_view>()(view);
-    });
-
-    _vm->bind__repr__(VM::tp_bytes, [](VM* vm, PyVar _0) -> Str {
-        const Bytes& self = _CAST(Bytes&, _0);
-        SStream ss;
-        ss << "b'";
-        for(int i = 0; i < self.size(); i++) {
-            ss << "\\x";
-            ss.write_hex((unsigned char)self[i]);
-        }
-        ss << "'";
-        return ss.str();
-    });
-    _vm->bind__len__(VM::tp_bytes, [](VM* vm, PyVar _0) {
-        return (i64)_CAST(Bytes&, _0).size();
-    });
-
-    _vm->bind_func(VM::tp_bytes, "decode", 1, [](VM* vm, ArgsView args) {
-        const Bytes& self = _CAST(Bytes&, args[0]);
-        return VAR(Str(std::string_view((char*)self.data(), self.size())));
-    });
-
-    _vm->bind__eq__(VM::tp_bytes, [](VM* vm, PyVar _0, PyVar _1) {
-        if(!is_type(_1, vm->tp_bytes)) return vm->NotImplemented;
-        const Bytes& lhs = _CAST(Bytes&, _0);
-        const Bytes& rhs = _CAST(Bytes&, _1);
-        if(lhs.size() != rhs.size()) return vm->False;
-        return VAR(memcmp(lhs.data(), rhs.data(), lhs.size()) == 0);
-    });
-
-    // tp_slice
-    _vm->bind_func(VM::tp_slice, __new__, 4, [](VM* vm, ArgsView args) {
-        return VAR(Slice(args[1], args[2], args[3]));
-    });
-
-    _vm->bind__eq__(VM::tp_slice, [](VM* vm, PyVar _0, PyVar _1) {
-        const Slice& self = _CAST(Slice&, _0);
-        if(!is_type(_1, vm->tp_slice)) return vm->NotImplemented;
-        const Slice& other = _CAST(Slice&, _1);
-        if(vm->py_ne(self.start, other.start)) return vm->False;
-        if(vm->py_ne(self.stop, other.stop)) return vm->False;
-        if(vm->py_ne(self.step, other.step)) return vm->False;
-        return vm->True;
-    });
-
-    _vm->bind__repr__(VM::tp_slice, [](VM* vm, PyVar _0) -> Str {
-        const Slice& self = _CAST(Slice&, _0);
-        SStream ss;
-        ss << "slice(";
-        ss << vm->py_repr(self.start) << ", ";
-        ss << vm->py_repr(self.stop) << ", ";
-        ss << vm->py_repr(self.step) << ")";
-        return ss.str();
-    });
-
-    // tp_mappingproxy
-    _vm->bind_func(VM::tp_mappingproxy, "keys", 1, [](VM* vm, ArgsView args) {
-        MappingProxy& self = _CAST(MappingProxy&, args[0]);
-        List keys;
-        for(StrName name: self.attr().keys())
-            keys.push_back(VAR(name.sv()));
-        return VAR(std::move(keys));
-    });
-
-    _vm->bind_func(VM::tp_mappingproxy, "values", 1, [](VM* vm, ArgsView args) {
-        MappingProxy& self = _CAST(MappingProxy&, args[0]);
-        List values;
-        for(auto [k, v]: self.attr().items())
-            values.push_back(v);
-        return VAR(std::move(values));
-    });
-
-    _vm->bind_func(VM::tp_mappingproxy, "items", 1, [](VM* vm, ArgsView args) {
-        MappingProxy& self = _CAST(MappingProxy&, args[0]);
-        List items;
-        for(auto [k, v]: self.attr().items()) {
-            PyVar t = VAR(Tuple(VAR(k.sv()), v));
-            items.push_back(std::move(t));
-        }
-        return VAR(std::move(items));
-    });
-
-    _vm->bind__len__(VM::tp_mappingproxy, [](VM* vm, PyVar _0) {
-        return (i64)_CAST(MappingProxy&, _0).attr().size();
-    });
-
-    _vm->bind__eq__(VM::tp_mappingproxy, [](VM* vm, PyVar _0, PyVar _1) {
-        const MappingProxy& a = _CAST(MappingProxy&, _0);
-        if(!is_type(_1, VM::tp_mappingproxy)) return vm->NotImplemented;
-        const MappingProxy& b = _CAST(MappingProxy&, _1);
-        return VAR(a.obj == b.obj);
-    });
-
-    _vm->bind__getitem__(VM::tp_mappingproxy, [](VM* vm, PyVar _0, PyVar _1) {
-        MappingProxy& self = _CAST(MappingProxy&, _0);
-        StrName key = CAST(Str&, _1);
-        PyVar ret = self.attr().try_get_likely_found(key);
-        if(ret == nullptr) vm->KeyError(_1);
-        return ret;
-    });
-
-    _vm->bind(_vm->_t(VM::tp_mappingproxy), "get(self, key, default=None)", [](VM* vm, ArgsView args) {
-        MappingProxy& self = _CAST(MappingProxy&, args[0]);
-        StrName key = CAST(Str&, args[1]);
-        PyVar ret = self.attr().try_get(key);
-        if(ret == nullptr) return args[2];
-        return ret;
-    });
-
-    _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyVar _0) -> Str {
-        if(vm->_repr_recursion_set.contains(_0.get())) return "{...}";
-        MappingProxy& self = _CAST(MappingProxy&, _0);
-        SStream ss;
-        ss << "mappingproxy({";
-        bool first = true;
-        vm->_repr_recursion_set.push_back(_0.get());
-        for(auto [k, v]: self.attr().items()) {
-            if(!first) ss << ", ";
-            first = false;
-            ss << k.escape() << ": ";
-            ss << vm->py_repr(v);
-        }
-        vm->_repr_recursion_set.pop_back();
-        ss << "})";
-        return ss.str();
-    });
-
-    _vm->bind__contains__(VM::tp_mappingproxy, [](VM* vm, PyVar _0, PyVar _1) {
-        MappingProxy& self = _CAST(MappingProxy&, _0);
-        return VAR(self.attr().contains(CAST(Str&, _1)));
-    });
-
-    // tp_dict
-    _vm->bind_func(VM::tp_dict, __new__, -1, [](VM* vm, ArgsView args)->PyVar {
-        Type cls_t = PK_OBJ_GET(Type, args[0]);
-        return vm->new_object<Dict>(cls_t);
-    });
-
-    _vm->bind_func(VM::tp_dict, __init__, -1, [](VM* vm, ArgsView args) {
-        if(args.size() == 1 + 0) return vm->None;
-        if(args.size() == 1 + 1) {
-            auto _lock = vm->gc_scope_lock();
-            Dict& self = PK_OBJ_GET(Dict, args[0]);
-            if(is_type(args[1], vm->tp_dict)) {
-                Dict& other = CAST(Dict&, args[1]);
-                self.update(vm, other);
-                return vm->None;
-            }
-            if(is_type(args[1], vm->tp_list)) {
-                List& list = PK_OBJ_GET(List, args[1]);
-                for(PyVar item: list) {
-                    Tuple& t = CAST(Tuple&, item);
-                    if(t.size() != 2) {
-                        vm->ValueError("dict() takes a list of tuples (key, value)");
-                        return vm->None;
-                    }
-                    self.set(vm, t[0], t[1]);
-                }
-                return vm->None;
-            }
-            vm->TypeError("dict() takes a dictionary or a list of tuples");
-        }
-        vm->TypeError("dict() takes at most 1 argument");
-    });
-
-    _vm->bind__len__(VM::tp_dict, [](VM* vm, PyVar _0) {
-        return (i64)PK_OBJ_GET(Dict, _0).size();
-    });
-
-    _vm->bind__getitem__(VM::tp_dict, [](VM* vm, PyVar _0, PyVar _1) {
-        Dict& self = PK_OBJ_GET(Dict, _0);
-        PyVar ret = self.try_get(vm, _1);
-        if(ret == nullptr) {
-            // try __missing__
-            PyVar self;
-            PyVar f_missing = vm->get_unbound_method(_0, __missing__, &self, false);
-            if(f_missing != nullptr) { return vm->call_method(self, f_missing, _1); }
-            vm->KeyError(_1);
-        }
-        return ret;
-    });
-
-    _vm->bind__setitem__(VM::tp_dict, [](VM* vm, PyVar _0, PyVar _1, PyVar _2) {
-        Dict& self = _CAST(Dict&, _0);
-        self.set(vm, _1, _2);
-    });
-
-    _vm->bind__delitem__(VM::tp_dict, [](VM* vm, PyVar _0, PyVar _1) {
-        Dict& self = _CAST(Dict&, _0);
-        bool ok = self.del(vm, _1);
-        if(!ok) vm->KeyError(_1);
-    });
-
-    _vm->bind_func(VM::tp_dict, "pop", -1, [](VM* vm, ArgsView args) {
-        if(args.size() != 2 && args.size() != 3) {
-            vm->TypeError("pop() expected 1 or 2 arguments");
-            return vm->None;
-        }
-        Dict& self = _CAST(Dict&, args[0]);
-        PyVar value = self.try_get(vm, args[1]);
-        if(value == nullptr) {
-            if(args.size() == 2) vm->KeyError(args[1]);
-            if(args.size() == 3) { return args[2]; }
-        }
-        self.del(vm, args[1]);
-        return value;
-    });
-
-    _vm->bind__contains__(VM::tp_dict, [](VM* vm, PyVar _0, PyVar _1) {
-        Dict& self = _CAST(Dict&, _0);
-        return VAR(self.contains(vm, _1));
-    });
-
-    _vm->bind__iter__(VM::tp_dict, [](VM* vm, PyVar _0) {
-        const Dict& self = _CAST(Dict&, _0);
-        return vm->py_iter(VAR(self.keys()));
-    });
-
-    _vm->bind_func(VM::tp_dict, "get", -1, [](VM* vm, ArgsView args) {
-        Dict& self = _CAST(Dict&, args[0]);
-        if(args.size() == 1 + 1) {
-            PyVar ret = self.try_get(vm, args[1]);
-            if(ret != nullptr) return ret;
-            return vm->None;
-        } else if(args.size() == 1 + 2) {
-            PyVar ret = self.try_get(vm, args[1]);
-            if(ret != nullptr) return ret;
-            return args[2];
-        }
-        vm->TypeError("get() takes at most 2 arguments");
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_dict, "keys", 1, [](VM* vm, ArgsView args) {
-        const Dict& self = _CAST(Dict&, args[0]);
-        return VAR(self.keys());
-    });
-
-    _vm->bind_func(VM::tp_dict, "values", 1, [](VM* vm, ArgsView args) {
-        const Dict& self = _CAST(Dict&, args[0]);
-        return VAR(self.values());
-    });
-
-    _vm->bind_func(VM::tp_dict, "items", 1, [](VM* vm, ArgsView args) {
-        return vm->new_user_object<DictItemsIter>(args[0]);
-    });
-
-    _vm->bind_func(VM::tp_dict, "update", 2, [](VM* vm, ArgsView args) {
-        Dict& self = _CAST(Dict&, args[0]);
-        const Dict& other = CAST(Dict&, args[1]);
-        self.update(vm, other);
-        return vm->None;
-    });
-
-    _vm->bind_func(VM::tp_dict, "copy", 1, [](VM* vm, ArgsView args) {
-        const Dict& self = _CAST(Dict&, args[0]);
-        return VAR(self);
-    });
-
-    _vm->bind_func(VM::tp_dict, "clear", 1, [](VM* vm, ArgsView args) {
-        Dict& self = _CAST(Dict&, args[0]);
-        self.clear();
-        return vm->None;
-    });
-
-    _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyVar _0) -> Str {
-        if(vm->_repr_recursion_set.contains(_0.get())) return "{...}";
-        Dict& self = _CAST(Dict&, _0);
-        SStream ss;
-        ss << "{";
-        bool first = true;
-        vm->_repr_recursion_set.push_back(_0.get());
-        self.apply([&](PyVar k, PyVar v) {
-            if(!first) ss << ", ";
-            first = false;
-            ss << vm->py_repr(k) << ": " << vm->py_repr(v);
-        });
-        vm->_repr_recursion_set.pop_back();
-        ss << "}";
-        return ss.str();
-    });
-
-    _vm->bind__eq__(VM::tp_dict, [](VM* vm, PyVar _0, PyVar _1) {
-        Dict& self = _CAST(Dict&, _0);
-        if(!vm->isinstance(_1, vm->tp_dict)) return vm->NotImplemented;
-        Dict& other = _CAST(Dict&, _1);
-        if(self.size() != other.size()) return vm->False;
-        pkpy_DictIter it = self.iter();
-        PyVar key, val;
-        while(pkpy_DictIter__next(&it, (PyVar*)(&key), (PyVar*)(&val))) {
-            PyVar other_val = other.try_get(vm, key);
-            if(other_val == nullptr) return vm->False;
-            if(!vm->py_eq(val, other_val)) return vm->False;
-        }
-        return vm->True;
-    });
-
-    _vm->bind__repr__(VM::tp_module, [](VM* vm, PyVar _0) -> Str {
-        const Str& path = CAST(Str&, _0->attr(__path__));
-        return _S("<module ", path.escape(), ">");
-    });
-
-    // tp_property
-    _vm->bind_func(VM::tp_property, __new__, -1, [](VM* vm, ArgsView args) {
-        if(args.size() == 1 + 1) {
-            return VAR(Property(args[1], vm->None));
-        } else if(args.size() == 1 + 2) {
-            return VAR(Property(args[1], args[2]));
-        }
-        vm->TypeError("property() takes at most 2 arguments");
-        return vm->None;
-    });
-
-    _vm->bind_property(_vm->_t(VM::tp_function), "__doc__", [](VM* vm, ArgsView args) {
-        Function& func = _CAST(Function&, args[0]);
-        if(!func.decl->docstring) return vm->None;
-        return VAR(func.decl->docstring);
-    });
-
-    _vm->bind_property(_vm->_t(VM::tp_native_func), "__doc__", [](VM* vm, ArgsView args) {
-        NativeFunc& func = _CAST(NativeFunc&, args[0]);
-        if(func.decl == nullptr) return vm->None;
-        if(!func.decl->docstring) return vm->None;
-        return VAR(func.decl->docstring);
-    });
-
-    // tp_exception
-    _vm->bind_func(VM::tp_exception, __new__, -1, [](VM* vm, ArgsView args) -> PyVar {
-        Type cls = PK_OBJ_GET(Type, args[0]);
-        StrName cls_name = _type_name(vm, cls);
-        PyObject* e_obj = vm->new_object<Exception>(cls, cls_name.index).get();
-        e_obj->_attr = new NameDict();
-        e_obj->as<Exception>().self = e_obj;
-        return e_obj;
-    });
-
-    _vm->bind(_vm->_t(VM::tp_exception), "__init__(self, msg=...)", [](VM* vm, ArgsView args) {
-        Exception& self = _CAST(Exception&, args[0]);
-        if(args[1].type != tp_ellipsis) {
-            const char* msg = CAST(Str&, args[1]).c_str();
-            pkpy_Str__dtor(&self.msg);
-            pkpy_Str__ctor(&self.msg, msg);
-        }
-        return vm->None;
-    });
-
-    _vm->bind__repr__(VM::tp_exception, [](VM* vm, PyVar _0) -> Str {
-        Exception& self = _CAST(Exception&, _0);
-        const char* msg_s = pkpy_Str__data(&self.msg);
-        return _S(_type_name(vm, _0.type), '(', Str(msg_s).escape(), ')');
-    });
-
-    _vm->bind__str__(VM::tp_exception, [](VM* vm, PyVar _0) -> Str {
-        Exception& self = _CAST(Exception&, _0);
-        return pkpy_Str__copy(&self.msg);
-    });
-
-    _vm->register_user_class<RangeIter>(_vm->builtins, "_range_iter");
-    _vm->register_user_class<RangeIterR>(_vm->builtins, "_range_iter_r");
-    _vm->register_user_class<ArrayIter>(_vm->builtins, "_array_iter");
-    _vm->register_user_class<StringIter>(_vm->builtins, "_string_iter");
-    _vm->register_user_class<Generator>(_vm->builtins, "generator");
-    _vm->register_user_class<DictItemsIter>(_vm->builtins, "_dict_items_iter");
-}
-
-void VM::__post_init_builtin_types() {
-    __init_builtins(this);
-
-    bind_func(tp_module, __new__, -1, PK_ACTION(vm->NotImplementedError()));
-
-    _all_types[tp_module].m__getattr__ = [](VM* vm, PyVar obj, StrName name) -> PyVar {
-        const Str& path = CAST(Str&, obj->attr(__path__));
-        PyObject* retval = vm->py_import(_S(path, ".", name.sv()), false);
-        if(retval) return retval;
-        return nullptr;
-    };
-
-    bind_func(tp_property, "setter", 2, [](VM* vm, ArgsView args) {
-        Property& self = _CAST(Property&, args[0]);
-        // The setter's name is not necessary to be the same as the property's name
-        // However, for cpython compatibility, we recommend to use the same name
-        self.setter = args[1];
-        return args[0];
-    });
-
-    // type
-    bind__getitem__(tp_type, [](VM* vm, PyVar self, PyVar _) {
-        return self;  // for generics
-    });
-
-    bind__repr__(tp_type, [](VM* vm, PyVar self) -> Str {
-        SStream ss;
-        const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, self)];
-        ss << "<class '" << info.name << "'>";
-        return ss.str();
-    });
-
-    bind_property(_t(tp_object), "__class__", PK_LAMBDA(vm->_t(args[0])));
-    bind_property(_t(tp_type), "__base__", [](VM* vm, ArgsView args) {
-        const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])];
-        return info.base ? vm->_all_types[info.base].obj : vm->None;
-    });
-    bind_property(_t(tp_type), "__name__", [](VM* vm, ArgsView args) {
-        const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])];
-        return VAR(info.name.sv());
-    });
-    bind_property(_t(tp_type), "__module__", [](VM* vm, ArgsView args) -> PyVar {
-        const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])];
-        if(info.mod == nullptr) return vm->None;
-        return info.mod;
-    });
-    bind_property(_t(tp_bound_method), "__self__", [](VM* vm, ArgsView args) {
-        return CAST(BoundMethod&, args[0]).self;
-    });
-    bind_property(_t(tp_bound_method), "__func__", [](VM* vm, ArgsView args) {
-        return CAST(BoundMethod&, args[0]).func;
-    });
-
-    bind__eq__(tp_bound_method, [](VM* vm, PyVar lhs, PyVar rhs) {
-        if(!is_type(rhs, vm->tp_bound_method)) return vm->NotImplemented;
-        const BoundMethod& _0 = PK_OBJ_GET(BoundMethod, lhs);
-        const BoundMethod& _1 = PK_OBJ_GET(BoundMethod, rhs);
-        return VAR(PyVar__IS_OP(&_0.self, &_1.self) && PyVar__IS_OP(&_0.func, &_1.func));
-    });
-
-    bind_property(_t(tp_slice), "start", [](VM* vm, ArgsView args) {
-        return CAST(Slice&, args[0]).start;
-    });
-    bind_property(_t(tp_slice), "stop", [](VM* vm, ArgsView args) {
-        return CAST(Slice&, args[0]).stop;
-    });
-    bind_property(_t(tp_slice), "step", [](VM* vm, ArgsView args) {
-        return CAST(Slice&, args[0]).step;
-    });
-
-    bind_property(_t(tp_object), "__dict__", [](VM* vm, ArgsView args) {
-        if(is_tagged(args[0]) || !args[0]->is_attr_valid()) return vm->None;
-        return VAR(MappingProxy(args[0].get()));
-    });
-
-    bind(builtins, "print(*args, sep=' ', end='\\n')", [](VM* vm, ArgsView args) {
-        const Tuple& _0 = CAST(Tuple&, args[0]);
-        const Str& _1 = CAST(Str&, args[1]);
-        const Str& _2 = CAST(Str&, args[2]);
-        SStream ss;
-        for(int i = 0; i < _0.size(); i++) {
-            ss << vm->py_str(_0[i]);
-            if(i != _0.size() - 1) ss << _1;
-        }
-        ss << _2;
-        vm->stdout_write(ss.str());
-        return vm->None;
-    });
-
-    add_module___builtins(vm);
-    add_module_sys(this);
-    add_module_traceback(this);
-    add_module_time(this);
-    add_module_json(this);
-    add_module_math(this);
-    add_module_dis(this);
-    add_module_c(this);
-    add_module_gc(this);
-    add_module_random(this);
-    add_module_base64(this);
-
-    _lazy_modules.insert("this", kPythonLibs_this);
-    _lazy_modules.insert("functools", kPythonLibs_functools);
-    _lazy_modules.insert("heapq", kPythonLibs_heapq);
-    _lazy_modules.insert("bisect", kPythonLibs_bisect);
-    _lazy_modules.insert("pickle", kPythonLibs_pickle);
-    _lazy_modules.insert("_long", kPythonLibs__long);
-    _lazy_modules.insert("colorsys", kPythonLibs_colorsys);
-    _lazy_modules.insert("typing", kPythonLibs_typing);
-    _lazy_modules.insert("datetime", kPythonLibs_datetime);
-    _lazy_modules.insert("cmath", kPythonLibs_cmath);
-    _lazy_modules.insert("itertools", kPythonLibs_itertools);
-    _lazy_modules.insert("operator", kPythonLibs_operator);
-    _lazy_modules.insert("collections", kPythonLibs_collections);
-
-    try {
-        // initialize dummy func_decl for exec/eval
-        CodeObject* code = compile("def _(): pass", "<dynamic>", EXEC_MODE);
-        __dynamic_func_decl = c11__getitem(FuncDecl_, &code->func_decls, 0);
-        PK_INCREF(__dynamic_func_decl);
-        CodeObject__delete(code);
-        // initialize builtins
-        code = compile(kPythonLibs_builtins, "<builtins>", EXEC_MODE);
-        this->_exec(code, this->builtins);
-        CodeObject__delete(code);
-        code = compile(kPythonLibs__set, "<set>", EXEC_MODE);
-        this->_exec(code, this->builtins);
-        CodeObject__delete(code);
-    } catch(TopLevelException e) {
-        std::cerr << e.summary() << std::endl;
-        std::cerr << "failed to load builtins module!!" << std::endl;
-        exit(1);
-    }
-
-    if(enable_os) {
-        add_module_io(this);
-        add_module_os(this);
-        _import_handler = &_default_import_handler;
-    }
-
-    add_module_csv(this);
-    add_module_dataclasses(this);
-    add_module_linalg(this);
-    add_module_easing(this);
-    add_module_array2d(this);
-    add_module_line_profiler(this);
-    add_module_enum(this);
-
-#ifdef PK_USE_CJSON
-    add_module_cjson(this);
-#endif
-}
-
-CodeObject* VM::compile(std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope) {
-    Compiler compiler(this, source, filename, mode, unknown_global_scope);
-    CodeObject* code;
-    Error* err = compiler.compile(&code);
-    if(err) __compile_error(err);
-    return code;
-}
-
-void VM::__compile_error(Error* err){
-    assert(err != nullptr);
-    if(err->type == std::string_view("NeedMoreLines")){
-        bool arg = (bool)err->userdata;
-        PK_DECREF(err->src);
-        std::free(err);
-        throw NeedMoreLines(arg);
-    }
-    __last_exception = vm->call(
-        vm->builtins->attr(err->type),
-        VAR((const char*)err->msg)
-    ).get();
-    Exception& e = __last_exception->as<Exception>();
-    e.stpush(err->src, err->lineno, err->cursor, "");
-    PK_DECREF(err->src);
-    std::free(err);
-    _error(__last_exception);
-}
-
-Str VM::precompile(std::string_view source, const Str& filename, CompileMode mode) {
-    Compiler compiler(this, source, filename, mode, false);
-    Str out;
-    Error* err = compiler.lexer.precompile(&out);
-    if(err) __compile_error(err);
-    return out;
-}
-
-}  // namespace pkpy

+ 0 - 575
src/pocketpy_c.cpp

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

+ 42 - 0
src2/main.c

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

+ 0 - 265
src2/pocketpy_c.c

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