From f3ac21ccc2d56b6d96e3761b06ad73522e610555 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 2 Jul 2023 01:39:24 +0800 Subject: [PATCH] ... --- .gitignore | 2 +- amalgamate.py | 2 +- build.py | 14 +- compile_flags.txt | 2 +- include/pocketpy/base64.h | 20 + {src => include/pocketpy}/c.pyi | 0 {src => include/pocketpy}/cffi.h | 0 {src => include/pocketpy}/codeobject.h | 0 {src => include/pocketpy}/common.h | 2 +- {src => include/pocketpy}/compiler.h | 0 {src => include/pocketpy}/config.h | 0 {src => include/pocketpy}/dict.h | 0 include/pocketpy/easing.h | 19 + {src => include/pocketpy}/easing.pyi | 0 include/pocketpy/error.h | 52 ++ {src => include/pocketpy}/export.h | 0 {src => include/pocketpy}/expr.h | 3 +- {src => include/pocketpy}/frame.h | 0 {src => include/pocketpy}/gc.h | 0 {src => include/pocketpy}/io.h | 2 - include/pocketpy/iter.h | 64 ++ {src => include/pocketpy}/lexer.h | 0 include/pocketpy/linalg.h | 387 ++++++++++++ {src => include/pocketpy}/linalg.pyi | 0 {src => include/pocketpy}/memory.h | 22 +- {src => include/pocketpy}/namedict.h | 26 +- {src => include/pocketpy}/obj.h | 0 {src => include/pocketpy}/opcodes.h | 0 include/pocketpy/pocketpy.h | 176 ++++++ {src => include/pocketpy}/random.h | 0 {src => include/pocketpy}/re.h | 0 {src => include/pocketpy}/repl.h | 2 +- {src => include/pocketpy}/str.h | 266 ++------- include/pocketpy/tuplelist.h | 52 ++ {src => include/pocketpy}/vector.h | 0 {src => include/pocketpy}/vm.h | 781 ------------------------ preprocess.py | 2 +- scripts/loc.py | 9 +- src/{base64.h => base64.cpp} | 25 +- src/{ceval.h => ceval.cpp} | 12 +- src/{easing.h => easing.cpp} | 18 +- src/{error.h => error.cpp} | 71 +-- src/{iter.h => iter.cpp} | 67 +-- src/{linalg.h => linalg.cpp} | 389 +----------- src/main.cpp | 2 +- src/namedict.cpp | 23 + src/{pocketpy.h => pocketpy.cpp} | 186 +----- src/str.cpp | 242 ++++++++ src/tuplelist.cpp | 55 ++ src/tuplelist.h | 97 --- src/vm.cpp | 790 +++++++++++++++++++++++++ 51 files changed, 2020 insertions(+), 1862 deletions(-) create mode 100644 include/pocketpy/base64.h rename {src => include/pocketpy}/c.pyi (100%) rename {src => include/pocketpy}/cffi.h (100%) rename {src => include/pocketpy}/codeobject.h (100%) rename {src => include/pocketpy}/common.h (99%) rename {src => include/pocketpy}/compiler.h (100%) rename {src => include/pocketpy}/config.h (100%) rename {src => include/pocketpy}/dict.h (100%) create mode 100644 include/pocketpy/easing.h rename {src => include/pocketpy}/easing.pyi (100%) create mode 100644 include/pocketpy/error.h rename {src => include/pocketpy}/export.h (100%) rename {src => include/pocketpy}/expr.h (99%) rename {src => include/pocketpy}/frame.h (100%) rename {src => include/pocketpy}/gc.h (100%) rename {src => include/pocketpy}/io.h (99%) create mode 100644 include/pocketpy/iter.h rename {src => include/pocketpy}/lexer.h (100%) create mode 100644 include/pocketpy/linalg.h rename {src => include/pocketpy}/linalg.pyi (100%) rename {src => include/pocketpy}/memory.h (95%) rename {src => include/pocketpy}/namedict.h (84%) rename {src => include/pocketpy}/obj.h (100%) rename {src => include/pocketpy}/opcodes.h (100%) create mode 100644 include/pocketpy/pocketpy.h rename {src => include/pocketpy}/random.h (100%) rename {src => include/pocketpy}/re.h (100%) rename {src => include/pocketpy}/repl.h (99%) rename {src => include/pocketpy}/str.h (65%) create mode 100644 include/pocketpy/tuplelist.h rename {src => include/pocketpy}/vector.h (100%) rename {src => include/pocketpy}/vm.h (53%) rename src/{base64.h => base64.cpp} (94%) rename src/{ceval.h => ceval.cpp} (99%) rename src/{easing.h => easing.cpp} (96%) rename src/{error.h => error.cpp} (64%) rename src/{iter.h => iter.cpp} (65%) rename src/{linalg.h => linalg.cpp} (54%) create mode 100644 src/namedict.cpp rename src/{pocketpy.h => pocketpy.cpp} (92%) create mode 100644 src/str.cpp create mode 100644 src/tuplelist.cpp delete mode 100644 src/tuplelist.h create mode 100644 src/vm.cpp diff --git a/.gitignore b/.gitignore index 3118c8b4..340a340a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ plugins/flutter/example/web/lib/pocketpy.js plugins/flutter/example/web/lib/pocketpy.wasm plugins/flutter/src/pocketpy.* plugins/macos/pocketpy/pocketpy.* -src/_generated.h +include/pocketpy/_generated.h profile.sh test src/httplib.h diff --git a/amalgamate.py b/amalgamate.py index ab0bd3ca..9763af0e 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -8,7 +8,7 @@ with open("src/opcodes.h", "rt", encoding='utf-8') as f: pipeline = [ ["config.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"], ["obj.h", "dict.h", "codeobject.h", "frame.h"], - ["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"], + ["gc.h", "vm.h", "expr.h", "compiler.h", "repl.h"], ["_generated.h", "cffi.h", "iter.h", "base64.h", "random.h", "re.h", "linalg.h", "easing.h", "io.h"], ["export.h", "pocketpy.h"] ] diff --git a/build.py b/build.py index 66b0b1e8..cf9ae94a 100644 --- a/build.py +++ b/build.py @@ -9,9 +9,17 @@ os.system("python3 preprocess.py") def DONE(code=0): exit(code) -linux_common = "-Wfatal-errors --std=c++17 -O2 -Wall -fno-rtti -stdlib=libc++" -linux_cmd = "clang++ -o pocketpy src/main.cpp " + linux_common -linux_lib_cmd = "clang++ -fPIC -shared -o pocketpy.so src/tmp.cpp " + linux_common +src_file_list = [] +for file in os.listdir("src"): + if file.endswith(".cpp") and file != "main.cpp" and file != "tmp.cpp": + src_file_list.append("src/" + file) + +main_src_arg = " ".join(src_file_list+["src/main.cpp"]) +tmp_src_arg = " ".join(src_file_list+["src/tmp.cpp"]) + +linux_common = " -Wfatal-errors --std=c++17 -O2 -Wall -fno-rtti -stdlib=libc++ -Iinclude/ " +linux_cmd = "clang++ -o pocketpy " + main_src_arg + linux_common +linux_lib_cmd = "clang++ -fPIC -shared -o pocketpy.so " + tmp_src_arg + linux_common class LibBuildEnv: def __enter__(self): diff --git a/compile_flags.txt b/compile_flags.txt index f5128fb2..126e3031 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -3,4 +3,4 @@ -W* -std=c++17 -stdlib=libc++ --Isrc \ No newline at end of file +-Iinclude/ \ No newline at end of file diff --git a/include/pocketpy/base64.h b/include/pocketpy/base64.h new file mode 100644 index 00000000..9d6a1703 --- /dev/null +++ b/include/pocketpy/base64.h @@ -0,0 +1,20 @@ +#pragma once + +#include "common.h" + +#if PK_MODULE_BASE64 + +#include "cffi.h" + +namespace pkpy { + +void add_module_base64(VM* vm); + +} // namespace pkpy + + +#else + +ADD_MODULE_PLACEHOLDER(base64) + +#endif \ No newline at end of file diff --git a/src/c.pyi b/include/pocketpy/c.pyi similarity index 100% rename from src/c.pyi rename to include/pocketpy/c.pyi diff --git a/src/cffi.h b/include/pocketpy/cffi.h similarity index 100% rename from src/cffi.h rename to include/pocketpy/cffi.h diff --git a/src/codeobject.h b/include/pocketpy/codeobject.h similarity index 100% rename from src/codeobject.h rename to include/pocketpy/codeobject.h diff --git a/src/common.h b/include/pocketpy/common.h similarity index 99% rename from src/common.h rename to include/pocketpy/common.h index 4933205b..743b9bb9 100644 --- a/src/common.h +++ b/include/pocketpy/common.h @@ -20,7 +20,7 @@ #include #include -#define PK_VERSION "1.0.7" +#define PK_VERSION "1.0.8" #include "config.h" diff --git a/src/compiler.h b/include/pocketpy/compiler.h similarity index 100% rename from src/compiler.h rename to include/pocketpy/compiler.h diff --git a/src/config.h b/include/pocketpy/config.h similarity index 100% rename from src/config.h rename to include/pocketpy/config.h diff --git a/src/dict.h b/include/pocketpy/dict.h similarity index 100% rename from src/dict.h rename to include/pocketpy/dict.h diff --git a/include/pocketpy/easing.h b/include/pocketpy/easing.h new file mode 100644 index 00000000..7c58b234 --- /dev/null +++ b/include/pocketpy/easing.h @@ -0,0 +1,19 @@ +#pragma once + +#include "common.h" + +#if PK_MODULE_EASING + +#include "cffi.h" + +namespace pkpy{ + +void add_module_easing(VM* vm); + +} // namespace pkpy + +#else + +ADD_MODULE_PLACEHOLDER(easing) + +#endif \ No newline at end of file diff --git a/src/easing.pyi b/include/pocketpy/easing.pyi similarity index 100% rename from src/easing.pyi rename to include/pocketpy/easing.pyi diff --git a/include/pocketpy/error.h b/include/pocketpy/error.h new file mode 100644 index 00000000..eda4ebe7 --- /dev/null +++ b/include/pocketpy/error.h @@ -0,0 +1,52 @@ +#pragma once + +#include "namedict.h" +#include "str.h" +#include "tuplelist.h" + +namespace pkpy{ + +struct NeedMoreLines { + NeedMoreLines(bool is_compiling_class) : is_compiling_class(is_compiling_class) {} + bool is_compiling_class; +}; + +struct HandledException {}; +struct UnhandledException {}; +struct ToBeRaisedException {}; + +enum CompileMode { + EXEC_MODE, + EVAL_MODE, + REPL_MODE, + JSON_MODE, + CELL_MODE +}; + +struct SourceData { + std::string source; + Str filename; + std::vector line_starts; + CompileMode mode; + + SourceData(const SourceData&) = delete; + SourceData& operator=(const SourceData&) = delete; + + SourceData(const Str& source, const Str& filename, CompileMode mode); + std::pair get_line(int lineno) const; + Str snapshot(int lineno, const char* cursor=nullptr); +}; + +struct Exception { + StrName type; + Str msg; + bool is_re; + stack stacktrace; + + Exception(StrName type, Str msg): type(type), msg(msg), is_re(true) {} + bool match_type(StrName t) const { return this->type == t;} + void st_push(Str snapshot); + Str summary() const; +}; + +} // namespace pkpy \ No newline at end of file diff --git a/src/export.h b/include/pocketpy/export.h similarity index 100% rename from src/export.h rename to include/pocketpy/export.h diff --git a/src/expr.h b/include/pocketpy/expr.h similarity index 99% rename from src/expr.h rename to include/pocketpy/expr.h index c0958549..9845a68f 100644 --- a/src/expr.h +++ b/include/pocketpy/expr.h @@ -4,8 +4,7 @@ #include "common.h" #include "lexer.h" #include "error.h" -#include "ceval.h" -#include "str.h" +#include "vm.h" namespace pkpy{ diff --git a/src/frame.h b/include/pocketpy/frame.h similarity index 100% rename from src/frame.h rename to include/pocketpy/frame.h diff --git a/src/gc.h b/include/pocketpy/gc.h similarity index 100% rename from src/gc.h rename to include/pocketpy/gc.h diff --git a/src/io.h b/include/pocketpy/io.h similarity index 99% rename from src/io.h rename to include/pocketpy/io.h index f83a5608..5a5dbcf9 100644 --- a/src/io.h +++ b/include/pocketpy/io.h @@ -1,8 +1,6 @@ #pragma once -#include "ceval.h" #include "cffi.h" -#include "common.h" #if PK_ENABLE_OS diff --git a/include/pocketpy/iter.h b/include/pocketpy/iter.h new file mode 100644 index 00000000..d465cd87 --- /dev/null +++ b/include/pocketpy/iter.h @@ -0,0 +1,64 @@ +#pragma once + +#include "cffi.h" +#include "common.h" +#include "frame.h" + +namespace pkpy{ + +struct RangeIter{ + PY_CLASS(RangeIter, builtins, "_range_iterator") + Range r; + i64 current; + RangeIter(Range r) : r(r), current(r.start) {} + + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +struct ArrayIter{ + PY_CLASS(ArrayIter, builtins, "_array_iterator") + PyObject* ref; + PyObject** begin; + PyObject** end; + PyObject** current; + + ArrayIter(PyObject* ref, PyObject** begin, PyObject** end) + : ref(ref), begin(begin), end(end), current(begin) {} + + void _gc_mark() const{ PK_OBJ_MARK(ref); } + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +struct StringIter{ + PY_CLASS(StringIter, builtins, "_string_iterator") + PyObject* ref; + Str* str; + int index; + + StringIter(PyObject* ref) : ref(ref), str(&PK_OBJ_GET(Str, ref)), index(0) {} + + void _gc_mark() const{ PK_OBJ_MARK(ref); } + + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +struct Generator{ + PY_CLASS(Generator, builtins, "_generator") + Frame frame; + int state; // 0,1,2 + List s_backup; + + Generator(Frame&& frame, ArgsView buffer): frame(std::move(frame)), state(0) { + for(PyObject* obj: buffer) s_backup.push_back(obj); + } + + void _gc_mark() const{ + frame._gc_mark(); + for(PyObject* obj: s_backup) PK_OBJ_MARK(obj); + } + + PyObject* next(VM* vm); + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +} // namespace pkpy \ No newline at end of file diff --git a/src/lexer.h b/include/pocketpy/lexer.h similarity index 100% rename from src/lexer.h rename to include/pocketpy/lexer.h diff --git a/include/pocketpy/linalg.h b/include/pocketpy/linalg.h new file mode 100644 index 00000000..660e87e0 --- /dev/null +++ b/include/pocketpy/linalg.h @@ -0,0 +1,387 @@ +#pragma once + +#include "common.h" + +#if PK_MODULE_LINALG + +#include "cffi.h" + +namespace pkpy{ + +static constexpr float kEpsilon = 1e-4f; +inline static bool isclose(float a, float b){ return fabsf(a - b) < kEpsilon; } + +struct Vec2{ + float x, y; + Vec2() : x(0.0f), y(0.0f) {} + Vec2(float x, float y) : x(x), y(y) {} + Vec2(const Vec2& v) : x(v.x), y(v.y) {} + + Vec2 operator+(const Vec2& v) const { return Vec2(x + v.x, y + v.y); } + Vec2& operator+=(const Vec2& v) { x += v.x; y += v.y; return *this; } + Vec2 operator-(const Vec2& v) const { return Vec2(x - v.x, y - v.y); } + Vec2& operator-=(const Vec2& v) { x -= v.x; y -= v.y; return *this; } + Vec2 operator*(float s) const { return Vec2(x * s, y * s); } + Vec2& operator*=(float s) { x *= s; y *= s; return *this; } + Vec2 operator/(float s) const { return Vec2(x / s, y / s); } + Vec2& operator/=(float s) { x /= s; y /= s; return *this; } + 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 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); } +}; + +struct Vec3{ + 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(const Vec3& v) : 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+=(const Vec3& v) { x += v.x; y += v.y; z += v.z; return *this; } + Vec3 operator-(const Vec3& v) const { return Vec3(x - v.x, y - v.y, z - v.z); } + Vec3& operator-=(const Vec3& v) { x -= v.x; y -= v.y; z -= v.z; return *this; } + Vec3 operator*(float s) const { return Vec3(x * s, y * s, z * s); } + Vec3& operator*=(float s) { x *= s; y *= s; z *= s; return *this; } + Vec3 operator/(float s) const { return Vec3(x / s, y / s, z / s); } + Vec3& operator/=(float s) { x /= s; y /= s; z /= s; return *this; } + 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 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{ + 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(const Vec4& v) : 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+=(const Vec4& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } + 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) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } + Vec4 operator*(float s) const { return Vec4(x * s, y * s, z * s, w * s); } + Vec4& operator*=(float s) { x *= s; y *= s; z *= s; w *= s; return *this; } + Vec4 operator/(float s) const { return Vec4(x / s, y / s, z / s, w / s); } + Vec4& operator/=(float s) { x /= s; y /= s; z /= s; w /= s; return *this; } + 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 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); } +}; + +struct Mat3x3{ + union { + struct { + float _11, _12, _13; + float _21, _22, _23; + float _31, _32, _33; + }; + float m[3][3]; + float v[9]; + }; + + 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) {} + + void set_zeros(){ for (int i=0; i<9; ++i) v[i] = 0.0f; } + void set_ones(){ for (int i=0; i<9; ++i) v[i] = 1.0f; } + void set_identity(){ set_zeros(); _11 = _22 = _33 = 1.0f; } + + static Mat3x3 zeros(){ + static Mat3x3 ret(0, 0, 0, 0, 0, 0, 0, 0, 0); + return ret; + } + + static Mat3x3 ones(){ + static Mat3x3 ret(1, 1, 1, 1, 1, 1, 1, 1, 1); + return ret; + } + + static Mat3x3 identity(){ + static Mat3x3 ret(1, 0, 0, 0, 1, 0, 0, 0, 1); + return ret; + } + + 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 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 operator*(float scalar) const{ + Mat3x3 ret; + for (int i=0; i<9; ++i) ret.v[i] = v[i] * scalar; + return ret; + } + + Mat3x3 operator/(float scalar) const{ + Mat3x3 ret; + for (int i=0; i<9; ++i) ret.v[i] = v[i] / scalar; + return ret; + } + + Mat3x3& operator+=(const Mat3x3& other){ + for (int i=0; i<9; ++i) v[i] += other.v[i]; + return *this; + } + + Mat3x3& operator-=(const Mat3x3& other){ + for (int i=0; i<9; ++i) v[i] -= other.v[i]; + return *this; + } + + Mat3x3& operator*=(float scalar){ + for (int i=0; i<9; ++i) v[i] *= scalar; + return *this; + } + + Mat3x3& operator/=(float scalar){ + for (int i=0; i<9; ++i) v[i] /= scalar; + return *this; + } + + Mat3x3 matmul(const Mat3x3& other) const{ + Mat3x3 ret; + ret._11 = _11 * other._11 + _12 * other._21 + _13 * other._31; + ret._12 = _11 * other._12 + _12 * other._22 + _13 * other._32; + ret._13 = _11 * other._13 + _12 * other._23 + _13 * other._33; + ret._21 = _21 * other._11 + _22 * other._21 + _23 * other._31; + ret._22 = _21 * other._12 + _22 * other._22 + _23 * other._32; + ret._23 = _21 * other._13 + _22 * other._23 + _23 * other._33; + ret._31 = _31 * other._11 + _32 * other._21 + _33 * other._31; + ret._32 = _31 * other._12 + _32 * other._22 + _33 * other._32; + ret._33 = _31 * other._13 + _32 * other._23 + _33 * other._33; + return ret; + } + + Vec3 matmul(const Vec3& other) const{ + Vec3 ret; + ret.x = _11 * other.x + _12 * other.y + _13 * other.z; + ret.y = _21 * other.x + _22 * other.y + _23 * other.z; + ret.z = _31 * other.x + _32 * other.y + _33 * other.z; + return ret; + } + + bool operator==(const Mat3x3& other) const{ + for (int i=0; i<9; ++i){ + if (!isclose(v[i], other.v[i])) return false; + } + return true; + } + + bool operator!=(const Mat3x3& other) const{ + for (int i=0; i<9; ++i){ + if (!isclose(v[i], other.v[i])) return true; + } + return false; + } + + float determinant() const{ + return _11 * _22 * _33 + _12 * _23 * _31 + _13 * _21 * _32 + - _11 * _23 * _32 - _12 * _21 * _33 - _13 * _22 * _31; + } + + 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 inverse(Mat3x3& ret) const{ + float det = determinant(); + if (fabsf(det) < kEpsilon) return false; + float inv_det = 1.0f / det; + ret._11 = (_22 * _33 - _23 * _32) * inv_det; + ret._12 = (_13 * _32 - _12 * _33) * inv_det; + ret._13 = (_12 * _23 - _13 * _22) * inv_det; + ret._21 = (_23 * _31 - _21 * _33) * inv_det; + ret._22 = (_11 * _33 - _13 * _31) * inv_det; + ret._23 = (_13 * _21 - _11 * _23) * inv_det; + ret._31 = (_21 * _32 - _22 * _31) * inv_det; + ret._32 = (_12 * _31 - _11 * _32) * inv_det; + ret._33 = (_11 * _22 - _12 * _21) * inv_det; + return true; + } + + /*************** affine transformations ***************/ + static 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 is_affine() const{ + float det = _11 * _22 - _12 * _21; + if(fabsf(det) < kEpsilon) return false; + return _31 == 0.0f && _32 == 0.0f && _33 == 1.0f; + } + + Mat3x3 inverse_affine() const{ + Mat3x3 ret; + float det = _11 * _22 - _12 * _21; + float inv_det = 1.0f / det; + ret._11 = _22 * inv_det; + ret._12 = -_12 * inv_det; + ret._13 = (_12 * _23 - _13 * _22) * inv_det; + ret._21 = -_21 * inv_det; + ret._22 = _11 * inv_det; + ret._23 = (_13 * _21 - _11 * _23) * inv_det; + ret._31 = 0.0f; + ret._32 = 0.0f; + ret._33 = 1.0f; + return ret; + } + + Mat3x3 matmul_affine(const Mat3x3& other) const{ + Mat3x3 ret; + ret._11 = _11 * other._11 + _12 * other._21; + ret._12 = _11 * other._12 + _12 * other._22; + ret._13 = _11 * other._13 + _12 * other._23 + _13; + ret._21 = _21 * other._11 + _22 * other._21; + ret._22 = _21 * other._12 + _22 * other._22; + ret._23 = _21 * other._13 + _22 * other._23 + _23; + ret._31 = 0.0f; + ret._32 = 0.0f; + ret._33 = 1.0f; + return ret; + } + + Vec2 translation() const { return Vec2(_13, _23); } + float rotation() const { return atan2f(_21, _11); } + Vec2 scale() const { + return Vec2( + sqrtf(_11 * _11 + _21 * _21), + sqrtf(_12 * _12 + _22 * _22) + ); + } + + Vec2 transform_point(Vec2 vec) const { + return Vec2(_11 * vec.x + _12 * vec.y + _13, _21 * vec.x + _22 * vec.y + _23); + } + + Vec2 transform_vector(Vec2 vec) const { + return Vec2(_11 * vec.x + _12 * vec.y, _21 * vec.x + _22 * vec.y); + } +}; + +struct PyVec2; +struct PyVec3; +struct PyVec4; +struct PyMat3x3; +PyObject* py_var(VM*, Vec2); +PyObject* py_var(VM*, const PyVec2&); +PyObject* py_var(VM*, Vec3); +PyObject* py_var(VM*, const PyVec3&); +PyObject* py_var(VM*, Vec4); +PyObject* py_var(VM*, const PyVec4&); +PyObject* py_var(VM*, const Mat3x3&); +PyObject* py_var(VM*, const PyMat3x3&); + + +struct PyVec2: Vec2 { + PY_CLASS(PyVec2, linalg, vec2) + + PyVec2() : Vec2() {} + PyVec2(const Vec2& v) : Vec2(v) {} + PyVec2(const PyVec2& v) : Vec2(v) {} + + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +struct PyVec3: Vec3 { + PY_CLASS(PyVec3, linalg, vec3) + + PyVec3() : Vec3() {} + PyVec3(const Vec3& v) : Vec3(v) {} + PyVec3(const PyVec3& v) : Vec3(v) {} + + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +struct PyVec4: Vec4{ + PY_CLASS(PyVec4, linalg, vec4) + + PyVec4(): Vec4(){} + PyVec4(const Vec4& v): Vec4(v){} + PyVec4(const PyVec4& v): Vec4(v){} + + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +struct PyMat3x3: Mat3x3{ + PY_CLASS(PyMat3x3, linalg, mat3x3) + + PyMat3x3(): Mat3x3(){} + PyMat3x3(const Mat3x3& other): Mat3x3(other){} + PyMat3x3(const PyMat3x3& other): Mat3x3(other){} + + static void _register(VM* vm, PyObject* mod, PyObject* type); +}; + +inline PyObject* py_var(VM* vm, Vec2 obj){ return VAR_T(PyVec2, obj); } +inline PyObject* py_var(VM* vm, const PyVec2& obj){ return VAR_T(PyVec2, obj);} + +inline PyObject* py_var(VM* vm, Vec3 obj){ return VAR_T(PyVec3, obj); } +inline PyObject* py_var(VM* vm, const PyVec3& obj){ return VAR_T(PyVec3, obj);} + +inline PyObject* py_var(VM* vm, Vec4 obj){ return VAR_T(PyVec4, obj); } +inline PyObject* py_var(VM* vm, const PyVec4& obj){ return VAR_T(PyVec4, obj);} + +inline PyObject* py_var(VM* vm, const Mat3x3& obj){ return VAR_T(PyMat3x3, obj); } +inline PyObject* py_var(VM* vm, const PyMat3x3& obj){ return VAR_T(PyMat3x3, obj); } + +template<> inline Vec2 py_cast(VM* vm, PyObject* obj) { return CAST(PyVec2&, obj); } +template<> inline Vec3 py_cast(VM* vm, PyObject* obj) { return CAST(PyVec3&, obj); } +template<> inline Vec4 py_cast(VM* vm, PyObject* obj) { return CAST(PyVec4&, obj); } +template<> inline Mat3x3 py_cast(VM* vm, PyObject* obj) { return CAST(PyMat3x3&, obj); } + +template<> inline Vec2 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyVec2&, obj); } +template<> inline Vec3 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyVec3&, obj); } +template<> inline Vec4 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyVec4&, obj); } +template<> inline Mat3x3 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyMat3x3&, obj); } + +inline void add_module_linalg(VM* vm){ + PyObject* linalg = vm->new_module("linalg"); + PyVec2::register_class(vm, linalg); + PyVec3::register_class(vm, linalg); + PyVec4::register_class(vm, linalg); + PyMat3x3::register_class(vm, linalg); +} + +static_assert(sizeof(Py_) <= 64); + +} // namespace pkpy + +#else + +ADD_MODULE_PLACEHOLDER(linalg) + +#endif \ No newline at end of file diff --git a/src/linalg.pyi b/include/pocketpy/linalg.pyi similarity index 100% rename from src/linalg.pyi rename to include/pocketpy/linalg.pyi diff --git a/src/memory.h b/include/pocketpy/memory.h similarity index 95% rename from src/memory.h rename to include/pocketpy/memory.h index 614e17d5..54a1a905 100644 --- a/src/memory.h +++ b/include/pocketpy/memory.h @@ -86,17 +86,17 @@ struct DoubleLinkedList{ _size--; } - void move_all_back(DoubleLinkedList& other){ - if(other.empty()) return; - other.tail.prev->next = &tail; - tail.prev->next = other.head.next; - other.head.next->prev = tail.prev; - tail.prev = other.tail.prev; - _size += other._size; - other.head.next = &other.tail; - other.tail.prev = &other.head; - other._size = 0; - } + // void move_all_back(DoubleLinkedList& other){ + // if(other.empty()) return; + // other.tail.prev->next = &tail; + // tail.prev->next = other.head.next; + // other.head.next->prev = tail.prev; + // tail.prev = other.tail.prev; + // _size += other._size; + // other.head.next = &other.tail; + // other.tail.prev = &other.head; + // other._size = 0; + // } bool empty() const { #if PK_DEBUG_MEMORY_POOL diff --git a/src/namedict.h b/include/pocketpy/namedict.h similarity index 84% rename from src/namedict.h rename to include/pocketpy/namedict.h index f9334328..9ddf513f 100644 --- a/src/namedict.h +++ b/include/pocketpy/namedict.h @@ -6,28 +6,14 @@ namespace pkpy{ -const uint16_t kHashSeeds[] = {9629, 43049, 13267, 59509, 39251, 1249, 27689, 9719, 19913}; +inline const uint16_t kHashSeeds[] = {9629, 43049, 13267, 59509, 39251, 1249, 27689, 9719, 19913}; -#define _hash(key, mask, hash_seed) ( ( (key).index * (hash_seed) >> 8 ) & (mask) ) - -inline uint16_t find_perfect_hash_seed(uint16_t capacity, const std::vector& keys){ - if(keys.empty()) return kHashSeeds[0]; - static std::set indices; - indices.clear(); - std::pair best_score = {kHashSeeds[0], 0.0f}; - const int kHashSeedsSize = sizeof(kHashSeeds) / sizeof(kHashSeeds[0]); - for(int i=0; i best_score.second) best_score = {kHashSeeds[i], score}; - } - return best_score.first; +inline uint16_t _hash(StrName key, uint16_t mask, uint16_t hash_seed){ + return ( (key).index * (hash_seed) >> 8 ) & (mask); } +uint16_t _find_perfect_hash_seed(uint16_t capacity, const std::vector& keys); + template struct NameDictImpl { using Item = std::pair; @@ -121,7 +107,7 @@ while(!_items[i].first.empty()) { \ } void _try_perfect_rehash(){ - _hash_seed = find_perfect_hash_seed(_capacity, keys()); + _hash_seed = _find_perfect_hash_seed(_capacity, keys()); _rehash(false); // do not resize } diff --git a/src/obj.h b/include/pocketpy/obj.h similarity index 100% rename from src/obj.h rename to include/pocketpy/obj.h diff --git a/src/opcodes.h b/include/pocketpy/opcodes.h similarity index 100% rename from src/opcodes.h rename to include/pocketpy/opcodes.h diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h new file mode 100644 index 00000000..c8f36c07 --- /dev/null +++ b/include/pocketpy/pocketpy.h @@ -0,0 +1,176 @@ +#pragma once + +#include "compiler.h" +#include "obj.h" +#include "repl.h" +#include "iter.h" +#include "base64.h" +#include "cffi.h" +#include "linalg.h" +#include "easing.h" +#include "io.h" +#include "_generated.h" +#include "export.h" +#include "vm.h" +#include "re.h" +#include "random.h" + +namespace pkpy { + +inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope) { + Compiler compiler(this, source, filename, mode, unknown_global_scope); + try{ + return compiler.compile(); + }catch(Exception& e){ +#if PK_DEBUG_FULL_EXCEPTION + std::cerr << e.summary() << std::endl; +#endif + _error(e); + return nullptr; + } +} + + +void init_builtins(VM* _vm); + +struct PyREPL{ + PY_CLASS(PyREPL, sys, _repl) + + REPL* repl; + + PyREPL(VM* vm){ repl = new REPL(vm); } + ~PyREPL(){ delete repl; } + + PyREPL(const PyREPL&) = delete; + PyREPL& operator=(const PyREPL&) = delete; + + PyREPL(PyREPL&& other) noexcept{ + repl = other.repl; + other.repl = nullptr; + } + + struct TempOut{ + PrintFunc backup; + VM* vm; + TempOut(VM* vm, PrintFunc f){ + this->vm = vm; + this->backup = vm->_stdout; + vm->_stdout = f; + } + ~TempOut(){ + vm->_stdout = backup; + } + TempOut(const TempOut&) = delete; + TempOut& operator=(const TempOut&) = delete; + TempOut(TempOut&&) = delete; + TempOut& operator=(TempOut&&) = delete; + }; + + static void _register(VM* vm, PyObject* mod, PyObject* type){ + vm->bind_constructor<1>(type, [](VM* vm, ArgsView args){ + return VAR_T(PyREPL, vm); + }); + + vm->bind_method<1>(type, "input", [](VM* vm, ArgsView args){ + PyREPL& self = _CAST(PyREPL&, args[0]); + const Str& s = CAST(Str&, args[1]); + static std::stringstream ss_out; + ss_out.str(""); + TempOut _(vm, [](VM* vm, const Str& s){ ss_out << s; }); + bool ok = self.repl->input(s.str()); + return VAR(Tuple({VAR(ok), VAR(ss_out.str())})); + }); + } +}; + + +void add_module_timeit(VM* vm); +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_dis(VM* vm); +void add_module_traceback(VM* vm); +void add_module_gc(VM* vm); + +} // namespace pkpy + +/*************************GLOBAL NAMESPACE*************************/ +extern "C" { + PK_LEGACY_EXPORT + void pkpy_free(void* p){ + free(p); + } + + PK_LEGACY_EXPORT + void pkpy_vm_exec(pkpy::VM* vm, const char* source){ + vm->exec(source, "main.py", pkpy::EXEC_MODE); + } + + PK_LEGACY_EXPORT + void pkpy_vm_exec_2(pkpy::VM* vm, const char* source, const char* filename, int mode, const char* module){ + pkpy::PyObject* mod; + if(module == nullptr) mod = vm->_main; + else{ + mod = vm->_modules.try_get(module); + if(mod == nullptr) return; + } + vm->exec(source, filename, (pkpy::CompileMode)mode, mod); + } + + PK_LEGACY_EXPORT + void pkpy_vm_compile(pkpy::VM* vm, const char* source, const char* filename, int mode, bool* ok, char** res){ + try{ + pkpy::CodeObject_ code = vm->compile(source, filename, (pkpy::CompileMode)mode); + *res = code->serialize(vm).c_str_dup(); + *ok = true; + }catch(pkpy::Exception& e){ + *ok = false; + *res = e.summary().c_str_dup(); + }catch(std::exception& e){ + *ok = false; + *res = strdup(e.what()); + }catch(...){ + *ok = false; + *res = strdup("unknown error"); + } + } + + PK_LEGACY_EXPORT + pkpy::REPL* pkpy_new_repl(pkpy::VM* vm){ + pkpy::REPL* p = new pkpy::REPL(vm); + return p; + } + + PK_LEGACY_EXPORT + bool pkpy_repl_input(pkpy::REPL* r, const char* line){ + return r->input(line); + } + + PK_LEGACY_EXPORT + void pkpy_vm_add_module(pkpy::VM* vm, const char* name, const char* source){ + vm->_lazy_modules[name] = source; + } + + PK_LEGACY_EXPORT + pkpy::VM* pkpy_new_vm(bool enable_os=true){ + pkpy::VM* p = new pkpy::VM(enable_os); + return p; + } + + PK_LEGACY_EXPORT + void pkpy_delete_vm(pkpy::VM* vm){ + delete vm; + } + + PK_LEGACY_EXPORT + void pkpy_delete_repl(pkpy::REPL* repl){ + delete repl; + } + + PK_LEGACY_EXPORT + void pkpy_vm_gc_on_delete(pkpy::VM* vm, void (*f)(pkpy::VM *, pkpy::PyObject *)){ + vm->heap._gc_on_delete = f; + } +} diff --git a/src/random.h b/include/pocketpy/random.h similarity index 100% rename from src/random.h rename to include/pocketpy/random.h diff --git a/src/re.h b/include/pocketpy/re.h similarity index 100% rename from src/re.h rename to include/pocketpy/re.h diff --git a/src/repl.h b/include/pocketpy/repl.h similarity index 99% rename from src/repl.h rename to include/pocketpy/repl.h index a582834e..9d68f64f 100644 --- a/src/repl.h +++ b/include/pocketpy/repl.h @@ -1,7 +1,7 @@ #pragma once #include "compiler.h" -#include "ceval.h" +#include "vm.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN diff --git a/src/str.h b/include/pocketpy/str.h similarity index 65% rename from src/str.h rename to include/pocketpy/str.h index 21267ec6..612b720b 100644 --- a/src/str.h +++ b/include/pocketpy/str.h @@ -89,31 +89,23 @@ struct Str{ bool empty() const { return size == 0; } size_t hash() const{ return std::hash()(sv()); } - Str& operator=(const Str& other){ - if(!is_inlined()) pool64.dealloc(data); - size = other.size; - is_ascii = other.is_ascii; - _alloc(); - memcpy(data, other.data, size); - return *this; - } + Str& operator=(const Str& other); + Str operator+(const Str& other) const; + Str operator+(const char* p) const; - ~Str(){ - if(!is_inlined()) pool64.dealloc(data); - if(_cached_c_str != nullptr) free((void*)_cached_c_str); - } + bool operator==(const Str& other) const; + bool operator!=(const Str& other) const; + bool operator==(const std::string_view other) const; + bool operator!=(const std::string_view other) const; + bool operator==(const char* p) const; + bool operator!=(const char* p) const; + bool operator<(const Str& other) const; + bool operator>(const Str& other) const; + bool operator<=(const Str& other) const; + bool operator>=(const Str& other) const; + bool operator<(const std::string_view other) const; - Str operator+(const Str& other) const { - Str ret(size + other.size, is_ascii && other.is_ascii); - memcpy(ret.data, data, size); - memcpy(ret.data + size, other.data, other.size); - return ret; - } - - Str operator+(const char* p) const { - Str other(p); - return *this + other; - } + ~Str(); friend Str operator+(const char* p, const Str& str){ Str other(p); @@ -125,222 +117,30 @@ struct Str{ return os; } - bool operator==(const Str& other) const { - if(size != other.size) return false; - return memcmp(data, other.data, size) == 0; - } - - bool operator!=(const Str& other) const { - if(size != other.size) return true; - return memcmp(data, other.data, size) != 0; - } - - bool operator==(const std::string_view other) const { - if(size != (int)other.size()) return false; - return memcmp(data, other.data(), size) == 0; - } - - bool operator!=(const std::string_view other) const { - if(size != (int)other.size()) return true; - return memcmp(data, other.data(), size) != 0; - } - - bool operator==(const char* p) const { - return *this == std::string_view(p); - } - - bool operator!=(const char* p) const { - return *this != std::string_view(p); - } - - bool operator<(const Str& other) const { - int ret = strncmp(data, other.data, std::min(size, other.size)); - if(ret != 0) return ret < 0; - return size < other.size; - } - - bool operator<(const std::string_view other) const { - int ret = strncmp(data, other.data(), std::min(size, (int)other.size())); - if(ret != 0) return ret < 0; - return size < (int)other.size(); - } - friend bool operator<(const std::string_view other, const Str& str){ return str > other; } - bool operator>(const Str& other) const { - int ret = strncmp(data, other.data, std::min(size, other.size)); - if(ret != 0) return ret > 0; - return size > other.size; - } - - bool operator<=(const Str& other) const { - int ret = strncmp(data, other.data, std::min(size, other.size)); - if(ret != 0) return ret < 0; - return size <= other.size; - } - - bool operator>=(const Str& other) const { - int ret = strncmp(data, other.data, std::min(size, other.size)); - if(ret != 0) return ret > 0; - return size >= other.size; - } - - Str substr(int start, int len) const { - Str ret(len, is_ascii); - memcpy(ret.data, data + start, len); - return ret; - } - - Str substr(int start) const { - return substr(start, size - start); - } - - char* c_str_dup() const { - char* p = (char*)malloc(size + 1); - memcpy(p, data, size); - p[size] = 0; - return p; - } - - const char* c_str(){ - if(_cached_c_str == nullptr){ - _cached_c_str = c_str_dup(); - } - return _cached_c_str; - } - - std::string_view sv() const { - return std::string_view(data, size); - } - - std::string str() const { - return std::string(data, size); - } - - Str lstrip() const { - std::string copy(data, size); - copy.erase(copy.begin(), std::find_if(copy.begin(), copy.end(), [](char c) { - // std::isspace(c) does not working on windows (Debug) - return c != ' ' && c != '\t' && c != '\r' && c != '\n'; - })); - return Str(copy); - } - - Str strip() const { - std::string copy(data, size); - copy.erase(copy.begin(), std::find_if(copy.begin(), copy.end(), [](char c) { - return c != ' ' && c != '\t' && c != '\r' && c != '\n'; - })); - copy.erase(std::find_if(copy.rbegin(), copy.rend(), [](char c) { - return c != ' ' && c != '\t' && c != '\r' && c != '\n'; - }).base(), copy.end()); - return Str(copy); - } - - Str lower() const{ - std::string copy(data, size); - std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c){ return std::tolower(c); }); - return Str(copy); - } - - Str upper() const{ - std::string copy(data, size); - std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c){ return std::toupper(c); }); - return Str(copy); - } - - Str escape(bool single_quote=true) const { - std::stringstream ss; - ss << (single_quote ? '\'' : '"'); - for (int i=0; ioperator[](i); - switch (c) { - case '"': - if(!single_quote) ss << '\\'; - ss << '"'; - break; - case '\'': - if(single_quote) ss << '\\'; - ss << '\''; - break; - case '\\': ss << '\\' << '\\'; break; - case '\n': ss << "\\n"; break; - case '\r': ss << "\\r"; break; - case '\t': ss << "\\t"; break; - default: - if ('\x00' <= c && c <= '\x1f') { - ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (int)c; - } else { - ss << c; - } - } - } - ss << (single_quote ? '\'' : '"'); - return ss.str(); - } - - int index(const Str& sub, int start=0) const { - auto p = std::search(data + start, data + size, sub.data, sub.data + sub.size); - if(p == data + size) return -1; - return p - data; - } - - Str replace(const Str& old, const Str& new_, int count=-1) const { - std::stringstream ss; - int start = 0; - while(true){ - int i = index(old, start); - if(i == -1) break; - ss << substr(start, i - start); - ss << new_; - start = i + old.size; - if(count != -1 && --count == 0) break; - } - ss << substr(start, size - start); - return ss.str(); - } + Str substr(int start, int len) const; + Str substr(int start) const; + char* c_str_dup() const; + const char* c_str(); + std::string_view sv() const; + std::string str() const; + Str lstrip() const; + Str strip() const; + Str lower() const; + Str upper() const; + Str escape(bool single_quote=true) const; + int index(const Str& sub, int start=0) const; + Str replace(const Str& old, const Str& new_, int count=-1) const; /*************unicode*************/ - - int _unicode_index_to_byte(int i) const{ - if(is_ascii) return i; - int j = 0; - while(i > 0){ - j += utf8len(data[j]); - i--; - } - return j; - } - - int _byte_index_to_unicode(int n) const{ - if(is_ascii) return n; - int cnt = 0; - for(int i=0; i0?istop; i+=step) ss << data[i]; - }else{ - for(int i=start; step>0?istop; i+=step) ss << u8_getitem(i); - } - return ss.str(); - } - - int u8_length() const { - return _byte_index_to_unicode(size); - } + int _unicode_index_to_byte(int i) const; + int _byte_index_to_unicode(int n) const; + Str u8_getitem(int i) const; + Str u8_slice(int start, int stop, int step) const; + int u8_length() const; }; template diff --git a/include/pocketpy/tuplelist.h b/include/pocketpy/tuplelist.h new file mode 100644 index 00000000..00449029 --- /dev/null +++ b/include/pocketpy/tuplelist.h @@ -0,0 +1,52 @@ +#pragma once + +#include "common.h" +#include "memory.h" +#include "str.h" +#include "vector.h" + +namespace pkpy { + +using List = pod_vector; + +struct Tuple { + PyObject** _args; + PyObject* _inlined[3]; + int _size; + + Tuple(int n); + Tuple(std::initializer_list list); + Tuple(const Tuple& other); + Tuple(Tuple&& other) noexcept; + Tuple(List&& other) noexcept; + ~Tuple(); + + bool is_inlined() const { return _args == _inlined; } + PyObject*& operator[](int i){ return _args[i]; } + PyObject* operator[](int i) const { return _args[i]; } + + int size() const { return _size; } + + PyObject** begin() const { return _args; } + PyObject** end() const { return _args + _size; } +}; + +// a lightweight view for function args, it does not own the memory +struct ArgsView{ + PyObject** _begin; + PyObject** _end; + + ArgsView(PyObject** begin, PyObject** end) : _begin(begin), _end(end) {} + ArgsView(const Tuple& t) : _begin(t.begin()), _end(t.end()) {} + + PyObject** begin() const { return _begin; } + PyObject** end() const { return _end; } + int size() const { return _end - _begin; } + bool empty() const { return _begin == _end; } + PyObject* operator[](int i) const { return _begin[i]; } + + List to_list() const; + Tuple to_tuple() const; +}; + +} // namespace pkpy \ No newline at end of file diff --git a/src/vector.h b/include/pocketpy/vector.h similarity index 100% rename from src/vector.h rename to include/pocketpy/vector.h diff --git a/src/vm.h b/include/pocketpy/vm.h similarity index 53% rename from src/vm.h rename to include/pocketpy/vm.h index 8463e6e5..41f004e4 100644 --- a/src/vm.h +++ b/include/pocketpy/vm.h @@ -694,16 +694,6 @@ public: void _prepare_py_call(PyObject**, ArgsView, ArgsView, const FuncDecl_&); }; -inline void NativeFunc::check_size(VM* vm, ArgsView args) const{ - if(args.size() != argc && argc != -1) { - vm->TypeError(fmt("expected ", argc, " arguments, got ", args.size())); - } -} - -inline PyObject* NativeFunc::call(VM *vm, ArgsView args) const { - return f(vm, args); -} - DEF_NATIVE_2(Str, tp_str) DEF_NATIVE_2(List, tp_list) DEF_NATIVE_2(Tuple, tp_tuple) @@ -742,7 +732,6 @@ PY_CAST_INT(unsigned int) PY_CAST_INT(unsigned long) PY_CAST_INT(unsigned long long) - template<> inline float py_cast(VM* vm, PyObject* obj){ vm->check_float(obj); i64 bits = PK_BITS(obj) & Number::c1; @@ -853,683 +842,6 @@ inline PyObject* py_var(VM* vm, PyObject* val){ return val; } -inline PyObject* VM::py_negate(PyObject* obj){ - const PyTypeInfo* ti = _inst_type_info(obj); - if(ti->m__neg__) return ti->m__neg__(this, obj); - return call_method(obj, __neg__); -} - -inline f64 VM::num_to_float(PyObject* obj){ - if(is_float(obj)){ - return _CAST(f64, obj); - } else if (is_int(obj)){ - return (f64)_CAST(i64, obj); - } - TypeError("expected 'int' or 'float', got " + OBJ_NAME(_t(obj)).escape()); - return 0; -} - -inline bool VM::py_bool(PyObject* obj){ - if(is_non_tagged_type(obj, tp_bool)) return obj == True; - if(obj == None) return false; - if(is_int(obj)) return _CAST(i64, obj) != 0; - if(is_float(obj)) return _CAST(f64, obj) != 0.0; - PyObject* self; - PyObject* len_f = get_unbound_method(obj, __len__, &self, false); - if(self != PY_NULL){ - PyObject* ret = call_method(self, len_f); - return CAST(i64, ret) > 0; - } - return true; -} - -inline PyObject* VM::py_list(PyObject* it){ - auto _lock = heap.gc_scope_lock(); - it = py_iter(it); - List list; - PyObject* obj = py_next(it); - while(obj != StopIteration){ - list.push_back(obj); - obj = py_next(it); - } - return VAR(std::move(list)); -} - -inline void VM::parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step){ - auto clip = [](int value, int min, int max){ - if(value < min) return min; - if(value > max) return max; - return value; - }; - if(s.step == None) step = 1; - else step = CAST(int, s.step); - if(step == 0) ValueError("slice step cannot be zero"); - if(step > 0){ - if(s.start == None){ - start = 0; - }else{ - start = CAST(int, s.start); - if(start < 0) start += length; - start = clip(start, 0, length); - } - if(s.stop == None){ - stop = length; - }else{ - stop = CAST(int, s.stop); - if(stop < 0) stop += length; - stop = clip(stop, 0, length); - } - }else{ - if(s.start == None){ - start = length - 1; - }else{ - start = CAST(int, s.start); - if(start < 0) start += length; - start = clip(start, -1, length - 1); - } - if(s.stop == None){ - stop = -1; - }else{ - stop = CAST(int, s.stop); - if(stop < 0) stop += length; - stop = clip(stop, -1, length - 1); - } - } -} - -inline i64 VM::py_hash(PyObject* obj){ - const PyTypeInfo* ti = _inst_type_info(obj); - if(ti->m__hash__) return ti->m__hash__(this, obj); - PyObject* ret = call_method(obj, __hash__); - return CAST(i64, ret); -} - -inline PyObject* VM::format(Str spec, PyObject* obj){ - if(spec.empty()) return py_str(obj); - char type; - switch(spec.end()[-1]){ - case 'f': case 'd': case 's': - type = spec.end()[-1]; - spec = spec.substr(0, spec.length() - 1); - break; - default: type = ' '; break; - } - - char pad_c = ' '; - if(spec[0] == '0'){ - pad_c = '0'; - spec = spec.substr(1); - } - char align; - if(spec[0] == '>'){ - align = '>'; - spec = spec.substr(1); - }else if(spec[0] == '<'){ - align = '<'; - spec = spec.substr(1); - }else{ - if(is_int(obj) || is_float(obj)) align = '>'; - else align = '<'; - } - - int dot = spec.index("."); - int width, precision; - try{ - if(dot >= 0){ - width = Number::stoi(spec.substr(0, dot).str()); - precision = Number::stoi(spec.substr(dot+1).str()); - }else{ - width = Number::stoi(spec.str()); - precision = -1; - } - }catch(...){ - ValueError("invalid format specifer"); - UNREACHABLE(); - } - - if(type != 'f' && dot >= 0) ValueError("precision not allowed in the format specifier"); - Str ret; - if(type == 'f'){ - f64 val = num_to_float(obj); - if(precision < 0) precision = 6; - std::stringstream ss; - ss << std::fixed << std::setprecision(precision) << val; - ret = ss.str(); - }else if(type == 'd'){ - ret = std::to_string(CAST(i64, obj)); - }else if(type == 's'){ - ret = CAST(Str&, obj); - }else{ - ret = CAST(Str&, py_str(obj)); - } - if(width > ret.length()){ - int pad = width - ret.length(); - std::string padding(pad, pad_c); - if(align == '>') ret = padding.c_str() + ret; - else ret = ret + padding.c_str(); - } - return VAR(ret); -} - -inline PyObject* VM::new_module(StrName name) { - PyObject* obj = heap._new(tp_module, DummyModule()); - obj->attr().set("__name__", VAR(name.sv())); - // we do not allow override in order to avoid memory leak - // it is because Module objects are not garbage collected - if(_modules.contains(name)) throw std::runtime_error("module already exists"); - _modules.set(name, obj); - return obj; -} - -inline std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){ - std::string argStr = byte.arg == -1 ? "" : std::to_string(byte.arg); - switch(byte.op){ - case OP_LOAD_CONST: - if(vm != nullptr){ - argStr += fmt(" (", CAST(Str, vm->py_repr(co->consts[byte.arg])), ")"); - } - break; - case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL: - case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR: - case OP_IMPORT_NAME: case OP_BEGIN_CLASS: case OP_RAISE: - case OP_DELETE_GLOBAL: case OP_INC_GLOBAL: case OP_DEC_GLOBAL: case OP_STORE_CLASS_ATTR: - argStr += fmt(" (", StrName(byte.arg).sv(), ")"); - break; - case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST: case OP_INC_FAST: case OP_DEC_FAST: - argStr += fmt(" (", co->varnames[byte.arg].sv(), ")"); - break; - case OP_LOAD_FUNCTION: - argStr += fmt(" (", co->func_decls[byte.arg]->code->name, ")"); - break; - } - return argStr; -} - -inline Str VM::disassemble(CodeObject_ co){ - auto pad = [](const Str& s, const int n){ - if(s.length() >= n) return s.substr(0, n); - return s + std::string(n - s.length(), ' '); - }; - - std::vector jumpTargets; - for(auto byte : co->codes){ - if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE || byte.op == OP_SHORTCUT_IF_FALSE_OR_POP){ - jumpTargets.push_back(byte.arg); - } - } - std::stringstream ss; - int prev_line = -1; - for(int i=0; icodes.size(); i++){ - const Bytecode& byte = co->codes[i]; - Str line = std::to_string(co->lines[i]); - if(co->lines[i] == prev_line) line = ""; - else{ - if(prev_line != -1) ss << "\n"; - prev_line = co->lines[i]; - } - - std::string pointer; - if(std::find(jumpTargets.begin(), jumpTargets.end(), i) != jumpTargets.end()){ - pointer = "-> "; - }else{ - pointer = " "; - } - ss << pad(line, 8) << pointer << pad(std::to_string(i), 3); - ss << " " << pad(OP_NAMES[byte.op], 25) << " "; - // ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5); - std::string argStr = _opcode_argstr(this, byte, co.get()); - ss << argStr; - // ss << pad(argStr, 40); // may overflow - // ss << co->blocks[byte.block].type; - if(i != co->codes.size() - 1) ss << '\n'; - } - - for(auto& decl: co->func_decls){ - ss << "\n\n" << "Disassembly of " << decl->code->name << ":\n"; - ss << disassemble(decl->code); - } - ss << "\n"; - return Str(ss.str()); -} - -#if PK_DEBUG_CEVAL_STEP -inline void VM::_log_s_data(const char* title) { - if(_main == nullptr) return; - if(callstack.empty()) return; - std::stringstream ss; - if(title) ss << title << " | "; - std::map sp_bases; - for(Frame& f: callstack.data()){ - if(f._sp_base == nullptr) FATAL_ERROR(); - sp_bases[f._sp_base] += 1; - } - FrameId frame = top_frame(); - int line = frame->co->lines[frame->_ip]; - ss << frame->co->name << ":" << line << " ["; - for(PyObject** p=s_data.begin(); p!=s_data.end(); p++){ - ss << std::string(sp_bases[p], '|'); - if(sp_bases[p] > 0) ss << " "; - PyObject* obj = *p; - if(obj == nullptr) ss << "(nil)"; - else if(obj == PY_NULL) ss << "NULL"; - else if(is_int(obj)) ss << CAST(i64, obj); - else if(is_float(obj)) ss << CAST(f64, obj); - else if(is_type(obj, tp_str)) ss << CAST(Str, obj).escape(); - else if(obj == None) ss << "None"; - else if(obj == True) ss << "True"; - else if(obj == False) ss << "False"; - else if(is_type(obj, tp_function)){ - auto& f = CAST(Function&, obj); - ss << f.decl->code->name << "(...)"; - } else if(is_type(obj, tp_type)){ - Type t = PK_OBJ_GET(Type, obj); - ss << ""; - } else if(is_type(obj, tp_list)){ - auto& t = CAST(List&, obj); - ss << "list(size=" << t.size() << ")"; - } else if(is_type(obj, tp_tuple)){ - auto& t = CAST(Tuple&, obj); - ss << "tuple(size=" << t.size() << ")"; - } else ss << "(" << obj_type_name(this, obj->type) << ")"; - ss << ", "; - } - std::string output = ss.str(); - if(!s_data.empty()) { - output.pop_back(); output.pop_back(); - } - output.push_back(']'); - Bytecode byte = frame->co->codes[frame->_ip]; - std::cout << output << " " << OP_NAMES[byte.op] << " " << _opcode_argstr(nullptr, byte, frame->co) << std::endl; -} -#endif - -inline void VM::init_builtin_types(){ - _all_types.push_back({heap._new(Type(1), Type(0)), -1, "object", true}); - _all_types.push_back({heap._new(Type(1), Type(1)), 0, "type", false}); - tp_object = 0; tp_type = 1; - - tp_int = _new_type_object("int"); - tp_float = _new_type_object("float"); - if(tp_int.index != kTpIntIndex || tp_float.index != kTpFloatIndex) FATAL_ERROR(); - - tp_bool = _new_type_object("bool"); - tp_str = _new_type_object("str"); - tp_list = _new_type_object("list"); - tp_tuple = _new_type_object("tuple"); - tp_slice = _new_type_object("slice"); - tp_range = _new_type_object("range"); - tp_module = _new_type_object("module"); - tp_function = _new_type_object("function"); - tp_native_func = _new_type_object("native_func"); - tp_bound_method = _new_type_object("bound_method"); - tp_super = _new_type_object("super"); - tp_exception = _new_type_object("Exception"); - tp_bytes = _new_type_object("bytes"); - tp_mappingproxy = _new_type_object("mappingproxy"); - tp_dict = _new_type_object("dict"); - tp_property = _new_type_object("property"); - tp_star_wrapper = _new_type_object("_star_wrapper"); - - this->None = heap._new(_new_type_object("NoneType"), {}); - this->NotImplemented = heap._new(_new_type_object("NotImplementedType"), {}); - this->Ellipsis = heap._new(_new_type_object("ellipsis"), {}); - this->True = heap._new(tp_bool, {}); - this->False = heap._new(tp_bool, {}); - this->StopIteration = heap._new(_new_type_object("StopIterationType"), {}); - - this->builtins = new_module("builtins"); - - // setup public types - builtins->attr().set("type", _t(tp_type)); - builtins->attr().set("object", _t(tp_object)); - builtins->attr().set("bool", _t(tp_bool)); - builtins->attr().set("int", _t(tp_int)); - builtins->attr().set("float", _t(tp_float)); - builtins->attr().set("str", _t(tp_str)); - builtins->attr().set("list", _t(tp_list)); - builtins->attr().set("tuple", _t(tp_tuple)); - builtins->attr().set("range", _t(tp_range)); - builtins->attr().set("bytes", _t(tp_bytes)); - builtins->attr().set("dict", _t(tp_dict)); - builtins->attr().set("property", _t(tp_property)); - builtins->attr().set("StopIteration", StopIteration); - builtins->attr().set("NotImplemented", NotImplemented); - builtins->attr().set("slice", _t(tp_slice)); - - post_init(); - for(int i=0; i<_all_types.size(); i++){ - _all_types[i].obj->attr()._try_perfect_rehash(); - } - for(auto [k, v]: _modules.items()) v->attr()._try_perfect_rehash(); - this->_main = new_module("__main__"); -} - -// `heap.gc_scope_lock();` needed before calling this function -inline void VM::_unpack_as_list(ArgsView args, List& list){ - for(PyObject* obj: args){ - if(is_non_tagged_type(obj, tp_star_wrapper)){ - const StarWrapper& w = _CAST(StarWrapper&, obj); - // maybe this check should be done in the compile time - if(w.level != 1) TypeError("expected level 1 star wrapper"); - PyObject* _0 = py_iter(w.obj); - PyObject* _1 = py_next(_0); - while(_1 != StopIteration){ - list.push_back(_1); - _1 = py_next(_0); - } - }else{ - list.push_back(obj); - } - } -} - -// `heap.gc_scope_lock();` needed before calling this function -inline void VM::_unpack_as_dict(ArgsView args, Dict& dict){ - for(PyObject* obj: args){ - if(is_non_tagged_type(obj, tp_star_wrapper)){ - const StarWrapper& w = _CAST(StarWrapper&, obj); - // maybe this check should be done in the compile time - if(w.level != 2) TypeError("expected level 2 star wrapper"); - const Dict& other = CAST(Dict&, w.obj); - dict.update(other); - }else{ - const Tuple& t = CAST(Tuple&, obj); - if(t.size() != 2) TypeError("expected tuple of length 2"); - dict.set(t[0], t[1]); - } - } -} - -inline void VM::_prepare_py_call(PyObject** buffer, ArgsView args, ArgsView kwargs, const FuncDecl_& decl){ - const CodeObject* co = decl->code.get(); - int co_nlocals = co->varnames.size(); - int decl_argc = decl->args.size(); - - if(args.size() < decl_argc){ - vm->TypeError(fmt( - "expected ", decl_argc, " positional arguments, got ", args.size(), - " (", co->name, ')' - )); - } - - int i = 0; - // prepare args - for(int index: decl->args) buffer[index] = args[i++]; - // set extra varnames to nullptr - for(int j=i; jkwargs) buffer[kv.key] = kv.value; - - // handle *args - if(decl->starred_arg != -1){ - ArgsView vargs(args.begin() + i, args.end()); - buffer[decl->starred_arg] = VAR(vargs.to_tuple()); - i += vargs.size(); - }else{ - // kwdefaults override - for(auto& kv: decl->kwargs){ - if(i >= args.size()) break; - buffer[kv.key] = args[i++]; - } - if(i < args.size()) TypeError(fmt("too many arguments", " (", decl->code->name, ')')); - } - - PyObject* vkwargs; - if(decl->starred_kwarg != -1){ - vkwargs = VAR(Dict(this)); - buffer[decl->starred_kwarg] = vkwargs; - }else{ - vkwargs = nullptr; - } - - for(int j=0; jvarnames_inv.try_get(key); - if(index < 0){ - if(vkwargs == nullptr){ - TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()")); - }else{ - Dict& dict = _CAST(Dict&, vkwargs); - dict.set(VAR(key.sv()), kwargs[j+1]); - } - }else{ - buffer[index] = kwargs[j+1]; - } - } -} - -inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ - PyObject** p1 = s_data._sp - KWARGC*2; - PyObject** p0 = p1 - ARGC - 2; - // [callable, , args..., kwargs...] - // ^p0 ^p1 ^_sp - PyObject* callable = p1[-(ARGC + 2)]; - bool method_call = p1[-(ARGC + 1)] != PY_NULL; - - // handle boundmethod, do a patch - if(is_non_tagged_type(callable, tp_bound_method)){ - if(method_call) FATAL_ERROR(); - auto& bm = CAST(BoundMethod&, callable); - callable = bm.func; // get unbound method - p1[-(ARGC + 2)] = bm.func; - p1[-(ARGC + 1)] = bm.self; - method_call = true; - // [unbound, self, args..., kwargs...] - } - - ArgsView args(p1 - ARGC - int(method_call), p1); - ArgsView kwargs(p1, s_data._sp); - - static THREAD_LOCAL PyObject* buffer[PK_MAX_CO_VARNAMES]; - - if(is_non_tagged_type(callable, tp_native_func)){ - const auto& f = PK_OBJ_GET(NativeFunc, callable); - PyObject* ret; - if(f.decl != nullptr){ - int co_nlocals = f.decl->code->varnames.size(); - _prepare_py_call(buffer, args, kwargs, f.decl); - // copy buffer back to stack - s_data.reset(args.begin()); - for(int j=0; jcode.get(); - int co_nlocals = co->varnames.size(); - - _prepare_py_call(buffer, args, kwargs, decl); - - if(co->is_generator){ - s_data.reset(p0); - return _py_generator( - Frame(&s_data, nullptr, co, fn._module, callable), - ArgsView(buffer, buffer + co_nlocals) - ); - } - - // copy buffer back to stack - s_data.reset(args.begin()); - for(int j=0; jheap.gcnew(t, {}); - }else{ - PUSH(new_f); - PUSH(PY_NULL); - PUSH(callable); // cls - for(PyObject* o: args) PUSH(o); - for(PyObject* o: kwargs) PUSH(o); - // if obj is not an instance of callable, the behavior is undefined - obj = vectorcall(ARGC+1, KWARGC); - } - - // __init__ - PyObject* self; - DEF_SNAME(__init__); - callable = get_unbound_method(obj, __init__, &self, false); - if (self != PY_NULL) { - // replace `NULL` with `self` - p1[-(ARGC + 2)] = callable; - p1[-(ARGC + 1)] = self; - // [init_f, self, args..., kwargs...] - vectorcall(ARGC, KWARGC); - // We just discard the return value of `__init__` - // in cpython it raises a TypeError if the return value is not None - }else{ - // manually reset the stack - s_data.reset(p0); - } - return obj; - } - - // handle `__call__` overload - PyObject* self; - DEF_SNAME(__call__); - PyObject* call_f = get_unbound_method(callable, __call__, &self, false); - if(self != PY_NULL){ - p1[-(ARGC + 2)] = call_f; - p1[-(ARGC + 1)] = self; - // [call_f, self, args..., kwargs...] - return vectorcall(ARGC, KWARGC, false); - } - TypeError(OBJ_NAME(_t(callable)).escape() + " object is not callable"); - return nullptr; -} - -// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance -inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){ - PyObject* objtype; - // handle super() proxy - if(is_non_tagged_type(obj, tp_super)){ - const Super& super = PK_OBJ_GET(Super, obj); - obj = super.first; - objtype = _t(super.second); - }else{ - objtype = _t(obj); - } - PyObject* cls_var = find_name_in_mro(objtype, name); - if(cls_var != nullptr){ - // handle descriptor - if(is_non_tagged_type(cls_var, tp_property)){ - const Property& prop = _CAST(Property&, cls_var); - return call(prop.getter, obj); - } - } - // handle instance __dict__ - if(!is_tagged(obj) && obj->is_attr_valid()){ - PyObject* val = obj->attr().try_get(name); - if(val != nullptr) return val; - } - if(cls_var != nullptr){ - // bound method is non-data descriptor - if(is_non_tagged_type(cls_var, tp_function) || is_non_tagged_type(cls_var, tp_native_func)){ - return VAR(BoundMethod(obj, cls_var)); - } - return cls_var; - } - if(throw_err) AttributeError(obj, name); - return nullptr; -} - -// used by OP_LOAD_METHOD -// try to load a unbound method (fallback to `getattr` if not found) -inline PyObject* VM::get_unbound_method(PyObject* obj, StrName name, PyObject** self, bool throw_err, bool fallback){ - *self = PY_NULL; - PyObject* objtype; - // handle super() proxy - if(is_non_tagged_type(obj, tp_super)){ - const Super& super = PK_OBJ_GET(Super, obj); - obj = super.first; - objtype = _t(super.second); - }else{ - objtype = _t(obj); - } - PyObject* cls_var = find_name_in_mro(objtype, name); - - if(fallback){ - if(cls_var != nullptr){ - // handle descriptor - if(is_non_tagged_type(cls_var, tp_property)){ - const Property& prop = _CAST(Property&, cls_var); - return call(prop.getter, obj); - } - } - // handle instance __dict__ - if(!is_tagged(obj) && obj->is_attr_valid()){ - PyObject* val = obj->attr().try_get(name); - if(val != nullptr) return val; - } - } - - if(cls_var != nullptr){ - if(is_non_tagged_type(cls_var, tp_function) || is_non_tagged_type(cls_var, tp_native_func)){ - *self = obj; - } - return cls_var; - } - if(throw_err) AttributeError(obj, name); - return nullptr; -} - -inline void VM::setattr(PyObject* obj, StrName name, PyObject* value){ - PyObject* objtype; - // handle super() proxy - if(is_non_tagged_type(obj, tp_super)){ - Super& super = PK_OBJ_GET(Super, obj); - obj = super.first; - objtype = _t(super.second); - }else{ - objtype = _t(obj); - } - PyObject* cls_var = find_name_in_mro(objtype, name); - if(cls_var != nullptr){ - // handle descriptor - if(is_non_tagged_type(cls_var, tp_property)){ - const Property& prop = _CAST(Property&, cls_var); - if(prop.setter != vm->None){ - call(prop.setter, obj, value); - }else{ - TypeError(fmt("readonly attribute: ", name.escape())); - } - return; - } - } - // handle instance __dict__ - if(is_tagged(obj) || !obj->is_attr_valid()) TypeError("cannot set attribute"); - obj->attr().set(name, value); -} - template PyObject* VM::bind_method(PyObject* obj, Str name, NativeFuncC fn) { check_non_tagged_type(obj, tp_type); @@ -1545,55 +857,6 @@ PyObject* VM::bind_func(PyObject* obj, Str name, NativeFuncC fn) { return nf; } -inline PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn){ - return bind(obj, sig, nullptr, fn); -} - -inline PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn){ - CodeObject_ co; - try{ - // fn(a, b, *c, d=1) -> None - co = compile("def " + Str(sig) + " : pass", "", EXEC_MODE); - }catch(Exception& e){ - throw std::runtime_error("invalid signature: " + std::string(sig)); - } - if(co->func_decls.size() != 1){ - throw std::runtime_error("expected 1 function declaration"); - } - FuncDecl_ decl = co->func_decls[0]; - decl->signature = Str(sig); - if(docstring != nullptr){ - decl->docstring = Str(docstring).strip(); - } - PyObject* f_obj = VAR(NativeFunc(fn, decl)); - obj->attr().set(decl->code->name, f_obj); - return f_obj; -} - -inline void VM::_error(Exception e){ - if(callstack.empty()){ - e.is_re = false; - throw e; - } - PUSH(VAR(e)); - _raise(); -} - -inline void ManagedHeap::mark() { - for(PyObject* obj: _no_gc) PK_OBJ_MARK(obj); - for(auto& frame : vm->callstack.data()) frame._gc_mark(); - for(PyObject* obj: vm->s_data) PK_OBJ_MARK(obj); - if(_gc_marker_ex) _gc_marker_ex(vm); - if(vm->_last_exception) PK_OBJ_MARK(vm->_last_exception); -} - -inline Str obj_type_name(VM *vm, Type type){ - return vm->_all_types[type].name; -} - -#undef PY_VAR_INT -#undef PY_VAR_FLOAT - /***************************************************/ template @@ -1614,48 +877,4 @@ PyObject* PyArrayGetItem(VM* vm, PyObject* obj, PyObject* index){ i = vm->normalized_index(i, self.size()); return self[i]; } - -inline void VM::bind__hash__(Type type, i64 (*f)(VM*, PyObject*)){ - PyObject* obj = _t(type); - _all_types[type].m__hash__ = f; - PyObject* nf = bind_method<0>(obj, "__hash__", [](VM* vm, ArgsView args){ - i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); - return VAR(ret); - }); - PK_OBJ_GET(NativeFunc, nf).set_userdata(f); -} - -inline void VM::bind__len__(Type type, i64 (*f)(VM*, PyObject*)){ - PyObject* obj = _t(type); - _all_types[type].m__len__ = f; - PyObject* nf = bind_method<0>(obj, "__len__", [](VM* vm, ArgsView args){ - i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); - return VAR(ret); - }); - PK_OBJ_GET(NativeFunc, nf).set_userdata(f); -} - - -inline void Dict::_probe(PyObject *key, bool &ok, int &i) const{ - ok = false; - i = vm->py_hash(key) & _mask; - while(_items[i].first != nullptr) { - if(vm->py_equals(_items[i].first, key)) { ok = true; break; } - // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166 - i = ((5*i) + 1) & _mask; - } -} - -inline void CodeObjectSerializer::write_object(VM *vm, PyObject *obj){ - if(is_int(obj)) write_int(_CAST(i64, obj)); - else if(is_float(obj)) write_float(_CAST(f64, obj)); - else if(is_type(obj, vm->tp_str)) write_str(_CAST(Str&, obj)); - else if(is_type(obj, vm->tp_bool)) write_bool(_CAST(bool, obj)); - else if(obj == vm->None) write_none(); - else if(obj == vm->Ellipsis) write_ellipsis(); - else{ - throw std::runtime_error(fmt(OBJ_NAME(vm->_t(obj)).escape(), " is not serializable")); - } -} - } // namespace pkpy \ No newline at end of file diff --git a/preprocess.py b/preprocess.py index 5d3d0862..b19723b4 100644 --- a/preprocess.py +++ b/preprocess.py @@ -37,5 +37,5 @@ namespace pkpy{ ''' return header -with open("src/_generated.h", "w", encoding='utf-8') as f: +with open("include/pocketpy/_generated.h", "w", encoding='utf-8') as f: f.write(generate_python_sources()) diff --git a/scripts/loc.py b/scripts/loc.py index 97e0ac5c..e66f761d 100644 --- a/scripts/loc.py +++ b/scripts/loc.py @@ -11,13 +11,16 @@ def get_loc_for_dir(path): loc_ex = 0 for root, dirs, files in os.walk(path): for file in files: - if file.endswith('.h'): + if file.endswith('.h') or file.endswith('.cpp'): _i = get_loc(os.path.join(root, file)) print(f"{file}: {_i}") if file.startswith('_'): loc_ex += _i else: loc += _i - return f'{loc} (+{loc_ex})' + return f'{path}: {loc} (+{loc_ex})' -print(get_loc_for_dir('src')) \ No newline at end of file + +print(get_loc_for_dir('include/pocketpy')) +print() +print(get_loc_for_dir('src')) diff --git a/src/base64.h b/src/base64.cpp similarity index 94% rename from src/base64.h rename to src/base64.cpp index e5d6a087..8b9a6005 100644 --- a/src/base64.h +++ b/src/base64.cpp @@ -1,12 +1,6 @@ -#pragma once +#include "pocketpy/base64.h" -#include "common.h" - -#if PK_MODULE_BASE64 - -#include "cffi.h" - -namespace pkpy { +namespace pkpy{ // https://github.com/zhicheng/base64/blob/master/base64.c @@ -77,7 +71,7 @@ const unsigned char base64de[] = { 49, 50, 51, 255, 255, 255, 255, 255 }; -inline static unsigned int +static unsigned int base64_encode(const unsigned char *in, unsigned int inlen, char *out) { int s; @@ -126,7 +120,7 @@ base64_encode(const unsigned char *in, unsigned int inlen, char *out) return j; } -inline static unsigned int +static unsigned int base64_decode(const char *in, unsigned int inlen, unsigned char *out) { unsigned int i; @@ -171,7 +165,7 @@ base64_decode(const char *in, unsigned int inlen, unsigned char *out) return j; } -inline void add_module_base64(VM* vm){ +void add_module_base64(VM* vm){ PyObject* mod = vm->new_module("base64"); // b64encode @@ -193,11 +187,4 @@ inline void add_module_base64(VM* vm){ }); } -} // namespace pkpy - - -#else - -ADD_MODULE_PLACEHOLDER(base64) - -#endif \ No newline at end of file +} // namespace pkpy \ No newline at end of file diff --git a/src/ceval.h b/src/ceval.cpp similarity index 99% rename from src/ceval.h rename to src/ceval.cpp index d5ccd140..a8e889cd 100644 --- a/src/ceval.h +++ b/src/ceval.cpp @@ -1,12 +1,10 @@ -#pragma once - -#include "common.h" -#include "namedict.h" -#include "vm.h" +#include "pocketpy/common.h" +#include "pocketpy/namedict.h" +#include "pocketpy/vm.h" namespace pkpy{ -inline PyObject* VM::_run_top_frame(){ +PyObject* VM::_run_top_frame(){ DEF_SNAME(add); DEF_SNAME(set); DEF_SNAME(__enter__); @@ -51,7 +49,7 @@ __NEXT_FRAME: #if PK_ENABLE_COMPUTED_GOTO static void* OP_LABELS[] = { #define OPCODE(name) &&CASE_OP_##name, - #include "opcodes.h" + #include "pocketpy/opcodes.h" #undef OPCODE }; diff --git a/src/easing.h b/src/easing.cpp similarity index 96% rename from src/easing.h rename to src/easing.cpp index ac55d944..816abe46 100644 --- a/src/easing.h +++ b/src/easing.cpp @@ -1,10 +1,4 @@ -#pragma once - -#include "common.h" - -#if PK_MODULE_EASING - -#include "cffi.h" +#include "pocketpy/easing.h" namespace pkpy{ @@ -212,7 +206,7 @@ static double easeInOutBounce( double x ) { : (1 + easeOutBounce(2 * x - 1)) / 2; } -inline void add_module_easing(VM* vm){ +void add_module_easing(VM* vm){ PyObject* mod = vm->new_module("easing"); #define EASE(name) \ @@ -256,10 +250,4 @@ inline void add_module_easing(VM* vm){ #undef EASE } -} // namespace pkpy - -#else - -ADD_MODULE_PLACEHOLDER(easing) - -#endif \ No newline at end of file +} // namespace pkpy \ No newline at end of file diff --git a/src/error.h b/src/error.cpp similarity index 64% rename from src/error.h rename to src/error.cpp index f5b86f05..822694b8 100644 --- a/src/error.h +++ b/src/error.cpp @@ -1,45 +1,8 @@ -#pragma once - -#include "namedict.h" -#include "str.h" -#include "tuplelist.h" +#include "pocketpy/error.h" namespace pkpy{ -struct NeedMoreLines { - NeedMoreLines(bool is_compiling_class) : is_compiling_class(is_compiling_class) {} - bool is_compiling_class; -}; - -struct HandledException {}; -struct UnhandledException {}; -struct ToBeRaisedException {}; - -enum CompileMode { - EXEC_MODE, - EVAL_MODE, - REPL_MODE, - JSON_MODE, - CELL_MODE -}; - -struct SourceData { - std::string source; - Str filename; - std::vector line_starts; - CompileMode mode; - - std::pair get_line(int lineno) const { - if(lineno == -1) return {nullptr, nullptr}; - lineno -= 1; - if(lineno < 0) lineno = 0; - const char* _start = line_starts.at(lineno); - const char* i = _start; - while(*i != '\n' && *i != '\0') i++; - return {_start, i}; - } - - SourceData(const Str& source, const Str& filename, CompileMode mode) { + SourceData::SourceData(const Str& source, const Str& filename, CompileMode mode) { int index = 0; // Skip utf8 BOM if there is any. if (strncmp(source.begin(), "\xEF\xBB\xBF", 3) == 0) index += 3; @@ -57,7 +20,17 @@ struct SourceData { this->mode = mode; } - Str snapshot(int lineno, const char* cursor=nullptr){ + std::pair SourceData::get_line(int lineno) const { + if(lineno == -1) return {nullptr, nullptr}; + lineno -= 1; + if(lineno < 0) lineno = 0; + const char* _start = line_starts.at(lineno); + const char* i = _start; + while(*i != '\n' && *i != '\0') i++; + return {_start, i}; + } + + Str SourceData::snapshot(int lineno, const char* cursor){ std::stringstream ss; ss << " " << "File \"" << filename << "\", line " << lineno << '\n'; std::pair pair = get_line(lineno); @@ -75,25 +48,14 @@ struct SourceData { } return ss.str(); } -}; -class Exception { - using StackTrace = stack; - StrName type; - Str msg; - StackTrace stacktrace; -public: - Exception(StrName type, Str msg): type(type), msg(msg) {} - bool match_type(StrName t) const { return this->type == t;} - bool is_re = true; - - void st_push(Str snapshot){ + void Exception::st_push(Str snapshot){ if(stacktrace.size() >= 8) return; stacktrace.push(snapshot); } - Str summary() const { - StackTrace st(stacktrace); + Str Exception::summary() const { + stack st(stacktrace); std::stringstream ss; if(is_re) ss << "Traceback (most recent call last):\n"; while(!st.empty()) { ss << st.top() << '\n'; st.pop(); } @@ -101,6 +63,5 @@ public: else ss << type.sv(); return ss.str(); } -}; } // namespace pkpy \ No newline at end of file diff --git a/src/iter.h b/src/iter.cpp similarity index 65% rename from src/iter.h rename to src/iter.cpp index c7bad046..773dea60 100644 --- a/src/iter.h +++ b/src/iter.cpp @@ -1,18 +1,8 @@ -#pragma once - -#include "cffi.h" -#include "common.h" -#include "frame.h" +#include "pocketpy/iter.h" namespace pkpy{ -struct RangeIter{ - PY_CLASS(RangeIter, builtins, "_range_iterator") - Range r; - i64 current; - RangeIter(Range r) : r(r), current(r.start) {} - - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void RangeIter::_register(VM* vm, PyObject* mod, PyObject* type){ vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false; vm->bind_notimplemented_constructor(type); vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ return obj; }); @@ -24,21 +14,8 @@ struct RangeIter{ return VAR(self.current - self.r.step); }); } -}; -struct ArrayIter{ - PY_CLASS(ArrayIter, builtins, "_array_iterator") - PyObject* ref; - PyObject** begin; - PyObject** end; - PyObject** current; - - ArrayIter(PyObject* ref, PyObject** begin, PyObject** end) - : ref(ref), begin(begin), end(end), current(begin) {} - - void _gc_mark() const{ PK_OBJ_MARK(ref); } - - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void ArrayIter::_register(VM* vm, PyObject* mod, PyObject* type){ vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false; vm->bind_notimplemented_constructor(type); vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ return obj; }); @@ -48,19 +25,8 @@ struct ArrayIter{ return *self.current++; }); } -}; -struct StringIter{ - PY_CLASS(StringIter, builtins, "_string_iterator") - PyObject* ref; - Str* str; - int index; - - StringIter(PyObject* ref) : ref(ref), str(&PK_OBJ_GET(Str, ref)), index(0) {} - - void _gc_mark() const{ PK_OBJ_MARK(ref); } - - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void StringIter::_register(VM* vm, PyObject* mod, PyObject* type){ vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false; vm->bind_notimplemented_constructor(type); vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ return obj; }); @@ -71,24 +37,8 @@ struct StringIter{ return VAR(self.str->u8_getitem(self.index++)); }); } -}; -struct Generator{ - PY_CLASS(Generator, builtins, "_generator") - Frame frame; - int state; // 0,1,2 - List s_backup; - - Generator(Frame&& frame, ArgsView buffer): frame(std::move(frame)), state(0) { - for(PyObject* obj: buffer) s_backup.push_back(obj); - } - - void _gc_mark() const{ - frame._gc_mark(); - for(PyObject* obj: s_backup) PK_OBJ_MARK(obj); - } - - PyObject* next(VM* vm){ + PyObject* Generator::next(VM* vm){ if(state == 2) return vm->StopIteration; // reset frame._sp_base frame._sp_base = frame._s->_sp; @@ -113,7 +63,7 @@ struct Generator{ } } - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void Generator::_register(VM* vm, PyObject* mod, PyObject* type){ vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false; vm->bind_notimplemented_constructor(type); vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ return obj; }); @@ -122,10 +72,9 @@ struct Generator{ return self.next(vm); }); } -}; -inline PyObject* VM::_py_generator(Frame&& frame, ArgsView buffer){ +PyObject* VM::_py_generator(Frame&& frame, ArgsView buffer){ return VAR_T(Generator, std::move(frame), buffer); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy \ No newline at end of file diff --git a/src/linalg.h b/src/linalg.cpp similarity index 54% rename from src/linalg.h rename to src/linalg.cpp index 193c5e77..600ec996 100644 --- a/src/linalg.h +++ b/src/linalg.cpp @@ -1,310 +1,7 @@ -#pragma once - -#include "common.h" - -#if PK_MODULE_LINALG - -#include "cffi.h" +#include "pocketpy/pocketpy.h" namespace pkpy{ -static constexpr float kEpsilon = 1e-4f; -inline static bool isclose(float a, float b){ return fabsf(a - b) < kEpsilon; } - -struct Vec2{ - float x, y; - Vec2() : x(0.0f), y(0.0f) {} - Vec2(float x, float y) : x(x), y(y) {} - Vec2(const Vec2& v) : x(v.x), y(v.y) {} - - Vec2 operator+(const Vec2& v) const { return Vec2(x + v.x, y + v.y); } - Vec2& operator+=(const Vec2& v) { x += v.x; y += v.y; return *this; } - Vec2 operator-(const Vec2& v) const { return Vec2(x - v.x, y - v.y); } - Vec2& operator-=(const Vec2& v) { x -= v.x; y -= v.y; return *this; } - Vec2 operator*(float s) const { return Vec2(x * s, y * s); } - Vec2& operator*=(float s) { x *= s; y *= s; return *this; } - Vec2 operator/(float s) const { return Vec2(x / s, y / s); } - Vec2& operator/=(float s) { x /= s; y /= s; return *this; } - 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 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); } -}; - -struct Vec3{ - 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(const Vec3& v) : 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+=(const Vec3& v) { x += v.x; y += v.y; z += v.z; return *this; } - Vec3 operator-(const Vec3& v) const { return Vec3(x - v.x, y - v.y, z - v.z); } - Vec3& operator-=(const Vec3& v) { x -= v.x; y -= v.y; z -= v.z; return *this; } - Vec3 operator*(float s) const { return Vec3(x * s, y * s, z * s); } - Vec3& operator*=(float s) { x *= s; y *= s; z *= s; return *this; } - Vec3 operator/(float s) const { return Vec3(x / s, y / s, z / s); } - Vec3& operator/=(float s) { x /= s; y /= s; z /= s; return *this; } - 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 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{ - 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(const Vec4& v) : 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+=(const Vec4& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } - 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) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } - Vec4 operator*(float s) const { return Vec4(x * s, y * s, z * s, w * s); } - Vec4& operator*=(float s) { x *= s; y *= s; z *= s; w *= s; return *this; } - Vec4 operator/(float s) const { return Vec4(x / s, y / s, z / s, w / s); } - Vec4& operator/=(float s) { x /= s; y /= s; z /= s; w /= s; return *this; } - 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 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); } -}; - -struct Mat3x3{ - union { - struct { - float _11, _12, _13; - float _21, _22, _23; - float _31, _32, _33; - }; - float m[3][3]; - float v[9]; - }; - - 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) {} - - void set_zeros(){ for (int i=0; i<9; ++i) v[i] = 0.0f; } - void set_ones(){ for (int i=0; i<9; ++i) v[i] = 1.0f; } - void set_identity(){ set_zeros(); _11 = _22 = _33 = 1.0f; } - - static Mat3x3 zeros(){ - static Mat3x3 ret(0, 0, 0, 0, 0, 0, 0, 0, 0); - return ret; - } - - static Mat3x3 ones(){ - static Mat3x3 ret(1, 1, 1, 1, 1, 1, 1, 1, 1); - return ret; - } - - static Mat3x3 identity(){ - static Mat3x3 ret(1, 0, 0, 0, 1, 0, 0, 0, 1); - return ret; - } - - 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 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 operator*(float scalar) const{ - Mat3x3 ret; - for (int i=0; i<9; ++i) ret.v[i] = v[i] * scalar; - return ret; - } - - Mat3x3 operator/(float scalar) const{ - Mat3x3 ret; - for (int i=0; i<9; ++i) ret.v[i] = v[i] / scalar; - return ret; - } - - Mat3x3& operator+=(const Mat3x3& other){ - for (int i=0; i<9; ++i) v[i] += other.v[i]; - return *this; - } - - Mat3x3& operator-=(const Mat3x3& other){ - for (int i=0; i<9; ++i) v[i] -= other.v[i]; - return *this; - } - - Mat3x3& operator*=(float scalar){ - for (int i=0; i<9; ++i) v[i] *= scalar; - return *this; - } - - Mat3x3& operator/=(float scalar){ - for (int i=0; i<9; ++i) v[i] /= scalar; - return *this; - } - - Mat3x3 matmul(const Mat3x3& other) const{ - Mat3x3 ret; - ret._11 = _11 * other._11 + _12 * other._21 + _13 * other._31; - ret._12 = _11 * other._12 + _12 * other._22 + _13 * other._32; - ret._13 = _11 * other._13 + _12 * other._23 + _13 * other._33; - ret._21 = _21 * other._11 + _22 * other._21 + _23 * other._31; - ret._22 = _21 * other._12 + _22 * other._22 + _23 * other._32; - ret._23 = _21 * other._13 + _22 * other._23 + _23 * other._33; - ret._31 = _31 * other._11 + _32 * other._21 + _33 * other._31; - ret._32 = _31 * other._12 + _32 * other._22 + _33 * other._32; - ret._33 = _31 * other._13 + _32 * other._23 + _33 * other._33; - return ret; - } - - Vec3 matmul(const Vec3& other) const{ - Vec3 ret; - ret.x = _11 * other.x + _12 * other.y + _13 * other.z; - ret.y = _21 * other.x + _22 * other.y + _23 * other.z; - ret.z = _31 * other.x + _32 * other.y + _33 * other.z; - return ret; - } - - bool operator==(const Mat3x3& other) const{ - for (int i=0; i<9; ++i){ - if (!isclose(v[i], other.v[i])) return false; - } - return true; - } - - bool operator!=(const Mat3x3& other) const{ - for (int i=0; i<9; ++i){ - if (!isclose(v[i], other.v[i])) return true; - } - return false; - } - - float determinant() const{ - return _11 * _22 * _33 + _12 * _23 * _31 + _13 * _21 * _32 - - _11 * _23 * _32 - _12 * _21 * _33 - _13 * _22 * _31; - } - - 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 inverse(Mat3x3& ret) const{ - float det = determinant(); - if (fabsf(det) < kEpsilon) return false; - float inv_det = 1.0f / det; - ret._11 = (_22 * _33 - _23 * _32) * inv_det; - ret._12 = (_13 * _32 - _12 * _33) * inv_det; - ret._13 = (_12 * _23 - _13 * _22) * inv_det; - ret._21 = (_23 * _31 - _21 * _33) * inv_det; - ret._22 = (_11 * _33 - _13 * _31) * inv_det; - ret._23 = (_13 * _21 - _11 * _23) * inv_det; - ret._31 = (_21 * _32 - _22 * _31) * inv_det; - ret._32 = (_12 * _31 - _11 * _32) * inv_det; - ret._33 = (_11 * _22 - _12 * _21) * inv_det; - return true; - } - - /*************** affine transformations ***************/ - static 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 is_affine() const{ - float det = _11 * _22 - _12 * _21; - if(fabsf(det) < kEpsilon) return false; - return _31 == 0.0f && _32 == 0.0f && _33 == 1.0f; - } - - Mat3x3 inverse_affine() const{ - Mat3x3 ret; - float det = _11 * _22 - _12 * _21; - float inv_det = 1.0f / det; - ret._11 = _22 * inv_det; - ret._12 = -_12 * inv_det; - ret._13 = (_12 * _23 - _13 * _22) * inv_det; - ret._21 = -_21 * inv_det; - ret._22 = _11 * inv_det; - ret._23 = (_13 * _21 - _11 * _23) * inv_det; - ret._31 = 0.0f; - ret._32 = 0.0f; - ret._33 = 1.0f; - return ret; - } - - Mat3x3 matmul_affine(const Mat3x3& other) const{ - Mat3x3 ret; - ret._11 = _11 * other._11 + _12 * other._21; - ret._12 = _11 * other._12 + _12 * other._22; - ret._13 = _11 * other._13 + _12 * other._23 + _13; - ret._21 = _21 * other._11 + _22 * other._21; - ret._22 = _21 * other._12 + _22 * other._22; - ret._23 = _21 * other._13 + _22 * other._23 + _23; - ret._31 = 0.0f; - ret._32 = 0.0f; - ret._33 = 1.0f; - return ret; - } - - Vec2 translation() const { return Vec2(_13, _23); } - float rotation() const { return atan2f(_21, _11); } - Vec2 scale() const { - return Vec2( - sqrtf(_11 * _11 + _21 * _21), - sqrtf(_12 * _12 + _22 * _22) - ); - } - - Vec2 transform_point(Vec2 vec) const { - return Vec2(_11 * vec.x + _12 * vec.y + _13, _21 * vec.x + _22 * vec.y + _23); - } - - Vec2 transform_vector(Vec2 vec) const { - return Vec2(_11 * vec.x + _12 * vec.y, _21 * vec.x + _22 * vec.y); - } -}; - -struct PyVec2; -struct PyVec3; -struct PyVec4; -struct PyMat3x3; -PyObject* py_var(VM*, Vec2); -PyObject* py_var(VM*, const PyVec2&); -PyObject* py_var(VM*, Vec3); -PyObject* py_var(VM*, const PyVec3&); -PyObject* py_var(VM*, Vec4); -PyObject* py_var(VM*, const PyVec4&); -PyObject* py_var(VM*, const Mat3x3&); -PyObject* py_var(VM*, const PyMat3x3&); - #define BIND_VEC_ADDR(D) \ vm->bind_method<0>(type, "addr", [](VM* vm, ArgsView args){ \ PyVec##D& self = _CAST(PyVec##D&, args[0]); \ @@ -348,14 +45,8 @@ PyObject* py_var(VM*, const PyMat3x3&); return vm->None; \ })); -struct PyVec2: Vec2 { - PY_CLASS(PyVec2, linalg, vec2) - PyVec2() : Vec2() {} - PyVec2(const Vec2& v) : Vec2(v) {} - PyVec2(const PyVec2& v) : Vec2(v) {} - - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void PyVec2::_register(VM* vm, PyObject* mod, PyObject* type){ vm->bind_constructor<3>(type, [](VM* vm, ArgsView args){ float x = CAST_F(args[1]); float y = CAST_F(args[2]); @@ -406,16 +97,8 @@ struct PyVec2: Vec2 { BIND_VEC_FUNCTION_0(2, length_squared) BIND_VEC_FUNCTION_0(2, normalize) } -}; -struct PyVec3: Vec3 { - PY_CLASS(PyVec3, linalg, vec3) - - PyVec3() : Vec3() {} - PyVec3(const Vec3& v) : Vec3(v) {} - PyVec3(const PyVec3& v) : Vec3(v) {} - - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void PyVec3::_register(VM* vm, PyObject* mod, PyObject* type){ vm->bind_constructor<4>(type, [](VM* vm, ArgsView args){ float x = CAST_F(args[1]); float y = CAST_F(args[2]); @@ -456,16 +139,8 @@ struct PyVec3: Vec3 { BIND_VEC_FUNCTION_0(3, length_squared) BIND_VEC_FUNCTION_0(3, normalize) } -}; -struct PyVec4: Vec4{ - PY_CLASS(PyVec4, linalg, vec4) - - PyVec4(): Vec4(){} - PyVec4(const Vec4& v): Vec4(v){} - PyVec4(const PyVec4& v): Vec4(v){} - - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void PyVec4::_register(VM* vm, PyObject* mod, PyObject* type){ vm->bind_constructor<1+4>(type, [](VM* vm, ArgsView args){ float x = CAST_F(args[1]); float y = CAST_F(args[2]); @@ -507,16 +182,15 @@ struct PyVec4: Vec4{ BIND_VEC_FUNCTION_0(4, length_squared) BIND_VEC_FUNCTION_0(4, normalize) } -}; -struct PyMat3x3: Mat3x3{ - PY_CLASS(PyMat3x3, linalg, mat3x3) +#undef BIND_VEC_ADDR +#undef BIND_VEC_VEC_OP +#undef BIND_VEC_FLOAT_OP +#undef BIND_VEC_FIELD +#undef BIND_VEC_FUNCTION_0 +#undef BIND_VEC_FUNCTION_1 - PyMat3x3(): Mat3x3(){} - PyMat3x3(const Mat3x3& other): Mat3x3(other){} - PyMat3x3(const PyMat3x3& other): Mat3x3(other){} - - static void _register(VM* vm, PyObject* mod, PyObject* type){ + void PyMat3x3::_register(VM* vm, PyObject* mod, PyObject* type){ vm->bind_constructor<-1>(type, [](VM* vm, ArgsView args){ if(args.size() == 1+0) return VAR_T(PyMat3x3, Mat3x3::zeros()); if(args.size() == 1+9){ @@ -768,44 +442,5 @@ struct PyMat3x3: Mat3x3{ return VAR_T(PyVec2, self.transform_vector(v)); }); } -}; -inline PyObject* py_var(VM* vm, Vec2 obj){ return VAR_T(PyVec2, obj); } -inline PyObject* py_var(VM* vm, const PyVec2& obj){ return VAR_T(PyVec2, obj);} - -inline PyObject* py_var(VM* vm, Vec3 obj){ return VAR_T(PyVec3, obj); } -inline PyObject* py_var(VM* vm, const PyVec3& obj){ return VAR_T(PyVec3, obj);} - -inline PyObject* py_var(VM* vm, Vec4 obj){ return VAR_T(PyVec4, obj); } -inline PyObject* py_var(VM* vm, const PyVec4& obj){ return VAR_T(PyVec4, obj);} - -inline PyObject* py_var(VM* vm, const Mat3x3& obj){ return VAR_T(PyMat3x3, obj); } -inline PyObject* py_var(VM* vm, const PyMat3x3& obj){ return VAR_T(PyMat3x3, obj); } - -template<> inline Vec2 py_cast(VM* vm, PyObject* obj) { return CAST(PyVec2&, obj); } -template<> inline Vec3 py_cast(VM* vm, PyObject* obj) { return CAST(PyVec3&, obj); } -template<> inline Vec4 py_cast(VM* vm, PyObject* obj) { return CAST(PyVec4&, obj); } -template<> inline Mat3x3 py_cast(VM* vm, PyObject* obj) { return CAST(PyMat3x3&, obj); } - -template<> inline Vec2 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyVec2&, obj); } -template<> inline Vec3 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyVec3&, obj); } -template<> inline Vec4 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyVec4&, obj); } -template<> inline Mat3x3 _py_cast(VM* vm, PyObject* obj) { return _CAST(PyMat3x3&, obj); } - -inline void add_module_linalg(VM* vm){ - PyObject* linalg = vm->new_module("linalg"); - PyVec2::register_class(vm, linalg); - PyVec3::register_class(vm, linalg); - PyVec4::register_class(vm, linalg); - PyMat3x3::register_class(vm, linalg); -} - -static_assert(sizeof(Py_) <= 64); - -} // namespace pkpy - -#else - -ADD_MODULE_PLACEHOLDER(linalg) - -#endif \ No newline at end of file +} // namespace pkpy \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4ae9f23e..bcad2c7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ #include #include -#include "pocketpy.h" +#include "pocketpy/pocketpy.h" #ifndef __EMSCRIPTEN__ diff --git a/src/namedict.cpp b/src/namedict.cpp new file mode 100644 index 00000000..bdbf72ff --- /dev/null +++ b/src/namedict.cpp @@ -0,0 +1,23 @@ +#include "pocketpy/namedict.h" + +namespace pkpy{ + +uint16_t _find_perfect_hash_seed(uint16_t capacity, const std::vector& keys){ + if(keys.empty()) return kHashSeeds[0]; + static std::set indices; + indices.clear(); + std::pair best_score = {kHashSeeds[0], 0.0f}; + const int kHashSeedsSize = sizeof(kHashSeeds) / sizeof(kHashSeeds[0]); + for(int i=0; i best_score.second) best_score = {kHashSeeds[i], score}; + } + return best_score.first; +} + +} // namespace pkpy \ No newline at end of file diff --git a/src/pocketpy.h b/src/pocketpy.cpp similarity index 92% rename from src/pocketpy.h rename to src/pocketpy.cpp index 79c9073c..c322c6a6 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.cpp @@ -1,38 +1,8 @@ -#pragma once +#include "pocketpy/pocketpy.h" -#include "ceval.h" -#include "compiler.h" -#include "obj.h" -#include "repl.h" -#include "iter.h" -#include "base64.h" -#include "cffi.h" -#include "linalg.h" -#include "easing.h" -#include "io.h" -#include "_generated.h" -#include "export.h" -#include "vm.h" -#include "re.h" -#include "random.h" +namespace pkpy{ -namespace pkpy { - -inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope) { - Compiler compiler(this, source, filename, mode, unknown_global_scope); - try{ - return compiler.compile(); - }catch(Exception& e){ -#if PK_DEBUG_FULL_EXCEPTION - std::cerr << e.summary() << std::endl; -#endif - _error(e); - return nullptr; - } -} - - -inline void init_builtins(VM* _vm) { +void init_builtins(VM* _vm) { #define BIND_NUM_ARITH_OPT(name, op) \ _vm->bind##name(_vm->tp_int, [](VM* vm, PyObject* lhs, PyObject* rhs) { \ if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs)); \ @@ -1178,7 +1148,8 @@ inline void init_builtins(VM* _vm) { Generator::register_class(_vm, _vm->builtins); } -inline void add_module_timeit(VM* vm){ + +void add_module_timeit(VM* vm){ PyObject* mod = vm->new_module("timeit"); vm->bind_func<2>(mod, "timeit", [](VM* vm, ArgsView args) { PyObject* f = args[0]; @@ -1191,7 +1162,7 @@ inline void add_module_timeit(VM* vm){ }); } -inline void add_module_time(VM* vm){ +void add_module_time(VM* vm){ PyObject* mod = vm->new_module("time"); vm->bind_func<0>(mod, "time", [](VM* vm, ArgsView args) { auto now = std::chrono::system_clock::now(); @@ -1227,57 +1198,8 @@ inline void add_module_time(VM* vm){ }); } -struct PyREPL{ - PY_CLASS(PyREPL, sys, _repl) - REPL* repl; - - PyREPL(VM* vm){ repl = new REPL(vm); } - ~PyREPL(){ delete repl; } - - PyREPL(const PyREPL&) = delete; - PyREPL& operator=(const PyREPL&) = delete; - - PyREPL(PyREPL&& other) noexcept{ - repl = other.repl; - other.repl = nullptr; - } - - struct TempOut{ - PrintFunc backup; - VM* vm; - TempOut(VM* vm, PrintFunc f){ - this->vm = vm; - this->backup = vm->_stdout; - vm->_stdout = f; - } - ~TempOut(){ - vm->_stdout = backup; - } - TempOut(const TempOut&) = delete; - TempOut& operator=(const TempOut&) = delete; - TempOut(TempOut&&) = delete; - TempOut& operator=(TempOut&&) = delete; - }; - - static void _register(VM* vm, PyObject* mod, PyObject* type){ - vm->bind_constructor<1>(type, [](VM* vm, ArgsView args){ - return VAR_T(PyREPL, vm); - }); - - vm->bind_method<1>(type, "input", [](VM* vm, ArgsView args){ - PyREPL& self = _CAST(PyREPL&, args[0]); - const Str& s = CAST(Str&, args[1]); - static std::stringstream ss_out; - ss_out.str(""); - TempOut _(vm, [](VM* vm, const Str& s){ ss_out << s; }); - bool ok = self.repl->input(s.str()); - return VAR(Tuple({VAR(ok), VAR(ss_out.str())})); - }); - } -}; - -inline void add_module_sys(VM* vm){ +void add_module_sys(VM* vm){ PyObject* mod = vm->new_module("sys"); PyREPL::register_class(vm, mod); vm->setattr(mod, "version", VAR(PK_VERSION)); @@ -1298,7 +1220,7 @@ inline void add_module_sys(VM* vm){ }); } -inline void add_module_json(VM* vm){ +void add_module_json(VM* vm){ PyObject* mod = vm->new_module("json"); vm->bind_func<1>(mod, "loads", [](VM* vm, ArgsView args) { const Str& expr = CAST(Str&, args[0]); @@ -1313,7 +1235,7 @@ inline void add_module_json(VM* vm){ // https://docs.python.org/3.5/library/math.html -inline void add_module_math(VM* vm){ +void add_module_math(VM* vm){ PyObject* mod = vm->new_module("math"); mod->attr().set("pi", VAR(3.1415926535897932384)); mod->attr().set("e" , VAR(2.7182818284590452354)); @@ -1388,7 +1310,7 @@ inline void add_module_math(VM* vm){ }); } -inline void add_module_traceback(VM* vm){ +void add_module_traceback(VM* vm){ PyObject* mod = vm->new_module("traceback"); vm->bind_func<0>(mod, "print_exc", [](VM* vm, ArgsView args) { if(vm->_last_exception==nullptr) vm->ValueError("no exception"); @@ -1404,7 +1326,7 @@ inline void add_module_traceback(VM* vm){ }); } -inline void add_module_dis(VM* vm){ +void add_module_dis(VM* vm){ PyObject* mod = vm->new_module("dis"); static const auto get_code = [](VM* vm, PyObject* obj)->CodeObject_{ @@ -1429,12 +1351,13 @@ inline void add_module_dis(VM* vm){ }); } -inline void add_module_gc(VM* vm){ +void add_module_gc(VM* vm){ PyObject* mod = vm->new_module("gc"); vm->bind_func<0>(mod, "collect", PK_LAMBDA(VAR(vm->heap.collect()))); } -inline void VM::post_init(){ + +void VM::post_init(){ init_builtins(this); _t(tp_object)->attr().set("__class__", property(PK_LAMBDA(vm->_t(args[0])))); @@ -1513,83 +1436,4 @@ inline void VM::post_init(){ #endif } -} // namespace pkpy - -/*************************GLOBAL NAMESPACE*************************/ -extern "C" { - PK_LEGACY_EXPORT - void pkpy_free(void* p){ - free(p); - } - - PK_LEGACY_EXPORT - void pkpy_vm_exec(pkpy::VM* vm, const char* source){ - vm->exec(source, "main.py", pkpy::EXEC_MODE); - } - - PK_LEGACY_EXPORT - void pkpy_vm_exec_2(pkpy::VM* vm, const char* source, const char* filename, int mode, const char* module){ - pkpy::PyObject* mod; - if(module == nullptr) mod = vm->_main; - else{ - mod = vm->_modules.try_get(module); - if(mod == nullptr) return; - } - vm->exec(source, filename, (pkpy::CompileMode)mode, mod); - } - - PK_LEGACY_EXPORT - void pkpy_vm_compile(pkpy::VM* vm, const char* source, const char* filename, int mode, bool* ok, char** res){ - try{ - pkpy::CodeObject_ code = vm->compile(source, filename, (pkpy::CompileMode)mode); - *res = code->serialize(vm).c_str_dup(); - *ok = true; - }catch(pkpy::Exception& e){ - *ok = false; - *res = e.summary().c_str_dup(); - }catch(std::exception& e){ - *ok = false; - *res = strdup(e.what()); - }catch(...){ - *ok = false; - *res = strdup("unknown error"); - } - } - - PK_LEGACY_EXPORT - pkpy::REPL* pkpy_new_repl(pkpy::VM* vm){ - pkpy::REPL* p = new pkpy::REPL(vm); - return p; - } - - PK_LEGACY_EXPORT - bool pkpy_repl_input(pkpy::REPL* r, const char* line){ - return r->input(line); - } - - PK_LEGACY_EXPORT - void pkpy_vm_add_module(pkpy::VM* vm, const char* name, const char* source){ - vm->_lazy_modules[name] = source; - } - - PK_LEGACY_EXPORT - pkpy::VM* pkpy_new_vm(bool enable_os=true){ - pkpy::VM* p = new pkpy::VM(enable_os); - return p; - } - - PK_LEGACY_EXPORT - void pkpy_delete_vm(pkpy::VM* vm){ - delete vm; - } - - PK_LEGACY_EXPORT - void pkpy_delete_repl(pkpy::REPL* repl){ - delete repl; - } - - PK_LEGACY_EXPORT - void pkpy_vm_gc_on_delete(pkpy::VM* vm, void (*f)(pkpy::VM *, pkpy::PyObject *)){ - vm->heap._gc_on_delete = f; - } -} +} // namespace pkpy \ No newline at end of file diff --git a/src/str.cpp b/src/str.cpp new file mode 100644 index 00000000..d98bb24d --- /dev/null +++ b/src/str.cpp @@ -0,0 +1,242 @@ +#include "pocketpy/str.h" + +namespace pkpy { + + Str& Str::operator=(const Str& other){ + if(!is_inlined()) pool64.dealloc(data); + size = other.size; + is_ascii = other.is_ascii; + _alloc(); + memcpy(data, other.data, size); + return *this; + } + + Str Str::operator+(const Str& other) const { + Str ret(size + other.size, is_ascii && other.is_ascii); + memcpy(ret.data, data, size); + memcpy(ret.data + size, other.data, other.size); + return ret; + } + + Str Str::operator+(const char* p) const { + Str other(p); + return *this + other; + } + + bool Str::operator==(const Str& other) const { + if(size != other.size) return false; + return memcmp(data, other.data, size) == 0; + } + + bool Str::operator!=(const Str& other) const { + if(size != other.size) return true; + return memcmp(data, other.data, size) != 0; + } + + bool Str::operator==(const std::string_view other) const { + if(size != (int)other.size()) return false; + return memcmp(data, other.data(), size) == 0; + } + + bool Str::operator!=(const std::string_view other) const { + if(size != (int)other.size()) return true; + return memcmp(data, other.data(), size) != 0; + } + + bool Str::operator==(const char* p) const { + return *this == std::string_view(p); + } + + bool Str::operator!=(const char* p) const { + return *this != std::string_view(p); + } + + bool Str::operator<(const Str& other) const { + int ret = strncmp(data, other.data, std::min(size, other.size)); + if(ret != 0) return ret < 0; + return size < other.size; + } + + bool Str::operator<(const std::string_view other) const { + int ret = strncmp(data, other.data(), std::min(size, (int)other.size())); + if(ret != 0) return ret < 0; + return size < (int)other.size(); + } + + bool Str::operator>(const Str& other) const { + int ret = strncmp(data, other.data, std::min(size, other.size)); + if(ret != 0) return ret > 0; + return size > other.size; + } + + bool Str::operator<=(const Str& other) const { + int ret = strncmp(data, other.data, std::min(size, other.size)); + if(ret != 0) return ret < 0; + return size <= other.size; + } + + bool Str::operator>=(const Str& other) const { + int ret = strncmp(data, other.data, std::min(size, other.size)); + if(ret != 0) return ret > 0; + return size >= other.size; + } + + Str::~Str(){ + if(!is_inlined()) pool64.dealloc(data); + if(_cached_c_str != nullptr) free((void*)_cached_c_str); + } + + Str Str::substr(int start, int len) const { + Str ret(len, is_ascii); + memcpy(ret.data, data + start, len); + return ret; + } + + Str Str::substr(int start) const { + return substr(start, size - start); + } + + char* Str::c_str_dup() const { + char* p = (char*)malloc(size + 1); + memcpy(p, data, size); + p[size] = 0; + return p; + } + + const char* Str::c_str(){ + if(_cached_c_str == nullptr){ + _cached_c_str = c_str_dup(); + } + return _cached_c_str; + } + + std::string_view Str::sv() const { + return std::string_view(data, size); + } + + std::string Str::str() const { + return std::string(data, size); + } + + Str Str::lstrip() const { + std::string copy(data, size); + copy.erase(copy.begin(), std::find_if(copy.begin(), copy.end(), [](char c) { + // std::isspace(c) does not working on windows (Debug) + return c != ' ' && c != '\t' && c != '\r' && c != '\n'; + })); + return Str(copy); + } + + Str Str::strip() const { + std::string copy(data, size); + copy.erase(copy.begin(), std::find_if(copy.begin(), copy.end(), [](char c) { + return c != ' ' && c != '\t' && c != '\r' && c != '\n'; + })); + copy.erase(std::find_if(copy.rbegin(), copy.rend(), [](char c) { + return c != ' ' && c != '\t' && c != '\r' && c != '\n'; + }).base(), copy.end()); + return Str(copy); + } + + Str Str::lower() const{ + std::string copy(data, size); + std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c){ return std::tolower(c); }); + return Str(copy); + } + + Str Str::upper() const{ + std::string copy(data, size); + std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c){ return std::toupper(c); }); + return Str(copy); + } + + Str Str::escape(bool single_quote) const { + std::stringstream ss; + ss << (single_quote ? '\'' : '"'); + for (int i=0; ioperator[](i); + switch (c) { + case '"': + if(!single_quote) ss << '\\'; + ss << '"'; + break; + case '\'': + if(single_quote) ss << '\\'; + ss << '\''; + break; + case '\\': ss << '\\' << '\\'; break; + case '\n': ss << "\\n"; break; + case '\r': ss << "\\r"; break; + case '\t': ss << "\\t"; break; + default: + if ('\x00' <= c && c <= '\x1f') { + ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (int)c; + } else { + ss << c; + } + } + } + ss << (single_quote ? '\'' : '"'); + return ss.str(); + } + + int Str::index(const Str& sub, int start) const { + auto p = std::search(data + start, data + size, sub.data, sub.data + sub.size); + if(p == data + size) return -1; + return p - data; + } + + Str Str::replace(const Str& old, const Str& new_, int count) const { + std::stringstream ss; + int start = 0; + while(true){ + int i = index(old, start); + if(i == -1) break; + ss << substr(start, i - start); + ss << new_; + start = i + old.size; + if(count != -1 && --count == 0) break; + } + ss << substr(start, size - start); + return ss.str(); + } + + + int Str::_unicode_index_to_byte(int i) const{ + if(is_ascii) return i; + int j = 0; + while(i > 0){ + j += utf8len(data[j]); + i--; + } + return j; + } + + int Str::_byte_index_to_unicode(int n) const{ + if(is_ascii) return n; + int cnt = 0; + for(int i=0; i0?istop; i+=step) ss << data[i]; + }else{ + for(int i=start; step>0?istop; i+=step) ss << u8_getitem(i); + } + return ss.str(); + } + + int Str::u8_length() const { + return _byte_index_to_unicode(size); + } +} // namespace pkpy \ No newline at end of file diff --git a/src/tuplelist.cpp b/src/tuplelist.cpp new file mode 100644 index 00000000..515b171c --- /dev/null +++ b/src/tuplelist.cpp @@ -0,0 +1,55 @@ +#include "pocketpy/tuplelist.h" + +namespace pkpy { + +Tuple::Tuple(int n){ + if(n <= 3){ + this->_args = _inlined; + }else{ + this->_args = (PyObject**)pool64.alloc(n * sizeof(void*)); + } + this->_size = n; +} + +Tuple::Tuple(const Tuple& other): Tuple(other._size){ + for(int i=0; i<_size; i++) _args[i] = other._args[i]; +} + +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(List&& other) noexcept { + _size = other.size(); + _args = other._data; + other._data = nullptr; +} + +Tuple::Tuple(std::initializer_list list): Tuple(list.size()){ + int i = 0; + for(PyObject* obj: list) _args[i++] = obj; +} + +Tuple::~Tuple(){ if(!is_inlined()) pool64.dealloc(_args); } + +List ArgsView::to_list() const{ + List ret(size()); + for(int i=0; i; - -class Tuple { - PyObject** _args; - PyObject* _inlined[3]; - int _size; - - bool is_inlined() const { return _args == _inlined; } - - void _alloc(int n){ - if(n <= 3){ - this->_args = _inlined; - }else{ - this->_args = (PyObject**)pool64.alloc(n * sizeof(void*)); - } - this->_size = n; - } - -public: - Tuple(int n){ _alloc(n); } - - Tuple(const Tuple& other){ - _alloc(other._size); - for(int i=0; i<_size; i++) _args[i] = other._args[i]; - } - - 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(List&& other) noexcept { - _size = other.size(); - _args = other._data; - other._data = nullptr; - } - - Tuple(std::initializer_list list) { - _alloc(list.size()); - int i = 0; - for(PyObject* obj: list) _args[i++] = obj; - } - - PyObject*& operator[](int i){ return _args[i]; } - PyObject* operator[](int i) const { return _args[i]; } - - int size() const { return _size; } - - PyObject** begin() const { return _args; } - PyObject** end() const { return _args + _size; } - - ~Tuple(){ if(!is_inlined()) pool64.dealloc(_args); } -}; - -// a lightweight view for function args, it does not own the memory -struct ArgsView{ - PyObject** _begin; - PyObject** _end; - - ArgsView(PyObject** begin, PyObject** end) : _begin(begin), _end(end) {} - ArgsView(const Tuple& t) : _begin(t.begin()), _end(t.end()) {} - - PyObject** begin() const { return _begin; } - PyObject** end() const { return _end; } - int size() const { return _end - _begin; } - bool empty() const { return _begin == _end; } - PyObject* operator[](int i) const { return _begin[i]; } - - List to_list() const{ - List ret(size()); - for(int i=0; im__neg__) return ti->m__neg__(this, obj); + return call_method(obj, __neg__); +} + +f64 VM::num_to_float(PyObject* obj){ + if(is_float(obj)){ + return _CAST(f64, obj); + } else if (is_int(obj)){ + return (f64)_CAST(i64, obj); + } + TypeError("expected 'int' or 'float', got " + OBJ_NAME(_t(obj)).escape()); + return 0; +} + + +bool VM::py_bool(PyObject* obj){ + if(is_non_tagged_type(obj, tp_bool)) return obj == True; + if(obj == None) return false; + if(is_int(obj)) return _CAST(i64, obj) != 0; + if(is_float(obj)) return _CAST(f64, obj) != 0.0; + PyObject* self; + PyObject* len_f = get_unbound_method(obj, __len__, &self, false); + if(self != PY_NULL){ + PyObject* ret = call_method(self, len_f); + return CAST(i64, ret) > 0; + } + return true; +} + +PyObject* VM::py_list(PyObject* it){ + auto _lock = heap.gc_scope_lock(); + it = py_iter(it); + List list; + PyObject* obj = py_next(it); + while(obj != StopIteration){ + list.push_back(obj); + obj = py_next(it); + } + return VAR(std::move(list)); +} + + + +void VM::parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step){ + auto clip = [](int value, int min, int max){ + if(value < min) return min; + if(value > max) return max; + return value; + }; + if(s.step == None) step = 1; + else step = CAST(int, s.step); + if(step == 0) ValueError("slice step cannot be zero"); + if(step > 0){ + if(s.start == None){ + start = 0; + }else{ + start = CAST(int, s.start); + if(start < 0) start += length; + start = clip(start, 0, length); + } + if(s.stop == None){ + stop = length; + }else{ + stop = CAST(int, s.stop); + if(stop < 0) stop += length; + stop = clip(stop, 0, length); + } + }else{ + if(s.start == None){ + start = length - 1; + }else{ + start = CAST(int, s.start); + if(start < 0) start += length; + start = clip(start, -1, length - 1); + } + if(s.stop == None){ + stop = -1; + }else{ + stop = CAST(int, s.stop); + if(stop < 0) stop += length; + stop = clip(stop, -1, length - 1); + } + } +} + +i64 VM::py_hash(PyObject* obj){ + const PyTypeInfo* ti = _inst_type_info(obj); + if(ti->m__hash__) return ti->m__hash__(this, obj); + PyObject* ret = call_method(obj, __hash__); + return CAST(i64, ret); +} + +PyObject* VM::format(Str spec, PyObject* obj){ + if(spec.empty()) return py_str(obj); + char type; + switch(spec.end()[-1]){ + case 'f': case 'd': case 's': + type = spec.end()[-1]; + spec = spec.substr(0, spec.length() - 1); + break; + default: type = ' '; break; + } + + char pad_c = ' '; + if(spec[0] == '0'){ + pad_c = '0'; + spec = spec.substr(1); + } + char align; + if(spec[0] == '>'){ + align = '>'; + spec = spec.substr(1); + }else if(spec[0] == '<'){ + align = '<'; + spec = spec.substr(1); + }else{ + if(is_int(obj) || is_float(obj)) align = '>'; + else align = '<'; + } + + int dot = spec.index("."); + int width, precision; + try{ + if(dot >= 0){ + width = Number::stoi(spec.substr(0, dot).str()); + precision = Number::stoi(spec.substr(dot+1).str()); + }else{ + width = Number::stoi(spec.str()); + precision = -1; + } + }catch(...){ + ValueError("invalid format specifer"); + UNREACHABLE(); + } + + if(type != 'f' && dot >= 0) ValueError("precision not allowed in the format specifier"); + Str ret; + if(type == 'f'){ + f64 val = num_to_float(obj); + if(precision < 0) precision = 6; + std::stringstream ss; + ss << std::fixed << std::setprecision(precision) << val; + ret = ss.str(); + }else if(type == 'd'){ + ret = std::to_string(CAST(i64, obj)); + }else if(type == 's'){ + ret = CAST(Str&, obj); + }else{ + ret = CAST(Str&, py_str(obj)); + } + if(width > ret.length()){ + int pad = width - ret.length(); + std::string padding(pad, pad_c); + if(align == '>') ret = padding.c_str() + ret; + else ret = ret + padding.c_str(); + } + return VAR(ret); +} + +PyObject* VM::new_module(StrName name) { + PyObject* obj = heap._new(tp_module, DummyModule()); + obj->attr().set("__name__", VAR(name.sv())); + // we do not allow override in order to avoid memory leak + // it is because Module objects are not garbage collected + if(_modules.contains(name)) throw std::runtime_error("module already exists"); + _modules.set(name, obj); + return obj; +} + +static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){ + std::string argStr = byte.arg == -1 ? "" : std::to_string(byte.arg); + switch(byte.op){ + case OP_LOAD_CONST: + if(vm != nullptr){ + argStr += fmt(" (", CAST(Str, vm->py_repr(co->consts[byte.arg])), ")"); + } + break; + case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL: + case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR: + case OP_IMPORT_NAME: case OP_BEGIN_CLASS: case OP_RAISE: + case OP_DELETE_GLOBAL: case OP_INC_GLOBAL: case OP_DEC_GLOBAL: case OP_STORE_CLASS_ATTR: + argStr += fmt(" (", StrName(byte.arg).sv(), ")"); + break; + case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST: case OP_INC_FAST: case OP_DEC_FAST: + argStr += fmt(" (", co->varnames[byte.arg].sv(), ")"); + break; + case OP_LOAD_FUNCTION: + argStr += fmt(" (", co->func_decls[byte.arg]->code->name, ")"); + break; + } + return argStr; +} + +Str VM::disassemble(CodeObject_ co){ + auto pad = [](const Str& s, const int n){ + if(s.length() >= n) return s.substr(0, n); + return s + std::string(n - s.length(), ' '); + }; + + std::vector jumpTargets; + for(auto byte : co->codes){ + if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE || byte.op == OP_SHORTCUT_IF_FALSE_OR_POP){ + jumpTargets.push_back(byte.arg); + } + } + std::stringstream ss; + int prev_line = -1; + for(int i=0; icodes.size(); i++){ + const Bytecode& byte = co->codes[i]; + Str line = std::to_string(co->lines[i]); + if(co->lines[i] == prev_line) line = ""; + else{ + if(prev_line != -1) ss << "\n"; + prev_line = co->lines[i]; + } + + std::string pointer; + if(std::find(jumpTargets.begin(), jumpTargets.end(), i) != jumpTargets.end()){ + pointer = "-> "; + }else{ + pointer = " "; + } + ss << pad(line, 8) << pointer << pad(std::to_string(i), 3); + ss << " " << pad(OP_NAMES[byte.op], 25) << " "; + // ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5); + std::string argStr = _opcode_argstr(this, byte, co.get()); + ss << argStr; + // ss << pad(argStr, 40); // may overflow + // ss << co->blocks[byte.block].type; + if(i != co->codes.size() - 1) ss << '\n'; + } + + for(auto& decl: co->func_decls){ + ss << "\n\n" << "Disassembly of " << decl->code->name << ":\n"; + ss << disassemble(decl->code); + } + ss << "\n"; + return Str(ss.str()); +} + +#if PK_DEBUG_CEVAL_STEP +void VM::_log_s_data(const char* title) { + if(_main == nullptr) return; + if(callstack.empty()) return; + std::stringstream ss; + if(title) ss << title << " | "; + std::map sp_bases; + for(Frame& f: callstack.data()){ + if(f._sp_base == nullptr) FATAL_ERROR(); + sp_bases[f._sp_base] += 1; + } + FrameId frame = top_frame(); + int line = frame->co->lines[frame->_ip]; + ss << frame->co->name << ":" << line << " ["; + for(PyObject** p=s_data.begin(); p!=s_data.end(); p++){ + ss << std::string(sp_bases[p], '|'); + if(sp_bases[p] > 0) ss << " "; + PyObject* obj = *p; + if(obj == nullptr) ss << "(nil)"; + else if(obj == PY_NULL) ss << "NULL"; + else if(is_int(obj)) ss << CAST(i64, obj); + else if(is_float(obj)) ss << CAST(f64, obj); + else if(is_type(obj, tp_str)) ss << CAST(Str, obj).escape(); + else if(obj == None) ss << "None"; + else if(obj == True) ss << "True"; + else if(obj == False) ss << "False"; + else if(is_type(obj, tp_function)){ + auto& f = CAST(Function&, obj); + ss << f.decl->code->name << "(...)"; + } else if(is_type(obj, tp_type)){ + Type t = PK_OBJ_GET(Type, obj); + ss << ""; + } else if(is_type(obj, tp_list)){ + auto& t = CAST(List&, obj); + ss << "list(size=" << t.size() << ")"; + } else if(is_type(obj, tp_tuple)){ + auto& t = CAST(Tuple&, obj); + ss << "tuple(size=" << t.size() << ")"; + } else ss << "(" << obj_type_name(this, obj->type) << ")"; + ss << ", "; + } + std::string output = ss.str(); + if(!s_data.empty()) { + output.pop_back(); output.pop_back(); + } + output.push_back(']'); + Bytecode byte = frame->co->codes[frame->_ip]; + std::cout << output << " " << OP_NAMES[byte.op] << " " << _opcode_argstr(nullptr, byte, frame->co) << std::endl; +} +#endif + +void VM::init_builtin_types(){ + _all_types.push_back({heap._new(Type(1), Type(0)), -1, "object", true}); + _all_types.push_back({heap._new(Type(1), Type(1)), 0, "type", false}); + tp_object = 0; tp_type = 1; + + tp_int = _new_type_object("int"); + tp_float = _new_type_object("float"); + if(tp_int.index != kTpIntIndex || tp_float.index != kTpFloatIndex) FATAL_ERROR(); + + tp_bool = _new_type_object("bool"); + tp_str = _new_type_object("str"); + tp_list = _new_type_object("list"); + tp_tuple = _new_type_object("tuple"); + tp_slice = _new_type_object("slice"); + tp_range = _new_type_object("range"); + tp_module = _new_type_object("module"); + tp_function = _new_type_object("function"); + tp_native_func = _new_type_object("native_func"); + tp_bound_method = _new_type_object("bound_method"); + tp_super = _new_type_object("super"); + tp_exception = _new_type_object("Exception"); + tp_bytes = _new_type_object("bytes"); + tp_mappingproxy = _new_type_object("mappingproxy"); + tp_dict = _new_type_object("dict"); + tp_property = _new_type_object("property"); + tp_star_wrapper = _new_type_object("_star_wrapper"); + + this->None = heap._new(_new_type_object("NoneType"), {}); + this->NotImplemented = heap._new(_new_type_object("NotImplementedType"), {}); + this->Ellipsis = heap._new(_new_type_object("ellipsis"), {}); + this->True = heap._new(tp_bool, {}); + this->False = heap._new(tp_bool, {}); + this->StopIteration = heap._new(_new_type_object("StopIterationType"), {}); + + this->builtins = new_module("builtins"); + + // setup public types + builtins->attr().set("type", _t(tp_type)); + builtins->attr().set("object", _t(tp_object)); + builtins->attr().set("bool", _t(tp_bool)); + builtins->attr().set("int", _t(tp_int)); + builtins->attr().set("float", _t(tp_float)); + builtins->attr().set("str", _t(tp_str)); + builtins->attr().set("list", _t(tp_list)); + builtins->attr().set("tuple", _t(tp_tuple)); + builtins->attr().set("range", _t(tp_range)); + builtins->attr().set("bytes", _t(tp_bytes)); + builtins->attr().set("dict", _t(tp_dict)); + builtins->attr().set("property", _t(tp_property)); + builtins->attr().set("StopIteration", StopIteration); + builtins->attr().set("NotImplemented", NotImplemented); + builtins->attr().set("slice", _t(tp_slice)); + + post_init(); + for(int i=0; i<_all_types.size(); i++){ + _all_types[i].obj->attr()._try_perfect_rehash(); + } + for(auto [k, v]: _modules.items()) v->attr()._try_perfect_rehash(); + this->_main = new_module("__main__"); +} + +// `heap.gc_scope_lock();` needed before calling this function +void VM::_unpack_as_list(ArgsView args, List& list){ + for(PyObject* obj: args){ + if(is_non_tagged_type(obj, tp_star_wrapper)){ + const StarWrapper& w = _CAST(StarWrapper&, obj); + // maybe this check should be done in the compile time + if(w.level != 1) TypeError("expected level 1 star wrapper"); + PyObject* _0 = py_iter(w.obj); + PyObject* _1 = py_next(_0); + while(_1 != StopIteration){ + list.push_back(_1); + _1 = py_next(_0); + } + }else{ + list.push_back(obj); + } + } +} + +// `heap.gc_scope_lock();` needed before calling this function +void VM::_unpack_as_dict(ArgsView args, Dict& dict){ + for(PyObject* obj: args){ + if(is_non_tagged_type(obj, tp_star_wrapper)){ + const StarWrapper& w = _CAST(StarWrapper&, obj); + // maybe this check should be done in the compile time + if(w.level != 2) TypeError("expected level 2 star wrapper"); + const Dict& other = CAST(Dict&, w.obj); + dict.update(other); + }else{ + const Tuple& t = CAST(Tuple&, obj); + if(t.size() != 2) TypeError("expected tuple of length 2"); + dict.set(t[0], t[1]); + } + } +} + + +void VM::_prepare_py_call(PyObject** buffer, ArgsView args, ArgsView kwargs, const FuncDecl_& decl){ + const CodeObject* co = decl->code.get(); + int co_nlocals = co->varnames.size(); + int decl_argc = decl->args.size(); + + if(args.size() < decl_argc){ + vm->TypeError(fmt( + "expected ", decl_argc, " positional arguments, got ", args.size(), + " (", co->name, ')' + )); + } + + int i = 0; + // prepare args + for(int index: decl->args) buffer[index] = args[i++]; + // set extra varnames to nullptr + for(int j=i; jkwargs) buffer[kv.key] = kv.value; + + // handle *args + if(decl->starred_arg != -1){ + ArgsView vargs(args.begin() + i, args.end()); + buffer[decl->starred_arg] = VAR(vargs.to_tuple()); + i += vargs.size(); + }else{ + // kwdefaults override + for(auto& kv: decl->kwargs){ + if(i >= args.size()) break; + buffer[kv.key] = args[i++]; + } + if(i < args.size()) TypeError(fmt("too many arguments", " (", decl->code->name, ')')); + } + + PyObject* vkwargs; + if(decl->starred_kwarg != -1){ + vkwargs = VAR(Dict(this)); + buffer[decl->starred_kwarg] = vkwargs; + }else{ + vkwargs = nullptr; + } + + for(int j=0; jvarnames_inv.try_get(key); + if(index < 0){ + if(vkwargs == nullptr){ + TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()")); + }else{ + Dict& dict = _CAST(Dict&, vkwargs); + dict.set(VAR(key.sv()), kwargs[j+1]); + } + }else{ + buffer[index] = kwargs[j+1]; + } + } +} + +PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ + PyObject** p1 = s_data._sp - KWARGC*2; + PyObject** p0 = p1 - ARGC - 2; + // [callable, , args..., kwargs...] + // ^p0 ^p1 ^_sp + PyObject* callable = p1[-(ARGC + 2)]; + bool method_call = p1[-(ARGC + 1)] != PY_NULL; + + // handle boundmethod, do a patch + if(is_non_tagged_type(callable, tp_bound_method)){ + if(method_call) FATAL_ERROR(); + auto& bm = CAST(BoundMethod&, callable); + callable = bm.func; // get unbound method + p1[-(ARGC + 2)] = bm.func; + p1[-(ARGC + 1)] = bm.self; + method_call = true; + // [unbound, self, args..., kwargs...] + } + + ArgsView args(p1 - ARGC - int(method_call), p1); + ArgsView kwargs(p1, s_data._sp); + + static THREAD_LOCAL PyObject* buffer[PK_MAX_CO_VARNAMES]; + + if(is_non_tagged_type(callable, tp_native_func)){ + const auto& f = PK_OBJ_GET(NativeFunc, callable); + PyObject* ret; + if(f.decl != nullptr){ + int co_nlocals = f.decl->code->varnames.size(); + _prepare_py_call(buffer, args, kwargs, f.decl); + // copy buffer back to stack + s_data.reset(args.begin()); + for(int j=0; jcode.get(); + int co_nlocals = co->varnames.size(); + + _prepare_py_call(buffer, args, kwargs, decl); + + if(co->is_generator){ + s_data.reset(p0); + return _py_generator( + Frame(&s_data, nullptr, co, fn._module, callable), + ArgsView(buffer, buffer + co_nlocals) + ); + } + + // copy buffer back to stack + s_data.reset(args.begin()); + for(int j=0; jheap.gcnew(t, {}); + }else{ + PUSH(new_f); + PUSH(PY_NULL); + PUSH(callable); // cls + for(PyObject* o: args) PUSH(o); + for(PyObject* o: kwargs) PUSH(o); + // if obj is not an instance of callable, the behavior is undefined + obj = vectorcall(ARGC+1, KWARGC); + } + + // __init__ + PyObject* self; + DEF_SNAME(__init__); + callable = get_unbound_method(obj, __init__, &self, false); + if (self != PY_NULL) { + // replace `NULL` with `self` + p1[-(ARGC + 2)] = callable; + p1[-(ARGC + 1)] = self; + // [init_f, self, args..., kwargs...] + vectorcall(ARGC, KWARGC); + // We just discard the return value of `__init__` + // in cpython it raises a TypeError if the return value is not None + }else{ + // manually reset the stack + s_data.reset(p0); + } + return obj; + } + + // handle `__call__` overload + PyObject* self; + DEF_SNAME(__call__); + PyObject* call_f = get_unbound_method(callable, __call__, &self, false); + if(self != PY_NULL){ + p1[-(ARGC + 2)] = call_f; + p1[-(ARGC + 1)] = self; + // [call_f, self, args..., kwargs...] + return vectorcall(ARGC, KWARGC, false); + } + TypeError(OBJ_NAME(_t(callable)).escape() + " object is not callable"); + return nullptr; +} + + + + + +// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance +PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){ + PyObject* objtype; + // handle super() proxy + if(is_non_tagged_type(obj, tp_super)){ + const Super& super = PK_OBJ_GET(Super, obj); + obj = super.first; + objtype = _t(super.second); + }else{ + objtype = _t(obj); + } + PyObject* cls_var = find_name_in_mro(objtype, name); + if(cls_var != nullptr){ + // handle descriptor + if(is_non_tagged_type(cls_var, tp_property)){ + const Property& prop = _CAST(Property&, cls_var); + return call(prop.getter, obj); + } + } + // handle instance __dict__ + if(!is_tagged(obj) && obj->is_attr_valid()){ + PyObject* val = obj->attr().try_get(name); + if(val != nullptr) return val; + } + if(cls_var != nullptr){ + // bound method is non-data descriptor + if(is_non_tagged_type(cls_var, tp_function) || is_non_tagged_type(cls_var, tp_native_func)){ + return VAR(BoundMethod(obj, cls_var)); + } + return cls_var; + } + if(throw_err) AttributeError(obj, name); + return nullptr; +} + +// used by OP_LOAD_METHOD +// try to load a unbound method (fallback to `getattr` if not found) +PyObject* VM::get_unbound_method(PyObject* obj, StrName name, PyObject** self, bool throw_err, bool fallback){ + *self = PY_NULL; + PyObject* objtype; + // handle super() proxy + if(is_non_tagged_type(obj, tp_super)){ + const Super& super = PK_OBJ_GET(Super, obj); + obj = super.first; + objtype = _t(super.second); + }else{ + objtype = _t(obj); + } + PyObject* cls_var = find_name_in_mro(objtype, name); + + if(fallback){ + if(cls_var != nullptr){ + // handle descriptor + if(is_non_tagged_type(cls_var, tp_property)){ + const Property& prop = _CAST(Property&, cls_var); + return call(prop.getter, obj); + } + } + // handle instance __dict__ + if(!is_tagged(obj) && obj->is_attr_valid()){ + PyObject* val = obj->attr().try_get(name); + if(val != nullptr) return val; + } + } + + if(cls_var != nullptr){ + if(is_non_tagged_type(cls_var, tp_function) || is_non_tagged_type(cls_var, tp_native_func)){ + *self = obj; + } + return cls_var; + } + if(throw_err) AttributeError(obj, name); + return nullptr; +} + +void VM::setattr(PyObject* obj, StrName name, PyObject* value){ + PyObject* objtype; + // handle super() proxy + if(is_non_tagged_type(obj, tp_super)){ + Super& super = PK_OBJ_GET(Super, obj); + obj = super.first; + objtype = _t(super.second); + }else{ + objtype = _t(obj); + } + PyObject* cls_var = find_name_in_mro(objtype, name); + if(cls_var != nullptr){ + // handle descriptor + if(is_non_tagged_type(cls_var, tp_property)){ + const Property& prop = _CAST(Property&, cls_var); + if(prop.setter != vm->None){ + call(prop.setter, obj, value); + }else{ + TypeError(fmt("readonly attribute: ", name.escape())); + } + return; + } + } + // handle instance __dict__ + if(is_tagged(obj) || !obj->is_attr_valid()) TypeError("cannot set attribute"); + obj->attr().set(name, value); +} + +PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn){ + return bind(obj, sig, nullptr, fn); +} + +PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn){ + CodeObject_ co; + try{ + // fn(a, b, *c, d=1) -> None + co = compile("def " + Str(sig) + " : pass", "", EXEC_MODE); + }catch(Exception& e){ + throw std::runtime_error("invalid signature: " + std::string(sig)); + } + if(co->func_decls.size() != 1){ + throw std::runtime_error("expected 1 function declaration"); + } + FuncDecl_ decl = co->func_decls[0]; + decl->signature = Str(sig); + if(docstring != nullptr){ + decl->docstring = Str(docstring).strip(); + } + PyObject* f_obj = VAR(NativeFunc(fn, decl)); + obj->attr().set(decl->code->name, f_obj); + return f_obj; +} + +void VM::_error(Exception e){ + if(callstack.empty()){ + e.is_re = false; + throw e; + } + PUSH(VAR(e)); + _raise(); +} + +void ManagedHeap::mark() { + for(PyObject* obj: _no_gc) PK_OBJ_MARK(obj); + for(auto& frame : vm->callstack.data()) frame._gc_mark(); + for(PyObject* obj: vm->s_data) PK_OBJ_MARK(obj); + if(_gc_marker_ex) _gc_marker_ex(vm); + if(vm->_last_exception) PK_OBJ_MARK(vm->_last_exception); +} + +Str obj_type_name(VM *vm, Type type){ + return vm->_all_types[type].name; +} + + +void VM::bind__hash__(Type type, i64 (*f)(VM*, PyObject*)){ + PyObject* obj = _t(type); + _all_types[type].m__hash__ = f; + PyObject* nf = bind_method<0>(obj, "__hash__", [](VM* vm, ArgsView args){ + i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(ret); + }); + PK_OBJ_GET(NativeFunc, nf).set_userdata(f); +} + +void VM::bind__len__(Type type, i64 (*f)(VM*, PyObject*)){ + PyObject* obj = _t(type); + _all_types[type].m__len__ = f; + PyObject* nf = bind_method<0>(obj, "__len__", [](VM* vm, ArgsView args){ + i64 ret = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(ret); + }); + PK_OBJ_GET(NativeFunc, nf).set_userdata(f); +} + +void Dict::_probe(PyObject *key, bool &ok, int &i) const{ + ok = false; + i = vm->py_hash(key) & _mask; + while(_items[i].first != nullptr) { + if(vm->py_equals(_items[i].first, key)) { ok = true; break; } + // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166 + i = ((5*i) + 1) & _mask; + } +} + +void CodeObjectSerializer::write_object(VM *vm, PyObject *obj){ + if(is_int(obj)) write_int(_CAST(i64, obj)); + else if(is_float(obj)) write_float(_CAST(f64, obj)); + else if(is_type(obj, vm->tp_str)) write_str(_CAST(Str&, obj)); + else if(is_type(obj, vm->tp_bool)) write_bool(_CAST(bool, obj)); + else if(obj == vm->None) write_none(); + else if(obj == vm->Ellipsis) write_ellipsis(); + else{ + throw std::runtime_error(fmt(OBJ_NAME(vm->_t(obj)).escape(), " is not serializable")); + } +} + +void NativeFunc::check_size(VM* vm, ArgsView args) const{ + if(args.size() != argc && argc != -1) { + vm->TypeError(fmt("expected ", argc, " arguments, got ", args.size())); + } +} + +PyObject* NativeFunc::call(VM *vm, ArgsView args) const { + return f(vm, args); +} + +} // namespace pkpy \ No newline at end of file