This commit is contained in:
blueloveTH 2023-07-02 01:39:24 +08:00
parent 320d2e1f3b
commit f3ac21ccc2
51 changed files with 2020 additions and 1862 deletions

2
.gitignore vendored
View File

@ -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

View File

@ -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"]
]

View File

@ -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):

View File

@ -3,4 +3,4 @@
-W*
-std=c++17
-stdlib=libc++
-Isrc
-Iinclude/

20
include/pocketpy/base64.h Normal file
View File

@ -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

View File

@ -20,7 +20,7 @@
#include <variant>
#include <type_traits>
#define PK_VERSION "1.0.7"
#define PK_VERSION "1.0.8"
#include "config.h"

19
include/pocketpy/easing.h Normal file
View File

@ -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

52
include/pocketpy/error.h Normal file
View File

@ -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<const char*> line_starts;
CompileMode mode;
SourceData(const SourceData&) = delete;
SourceData& operator=(const SourceData&) = delete;
SourceData(const Str& source, const Str& filename, CompileMode mode);
std::pair<const char*,const char*> get_line(int lineno) const;
Str snapshot(int lineno, const char* cursor=nullptr);
};
struct Exception {
StrName type;
Str msg;
bool is_re;
stack<Str> 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

View File

@ -4,8 +4,7 @@
#include "common.h"
#include "lexer.h"
#include "error.h"
#include "ceval.h"
#include "str.h"
#include "vm.h"
namespace pkpy{

View File

@ -1,8 +1,6 @@
#pragma once
#include "ceval.h"
#include "cffi.h"
#include "common.h"
#if PK_ENABLE_OS

64
include/pocketpy/iter.h Normal file
View File

@ -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

387
include/pocketpy/linalg.h Normal file
View File

@ -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<Vec2>(VM* vm, PyObject* obj) { return CAST(PyVec2&, obj); }
template<> inline Vec3 py_cast<Vec3>(VM* vm, PyObject* obj) { return CAST(PyVec3&, obj); }
template<> inline Vec4 py_cast<Vec4>(VM* vm, PyObject* obj) { return CAST(PyVec4&, obj); }
template<> inline Mat3x3 py_cast<Mat3x3>(VM* vm, PyObject* obj) { return CAST(PyMat3x3&, obj); }
template<> inline Vec2 _py_cast<Vec2>(VM* vm, PyObject* obj) { return _CAST(PyVec2&, obj); }
template<> inline Vec3 _py_cast<Vec3>(VM* vm, PyObject* obj) { return _CAST(PyVec3&, obj); }
template<> inline Vec4 _py_cast<Vec4>(VM* vm, PyObject* obj) { return _CAST(PyVec4&, obj); }
template<> inline Mat3x3 _py_cast<Mat3x3>(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_<PyMat3x3>) <= 64);
} // namespace pkpy
#else
ADD_MODULE_PLACEHOLDER(linalg)
#endif

View File

@ -86,17 +86,17 @@ struct DoubleLinkedList{
_size--;
}
void move_all_back(DoubleLinkedList<T>& 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<T>& 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

View File

@ -6,27 +6,13 @@
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 _hash(StrName key, uint16_t mask, uint16_t hash_seed){
return ( (key).index * (hash_seed) >> 8 ) & (mask);
}
inline uint16_t find_perfect_hash_seed(uint16_t capacity, const std::vector<StrName>& keys){
if(keys.empty()) return kHashSeeds[0];
static std::set<uint16_t> indices;
indices.clear();
std::pair<uint16_t, float> best_score = {kHashSeeds[0], 0.0f};
const int kHashSeedsSize = sizeof(kHashSeeds) / sizeof(kHashSeeds[0]);
for(int i=0; i<kHashSeedsSize; i++){
indices.clear();
for(auto key: keys){
uint16_t index = _hash(key, capacity-1, kHashSeeds[i]);
indices.insert(index);
}
float score = indices.size() / (float)keys.size();
if(score > best_score.second) best_score = {kHashSeeds[i], score};
}
return best_score.first;
}
uint16_t _find_perfect_hash_seed(uint16_t capacity, const std::vector<StrName>& keys);
template<typename T>
struct NameDictImpl {
@ -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
}

176
include/pocketpy/pocketpy.h Normal file
View File

@ -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;
}
}

