This commit is contained in:
blueloveTH 2023-07-02 15:24:32 +08:00
parent 245388df85
commit efb7fce3c1
18 changed files with 1499 additions and 1365 deletions

View File

@ -4,65 +4,25 @@ import shutil
assert __name__ == "__main__"
os.system("python3 preprocess.py")
def DONE(code=0):
exit(code)
os.system("python3 prebuild.py")
src_file_list = []
for file in os.listdir("src"):
if file.endswith(".cpp") and file != "main.cpp" and file != "tmp.cpp":
if file.endswith(".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"])
main_src_arg = " ".join(src_file_list+["src2/main.cpp"])
print(main_src_arg)
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):
shutil.copy("c_bindings/pocketpy_c.h", "src/")
shutil.copy("c_bindings/pocketpy_c.cpp", "src/tmp.cpp")
def __exit__(self, *args):
if os.path.exists("src/pocketpy_c.h"):
os.remove("src/pocketpy_c.h")
if os.path.exists("src/tmp.cpp"):
os.remove("src/tmp.cpp")
windows_common = "CL -std:c++17 /utf-8 -GR- -EHsc -O2"
windows_cmd = windows_common + " -Fe:pocketpy src/main.cpp"
windows_lib_cmd = windows_common + " -LD -Fe:pocketpy src/tmp.cpp"
if sys.argv.__len__() == 1:
os.system(linux_cmd)
DONE()
if "windows" in sys.argv:
if "-lib" in sys.argv:
with LibBuildEnv():
os.system(windows_lib_cmd)
else:
os.system(windows_cmd)
DONE()
if "linux" in sys.argv:
if "-lib" in sys.argv:
with LibBuildEnv():
os.system(linux_lib_cmd)
else:
os.system(linux_cmd)
DONE()
if "web" in sys.argv:
os.system(r'''
rm -rf web/lib/
mkdir -p web/lib/
em++ src/main.cpp -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js
em++ ''' + main_src_arg + '''-Iinclude/ -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js
''')
DONE()
print("invalid usage!!")
exit(2)
else:
os.system(linux_cmd)

3
include/pocketpy.h Normal file
View File

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

View File

