From efb7fce3c12d6f2d86c32edde74f1c081e947da4 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 2 Jul 2023 15:24:32 +0800 Subject: [PATCH] ... --- build.py | 58 +--- include/pocketpy.h | 3 + include/pocketpy/codeobject.h | 163 +-------- include/pocketpy/dict.h | 183 +--------- include/pocketpy/expr.h | 615 +++------------------------------- include/pocketpy/frame.h | 97 +----- include/pocketpy/gc.h | 63 +--- include/pocketpy/obj.h | 32 +- include/pocketpy/vm.h | 282 ++-------------- src/codeobject.cpp | 157 +++++++++ src/dict.cpp | 176 ++++++++++ src/expr.cpp | 583 ++++++++++++++++++++++++++++++++ src/frame.cpp | 90 +++++ src/gc.cpp | 65 ++++ src/obj.cpp | 24 ++ src/vm.cpp | 269 +++++++++++++++ src2/lib.cpp | 2 +- src2/main.cpp | 2 +- 18 files changed, 1499 insertions(+), 1365 deletions(-) create mode 100644 include/pocketpy.h create mode 100644 src/codeobject.cpp create mode 100644 src/dict.cpp create mode 100644 src/expr.cpp create mode 100644 src/frame.cpp create mode 100644 src/gc.cpp create mode 100644 src/obj.cpp diff --git a/build.py b/build.py index cf9ae94a..9d975e05 100644 --- a/build.py +++ b/build.py @@ -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/ + 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) \ No newline at end of file +else: + os.system(linux_cmd) \ No newline at end of file diff --git a/include/pocketpy.h b/include/pocketpy.h new file mode 100644 index 00000000..18ce7049 --- /dev/null +++ b/include/pocketpy.h @@ -0,0 +1,3 @@ +#pragma once + +#include "pocketpy/pocketpy.h" \ No newline at end of file diff --git a/include/pocketpy/codeobject.h b/include/pocketpy/codeobject.h index 79ce2b7e..4b175114 100644 --- a/include/pocketpy/codeobject.h +++ b/include/pocketpy/codeobject.h @@ -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 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 src, const Str& name): - src(src), name(name) {} - std::vector codes; std::vector lines; // line number for each bytecode List consts; @@ -160,84 +101,10 @@ struct CodeObject { NameDictInt labels; std::vector 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 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 \ No newline at end of file diff --git a/include/pocketpy/dict.h b/include/pocketpy/dict.h index 7980e5d8..56fc5262 100644 --- a/include/pocketpy/dict.h +++ b/include/pocketpy/dict.h @@ -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 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 \ No newline at end of file diff --git a/include/pocketpy/expr.h b/include/pocketpy/expr.h index 9845a68f..7bb87a60 100644 --- a/include/pocketpy/expr.h +++ b/include/pocketpy/expr.h @@ -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; iconsts.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(value)){ - return std::to_string(std::get(value)); - } - if(std::holds_alternative(value)){ - return std::to_string(std::get(value)); - } - if(std::holds_alternative(value)){ - Str s = std::get(value).escape(); - return s.str(); - } - FATAL_ERROR(); - } - - void emit(CodeEmitContext* ctx) override { - VM* vm = ctx->vm; - PyObject* obj = nullptr; - if(std::holds_alternative(value)){ - i64 _val = std::get(value); - if(_val >= INT16_MIN && _val <= INT16_MAX){ - ctx->emit(OP_LOAD_INTEGER, (int)_val, line); - return; - } - obj = VAR(_val); - } - if(std::holds_alternative(value)){ - obj = VAR(std::get(value)); - } - if(std::holds_alternative(value)){ - obj = VAR(std::get(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(child.get()); - if(std::holds_alternative(lit->value)){ - i64 _val = -std::get(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(lit->value)){ - PyObject* obj = VAR(-std::get(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; iis_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> 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(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& jmps){ - if(lhs->is_compare()){ - static_cast(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 jmps; - if(is_compare() && lhs->is_compare()){ - // (a < b) < c - static_cast(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& 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; }; diff --git a/include/pocketpy/frame.h b/include/pocketpy/frame.h index 3266a749..5d84ce5f 100644 --- a/include/pocketpy/frame.h +++ b/include/pocketpy/frame.h @@ -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(); - 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 @@ -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); diff --git a/include/pocketpy/gc.h b/include/pocketpy/gc.h index 38ba286c..bb50662e 100644 --- a/include/pocketpy/gc.h +++ b/include/pocketpy/gc.h @@ -69,66 +69,11 @@ struct ManagedHeap{ inline static std::map deleted; #endif - int sweep(){ - std::vector 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 CodeObject_; + struct Frame; -struct Function; class VM; #if PK_ENABLE_STD_FUNCTION @@ -17,8 +18,6 @@ using NativeFuncC = std::function; typedef PyObject* (*NativeFuncC)(VM*, ArgsView); #endif -typedef shared_ptr CodeObject_; - struct FuncDecl { struct KwArg { int key; // index in co->varnames @@ -70,19 +69,8 @@ struct NativeFunc { return reinterpret_cast(_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(lf); } }; @@ -444,11 +428,9 @@ struct Py_ final: PyObject { }; template -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(); else return PK_OBJ_GET(NativeFunc, p[-2]).get_userdata(); } - - } // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 41f004e4..d3383141 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -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 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(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 PyObject* bind_func(Str type, Str name, NativeFuncC fn) { @@ -498,19 +330,8 @@ public: return bind_func(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 diff --git a/src/codeobject.cpp b/src/codeobject.cpp new file mode 100644 index 00000000..2d7c03dd --- /dev/null +++ b/src/codeobject.cpp @@ -0,0 +1,157 @@ +#include "pocketpy/codeobject.h" + +namespace pkpy{ + + CodeObject::CodeObject(shared_ptr 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 \ No newline at end of file diff --git a/src/dict.cpp b/src/dict.cpp new file mode 100644 index 00000000..de71e33d --- /dev/null +++ b/src/dict.cpp @@ -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; iblocks[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; iconsts.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(value)){ + return std::to_string(std::get(value)); + } + if(std::holds_alternative(value)){ + return std::to_string(std::get(value)); + } + if(std::holds_alternative(value)){ + Str s = std::get(value).escape(); + return s.str(); + } + FATAL_ERROR(); + } + + void LiteralExpr::emit(CodeEmitContext* ctx) { + VM* vm = ctx->vm; + PyObject* obj = nullptr; + if(std::holds_alternative(value)){ + i64 _val = std::get(value); + if(_val >= INT16_MIN && _val <= INT16_MAX){ + ctx->emit(OP_LOAD_INTEGER, (int)_val, line); + return; + } + obj = VAR(_val); + } + if(std::holds_alternative(value)){ + obj = VAR(std::get(value)); + } + if(std::holds_alternative(value)){ + obj = VAR(std::get(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(child.get()); + if(std::holds_alternative(lit->value)){ + i64 _val = -std::get(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(lit->value)){ + PyObject* obj = VAR(-std::get(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; iis_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(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& jmps){ + if(lhs->is_compare()){ + static_cast(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 jmps; + if(is_compare() && lhs->is_compare()){ + // (a < b) < c + static_cast(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 \ No newline at end of file diff --git a/src/frame.cpp b/src/frame.cpp new file mode 100644 index 00000000..053ab21e --- /dev/null +++ b/src/frame.cpp @@ -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(); + 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 \ No newline at end of file diff --git a/src/gc.cpp b/src/gc.cpp new file mode 100644 index 00000000..9fac2e8e --- /dev/null +++ b/src/gc.cpp @@ -0,0 +1,65 @@ +#include "pocketpy/gc.h" + +namespace pkpy{ + + int ManagedHeap::sweep(){ + std::vector 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; if = 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 \ No newline at end of file diff --git a/src/vm.cpp b/src/vm.cpp index 9555c973..329fa94f 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -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(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); diff --git a/src2/lib.cpp b/src2/lib.cpp index 9f1e34a4..a11446e0 100644 --- a/src2/lib.cpp +++ b/src2/lib.cpp @@ -1 +1 @@ -#include "pocketpy/pocketpy.h" \ No newline at end of file +#include "pocketpy.h" \ No newline at end of file diff --git a/src2/main.cpp b/src2/main.cpp index bcad2c7a..4ae9f23e 100644 --- a/src2/main.cpp +++ b/src2/main.cpp @@ -1,7 +1,7 @@ #include #include -#include "pocketpy/pocketpy.h" +#include "pocketpy.h" #ifndef __EMSCRIPTEN__