View File

@ -1,7 +1,7 @@
#pragma once
#include "compiler.h"
#include "ceval.h"
#include "vm.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN

View File

@ -89,31 +89,23 @@ struct Str{
bool empty() const { return size == 0; }
size_t hash() const{ return std::hash<std::string_view>()(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; i<length(); i++) {
char c = this->operator[](i);
switch (c) {
case '"':
if(!single_quote) ss << '\\';
ss << '"';
break;
case '\'':
if(single_quote) ss << '\\';
ss << '\'';
break;
case '\\': ss << '\\' << '\\'; break;
case '\n': ss << "\\n"; break;
case '\r': ss << "\\r"; break;
case '\t': ss << "\\t"; break;
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; i<n; i++){
if((data[i] & 0xC0) != 0x80) cnt++;
}
return cnt;
}
Str u8_getitem(int i) const{
i = _unicode_index_to_byte(i);
return substr(i, utf8len(data[i]));
}
Str u8_slice(int start, int stop, int step) const{
std::stringstream ss;
if(is_ascii){
for(int i=start; step>0?i<stop:i>stop; i+=step) ss << data[i];
}else{
for(int i=start; step>0?i<stop:i>stop; 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<typename... Args>

View File

@ -0,0 +1,52 @@
#pragma once
#include "common.h"
#include "memory.h"
#include "str.h"
#include "vector.h"
namespace pkpy {
using List = pod_vector<PyObject*>;
struct Tuple {
PyObject** _args;
PyObject* _inlined[3];
int _size;
Tuple(int n);
Tuple(std::initializer_list<PyObject*> 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

View File

@ -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<float>(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<DummyModule>(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<int> 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; i<co->codes.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<PyObject**, int> 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 << "<class " + _all_types[t].name.escape() + ">";
} 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>(Type(1), Type(0)), -1, "object", true});
_all_types.push_back({heap._new<Type>(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<Dummy>(_new_type_object("NoneType"), {});
this->NotImplemented = heap._new<Dummy>(_new_type_object("NotImplementedType"), {});
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
this->True = heap._new<Dummy>(tp_bool, {});
this->False = heap._new<Dummy>(tp_bool, {});
this->StopIteration = heap._new<Dummy>(_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; j<co_nlocals; j++) buffer[j] = PY_NULL;
// prepare kwdefaults
for(auto& kv: decl->kwargs) 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; j<kwargs.size(); j+=2){
StrName key(CAST(int, kwargs[j]));
int index = co->varnames_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, <self>, 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; j<co_nlocals; j++) PUSH(buffer[j]);
ret = f.call(vm, ArgsView(s_data._sp - co_nlocals, s_data._sp));
}else{
if(KWARGC != 0) TypeError("old-style native_func does not accept keyword arguments");
f.check_size(this, args);
ret = f.call(this, args);
}
s_data.reset(p0);
return ret;
}
if(is_non_tagged_type(callable, tp_function)){
/*****************_py_call*****************/
// callable must be a `function` object
if(s_data.is_overflow()) StackOverflowError();
const Function& fn = PK_OBJ_GET(Function, callable);
const FuncDecl_& decl = fn.decl;
const CodeObject* co = decl->code.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; j<co_nlocals; j++) PUSH(buffer[j]);
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
if(op_call) return PY_OP_CALL;
return _run_top_frame();
/*****************_py_call*****************/
}
if(is_non_tagged_type(callable, tp_type)){
if(method_call) FATAL_ERROR();
// [type, NULL, args..., kwargs...]
DEF_SNAME(__new__);
PyObject* new_f = find_name_in_mro(callable, __new__);
PyObject* obj;
#if PK_DEBUG_EXTRA_CHECK
PK_ASSERT(new_f != nullptr);
#endif
if(new_f == cached_object__new__) {
// fast path for object.__new__
Type t = PK_OBJ_GET(Type, callable);
obj= vm->heap.gcnew<DummyInstance>(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<int ARGC>
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", "<bind>", 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<typename T>
@ -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<i64(*)(VM*, PyObject*)>(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<i64(*)(VM*, PyObject*)>(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

View File

@ -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())

View File

@ -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('include/pocketpy'))
print()
print(get_loc_for_dir('src'))

View File

@ -1,10 +1,4 @@
#pragma once
#include "common.h"
#if PK_MODULE_BASE64
#include "cffi.h"
#include "pocketpy/base64.h"
namespace pkpy{
@ -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
@ -194,10 +188,3 @@ inline void add_module_base64(VM* vm){
}
} // namespace pkpy
#else
ADD_MODULE_PLACEHOLDER(base64)
#endif

View File

@ -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
};

View File

@ -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) \
@ -257,9 +251,3 @@ inline void add_module_easing(VM* vm){
}
} // namespace pkpy
#else
ADD_MODULE_PLACEHOLDER(easing)
#endif

View File

@ -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<const char*> line_starts;
CompileMode mode;
std::pair<const char*,const char*> 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<const char*,const char*> 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<const char*,const char*> pair = get_line(lineno);
@ -75,25 +48,14 @@ struct SourceData {
}
return ss.str();
}
};
class Exception {
using StackTrace = stack<Str>;
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<Str> 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

View File

@ -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<RangeIter>(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<ArrayIter>(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<StringIter>(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<Generator>(type);
vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ return obj; });
@ -122,9 +72,8 @@ 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);
}

View File

@ -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<Vec2>(VM* vm, PyObject* obj) { return CAST(PyVec2&, obj); }
template<> inline Vec3 py_cast<Vec3>(VM* vm, PyObject* obj) { return CAST(PyVec3&, obj); }
template<> inline Vec4 py_cast<Vec4>(VM* vm, PyObject* obj) { return CAST(PyVec4&, obj); }
template<> inline Mat3x3 py_cast<Mat3x3>(VM* vm, PyObject* obj) { return CAST(PyMat3x3&, obj); }
template<> inline Vec2 _py_cast<Vec2>(VM* vm, PyObject* obj) { return _CAST(PyVec2&, obj); }
template<> inline Vec3 _py_cast<Vec3>(VM* vm, PyObject* obj) { return _CAST(PyVec3&, obj); }
template<> inline Vec4 _py_cast<Vec4>(VM* vm, PyObject* obj) { return _CAST(PyVec4&, obj); }
template<> inline Mat3x3 _py_cast<Mat3x3>(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_<PyMat3x3>) <= 64);
} // namespace pkpy
#else
ADD_MODULE_PLACEHOLDER(linalg)
#endif

View File

@ -1,7 +1,7 @@
#include <fstream>
#include <filesystem>
#include "pocketpy.h"
#include "pocketpy/pocketpy.h"
#ifndef __EMSCRIPTEN__

23
src/namedict.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "pocketpy/namedict.h"
namespace pkpy{
uint16_t _find_perfect_hash_seed(uint16_t capacity, const std::vector<StrName>& keys){
if(keys.empty()) return kHashSeeds[0];
static std::set<uint16_t> indices;
indices.clear();
std::pair<uint16_t, float> best_score = {kHashSeeds[0], 0.0f};
const int kHashSeedsSize = sizeof(kHashSeeds) / sizeof(kHashSeeds[0]);
for(int i=0; i<kHashSeedsSize; i++){
indices.clear();
for(auto key: keys){
uint16_t index = _hash(key, capacity-1, kHashSeeds[i]);
indices.insert(index);
}
float score = indices.size() / (float)keys.size();
if(score > best_score.second) best_score = {kHashSeeds[i], score};
}
return best_score.first;
}
} // namespace pkpy

View File

@ -1,38 +1,8 @@
#pragma once
#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"
#include "pocketpy/pocketpy.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;
}
}
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]))));
@ -1514,82 +1437,3 @@ inline void VM::post_init(){
}
} // 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;
}
}