@ -56,64 +56,17 @@ struct CodeObjectSerializer{
static const char END = '\n';
CodeObjectSerializer(){
write_str(PK_VERSION);
}
CodeObjectSerializer();
void write_int(i64 v){
buffer += 'i';
buffer += std::to_string(v);
buffer += END;
}
void write_float(f64 v){
buffer += 'f';
buffer += std::to_string(v);
buffer += END;
}
void write_str(const Str& v){
buffer += 's';
buffer += v.escape(false).str();
buffer += END;
}
void write_none(){
buffer += 'N';
buffer += END;
}
void write_ellipsis(){
buffer += 'E';
buffer += END;
}
void write_bool(bool v){
buffer += 'b';
buffer += v ? '1' : '0';
buffer += END;
}
void write_begin_mark(){
buffer += '[';
buffer += END;
depth++;
}
void write_name(StrName name){
PK_ASSERT(StrName::is_valid(name.index));
buffer += 'n';
buffer += std::to_string(name.index);
buffer += END;
names.insert(name);
}
void write_end_mark(){
buffer += ']';
buffer += END;
depth--;
PK_ASSERT(depth >= 0);
}
void write_int(i64 v);
void write_float(f64 v);
void write_str(const Str& v);
void write_none();
void write_ellipsis();
void write_bool(bool v);
void write_begin_mark();
void write_name(StrName name);
void write_end_mark();
template<typename T>
void write_bytes(T v){
@ -130,16 +83,7 @@ struct CodeObjectSerializer{
void write_object(VM* vm, PyObject* obj);
void write_code(VM* vm, const CodeObject* co);
std::string str(){
PK_ASSERT(depth == 0);
for(auto name: names){
PK_ASSERT(StrName::is_valid(name.index));
write_name(name);
write_str(name.sv());
}
return std::move(buffer);
}
std::string str();
};
@ -148,9 +92,6 @@ struct CodeObject {
Str name;
bool is_generator = false;
CodeObject(shared_ptr<SourceData> src, const Str& name):
src(src), name(name) {}
std::vector<Bytecode> codes;
std::vector<int> lines; // line number for each bytecode
List consts;
@ -160,84 +101,10 @@ struct CodeObject {
NameDictInt labels;
std::vector<FuncDecl_> func_decls;
void _gc_mark() const {
for(PyObject* v : consts) PK_OBJ_MARK(v);
for(auto& decl: func_decls) decl->_gc_mark();
}
void write(VM* vm, CodeObjectSerializer& ss) const{
ss.write_begin_mark(); // [
ss.write_str(src->filename); // src->filename
ss.write_int(src->mode); // src->mode
ss.write_end_mark(); // ]
ss.write_str(name); // name
ss.write_bool(is_generator); // is_generator
ss.write_begin_mark(); // [
for(Bytecode bc: codes){
if(StrName::is_valid(bc.arg)) ss.names.insert(StrName(bc.arg));
ss.write_bytes(bc);
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(int line: lines){
ss.write_int(line); // line
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(PyObject* o: consts){
ss.write_object(vm, o);
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(StrName vn: varnames){
ss.write_name(vn); // name
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(CodeBlock block: blocks){
ss.write_bytes(block); // block
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(auto& label: labels.items()){
ss.write_name(label.first); // label.first
ss.write_int(label.second); // label.second
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(auto& decl: func_decls){
ss.write_code(vm, decl->code.get()); // decl->code
ss.write_begin_mark(); // [
for(int arg: decl->args) ss.write_int(arg);
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(auto kw: decl->kwargs){
ss.write_int(kw.key); // kw.key
ss.write_object(vm, kw.value); // kw.value
}
ss.write_end_mark(); // ]
ss.write_int(decl->starred_arg);
ss.write_int(decl->starred_kwarg);
ss.write_bool(decl->nested);
}
ss.write_end_mark(); // ]
}
Str serialize(VM* vm) const{
CodeObjectSerializer ss;
ss.write_code(vm, this);
return ss.str();
}
CodeObject(shared_ptr<SourceData> src, const Str& name);
void _gc_mark() const;
void write(VM* vm, CodeObjectSerializer& ss) const;
Str serialize(VM* vm) const;
};
inline void CodeObjectSerializer::write_code(VM* vm, const CodeObject* co){
buffer += '(';
buffer += END;
co->write(vm, *this);
buffer += ')';
buffer += END;
}
} // namespace pkpy

View File

@ -33,137 +33,24 @@ struct Dict{
Item* _items;
ItemNode* _nodes; // for order preserving
Dict(VM* vm): vm(vm), _capacity(__Capacity),
_mask(__Capacity-1),
_size(0), _critical_size(__Capacity*__LoadFactor+0.5f), _head_idx(-1), _tail_idx(-1){
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
memset(_items, 0, _capacity * sizeof(Item));
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
memset(_nodes, -1, _capacity * sizeof(ItemNode));
}
int size() const { return _size; }
Dict(Dict&& other){
vm = other.vm;
_capacity = other._capacity;
_mask = other._mask;
_size = other._size;
_critical_size = other._critical_size;
_head_idx = other._head_idx;
_tail_idx = other._tail_idx;
_items = other._items;
_nodes = other._nodes;
other._items = nullptr;
other._nodes = nullptr;
}
Dict(const Dict& other){
vm = other.vm;
_capacity = other._capacity;
_mask = other._mask;
_size = other._size;
_critical_size = other._critical_size;
_head_idx = other._head_idx;
_tail_idx = other._tail_idx;
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
memcpy(_items, other._items, _capacity * sizeof(Item));
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
memcpy(_nodes, other._nodes, _capacity * sizeof(ItemNode));
}
Dict(VM* vm);
Dict(Dict&& other);
Dict(const Dict& other);
Dict& operator=(const Dict&) = delete;
Dict& operator=(Dict&&) = delete;
int size() const { return _size; }
void _probe(PyObject* key, bool& ok, int& i) const;
void set(PyObject* key, PyObject* val){
// do possible rehash
if(_size+1 > _critical_size) _rehash();
bool ok; int i;
_probe(key, ok, i);
if(!ok) {
_size++;
_items[i].first = key;
void set(PyObject* key, PyObject* val);
void _rehash();
// append to tail
if(_size == 0+1){
_head_idx = i;
_tail_idx = i;
}else{
_nodes[i].prev = _tail_idx;
_nodes[_tail_idx].next = i;
_tail_idx = i;
}
}
_items[i].second = val;
}
PyObject* try_get(PyObject* key) const;
void _rehash(){
Item* old_items = _items;
int old_capacity = _capacity;
_capacity *= 2;
_mask = _capacity - 1;
_size = 0;
_critical_size = _capacity*__LoadFactor+0.5f;
_head_idx = -1;
_tail_idx = -1;
pool64.dealloc(_nodes);
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
memset(_items, 0, _capacity * sizeof(Item));
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
memset(_nodes, -1, _capacity * sizeof(ItemNode));
for(int i=0; i<old_capacity; i++){
if(old_items[i].first == nullptr) continue;
set(old_items[i].first, old_items[i].second);
}
pool128.dealloc(old_items);
}
PyObject* try_get(PyObject* key) const{
bool ok; int i;
_probe(key, ok, i);
if(!ok) return nullptr;
return _items[i].second;
}
bool contains(PyObject* key) const{
bool ok; int i;
_probe(key, ok, i);
return ok;
}
void erase(PyObject* key){
bool ok; int i;
_probe(key, ok, i);
if(!ok) return;
_items[i].first = nullptr;
_items[i].second = nullptr;
_size--;
if(_size == 0){
_head_idx = -1;
_tail_idx = -1;
}else{
if(_head_idx == i){
_head_idx = _nodes[i].next;
_nodes[_head_idx].prev = -1;
}else if(_tail_idx == i){
_tail_idx = _nodes[i].prev;
_nodes[_tail_idx].next = -1;
}else{
_nodes[_nodes[i].prev].next = _nodes[i].next;
_nodes[_nodes[i].next].prev = _nodes[i].prev;
}
}
_nodes[i].prev = -1;
_nodes[i].next = -1;
}
void update(const Dict& other){
other.apply([&](PyObject* k, PyObject* v){ set(k, v); });
}
bool contains(PyObject* key) const;
void erase(PyObject* key);
void update(const Dict& other);
template<typename __Func>
void apply(__Func f) const {
@ -174,50 +61,12 @@ struct Dict{
}
}
Tuple keys() const{
Tuple t(_size);
int i = _head_idx;
int j = 0;
while(i != -1){
t[j++] = _items[i].first;
i = _nodes[i].next;
}
PK_ASSERT(j == _size);
return t;
}
Tuple keys() const;
Tuple values() const;
void clear();
~Dict();
Tuple values() const{
Tuple t(_size);
int i = _head_idx;
int j = 0;
while(i != -1){
t[j++] = _items[i].second;
i = _nodes[i].next;
}
PK_ASSERT(j == _size);
return t;
}
void clear(){
_size = 0;
_head_idx = -1;
_tail_idx = -1;
memset(_items, 0, _capacity * sizeof(Item));
memset(_nodes, -1, _capacity * sizeof(ItemNode));
}
~Dict(){
if(_items==nullptr) return;
pool128.dealloc(_items);
pool64.dealloc(_nodes);
}
void _gc_mark() const{
apply([](PyObject* k, PyObject* v){
PK_OBJ_MARK(k);
PK_OBJ_MARK(v);
});
}
void _gc_mark() const;
};
} // namespace pkpy

View File

@ -53,92 +53,17 @@ struct CodeEmitContext{
bool is_compiling_class = false;
int for_loop_depth = 0;
bool is_curr_block_loop() const {
return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP;
}
void enter_block(CodeBlockType type){
if(type == FOR_LOOP) for_loop_depth++;
co->blocks.push_back(CodeBlock(
type, curr_block_i, for_loop_depth, (int)co->codes.size()
));
curr_block_i = co->blocks.size()-1;
}
void exit_block(){
auto curr_type = co->blocks[curr_block_i].type;
if(curr_type == FOR_LOOP) for_loop_depth--;
co->blocks[curr_block_i].end = co->codes.size();
curr_block_i = co->blocks[curr_block_i].parent;
if(curr_block_i < 0) FATAL_ERROR();
if(curr_type == FOR_LOOP){
// add a no op here to make block check work
emit(OP_NO_OP, BC_NOARG, BC_KEEPLINE);
}
}
// clear the expression stack and generate bytecode
void emit_expr(){
if(s_expr.size() != 1){
throw std::runtime_error("s_expr.size() != 1\n" + _log_s_expr());
}
Expr_ expr = s_expr.popx();
expr->emit(this);
}
std::string _log_s_expr(){
std::stringstream ss;
for(auto& e: s_expr.data()) ss << e->str() << " ";
return ss.str();
}
int emit(Opcode opcode, int arg, int line) {
co->codes.push_back(
Bytecode{(uint16_t)opcode, (uint16_t)curr_block_i, arg}
);
co->lines.push_back(line);
int i = co->codes.size() - 1;
if(line==BC_KEEPLINE){
if(i>=1) co->lines[i] = co->lines[i-1];
else co->lines[i] = 1;
}
return i;
}
void patch_jump(int index) {
int target = co->codes.size();
co->codes[index].arg = target;
}
bool add_label(StrName name){
if(co->labels.contains(name)) return false;
co->labels.set(name, co->codes.size());
return true;
}
int add_varname(StrName name){
int index = co->varnames_inv.try_get(name);
if(index >= 0) return index;
co->varnames.push_back(name);
index = co->varnames.size() - 1;
co->varnames_inv.set(name, index);
return index;
}
int add_const(PyObject* v){
// simple deduplication, only works for int/float
for(int i=0; i<co->consts.size(); i++){
if(co->consts[i] == v) return i;
}
co->consts.push_back(v);
return co->consts.size() - 1;
}
int add_func_decl(FuncDecl_ decl){
co->func_decls.push_back(decl);
return co->func_decls.size() - 1;
}
bool is_curr_block_loop() const;
void enter_block(CodeBlockType type);
void exit_block();
void emit_expr(); // clear the expression stack and generate bytecode
std::string _log_s_expr();
int emit(Opcode opcode, int arg, int line);
void patch_jump(int index);
bool add_label(StrName name);
int add_varname(StrName name);
int add_const(PyObject* v);
int add_func_decl(FuncDecl_ decl);
};
struct NameExpr: Expr{
@ -148,54 +73,9 @@ struct NameExpr: Expr{
std::string str() const override { return fmt("Name(", name.escape(), ")"); }
void emit(CodeEmitContext* ctx) override {
int index = ctx->co->varnames_inv.try_get(name);
if(scope == NAME_LOCAL && index >= 0){
ctx->emit(OP_LOAD_FAST, index, line);
}else{
Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
// we cannot determine the scope when calling exec()/eval()
if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME;
ctx->emit(op, StrName(name).index, line);
}
}
bool emit_del(CodeEmitContext* ctx) override {
switch(scope){
case NAME_LOCAL:
ctx->emit(OP_DELETE_FAST, ctx->add_varname(name), line);
break;
case NAME_GLOBAL:
ctx->emit(OP_DELETE_GLOBAL, StrName(name).index, line);
break;
case NAME_GLOBAL_UNKNOWN:
ctx->emit(OP_DELETE_NAME, StrName(name).index, line);
break;
default: FATAL_ERROR(); break;
}
return true;
}
bool emit_store(CodeEmitContext* ctx) override {
if(ctx->is_compiling_class){
int index = StrName(name).index;
ctx->emit(OP_STORE_CLASS_ATTR, index, line);
return true;
}
switch(scope){
case NAME_LOCAL:
ctx->emit(OP_STORE_FAST, ctx->add_varname(name), line);
break;
case NAME_GLOBAL:
ctx->emit(OP_STORE_GLOBAL, StrName(name).index, line);
break;
case NAME_GLOBAL_UNKNOWN:
ctx->emit(OP_STORE_NAME, StrName(name).index, line);
break;
default: FATAL_ERROR(); break;
}
return true;
}
void emit(CodeEmitContext* ctx) override;
bool emit_del(CodeEmitContext* ctx) override;
bool emit_store(CodeEmitContext* ctx) override;
};
struct StarredExpr: Expr{
@ -203,19 +83,9 @@ struct StarredExpr: Expr{
Expr_ child;
StarredExpr(int level, Expr_&& child): level(level), child(std::move(child)) {}
std::string str() const override { return fmt("Starred(level=", level, ")"); }
int star_level() const override { return level; }
void emit(CodeEmitContext* ctx) override {
child->emit(ctx);
ctx->emit(OP_UNARY_STAR, level, line);
}
bool emit_store(CodeEmitContext* ctx) override {
if(level != 1) return false;
// simply proxy to child
return child->emit_store(ctx);
}
void emit(CodeEmitContext* ctx) override;
bool emit_store(CodeEmitContext* ctx) override;
};
struct NotExpr: Expr{
@ -223,36 +93,21 @@ struct NotExpr: Expr{
NotExpr(Expr_&& child): child(std::move(child)) {}
std::string str() const override { return "Not()"; }
void emit(CodeEmitContext* ctx) override {
child->emit(ctx);
ctx->emit(OP_UNARY_NOT, BC_NOARG, line);
}
void emit(CodeEmitContext* ctx) override;
};
struct AndExpr: Expr{
Expr_ lhs;
Expr_ rhs;
std::string str() const override { return "And()"; }
void emit(CodeEmitContext* ctx) override {
lhs->emit(ctx);
int patch = ctx->emit(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line);
rhs->emit(ctx);
ctx->patch_jump(patch);
}
void emit(CodeEmitContext* ctx) override;
};
struct OrExpr: Expr{
Expr_ lhs;
Expr_ rhs;
std::string str() const override { return "Or()"; }
void emit(CodeEmitContext* ctx) override {
lhs->emit(ctx);
int patch = ctx->emit(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line);
rhs->emit(ctx);
ctx->patch_jump(patch);
}
void emit(CodeEmitContext* ctx) override;
};
// [None, True, False, ...]
@ -260,71 +115,24 @@ struct Literal0Expr: Expr{
TokenIndex token;
Literal0Expr(TokenIndex token): token(token) {}
std::string str() const override { return TK_STR(token); }
void emit(CodeEmitContext* ctx) override {
switch (token) {
case TK("None"): ctx->emit(OP_LOAD_NONE, BC_NOARG, line); break;
case TK("True"): ctx->emit(OP_LOAD_TRUE, BC_NOARG, line); break;
case TK("False"): ctx->emit(OP_LOAD_FALSE, BC_NOARG, line); break;
case TK("..."): ctx->emit(OP_LOAD_ELLIPSIS, BC_NOARG, line); break;
default: FATAL_ERROR();
}
}
bool is_json_object() const override { return true; }
void emit(CodeEmitContext* ctx) override;
};
struct LongExpr: Expr{
Str s;
LongExpr(const Str& s): s(s) {}
std::string str() const override { return s.str(); }
void emit(CodeEmitContext* ctx) override {
VM* vm = ctx->vm;
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(s)), line);
ctx->emit(OP_BUILD_LONG, BC_NOARG, line);
}
void emit(CodeEmitContext* ctx) override;
};
// @num, @str which needs to invoke OP_LOAD_CONST
struct LiteralExpr: Expr{
TokenValue value;
LiteralExpr(TokenValue value): value(value) {}
std::string str() const override {
if(std::holds_alternative<i64>(value)){
return std::to_string(std::get<i64>(value));
}
if(std::holds_alternative<f64>(value)){
return std::to_string(std::get<f64>(value));
}
if(std::holds_alternative<Str>(value)){
Str s = std::get<Str>(value).escape();
return s.str();
}
FATAL_ERROR();
}
void emit(CodeEmitContext* ctx) override {
VM* vm = ctx->vm;
PyObject* obj = nullptr;
if(std::holds_alternative<i64>(value)){
i64 _val = std::get<i64>(value);
if(_val >= INT16_MIN && _val <= INT16_MAX){
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
return;
}
obj = VAR(_val);
}
if(std::holds_alternative<f64>(value)){
obj = VAR(std::get<f64>(value));
}
if(std::holds_alternative<Str>(value)){
obj = VAR(std::get<Str>(value));
}
if(obj == nullptr) FATAL_ERROR();
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
}
std::string str() const override;
void emit(CodeEmitContext* ctx) override;
bool is_literal() const override { return true; }
bool is_json_object() const override { return true; }
};
@ -334,33 +142,8 @@ struct NegatedExpr: Expr{
NegatedExpr(Expr_&& child): child(std::move(child)) {}
std::string str() const override { return "Negated()"; }
void emit(CodeEmitContext* ctx) override {
VM* vm = ctx->vm;
// if child is a int of float, do constant folding
if(child->is_literal()){
LiteralExpr* lit = static_cast<LiteralExpr*>(child.get());
if(std::holds_alternative<i64>(lit->value)){
i64 _val = -std::get<i64>(lit->value);
if(_val >= INT16_MIN && _val <= INT16_MAX){
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
}else{
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
}
return;
}
if(std::holds_alternative<f64>(lit->value)){
PyObject* obj = VAR(-std::get<f64>(lit->value));
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
return;
}
}
child->emit(ctx);
ctx->emit(OP_UNARY_NEGATIVE, BC_NOARG, line);
}
bool is_json_object() const override {
return child->is_literal();
}
void emit(CodeEmitContext* ctx) override;
bool is_json_object() const override { return child->is_literal(); }
};
struct SliceExpr: Expr{
@ -368,47 +151,15 @@ struct SliceExpr: Expr{
Expr_ stop;
Expr_ step;
std::string str() const override { return "Slice()"; }
void emit(CodeEmitContext* ctx) override {
if(start){
start->emit(ctx);
}else{
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
}
if(stop){
stop->emit(ctx);
}else{
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
}
if(step){
step->emit(ctx);
}else{
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
}
ctx->emit(OP_BUILD_SLICE, BC_NOARG, line);
}
void emit(CodeEmitContext* ctx) override;
};
struct DictItemExpr: Expr{
Expr_ key; // maybe nullptr if it is **kwargs
Expr_ value;
std::string str() const override { return "DictItem()"; }
int star_level() const override { return value->star_level(); }
void emit(CodeEmitContext* ctx) override {
if(is_starred()){
PK_ASSERT(key == nullptr);
value->emit(ctx);
}else{
value->emit(ctx);
key->emit(ctx); // reverse order
ctx->emit(OP_BUILD_TUPLE, 2, line);
}
}
void emit(CodeEmitContext* ctx) override;
};
struct SequenceExpr: Expr{
@ -463,49 +214,8 @@ struct TupleExpr: SequenceExpr{
return OP_BUILD_TUPLE;
}
bool emit_store(CodeEmitContext* ctx) override {
// TOS is an iterable
// items may contain StarredExpr, we should check it
int starred_i = -1;
for(int i=0; i<items.size(); i++){
if(!items[i]->is_starred()) continue;
if(starred_i == -1) starred_i = i;
else return false; // multiple StarredExpr not allowed
}
if(starred_i == -1){
Bytecode& prev = ctx->co->codes.back();
if(prev.op == OP_BUILD_TUPLE && prev.arg == items.size()){
// build tuple and unpack it is meaningless
prev.op = OP_NO_OP;
prev.arg = BC_NOARG;
}else{
ctx->emit(OP_UNPACK_SEQUENCE, items.size(), line);
}
}else{
// starred assignment target must be in a tuple
if(items.size() == 1) return false;
// starred assignment target must be the last one (differ from cpython)
if(starred_i != items.size()-1) return false;
// a,*b = [1,2,3]
// stack is [1,2,3] -> [1,[2,3]]
ctx->emit(OP_UNPACK_EX, items.size()-1, line);
}
// do reverse emit
for(int i=items.size()-1; i>=0; i--){
bool ok = items[i]->emit_store(ctx);
if(!ok) return false;
}
return true;
}
bool emit_del(CodeEmitContext* ctx) override{
for(auto& e: items){
bool ok = e->emit_del(ctx);
if(!ok) return false;
}
return true;
}
bool emit_store(CodeEmitContext* ctx) override;
bool emit_del(CodeEmitContext* ctx) override;
};
struct CompExpr: Expr{
@ -517,28 +227,7 @@ struct CompExpr: Expr{
virtual Opcode op0() = 0;
virtual Opcode op1() = 0;
void emit(CodeEmitContext* ctx){
ctx->emit(op0(), 0, line);
iter->emit(ctx);
ctx->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
ctx->enter_block(FOR_LOOP);
ctx->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
bool ok = vars->emit_store(ctx);
// this error occurs in `vars` instead of this line, but...nevermind
PK_ASSERT(ok); // TODO: raise a SyntaxError instead
if(cond){
cond->emit(ctx);
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);
expr->emit(ctx);
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
ctx->patch_jump(patch);
}else{
expr->emit(ctx);
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
}
ctx->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
ctx->exit_block();
}
void emit(CodeEmitContext* ctx) override;
};
struct ListCompExpr: CompExpr{
@ -578,52 +267,8 @@ struct FStringExpr: Expr{
return fmt("f", src.escape());
}
void _load_simple_expr(CodeEmitContext* ctx, Str expr){
// TODO: pre compile this into a function
int dot = expr.index(".");
if(dot < 0){
ctx->emit(OP_LOAD_NAME, StrName(expr.sv()).index, line);
}else{
StrName name(expr.substr(0, dot).sv());
StrName attr(expr.substr(dot+1).sv());
ctx->emit(OP_LOAD_NAME, name.index, line);
ctx->emit(OP_LOAD_ATTR, attr.index, line);
}
}
void emit(CodeEmitContext* ctx) override {
VM* vm = ctx->vm;
static const std::regex pattern(R"(\{(.*?)\})");
std::cregex_iterator begin(src.begin(), src.end(), pattern);
std::cregex_iterator end;
int size = 0;
int i = 0;
for(auto it = begin; it != end; it++) {
std::cmatch m = *it;
if (i < m.position()) {
Str literal = src.substr(i, m.position() - i);
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
size++;
}
Str expr = m[1].str();
int conon = expr.index(":");
if(conon >= 0){
_load_simple_expr(ctx, expr.substr(0, conon));
Str spec = expr.substr(conon+1);
ctx->emit(OP_FORMAT_STRING, ctx->add_const(VAR(spec)), line);
}else{
_load_simple_expr(ctx, expr);
}
size++;
i = (int)(m.position() + m.length());
}
if (i < src.length()) {
Str literal = src.substr(i, src.length() - i);
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
size++;
}
ctx->emit(OP_BUILD_STRING, size, line);
}
void _load_simple_expr(CodeEmitContext* ctx, Str expr);
void emit(CodeEmitContext* ctx) override;
};
struct SubscrExpr: Expr{
@ -631,25 +276,9 @@ struct SubscrExpr: Expr{
Expr_ b;
std::string str() const override { return "Subscr()"; }
void emit(CodeEmitContext* ctx) override{
a->emit(ctx);
b->emit(ctx);
ctx->emit(OP_LOAD_SUBSCR, BC_NOARG, line);
}
bool emit_del(CodeEmitContext* ctx) override {
a->emit(ctx);
b->emit(ctx);
ctx->emit(OP_DELETE_SUBSCR, BC_NOARG, line);
return true;
}
bool emit_store(CodeEmitContext* ctx) override {
a->emit(ctx);
b->emit(ctx);
ctx->emit(OP_STORE_SUBSCR, BC_NOARG, line);
return true;
}
void emit(CodeEmitContext* ctx) override;
bool emit_del(CodeEmitContext* ctx) override;
bool emit_store(CodeEmitContext* ctx) override;
};
struct AttribExpr: Expr{
@ -659,32 +288,10 @@ struct AttribExpr: Expr{
AttribExpr(Expr_ a, Str&& b): a(std::move(a)), b(std::move(b)) {}
std::string str() const override { return "Attrib()"; }
void emit(CodeEmitContext* ctx) override{
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_LOAD_ATTR, index, line);
}
bool emit_del(CodeEmitContext* ctx) override {
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_DELETE_ATTR, index, line);
return true;
}
bool emit_store(CodeEmitContext* ctx) override {
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_STORE_ATTR, index, line);
return true;
}
void emit_method(CodeEmitContext* ctx) {
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_LOAD_METHOD, index, line);
}
void emit(CodeEmitContext* ctx) override;
bool emit_del(CodeEmitContext* ctx) override;
bool emit_store(CodeEmitContext* ctx) override;
void emit_method(CodeEmitContext* ctx);
bool is_attrib() const override { return true; }
};
@ -694,57 +301,7 @@ struct CallExpr: Expr{
// **a will be interpreted as a special keyword argument: {"**": a}
std::vector<std::pair<Str, Expr_>> kwargs;
std::string str() const override { return "Call()"; }
void emit(CodeEmitContext* ctx) override {
bool vargs = false;
bool vkwargs = false;
for(auto& arg: args) if(arg->is_starred()) vargs = true;
for(auto& item: kwargs) if(item.second->is_starred()) vkwargs = true;
// if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy
if(callable->is_attrib()){
auto p = static_cast<AttribExpr*>(callable.get());
p->emit_method(ctx); // OP_LOAD_METHOD
}else{
callable->emit(ctx);
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
}
if(vargs || vkwargs){
for(auto& item: args) item->emit(ctx);
ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line);
if(!kwargs.empty()){
for(auto& item: kwargs){
if(item.second->is_starred()){
if(item.second->star_level() != 2) FATAL_ERROR();
item.second->emit(ctx);
}else{
// k=v
int index = ctx->add_const(py_var(ctx->vm, item.first));
ctx->emit(OP_LOAD_CONST, index, line);
item.second->emit(ctx);
ctx->emit(OP_BUILD_TUPLE, 2, line);
}
}
ctx->emit(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line);
ctx->emit(OP_CALL_TP, 1, line);
}else{
ctx->emit(OP_CALL_TP, 0, line);
}
}else{
// vectorcall protocal
for(auto& item: args) item->emit(ctx);
for(auto& item: kwargs){
int index = StrName(item.first.sv()).index;
ctx->emit(OP_LOAD_INTEGER, index, line);
item.second->emit(ctx);
}
int KWARGC = (int)kwargs.size();
int ARGC = (int)args.size();
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
}
}
void emit(CodeEmitContext* ctx) override;
};
struct GroupedExpr: Expr{
@ -772,82 +329,9 @@ struct BinaryExpr: Expr{
Expr_ rhs;
std::string str() const override { return TK_STR(op); }
bool is_compare() const override {
switch(op){
case TK("<"): case TK("<="): case TK("=="):
case TK("!="): case TK(">"): case TK(">="): return true;
default: return false;
}
}
void _emit_compare(CodeEmitContext* ctx, std::vector<int>& jmps){
if(lhs->is_compare()){
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
}else{
lhs->emit(ctx); // [a]
}
rhs->emit(ctx); // [a, b]
ctx->emit(OP_DUP_TOP, BC_NOARG, line); // [a, b, b]
ctx->emit(OP_ROT_THREE, BC_NOARG, line); // [b, a, b]
switch(op){
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
default: UNREACHABLE();
}
// [b, RES]
int index = ctx->emit(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line);
jmps.push_back(index);
}
void emit(CodeEmitContext* ctx) override {
std::vector<int> jmps;
if(is_compare() && lhs->is_compare()){
// (a < b) < c
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
// [b, RES]
}else{
// (1 + 2) < c
lhs->emit(ctx);
}
rhs->emit(ctx);
switch (op) {
case TK("+"): ctx->emit(OP_BINARY_ADD, BC_NOARG, line); break;
case TK("-"): ctx->emit(OP_BINARY_SUB, BC_NOARG, line); break;
case TK("*"): ctx->emit(OP_BINARY_MUL, BC_NOARG, line); break;
case TK("/"): ctx->emit(OP_BINARY_TRUEDIV, BC_NOARG, line); break;
case TK("//"): ctx->emit(OP_BINARY_FLOORDIV, BC_NOARG, line); break;
case TK("%"): ctx->emit(OP_BINARY_MOD, BC_NOARG, line); break;
case TK("**"): ctx->emit(OP_BINARY_POW, BC_NOARG, line); break;
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
case TK("in"): ctx->emit(OP_CONTAINS_OP, 0, line); break;
case TK("not in"): ctx->emit(OP_CONTAINS_OP, 1, line); break;
case TK("is"): ctx->emit(OP_IS_OP, 0, line); break;
case TK("is not"): ctx->emit(OP_IS_OP, 1, line); break;
case TK("<<"): ctx->emit(OP_BITWISE_LSHIFT, BC_NOARG, line); break;
case TK(">>"): ctx->emit(OP_BITWISE_RSHIFT, BC_NOARG, line); break;
case TK("&"): ctx->emit(OP_BITWISE_AND, BC_NOARG, line); break;
case TK("|"): ctx->emit(OP_BITWISE_OR, BC_NOARG, line); break;
case TK("^"): ctx->emit(OP_BITWISE_XOR, BC_NOARG, line); break;
case TK("@"): ctx->emit(OP_BINARY_MATMUL, BC_NOARG, line); break;
default: FATAL_ERROR();
}
for(int i: jmps) ctx->patch_jump(i);
}
bool is_compare() const override;
void _emit_compare(CodeEmitContext* ctx, std::vector<int>& jmps);
void emit(CodeEmitContext* ctx) override;
};
@ -856,16 +340,7 @@ struct TernaryExpr: Expr{
Expr_ true_expr;
Expr_ false_expr;
std::string str() const override { return "Ternary()"; }
void emit(CodeEmitContext* ctx) override {
cond->emit(ctx);
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line);
true_expr->emit(ctx);
int patch_2 = ctx->emit(OP_JUMP_ABSOLUTE, BC_NOARG, true_expr->line);
ctx->patch_jump(patch);
false_expr->emit(ctx);
ctx->patch_jump(patch_2);
}
void emit(CodeEmitContext* ctx) override;
};

View File

@ -14,9 +14,7 @@ struct FastLocals{
const NameDictInt* varnames_inv;
PyObject** a;
int size() const{
return varnames_inv->size();
}
int size() const{ return varnames_inv->size();}
PyObject*& operator[](int i){ return a[i]; }
PyObject* operator[](int i) const { return a[i]; }
@ -24,37 +22,11 @@ struct FastLocals{
FastLocals(const CodeObject* co, PyObject** a): varnames_inv(&co->varnames_inv), a(a) {}
FastLocals(const FastLocals& other): varnames_inv(other.varnames_inv), a(other.a) {}
PyObject* try_get(StrName name){
int index = varnames_inv->try_get(name);
if(index == -1) return nullptr;
return a[index];
}
bool contains(StrName name){
return varnames_inv->contains(name);
}
void erase(StrName name){
int index = varnames_inv->try_get(name);
if(index == -1) FATAL_ERROR();
a[index] = nullptr;
}
bool try_set(StrName name, PyObject* value){
int index = varnames_inv->try_get(name);
if(index == -1) return false;
a[index] = value;
return true;
}
NameDict_ to_namedict(){
NameDict_ dict = make_sp<NameDict>();
varnames_inv->apply([&](StrName name, int index){
PyObject* value = a[index];
if(value != PY_NULL) dict->set(name, value);
});
return dict;
}
PyObject* try_get(StrName name);
bool contains(StrName name);
void erase(StrName name);
bool try_set(StrName name, PyObject* value);
NameDict_ to_namedict();
};
template<size_t MAX_SIZE>
@ -116,12 +88,7 @@ struct Frame {
NameDict& f_globals() noexcept { return _module->attr(); }
PyObject* f_closure_try_get(StrName name){
if(_callable == nullptr) return nullptr;
Function& fn = PK_OBJ_GET(Function, _callable);
if(fn._closure == nullptr) return nullptr;
return fn._closure->try_get(name);
}
PyObject* f_closure_try_get(StrName name);
Frame(ValueStack* _s, PyObject** p0, const CodeObject* co, PyObject* _module, PyObject* _callable)
: _s(_s), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, p0) { }
@ -140,58 +107,16 @@ struct Frame {
return co->codes[_ip];
}
Str snapshot(){
int line = co->lines[_ip];
return co->src->snapshot(line);
}
Str snapshot();
PyObject** actual_sp_base() const { return _locals.a; }
int stack_size() const { return _s->_sp - actual_sp_base(); }
ArgsView stack_view() const { return ArgsView(actual_sp_base(), _s->_sp); }
void jump_abs(int i){ _next_ip = i; }
// void jump_rel(int i){ _next_ip += i; }
bool jump_to_exception_handler(){
// try to find a parent try block
int block = co->codes[_ip].block;
while(block >= 0){
if(co->blocks[block].type == TRY_EXCEPT) break;
block = co->blocks[block].parent;
}
if(block < 0) return false;
PyObject* obj = _s->popx(); // pop exception object
// get the stack size of the try block (depth of for loops)
int _stack_size = co->blocks[block].for_loop_depth;
if(stack_size() < _stack_size) throw std::runtime_error("invalid stack size");
_s->reset(actual_sp_base() + _locals.size() + _stack_size); // rollback the stack
_s->push(obj); // push exception object
_next_ip = co->blocks[block].end;
return true;
}
int _exit_block(int i){
if(co->blocks[i].type == FOR_LOOP) _s->pop();
return co->blocks[i].parent;
}
void jump_abs_break(int target){
const Bytecode& prev = co->codes[_ip];
int i = prev.block;
_next_ip = target;
if(_next_ip >= co->codes.size()){
while(i>=0) i = _exit_block(i);
}else{
// BUG!!!
// for i in range(4):
// _ = 0
// # if there is no op here, the block check will fail
// while i: --i
const Bytecode& next = co->codes[target];
while(i>=0 && i!=next.block) i = _exit_block(i);
if(i!=next.block) throw std::runtime_error("invalid jump");
}
}
bool jump_to_exception_handler();
int _exit_block(int i);
void jump_abs_break(int target);
void _gc_mark() const {
PK_OBJ_MARK(_module);

View File

@ -69,66 +69,11 @@ struct ManagedHeap{
inline static std::map<Type, int> deleted;
#endif
int sweep(){
std::vector<PyObject*> alive;
for(PyObject* obj: gen){
if(obj->gc.marked){
obj->gc.marked = false;
alive.push_back(obj);
}else{
#if PK_DEBUG_GC_STATS
deleted[obj->type] += 1;
#endif
if(_gc_on_delete) _gc_on_delete(vm, obj);
obj->~PyObject();
pool64.dealloc(obj);
}
}
// clear _no_gc marked flag
for(PyObject* obj: _no_gc) obj->gc.marked = false;
int freed = gen.size() - alive.size();
// std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
gen.clear();
gen.swap(alive);
return freed;
}
void _auto_collect(){
#if !PK_DEBUG_NO_AUTO_GC
if(_gc_lock_counter > 0) return;
if(gc_counter < gc_threshold) return;
gc_counter = 0;
collect();
gc_threshold = gen.size() * 2;
if(gc_threshold < kMinGCThreshold) gc_threshold = kMinGCThreshold;
#endif
}
int collect(){
if(_gc_lock_counter > 0) FATAL_ERROR();
mark();
int freed = sweep();
return freed;
}
int sweep();
void _auto_collect();
int collect();
void mark();
~ManagedHeap(){
for(PyObject* obj: _no_gc) { obj->~PyObject(); pool64.dealloc(obj); }
for(PyObject* obj: gen) { obj->~PyObject(); pool64.dealloc(obj); }
#if PK_DEBUG_GC_STATS
for(auto& [type, count]: deleted){
std::cout << "GC: " << obj_type_name(vm, type) << "=" << count << std::endl;
}
#endif
}
~ManagedHeap();
};
inline void FuncDecl::_gc_mark() const{
code->_gc_mark();
for(int i=0; i<kwargs.size(); i++) PK_OBJ_MARK(kwargs[i].value);
}
} // namespace pkpy

View File

@ -7,8 +7,9 @@
namespace pkpy {
struct CodeObject;
typedef shared_ptr<CodeObject> CodeObject_;
struct Frame;
struct Function;
class VM;
#if PK_ENABLE_STD_FUNCTION
@ -17,8 +18,6 @@ using NativeFuncC = std::function<PyObject*(VM*, ArgsView)>;
typedef PyObject* (*NativeFuncC)(VM*, ArgsView);
#endif
typedef shared_ptr<CodeObject> CodeObject_;
struct FuncDecl {
struct KwArg {
int key; // index in co->varnames
@ -70,19 +69,8 @@ struct NativeFunc {
return reinterpret_cast<const T&>(_userdata);
}
NativeFunc(NativeFuncC f, int argc, bool method){
this->f = f;
this->argc = argc;
if(argc != -1) this->argc += (int)method;
_has_userdata = false;
}
NativeFunc(NativeFuncC f, FuncDecl_ decl){
this->f = f;
this->argc = -1;
this->decl = decl;
_has_userdata = false;
}
NativeFunc(NativeFuncC f, int argc, bool method);
NativeFunc(NativeFuncC f, FuncDecl_ decl);
void check_size(VM* vm, ArgsView args) const;
PyObject* call(VM* vm, ArgsView args) const;
@ -175,13 +163,9 @@ struct PyObject{
PyObject(Type type) : type(type), _attr(nullptr) {}
virtual ~PyObject() {
if(_attr == nullptr) return;
_attr->~NameDict();
pool64.dealloc(_attr);
}
virtual ~PyObject();
void enable_instance_dict(float lf=kInstAttrLoadFactor) noexcept {
void enable_instance_dict(float lf=kInstAttrLoadFactor) {
_attr = new(pool64.alloc<NameDict>()) NameDict(lf);
}
};
@ -444,11 +428,9 @@ struct Py_<DummyModule> final: PyObject {
};
template<typename T>
inline T lambda_get_userdata(PyObject** p){
T lambda_get_userdata(PyObject** p){
if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1]).get_userdata<T>();
else return PK_OBJ_GET(NativeFunc, p[-2]).get_userdata<T>();
}
} // namespace pkpy

View File

@ -147,109 +147,19 @@ public:
const bool enable_os;
VM(bool enable_os=true) : heap(this), enable_os(enable_os) {
this->vm = this;
_stdout = [](VM* vm, const Str& s) {
PK_UNUSED(vm);
std::cout << s;
};
_stderr = [](VM* vm, const Str& s) {
PK_UNUSED(vm);
std::cerr << s;
};
callstack.reserve(8);
_main = nullptr;
_last_exception = nullptr;
_import_handler = [](const Str& name) {
PK_UNUSED(name);
return Bytes();
};
init_builtin_types();
}
VM(bool enable_os=true);
FrameId top_frame() {
#if PK_DEBUG_EXTRA_CHECK
if(callstack.empty()) FATAL_ERROR();
#endif
return FrameId(&callstack.data(), callstack.size()-1);
}
FrameId top_frame();
void _pop_frame();
PyObject* py_str(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__str__) return ti->m__str__(this, obj);
PyObject* self;
PyObject* f = get_unbound_method(obj, __str__, &self, false);
if(self != PY_NULL) return call_method(self, f);
return py_repr(obj);
}
PyObject* py_str(PyObject* obj);
PyObject* py_repr(PyObject* obj);
PyObject* py_json(PyObject* obj);
PyObject* py_iter(PyObject* obj);
PyObject* py_repr(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__repr__) return ti->m__repr__(this, obj);
return call_method(obj, __repr__);
}
PyObject* py_json(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__json__) return ti->m__json__(this, obj);
return call_method(obj, __json__);
}
PyObject* py_iter(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__iter__) return ti->m__iter__(this, obj);
PyObject* self;
PyObject* iter_f = get_unbound_method(obj, __iter__, &self, false);
if(self != PY_NULL) return call_method(self, iter_f);
TypeError(OBJ_NAME(_t(obj)).escape() + " object is not iterable");
return nullptr;
}
PyObject* find_name_in_mro(PyObject* cls, StrName name){
PyObject* val;
do{
val = cls->attr().try_get(name);
if(val != nullptr) return val;
Type base = _all_types[PK_OBJ_GET(Type, cls)].base;
if(base.index == -1) break;
cls = _all_types[base].obj;
}while(true);
return nullptr;
}
bool isinstance(PyObject* obj, Type cls_t){
Type obj_t = PK_OBJ_GET(Type, _t(obj));
do{
if(obj_t == cls_t) return true;
Type base = _all_types[obj_t].base;
if(base.index == -1) break;
obj_t = base;
}while(true);
return false;
}
PyObject* exec(Str source, Str filename, CompileMode mode, PyObject* _module=nullptr){
if(_module == nullptr) _module = _main;
try {
CodeObject_ code = compile(source, filename, mode);
#if PK_DEBUG_DIS_EXEC
if(_module == _main) std::cout << disassemble(code) << '\n';
#endif
return _exec(code, _module);
}catch (const Exception& e){
_stderr(this, e.summary() + "\n");
}
#if !PK_DEBUG_FULL_EXCEPTION
catch (const std::exception& e) {
Str msg = "An std::exception occurred! It could be a bug.\n";
msg = msg + e.what();
_stderr(this, msg + "\n");
}
#endif
callstack.clear();
s_data.clear();
return nullptr;
}
PyObject* find_name_in_mro(PyObject* cls, StrName name);
bool isinstance(PyObject* obj, Type cls_t);
PyObject* exec(Str source, Str filename, CompileMode mode, PyObject* _module=nullptr);
template<typename ...Args>
PyObject* _exec(Args&&... args){
@ -257,12 +167,6 @@ public:
return _run_top_frame();
}
void _pop_frame(){
Frame* frame = &callstack.top();
s_data.reset(frame->_sp_base);
callstack.pop();
}
void _push_varargs(){ }
void _push_varargs(PyObject* _0){ PUSH(_0); }
void _push_varargs(PyObject* _0, PyObject* _1){ PUSH(_0); PUSH(_1); }
@ -291,68 +195,15 @@ public:
return call_method(self, callable, args...);
}
PyObject* property(NativeFuncC fget, NativeFuncC fset=nullptr){
PyObject* _0 = heap.gcnew(tp_native_func, NativeFunc(fget, 1, false));
PyObject* _1 = vm->None;
if(fset != nullptr) _1 = heap.gcnew(tp_native_func, NativeFunc(fset, 2, false));
return call(_t(tp_property), _0, _1);
}
PyObject* property(NativeFuncC fget, NativeFuncC fset=nullptr);
PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true);
Type _new_type_object(StrName name, Type base=0);
PyObject* _find_type_object(const Str& type);
PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true){
PyObject* obj = heap._new<Type>(tp_type, _all_types.size());
const PyTypeInfo& base_info = _all_types[base];
if(!base_info.subclass_enabled){
TypeError(fmt("type ", base_info.name.escape(), " is not `subclass_enabled`"));
}
PyTypeInfo info{
obj,
base,
(mod!=nullptr && mod!=builtins) ? Str(OBJ_NAME(mod)+"."+name.sv()): name.sv(),
subclass_enabled,
};
if(mod != nullptr) mod->attr().set(name, obj);
_all_types.push_back(info);
return obj;
}
Type _new_type_object(StrName name, Type base=0) {
PyObject* obj = new_type_object(nullptr, name, base, false);
return PK_OBJ_GET(Type, obj);
}
PyObject* _find_type_object(const Str& type){
PyObject* obj = builtins->attr().try_get(type);
if(obj == nullptr){
for(auto& t: _all_types) if(t.name == type) return t.obj;
throw std::runtime_error(fmt("type not found: ", type));
}
check_non_tagged_type(obj, tp_type);
return obj;
}
Type _type(const Str& type){
PyObject* obj = _find_type_object(type);
return PK_OBJ_GET(Type, obj);
}
PyTypeInfo* _type_info(const Str& type){
PyObject* obj = builtins->attr().try_get(type);
if(obj == nullptr){
for(auto& t: _all_types) if(t.name == type) return &t;
FATAL_ERROR();
}
return &_all_types[PK_OBJ_GET(Type, obj)];
}
PyTypeInfo* _type_info(Type type){
return &_all_types[type];
}
const PyTypeInfo* _inst_type_info(PyObject* obj){
if(is_int(obj)) return &_all_types[tp_int];
if(is_float(obj)) return &_all_types[tp_float];
return &_all_types[obj->type];
}
Type _type(const Str& type);
PyTypeInfo* _type_info(const Str& type);
PyTypeInfo* _type_info(Type type);
const PyTypeInfo* _inst_type_info(PyObject* obj);
#define BIND_UNARY_SPECIAL(name) \
void bind##name(Type type, PyObject* (*f)(VM*, PyObject*)){ \
@ -439,26 +290,7 @@ public:
PK_OBJ_GET(NativeFunc, nf).set_userdata(f);
}
bool py_equals(PyObject* lhs, PyObject* rhs){
if(lhs == rhs) return true;
const PyTypeInfo* ti = _inst_type_info(lhs);
PyObject* res;
if(ti->m__eq__){
res = ti->m__eq__(this, lhs, rhs);
if(res != vm->NotImplemented) return res == vm->True;
}
res = call_method(lhs, __eq__, rhs);
if(res != vm->NotImplemented) return res == vm->True;
ti = _inst_type_info(rhs);
if(ti->m__eq__){
res = ti->m__eq__(this, rhs, lhs);
if(res != vm->NotImplemented) return res == vm->True;
}
res = call_method(rhs, __eq__, lhs);
if(res != vm->NotImplemented) return res == vm->True;
return false;
}
bool py_equals(PyObject* lhs, PyObject* rhs);
template<int ARGC>
PyObject* bind_func(Str type, Str name, NativeFuncC fn) {
@ -498,19 +330,8 @@ public:
return bind_func<ARGC>(builtins, name, fn);
}
int normalized_index(int index, int size){
if(index < 0) index += size;
if(index < 0 || index >= size){
IndexError(std::to_string(index) + " not in [0, " + std::to_string(size) + ")");
}
return index;
}
PyObject* py_next(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__next__) return ti->m__next__(this, obj);
return call_method(obj, __next__);
}
int normalized_index(int index, int size);
PyObject* py_next(PyObject* obj);
/***** Error Reporter *****/
void _error(StrName name, const Str& msg){
@ -600,66 +421,9 @@ public:
};
ImportContext _import_context;
PyObject* py_import(StrName name, bool relative=false);
~VM();
PyObject* py_import(StrName name, bool relative=false){
Str filename;
int type;
if(relative){
ImportContext* ctx = &_import_context;
type = 2;
for(auto it=ctx->pending.rbegin(); it!=ctx->pending.rend(); ++it){
if(it->second == 2) continue;
if(it->second == 1){
filename = fmt(it->first, kPlatformSep, name, ".py");
name = fmt(it->first, '.', name).c_str();
break;
}
}
if(filename.length() == 0) _error("ImportError", "relative import outside of package");
}else{
type = 0;
filename = fmt(name, ".py");
}
for(auto& [k, v]: _import_context.pending){
if(k == name){
vm->_error("ImportError", fmt("circular import ", name.escape()));
}
}
PyObject* ext_mod = _modules.try_get(name);
if(ext_mod == nullptr){
Str source;
auto it = _lazy_modules.find(name);
if(it == _lazy_modules.end()){
Bytes b = _import_handler(filename);
if(!relative && !b){
filename = fmt(name, kPlatformSep, "__init__.py");
b = _import_handler(filename);
if(b) type = 1;
}
if(!b) _error("ImportError", fmt("module ", name.escape(), " not found"));
source = Str(b.str());
}else{
source = it->second;
_lazy_modules.erase(it);
}
auto _ = _import_context.temp(this, name, type);
CodeObject_ code = compile(source, filename, EXEC_MODE);
PyObject* new_mod = new_module(name);
_exec(code, new_mod);
new_mod->attr()._try_perfect_rehash();
return new_mod;
}else{
return ext_mod;
}
}
~VM() {
callstack.clear();
s_data.clear();
_all_types.clear();
_modules.clear();
_lazy_modules.clear();
}
#if PK_DEBUG_CEVAL_STEP
void _log_s_data(const char* title = nullptr);
#endif