242
src/str.cpp Normal file
View File

@ -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; i<length(); i++) {
char c = this->operator[](i);
switch (c) {
case '"':
if(!single_quote) ss << '\\';
ss << '"';
break;
case '\'':
if(single_quote) ss << '\\';
ss << '\'';
break;
case '\\': ss << '\\' << '\\'; break;
case '\n': ss << "\\n"; break;
case '\r': ss << "\\r"; break;
case '\t': ss << "\\t"; break;
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; i<n; i++){
if((data[i] & 0xC0) != 0x80) cnt++;
}
return cnt;
}
Str Str::u8_getitem(int i) const{
i = _unicode_index_to_byte(i);
return substr(i, utf8len(data[i]));
}
Str Str::u8_slice(int start, int stop, int step) const{
std::stringstream ss;
if(is_ascii){
for(int i=start; step>0?i<stop:i>stop; i+=step) ss << data[i];
}else{
for(int i=start; step>0?i<stop:i>stop; i+=step) ss << u8_getitem(i);
}
return ss.str();
}
int Str::u8_length() const {
return _byte_index_to_unicode(size);
}
} // namespace pkpy

55
src/tuplelist.cpp Normal file
View File

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

View File

@ -1,97 +0,0 @@
#pragma once
#include "common.h"
#include "memory.h"
#include "str.h"
#include "vector.h"
namespace pkpy {
using List = pod_vector<PyObject*>;
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<PyObject*> 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; i<size(); i++) ret[i] = _begin[i];
return ret;
}
Tuple to_tuple() const{
Tuple ret(size());
for(int i=0; i<size(); i++) ret[i] = _begin[i];
return ret;
}
};
} // namespace pkpy