157
src/codeobject.cpp Normal file
View File

@ -0,0 +1,157 @@
#include "pocketpy/codeobject.h"
namespace pkpy{
CodeObject::CodeObject(shared_ptr<SourceData> src, const Str& name):
src(src), name(name) {}
void CodeObject::_gc_mark() const {
for(PyObject* v : consts) PK_OBJ_MARK(v);
for(auto& decl: func_decls) decl->_gc_mark();
}
void CodeObject::write(VM* vm, CodeObjectSerializer& ss) const{
ss.write_begin_mark(); // [
ss.write_str(src->filename); // src->filename
ss.write_int(src->mode); // src->mode
ss.write_end_mark(); // ]
ss.write_str(name); // name
ss.write_bool(is_generator); // is_generator
ss.write_begin_mark(); // [
for(Bytecode bc: codes){
if(StrName::is_valid(bc.arg)) ss.names.insert(StrName(bc.arg));
ss.write_bytes(bc);
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(int line: lines){
ss.write_int(line); // line
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(PyObject* o: consts){
ss.write_object(vm, o);
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(StrName vn: varnames){
ss.write_name(vn); // name
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(CodeBlock block: blocks){
ss.write_bytes(block); // block
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(auto& label: labels.items()){
ss.write_name(label.first); // label.first
ss.write_int(label.second); // label.second
}
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(auto& decl: func_decls){
ss.write_code(vm, decl->code.get()); // decl->code
ss.write_begin_mark(); // [
for(int arg: decl->args) ss.write_int(arg);
ss.write_end_mark(); // ]
ss.write_begin_mark(); // [
for(auto kw: decl->kwargs){
ss.write_int(kw.key); // kw.key
ss.write_object(vm, kw.value); // kw.value
}
ss.write_end_mark(); // ]
ss.write_int(decl->starred_arg);
ss.write_int(decl->starred_kwarg);
ss.write_bool(decl->nested);
}
ss.write_end_mark(); // ]
}
Str CodeObject::serialize(VM* vm) const{
CodeObjectSerializer ss;
ss.write_code(vm, this);
return ss.str();
}
void CodeObjectSerializer::write_int(i64 v){
buffer += 'i';
buffer += std::to_string(v);
buffer += END;
}
void CodeObjectSerializer::write_float(f64 v){
buffer += 'f';
buffer += std::to_string(v);
buffer += END;
}
void CodeObjectSerializer::write_str(const Str& v){
buffer += 's';
buffer += v.escape(false).str();
buffer += END;
}
void CodeObjectSerializer::write_none(){
buffer += 'N';
buffer += END;
}
void CodeObjectSerializer::write_ellipsis(){
buffer += 'E';
buffer += END;
}
void CodeObjectSerializer::write_bool(bool v){
buffer += 'b';
buffer += v ? '1' : '0';
buffer += END;
}
void CodeObjectSerializer::write_begin_mark(){
buffer += '[';
buffer += END;
depth++;
}
void CodeObjectSerializer::write_name(StrName name){
PK_ASSERT(StrName::is_valid(name.index));
buffer += 'n';
buffer += std::to_string(name.index);
buffer += END;
names.insert(name);
}
void CodeObjectSerializer::write_end_mark(){
buffer += ']';
buffer += END;
depth--;
PK_ASSERT(depth >= 0);
}
std::string CodeObjectSerializer::str(){
PK_ASSERT(depth == 0);
for(auto name: names){
PK_ASSERT(StrName::is_valid(name.index));
write_name(name);
write_str(name.sv());
}
return std::move(buffer);
}
CodeObjectSerializer::CodeObjectSerializer(){
write_str(PK_VERSION);
}
void CodeObjectSerializer::write_code(VM* vm, const CodeObject* co){
buffer += '(';
buffer += END;
co->write(vm, *this);
buffer += ')';
buffer += END;
}
} // namespace pkpy

176
src/dict.cpp Normal file
View File

@ -0,0 +1,176 @@
#include "pocketpy/dict.h"
namespace pkpy{
Dict::Dict(VM* vm): vm(vm), _capacity(__Capacity),
_mask(__Capacity-1),
_size(0), _critical_size(__Capacity*__LoadFactor+0.5f), _head_idx(-1), _tail_idx(-1){
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
memset(_items, 0, _capacity * sizeof(Item));
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
memset(_nodes, -1, _capacity * sizeof(ItemNode));
}
Dict::Dict(Dict&& other){
vm = other.vm;
_capacity = other._capacity;
_mask = other._mask;
_size = other._size;
_critical_size = other._critical_size;
_head_idx = other._head_idx;
_tail_idx = other._tail_idx;
_items = other._items;
_nodes = other._nodes;
other._items = nullptr;
other._nodes = nullptr;
}
Dict::Dict(const Dict& other){
vm = other.vm;
_capacity = other._capacity;
_mask = other._mask;
_size = other._size;
_critical_size = other._critical_size;
_head_idx = other._head_idx;
_tail_idx = other._tail_idx;
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
memcpy(_items, other._items, _capacity * sizeof(Item));
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
memcpy(_nodes, other._nodes, _capacity * sizeof(ItemNode));
}
void Dict::set(PyObject* key, PyObject* val){
// do possible rehash
if(_size+1 > _critical_size) _rehash();
bool ok; int i;
_probe(key, ok, i);
if(!ok) {
_size++;
_items[i].first = key;
// append to tail
if(_size == 0+1){
_head_idx = i;
_tail_idx = i;
}else{
_nodes[i].prev = _tail_idx;
_nodes[_tail_idx].next = i;
_tail_idx = i;
}
}
_items[i].second = val;
}
void Dict::_rehash(){
Item* old_items = _items;
int old_capacity = _capacity;
_capacity *= 2;
_mask = _capacity - 1;
_size = 0;
_critical_size = _capacity*__LoadFactor+0.5f;
_head_idx = -1;
_tail_idx = -1;
pool64.dealloc(_nodes);
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
memset(_items, 0, _capacity * sizeof(Item));
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
memset(_nodes, -1, _capacity * sizeof(ItemNode));
for(int i=0; i<old_capacity; i++){
if(old_items[i].first == nullptr) continue;
set(old_items[i].first, old_items[i].second);
}
pool128.dealloc(old_items);
}
PyObject* Dict::try_get(PyObject* key) const{
bool ok; int i;
_probe(key, ok, i);
if(!ok) return nullptr;
return _items[i].second;
}
bool Dict::contains(PyObject* key) const{
bool ok; int i;
_probe(key, ok, i);
return ok;
}
void Dict::erase(PyObject* key){
bool ok; int i;
_probe(key, ok, i);
if(!ok) return;
_items[i].first = nullptr;
_items[i].second = nullptr;
_size--;
if(_size == 0){
_head_idx = -1;
_tail_idx = -1;
}else{
if(_head_idx == i){
_head_idx = _nodes[i].next;
_nodes[_head_idx].prev = -1;
}else if(_tail_idx == i){
_tail_idx = _nodes[i].prev;
_nodes[_tail_idx].next = -1;
}else{
_nodes[_nodes[i].prev].next = _nodes[i].next;
_nodes[_nodes[i].next].prev = _nodes[i].prev;
}
}
_nodes[i].prev = -1;
_nodes[i].next = -1;
}
void Dict::update(const Dict& other){
other.apply([&](PyObject* k, PyObject* v){ set(k, v); });
}
Tuple Dict::keys() const{
Tuple t(_size);
int i = _head_idx;
int j = 0;
while(i != -1){
t[j++] = _items[i].first;
i = _nodes[i].next;
}
PK_ASSERT(j == _size);
return t;
}
Tuple Dict::values() const{
Tuple t(_size);
int i = _head_idx;
int j = 0;
while(i != -1){
t[j++] = _items[i].second;
i = _nodes[i].next;
}
PK_ASSERT(j == _size);
return t;
}
void Dict::clear(){
_size = 0;
_head_idx = -1;
_tail_idx = -1;
memset(_items, 0, _capacity * sizeof(Item));
memset(_nodes, -1, _capacity * sizeof(ItemNode));
}
Dict::~Dict(){
if(_items==nullptr) return;
pool128.dealloc(_items);
pool64.dealloc(_nodes);
}
void Dict::_gc_mark() const{
apply([](PyObject* k, PyObject* v){
PK_OBJ_MARK(k);
PK_OBJ_MARK(v);
});
}
} // namespace pkpy

583
src/expr.cpp Normal file
View File

@ -0,0 +1,583 @@
#include "pocketpy/expr.h"
namespace pkpy{
bool CodeEmitContext::is_curr_block_loop() const {
return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP;
}
void CodeEmitContext::enter_block(CodeBlockType type){
if(type == FOR_LOOP) for_loop_depth++;
co->blocks.push_back(CodeBlock(
type, curr_block_i, for_loop_depth, (int)co->codes.size()
));
curr_block_i = co->blocks.size()-1;
}
void CodeEmitContext::exit_block(){
auto curr_type = co->blocks[curr_block_i].type;
if(curr_type == FOR_LOOP) for_loop_depth--;
co->blocks[curr_block_i].end = co->codes.size();
curr_block_i = co->blocks[curr_block_i].parent;
if(curr_block_i < 0) FATAL_ERROR();
if(curr_type == FOR_LOOP){
// add a no op here to make block check work
emit(OP_NO_OP, BC_NOARG, BC_KEEPLINE);
}
}
// clear the expression stack and generate bytecode
void CodeEmitContext::emit_expr(){
if(s_expr.size() != 1){
throw std::runtime_error("s_expr.size() != 1\n" + _log_s_expr());
}
Expr_ expr = s_expr.popx();
expr->emit(this);
}
std::string CodeEmitContext::_log_s_expr(){
std::stringstream ss;
for(auto& e: s_expr.data()) ss << e->str() << " ";
return ss.str();
}
int CodeEmitContext::emit(Opcode opcode, int arg, int line) {
co->codes.push_back(
Bytecode{(uint16_t)opcode, (uint16_t)curr_block_i, arg}
);
co->lines.push_back(line);
int i = co->codes.size() - 1;
if(line==BC_KEEPLINE){
if(i>=1) co->lines[i] = co->lines[i-1];
else co->lines[i] = 1;
}
return i;
}
void CodeEmitContext::patch_jump(int index) {
int target = co->codes.size();
co->codes[index].arg = target;
}
bool CodeEmitContext::add_label(StrName name){
if(co->labels.contains(name)) return false;
co->labels.set(name, co->codes.size());
return true;
}
int CodeEmitContext::add_varname(StrName name){
int index = co->varnames_inv.try_get(name);
if(index >= 0) return index;
co->varnames.push_back(name);
index = co->varnames.size() - 1;
co->varnames_inv.set(name, index);
return index;
}
int CodeEmitContext::add_const(PyObject* v){
// simple deduplication, only works for int/float
for(int i=0; i<co->consts.size(); i++){
if(co->consts[i] == v) return i;
}
co->consts.push_back(v);
return co->consts.size() - 1;
}
int CodeEmitContext::add_func_decl(FuncDecl_ decl){
co->func_decls.push_back(decl);
return co->func_decls.size() - 1;
}
void NameExpr::emit(CodeEmitContext* ctx) {
int index = ctx->co->varnames_inv.try_get(name);
if(scope == NAME_LOCAL && index >= 0){
ctx->emit(OP_LOAD_FAST, index, line);
}else{
Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
// we cannot determine the scope when calling exec()/eval()
if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME;
ctx->emit(op, StrName(name).index, line);
}
}
bool NameExpr::emit_del(CodeEmitContext* ctx) {
switch(scope){
case NAME_LOCAL:
ctx->emit(OP_DELETE_FAST, ctx->add_varname(name), line);
break;
case NAME_GLOBAL:
ctx->emit(OP_DELETE_GLOBAL, StrName(name).index, line);
break;
case NAME_GLOBAL_UNKNOWN:
ctx->emit(OP_DELETE_NAME, StrName(name).index, line);
break;
default: FATAL_ERROR(); break;
}
return true;
}
bool NameExpr::emit_store(CodeEmitContext* ctx) {
if(ctx->is_compiling_class){
int index = StrName(name).index;
ctx->emit(OP_STORE_CLASS_ATTR, index, line);
return true;
}
switch(scope){
case NAME_LOCAL:
ctx->emit(OP_STORE_FAST, ctx->add_varname(name), line);
break;
case NAME_GLOBAL:
ctx->emit(OP_STORE_GLOBAL, StrName(name).index, line);
break;
case NAME_GLOBAL_UNKNOWN:
ctx->emit(OP_STORE_NAME, StrName(name).index, line);
break;
default: FATAL_ERROR(); break;
}
return true;
}
void StarredExpr::emit(CodeEmitContext* ctx) {
child->emit(ctx);
ctx->emit(OP_UNARY_STAR, level, line);
}
bool StarredExpr::emit_store(CodeEmitContext* ctx) {
if(level != 1) return false;
// simply proxy to child
return child->emit_store(ctx);
}
void NotExpr::emit(CodeEmitContext* ctx) {
child->emit(ctx);
ctx->emit(OP_UNARY_NOT, BC_NOARG, line);
}
void AndExpr::emit(CodeEmitContext* ctx) {
lhs->emit(ctx);
int patch = ctx->emit(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line);
rhs->emit(ctx);
ctx->patch_jump(patch);
}
void OrExpr::emit(CodeEmitContext* ctx) {
lhs->emit(ctx);
int patch = ctx->emit(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line);
rhs->emit(ctx);
ctx->patch_jump(patch);
}
void Literal0Expr::emit(CodeEmitContext* ctx){
switch (token) {
case TK("None"): ctx->emit(OP_LOAD_NONE, BC_NOARG, line); break;
case TK("True"): ctx->emit(OP_LOAD_TRUE, BC_NOARG, line); break;
case TK("False"): ctx->emit(OP_LOAD_FALSE, BC_NOARG, line); break;
case TK("..."): ctx->emit(OP_LOAD_ELLIPSIS, BC_NOARG, line); break;
default: FATAL_ERROR();
}
}
void LongExpr::emit(CodeEmitContext* ctx) {
VM* vm = ctx->vm;
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(s)), line);
ctx->emit(OP_BUILD_LONG, BC_NOARG, line);
}
std::string LiteralExpr::str() const{
if(std::holds_alternative<i64>(value)){
return std::to_string(std::get<i64>(value));
}
if(std::holds_alternative<f64>(value)){
return std::to_string(std::get<f64>(value));
}
if(std::holds_alternative<Str>(value)){
Str s = std::get<Str>(value).escape();
return s.str();
}
FATAL_ERROR();
}
void LiteralExpr::emit(CodeEmitContext* ctx) {
VM* vm = ctx->vm;
PyObject* obj = nullptr;
if(std::holds_alternative<i64>(value)){
i64 _val = std::get<i64>(value);
if(_val >= INT16_MIN && _val <= INT16_MAX){
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
return;
}
obj = VAR(_val);
}
if(std::holds_alternative<f64>(value)){
obj = VAR(std::get<f64>(value));
}
if(std::holds_alternative<Str>(value)){
obj = VAR(std::get<Str>(value));
}
if(obj == nullptr) FATAL_ERROR();
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
}
void NegatedExpr::emit(CodeEmitContext* ctx){
VM* vm = ctx->vm;
// if child is a int of float, do constant folding
if(child->is_literal()){
LiteralExpr* lit = static_cast<LiteralExpr*>(child.get());
if(std::holds_alternative<i64>(lit->value)){
i64 _val = -std::get<i64>(lit->value);
if(_val >= INT16_MIN && _val <= INT16_MAX){
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
}else{
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
}
return;
}
if(std::holds_alternative<f64>(lit->value)){
PyObject* obj = VAR(-std::get<f64>(lit->value));
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
return;
}
}
child->emit(ctx);
ctx->emit(OP_UNARY_NEGATIVE, BC_NOARG, line);
}
void SliceExpr::emit(CodeEmitContext* ctx){
if(start){
start->emit(ctx);
}else{
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
}
if(stop){
stop->emit(ctx);
}else{
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
}
if(step){
step->emit(ctx);
}else{
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
}
ctx->emit(OP_BUILD_SLICE, BC_NOARG, line);
}
void DictItemExpr::emit(CodeEmitContext* ctx) {
if(is_starred()){
PK_ASSERT(key == nullptr);
value->emit(ctx);
}else{
value->emit(ctx);
key->emit(ctx); // reverse order
ctx->emit(OP_BUILD_TUPLE, 2, line);
}
}
bool TupleExpr::emit_store(CodeEmitContext* ctx) {
// TOS is an iterable
// items may contain StarredExpr, we should check it
int starred_i = -1;
for(int i=0; i<items.size(); i++){
if(!items[i]->is_starred()) continue;
if(starred_i == -1) starred_i = i;
else return false; // multiple StarredExpr not allowed
}
if(starred_i == -1){
Bytecode& prev = ctx->co->codes.back();
if(prev.op == OP_BUILD_TUPLE && prev.arg == items.size()){
// build tuple and unpack it is meaningless
prev.op = OP_NO_OP;
prev.arg = BC_NOARG;
}else{
ctx->emit(OP_UNPACK_SEQUENCE, items.size(), line);
}
}else{
// starred assignment target must be in a tuple
if(items.size() == 1) return false;
// starred assignment target must be the last one (differ from cpython)
if(starred_i != items.size()-1) return false;
// a,*b = [1,2,3]
// stack is [1,2,3] -> [1,[2,3]]
ctx->emit(OP_UNPACK_EX, items.size()-1, line);
}
// do reverse emit
for(int i=items.size()-1; i>=0; i--){
bool ok = items[i]->emit_store(ctx);
if(!ok) return false;
}
return true;
}
bool TupleExpr::emit_del(CodeEmitContext* ctx){
for(auto& e: items){
bool ok = e->emit_del(ctx);
if(!ok) return false;
}
return true;
}
void CompExpr::emit(CodeEmitContext* ctx){
ctx->emit(op0(), 0, line);
iter->emit(ctx);
ctx->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
ctx->enter_block(FOR_LOOP);
ctx->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
bool ok = vars->emit_store(ctx);
// this error occurs in `vars` instead of this line, but...nevermind
PK_ASSERT(ok); // TODO: raise a SyntaxError instead
if(cond){
cond->emit(ctx);
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);
expr->emit(ctx);
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
ctx->patch_jump(patch);
}else{
expr->emit(ctx);
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
}
ctx->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
ctx->exit_block();
}
void FStringExpr::_load_simple_expr(CodeEmitContext* ctx, Str expr){
// TODO: pre compile this into a function
int dot = expr.index(".");
if(dot < 0){
ctx->emit(OP_LOAD_NAME, StrName(expr.sv()).index, line);
}else{
StrName name(expr.substr(0, dot).sv());
StrName attr(expr.substr(dot+1).sv());
ctx->emit(OP_LOAD_NAME, name.index, line);
ctx->emit(OP_LOAD_ATTR, attr.index, line);
}
}
void FStringExpr::emit(CodeEmitContext* ctx){
VM* vm = ctx->vm;
static const std::regex pattern(R"(\{(.*?)\})");
std::cregex_iterator begin(src.begin(), src.end(), pattern);
std::cregex_iterator end;
int size = 0;
int i = 0;
for(auto it = begin; it != end; it++) {
std::cmatch m = *it;
if (i < m.position()) {
Str literal = src.substr(i, m.position() - i);
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
size++;
}
Str expr = m[1].str();
int conon = expr.index(":");
if(conon >= 0){
_load_simple_expr(ctx, expr.substr(0, conon));
Str spec = expr.substr(conon+1);
ctx->emit(OP_FORMAT_STRING, ctx->add_const(VAR(spec)), line);
}else{
_load_simple_expr(ctx, expr);
}
size++;
i = (int)(m.position() + m.length());
}
if (i < src.length()) {
Str literal = src.substr(i, src.length() - i);
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
size++;
}
ctx->emit(OP_BUILD_STRING, size, line);
}
void SubscrExpr::emit(CodeEmitContext* ctx){
a->emit(ctx);
b->emit(ctx);
ctx->emit(OP_LOAD_SUBSCR, BC_NOARG, line);
}
bool SubscrExpr::emit_del(CodeEmitContext* ctx){
a->emit(ctx);
b->emit(ctx);
ctx->emit(OP_DELETE_SUBSCR, BC_NOARG, line);
return true;
}
bool SubscrExpr::emit_store(CodeEmitContext* ctx){
a->emit(ctx);
b->emit(ctx);
ctx->emit(OP_STORE_SUBSCR, BC_NOARG, line);
return true;
}
void AttribExpr::emit(CodeEmitContext* ctx){
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_LOAD_ATTR, index, line);
}
bool AttribExpr::emit_del(CodeEmitContext* ctx) {
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_DELETE_ATTR, index, line);
return true;
}
bool AttribExpr::emit_store(CodeEmitContext* ctx){
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_STORE_ATTR, index, line);
return true;
}
void AttribExpr::emit_method(CodeEmitContext* ctx) {
a->emit(ctx);
int index = StrName(b).index;
ctx->emit(OP_LOAD_METHOD, index, line);
}
void CallExpr::emit(CodeEmitContext* ctx) {
bool vargs = false;
bool vkwargs = false;
for(auto& arg: args) if(arg->is_starred()) vargs = true;
for(auto& item: kwargs) if(item.second->is_starred()) vkwargs = true;
// if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy
if(callable->is_attrib()){
auto p = static_cast<AttribExpr*>(callable.get());
p->emit_method(ctx); // OP_LOAD_METHOD
}else{
callable->emit(ctx);
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
}
if(vargs || vkwargs){
for(auto& item: args) item->emit(ctx);
ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line);
if(!kwargs.empty()){
for(auto& item: kwargs){
if(item.second->is_starred()){
if(item.second->star_level() != 2) FATAL_ERROR();
item.second->emit(ctx);
}else{
// k=v
int index = ctx->add_const(py_var(ctx->vm, item.first));
ctx->emit(OP_LOAD_CONST, index, line);
item.second->emit(ctx);
ctx->emit(OP_BUILD_TUPLE, 2, line);
}
}
ctx->emit(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line);
ctx->emit(OP_CALL_TP, 1, line);
}else{
ctx->emit(OP_CALL_TP, 0, line);
}
}else{
// vectorcall protocal
for(auto& item: args) item->emit(ctx);
for(auto& item: kwargs){
int index = StrName(item.first.sv()).index;
ctx->emit(OP_LOAD_INTEGER, index, line);
item.second->emit(ctx);
}
int KWARGC = (int)kwargs.size();
int ARGC = (int)args.size();
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
}
}
bool BinaryExpr::is_compare() const {
switch(op){
case TK("<"): case TK("<="): case TK("=="):
case TK("!="): case TK(">"): case TK(">="): return true;
default: return false;
}
}
void BinaryExpr::_emit_compare(CodeEmitContext* ctx, std::vector<int>& jmps){
if(lhs->is_compare()){
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
}else{
lhs->emit(ctx); // [a]
}
rhs->emit(ctx); // [a, b]
ctx->emit(OP_DUP_TOP, BC_NOARG, line); // [a, b, b]
ctx->emit(OP_ROT_THREE, BC_NOARG, line); // [b, a, b]
switch(op){
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
default: UNREACHABLE();
}
// [b, RES]
int index = ctx->emit(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line);
jmps.push_back(index);
}
void BinaryExpr::emit(CodeEmitContext* ctx) {
std::vector<int> jmps;
if(is_compare() && lhs->is_compare()){
// (a < b) < c
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
// [b, RES]
}else{
// (1 + 2) < c
lhs->emit(ctx);
}
rhs->emit(ctx);
switch (op) {
case TK("+"): ctx->emit(OP_BINARY_ADD, BC_NOARG, line); break;
case TK("-"): ctx->emit(OP_BINARY_SUB, BC_NOARG, line); break;
case TK("*"): ctx->emit(OP_BINARY_MUL, BC_NOARG, line); break;
case TK("/"): ctx->emit(OP_BINARY_TRUEDIV, BC_NOARG, line); break;
case TK("//"): ctx->emit(OP_BINARY_FLOORDIV, BC_NOARG, line); break;
case TK("%"): ctx->emit(OP_BINARY_MOD, BC_NOARG, line); break;
case TK("**"): ctx->emit(OP_BINARY_POW, BC_NOARG, line); break;
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
case TK("in"): ctx->emit(OP_CONTAINS_OP, 0, line); break;
case TK("not in"): ctx->emit(OP_CONTAINS_OP, 1, line); break;
case TK("is"): ctx->emit(OP_IS_OP, 0, line); break;
case TK("is not"): ctx->emit(OP_IS_OP, 1, line); break;
case TK("<<"): ctx->emit(OP_BITWISE_LSHIFT, BC_NOARG, line); break;
case TK(">>"): ctx->emit(OP_BITWISE_RSHIFT, BC_NOARG, line); break;
case TK("&"): ctx->emit(OP_BITWISE_AND, BC_NOARG, line); break;
case TK("|"): ctx->emit(OP_BITWISE_OR, BC_NOARG, line); break;
case TK("^"): ctx->emit(OP_BITWISE_XOR, BC_NOARG, line); break;
case TK("@"): ctx->emit(OP_BINARY_MATMUL, BC_NOARG, line); break;
default: FATAL_ERROR();
}
for(int i: jmps) ctx->patch_jump(i);
}
void TernaryExpr::emit(CodeEmitContext* ctx){
cond->emit(ctx);
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line);
true_expr->emit(ctx);
int patch_2 = ctx->emit(OP_JUMP_ABSOLUTE, BC_NOARG, true_expr->line);
ctx->patch_jump(patch);
false_expr->emit(ctx);
ctx->patch_jump(patch_2);
}
} // namespace pkpy

90
src/frame.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "pocketpy/frame.h"
namespace pkpy{
PyObject* FastLocals::try_get(StrName name){
int index = varnames_inv->try_get(name);
if(index == -1) return nullptr;
return a[index];
}
bool FastLocals::contains(StrName name){
return varnames_inv->contains(name);
}
void FastLocals::erase(StrName name){
int index = varnames_inv->try_get(name);
if(index == -1) FATAL_ERROR();
a[index] = nullptr;
}
bool FastLocals::try_set(StrName name, PyObject* value){
int index = varnames_inv->try_get(name);
if(index == -1) return false;
a[index] = value;
return true;
}
NameDict_ FastLocals::to_namedict(){
NameDict_ dict = make_sp<NameDict>();
varnames_inv->apply([&](StrName name, int index){
PyObject* value = a[index];
if(value != PY_NULL) dict->set(name, value);
});
return dict;
}
PyObject* Frame::f_closure_try_get(StrName name){
if(_callable == nullptr) return nullptr;
Function& fn = PK_OBJ_GET(Function, _callable);
if(fn._closure == nullptr) return nullptr;
return fn._closure->try_get(name);
}
Str Frame::snapshot(){
int line = co->lines[_ip];
return co->src->snapshot(line);
}
bool Frame::jump_to_exception_handler(){
// try to find a parent try block
int block = co->codes[_ip].block;
while(block >= 0){
if(co->blocks[block].type == TRY_EXCEPT) break;
block = co->blocks[block].parent;
}
if(block < 0) return false;
PyObject* obj = _s->popx(); // pop exception object
// get the stack size of the try block (depth of for loops)
int _stack_size = co->blocks[block].for_loop_depth;
if(stack_size() < _stack_size) throw std::runtime_error("invalid stack size");
_s->reset(actual_sp_base() + _locals.size() + _stack_size); // rollback the stack
_s->push(obj); // push exception object
_next_ip = co->blocks[block].end;
return true;
}
int Frame::_exit_block(int i){
if(co->blocks[i].type == FOR_LOOP) _s->pop();
return co->blocks[i].parent;
}
void Frame::jump_abs_break(int target){
const Bytecode& prev = co->codes[_ip];
int i = prev.block;
_next_ip = target;
if(_next_ip >= co->codes.size()){
while(i>=0) i = _exit_block(i);
}else{
// BUG!!!
// for i in range(4):
// _ = 0
// # if there is no op here, the block check will fail
// while i: --i
const Bytecode& next = co->codes[target];
while(i>=0 && i!=next.block) i = _exit_block(i);
if(i!=next.block) throw std::runtime_error("invalid jump");
}
}
} // namespace pkpy