790
src/vm.cpp Normal file
View File

@ -0,0 +1,790 @@
#include "pocketpy/vm.h"
namespace pkpy{
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__);
}
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<DummyModule>(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<int> 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; i<co->codes.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<PyObject**, int> 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 << "<class " + _all_types[t].name.escape() + ">";
} 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>(Type(1), Type(0)), -1, "object", true});
_all_types.push_back({heap._new<Type>(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<Dummy>(_new_type_object("NoneType"), {});
this->NotImplemented = heap._new<Dummy>(_new_type_object("NotImplementedType"), {});
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
this->True = heap._new<Dummy>(tp_bool, {});
this->False = heap._new<Dummy>(tp_bool, {});
this->StopIteration = heap._new<Dummy>(_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; j<co_nlocals; j++) buffer[j] = PY_NULL;
// prepare kwdefaults
for(auto& kv: decl->kwargs) 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; j<kwargs.size(); j+=2){
StrName key(CAST(int, kwargs[j]));
int index = co->varnames_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, <self>, 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; j<co_nlocals; j++) PUSH(buffer[j]);
ret = f.call(vm, ArgsView(s_data._sp - co_nlocals, s_data._sp));
}else{
if(KWARGC != 0) TypeError("old-style native_func does not accept keyword arguments");
f.check_size(this, args);
ret = f.call(this, args);
}
s_data.reset(p0);
return ret;
}
if(is_non_tagged_type(callable, tp_function)){
/*****************_py_call*****************/
// callable must be a `function` object
if(s_data.is_overflow()) StackOverflowError();
const Function& fn = PK_OBJ_GET(Function, callable);
const FuncDecl_& decl = fn.decl;
const CodeObject* co = decl->code.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; j<co_nlocals; j++) PUSH(buffer[j]);
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
if(op_call) return PY_OP_CALL;
return _run_top_frame();
/*****************_py_call*****************/
}
if(is_non_tagged_type(callable, tp_type)){
if(method_call) FATAL_ERROR();
// [type, NULL, args..., kwargs...]
DEF_SNAME(__new__);
PyObject* new_f = find_name_in_mro(callable, __new__);
PyObject* obj;
#if PK_DEBUG_EXTRA_CHECK
PK_ASSERT(new_f != nullptr);
#endif
if(new_f == cached_object__new__) {
// fast path for object.__new__
Type t = PK_OBJ_GET(Type, callable);
obj= vm->heap.gcnew<DummyInstance>(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", "<bind>", 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<i64(*)(VM*, PyObject*)>(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<i64(*)(VM*, PyObject*)>(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