65
src/gc.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "pocketpy/gc.h"
namespace pkpy{
int ManagedHeap::sweep(){
std::vector<PyObject*> alive;
for(PyObject* obj: gen){
if(obj->gc.marked){
obj->gc.marked = false;
alive.push_back(obj);
}else{
#if PK_DEBUG_GC_STATS
deleted[obj->type] += 1;
#endif
if(_gc_on_delete) _gc_on_delete(vm, obj);
obj->~PyObject();
pool64.dealloc(obj);
}
}
// clear _no_gc marked flag
for(PyObject* obj: _no_gc) obj->gc.marked = false;
int freed = gen.size() - alive.size();
// std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
gen.clear();
gen.swap(alive);
return freed;
}
void ManagedHeap::_auto_collect(){
#if !PK_DEBUG_NO_AUTO_GC
if(_gc_lock_counter > 0) return;
if(gc_counter < gc_threshold) return;
gc_counter = 0;
collect();
gc_threshold = gen.size() * 2;
if(gc_threshold < kMinGCThreshold) gc_threshold = kMinGCThreshold;
#endif
}
int ManagedHeap::collect(){
if(_gc_lock_counter > 0) FATAL_ERROR();
mark();
int freed = sweep();
return freed;
}
ManagedHeap::~ManagedHeap(){
for(PyObject* obj: _no_gc) { obj->~PyObject(); pool64.dealloc(obj); }
for(PyObject* obj: gen) { obj->~PyObject(); pool64.dealloc(obj); }
#if PK_DEBUG_GC_STATS
for(auto& [type, count]: deleted){
std::cout << "GC: " << obj_type_name(vm, type) << "=" << count << std::endl;
}
#endif
}
void FuncDecl::_gc_mark() const{
code->_gc_mark();
for(int i=0; i<kwargs.size(); i++) PK_OBJ_MARK(kwargs[i].value);
}
} // namespace pkpy

24
src/obj.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "pocketpy/obj.h"
namespace pkpy{
NativeFunc::NativeFunc(NativeFuncC f, int argc, bool method){
this->f = f;
this->argc = argc;
if(argc != -1) this->argc += (int)method;
_has_userdata = false;
}
NativeFunc::NativeFunc(NativeFuncC f, FuncDecl_ decl){
this->f = f;
this->argc = -1;
this->decl = decl;
_has_userdata = false;
}
PyObject::~PyObject() {
if(_attr == nullptr) return;
_attr->~NameDict();
pool64.dealloc(_attr);
}
} // namespace pkpy

View File

@ -2,6 +2,275 @@
namespace pkpy{
VM::VM(bool enable_os) : heap(this), enable_os(enable_os) {
this->vm = this;
_stdout = [](VM* vm, const Str& s) {
PK_UNUSED(vm);
std::cout << s;
};
_stderr = [](VM* vm, const Str& s) {
PK_UNUSED(vm);
std::cerr << s;
};
callstack.reserve(8);
_main = nullptr;
_last_exception = nullptr;
_import_handler = [](const Str& name) {
PK_UNUSED(name);
return Bytes();
};
init_builtin_types();
}
PyObject* VM::py_str(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__str__) return ti->m__str__(this, obj);
PyObject* self;
PyObject* f = get_unbound_method(obj, __str__, &self, false);
if(self != PY_NULL) return call_method(self, f);
return py_repr(obj);
}
PyObject* VM::py_repr(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__repr__) return ti->m__repr__(this, obj);
return call_method(obj, __repr__);
}
PyObject* VM::py_json(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__json__) return ti->m__json__(this, obj);
return call_method(obj, __json__);
}
PyObject* VM::py_iter(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__iter__) return ti->m__iter__(this, obj);
PyObject* self;
PyObject* iter_f = get_unbound_method(obj, __iter__, &self, false);
if(self != PY_NULL) return call_method(self, iter_f);
TypeError(OBJ_NAME(_t(obj)).escape() + " object is not iterable");
return nullptr;
}
FrameId VM::top_frame(){
#if PK_DEBUG_EXTRA_CHECK
if(callstack.empty()) FATAL_ERROR();
#endif
return FrameId(&callstack.data(), callstack.size()-1);
}
void VM::_pop_frame(){
Frame* frame = &callstack.top();
s_data.reset(frame->_sp_base);
callstack.pop();
}
PyObject* VM::find_name_in_mro(PyObject* cls, StrName name){
PyObject* val;
do{
val = cls->attr().try_get(name);
if(val != nullptr) return val;
Type base = _all_types[PK_OBJ_GET(Type, cls)].base;
if(base.index == -1) break;
cls = _all_types[base].obj;
}while(true);
return nullptr;
}
bool VM::isinstance(PyObject* obj, Type cls_t){
Type obj_t = PK_OBJ_GET(Type, _t(obj));
do{
if(obj_t == cls_t) return true;
Type base = _all_types[obj_t].base;
if(base.index == -1) break;
obj_t = base;
}while(true);
return false;
}
PyObject* VM::exec(Str source, Str filename, CompileMode mode, PyObject* _module){
if(_module == nullptr) _module = _main;
try {
CodeObject_ code = compile(source, filename, mode);
#if PK_DEBUG_DIS_EXEC
if(_module == _main) std::cout << disassemble(code) << '\n';
#endif
return _exec(code, _module);
}catch (const Exception& e){
_stderr(this, e.summary() + "\n");
}
#if !PK_DEBUG_FULL_EXCEPTION
catch (const std::exception& e) {
Str msg = "An std::exception occurred! It could be a bug.\n";
msg = msg + e.what();
_stderr(this, msg + "\n");
}
#endif
callstack.clear();
s_data.clear();
return nullptr;
}
PyObject* VM::property(NativeFuncC fget, NativeFuncC fset){
PyObject* _0 = heap.gcnew(tp_native_func, NativeFunc(fget, 1, false));
PyObject* _1 = vm->None;
if(fset != nullptr) _1 = heap.gcnew(tp_native_func, NativeFunc(fset, 2, false));
return call(_t(tp_property), _0, _1);
}
PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled){
PyObject* obj = heap._new<Type>(tp_type, _all_types.size());
const PyTypeInfo& base_info = _all_types[base];
if(!base_info.subclass_enabled){
TypeError(fmt("type ", base_info.name.escape(), " is not `subclass_enabled`"));
}
PyTypeInfo info{
obj,
base,
(mod!=nullptr && mod!=builtins) ? Str(OBJ_NAME(mod)+"."+name.sv()): name.sv(),
subclass_enabled,
};
if(mod != nullptr) mod->attr().set(name, obj);
_all_types.push_back(info);
return obj;
}
Type VM::_new_type_object(StrName name, Type base) {
PyObject* obj = new_type_object(nullptr, name, base, false);
return PK_OBJ_GET(Type, obj);
}
PyObject* VM::_find_type_object(const Str& type){
PyObject* obj = builtins->attr().try_get(type);
if(obj == nullptr){
for(auto& t: _all_types) if(t.name == type) return t.obj;
throw std::runtime_error(fmt("type not found: ", type));
}
check_non_tagged_type(obj, tp_type);
return obj;
}
Type VM::_type(const Str& type){
PyObject* obj = _find_type_object(type);
return PK_OBJ_GET(Type, obj);
}
PyTypeInfo* VM::_type_info(const Str& type){
PyObject* obj = builtins->attr().try_get(type);
if(obj == nullptr){
for(auto& t: _all_types) if(t.name == type) return &t;
FATAL_ERROR();
}
return &_all_types[PK_OBJ_GET(Type, obj)];
}
PyTypeInfo* VM::_type_info(Type type){
return &_all_types[type];
}
const PyTypeInfo* VM::_inst_type_info(PyObject* obj){
if(is_int(obj)) return &_all_types[tp_int];
if(is_float(obj)) return &_all_types[tp_float];
return &_all_types[obj->type];
}
bool VM::py_equals(PyObject* lhs, PyObject* rhs){
if(lhs == rhs) return true;
const PyTypeInfo* ti = _inst_type_info(lhs);
PyObject* res;
if(ti->m__eq__){
res = ti->m__eq__(this, lhs, rhs);
if(res != vm->NotImplemented) return res == vm->True;
}
res = call_method(lhs, __eq__, rhs);
if(res != vm->NotImplemented) return res == vm->True;
ti = _inst_type_info(rhs);
if(ti->m__eq__){
res = ti->m__eq__(this, rhs, lhs);
if(res != vm->NotImplemented) return res == vm->True;
}
res = call_method(rhs, __eq__, lhs);
if(res != vm->NotImplemented) return res == vm->True;
return false;
}
int VM::normalized_index(int index, int size){
if(index < 0) index += size;
if(index < 0 || index >= size){
IndexError(std::to_string(index) + " not in [0, " + std::to_string(size) + ")");
}
return index;
}
PyObject* VM::py_next(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__next__) return ti->m__next__(this, obj);
return call_method(obj, __next__);
}
PyObject* VM::py_import(StrName name, bool relative){
Str filename;
int type;
if(relative){
ImportContext* ctx = &_import_context;
type = 2;
for(auto it=ctx->pending.rbegin(); it!=ctx->pending.rend(); ++it){
if(it->second == 2) continue;
if(it->second == 1){
filename = fmt(it->first, kPlatformSep, name, ".py");
name = fmt(it->first, '.', name).c_str();
break;
}
}
if(filename.length() == 0) _error("ImportError", "relative import outside of package");
}else{
type = 0;
filename = fmt(name, ".py");
}
for(auto& [k, v]: _import_context.pending){
if(k == name){
vm->_error("ImportError", fmt("circular import ", name.escape()));
}
}
PyObject* ext_mod = _modules.try_get(name);
if(ext_mod == nullptr){
Str source;
auto it = _lazy_modules.find(name);
if(it == _lazy_modules.end()){
Bytes b = _import_handler(filename);
if(!relative && !b){
filename = fmt(name, kPlatformSep, "__init__.py");
b = _import_handler(filename);
if(b) type = 1;
}
if(!b) _error("ImportError", fmt("module ", name.escape(), " not found"));
source = Str(b.str());
}else{
source = it->second;
_lazy_modules.erase(it);
}
auto _ = _import_context.temp(this, name, type);
CodeObject_ code = compile(source, filename, EXEC_MODE);
PyObject* new_mod = new_module(name);
_exec(code, new_mod);
new_mod->attr()._try_perfect_rehash();
return new_mod;
}else{
return ext_mod;
}
}
VM::~VM() {
callstack.clear();
s_data.clear();
_all_types.clear();
_modules.clear();
_lazy_modules.clear();
}
PyObject* VM::py_negate(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);

View File

@ -1 +1 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy.h"

View File

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