diff --git a/src/cffi.h b/src/cffi.h index fbb13a8e..117d1248 100644 --- a/src/cffi.h +++ b/src/cffi.h @@ -2,8 +2,6 @@ #include "common.h" #include "vm.h" -#include -#include namespace pkpy { diff --git a/src/common.h b/src/common.h index 2ae87052..88875867 100644 --- a/src/common.h +++ b/src/common.h @@ -26,12 +26,13 @@ #include #include #include +#include #define PK_VERSION "0.9.6" // debug macros -#define DEBUG_NO_BUILTIN_MODULES 0 -#define DEBUG_EXTRA_CHECK 1 +#define DEBUG_NO_BUILTIN_MODULES 1 +#define DEBUG_MODE 1 #if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__) #define PK_ENABLE_FILEIO 0 diff --git a/src/compiler.h b/src/compiler.h index 522c294f..0f8a371d 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -26,7 +26,7 @@ class Compiler { const Token& prev() { return tokens.at(i-1); } const Token& curr() { return tokens.at(i); } const Token& next() { return tokens.at(i+1); } - void advance() { i++; } + void advance(int delta=1) { i += delta; } CodeEmitContext* ctx() { return &contexts.top(); } CompileMode mode() const{ return lexer->src->mode; } @@ -42,7 +42,7 @@ class Compiler { void pop_context(){ if(!ctx()->s_expr.empty()) UNREACHABLE(); // if the last op does not return, add a default return None - if(ctx()->co->codes.back().op != OP_RETURN_VALUE){ + if(ctx()->co->codes.empty() || ctx()->co->codes.back().op != OP_RETURN_VALUE){ ctx()->emit(OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE); ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); } @@ -185,12 +185,14 @@ class Compiler { // PASS void exprTuple(){ - auto e = make_expr(); + std::vector items; do { EXPR(); // NOTE: "1," will fail, "1,2" will be ok - e->items.push_back(ctx()->s_expr.popx()); + items.push_back(ctx()->s_expr.popx()); } while(match(TK(","))); - ctx()->s_expr.push(std::move(e)); + ctx()->s_expr.push(make_expr( + std::move(items) + )); } // PASS @@ -577,7 +579,7 @@ class Compiler { EXPR(false); // TODO: support multiple decorator // use a while loop to consume '@' - if(!match_newlines(mode()==REPL_MODE)) SyntaxError(); + if(!match_newlines_repl()) SyntaxError(); ctx()->emit(OP_SETUP_DECORATOR, BC_NOARG, prev().line); consume(TK("def")); compile_function(); @@ -585,11 +587,9 @@ class Compiler { bool try_compile_assignment(){ Expr* lhs_p = ctx()->s_expr.top().get(); - bool inplace; switch (curr().type) { case TK("+="): case TK("-="): case TK("*="): case TK("/="): case TK("//="): case TK("%="): case TK("<<="): case TK(">>="): case TK("&="): case TK("|="): case TK("^="): { - inplace = true; advance(); auto e = make_expr(); e->op = prev().type - 1; // -1 to remove = @@ -599,7 +599,6 @@ class Compiler { ctx()->s_expr.push(std::move(e)); } break; case TK("="): - inplace = false; advance(); EXPR_TUPLE(); break; @@ -625,7 +624,7 @@ class Compiler { ctx()->emit(OP_LOOP_CONTINUE, BC_NOARG, kw_line); consume_end_stmt(); break; - case TK("yield"): + case TK("yield"): if (contexts.size() <= 1) SyntaxError("'yield' outside function"); EXPR_TUPLE(true); // if yield present, mark the function as generator @@ -634,7 +633,7 @@ class Compiler { consume_end_stmt(); break; case TK("return"): - if (contexts.size() <= 1) SyntaxError("'ret urn' outside function"); + if (contexts.size() <= 1) SyntaxError("'return' outside function"); if(match_end_stmt()){ ctx()->emit(OP_LOAD_NONE, BC_NOARG, kw_line); }else{ @@ -717,6 +716,7 @@ class Compiler { /*************************************************/ // handle dangling expression or assignment default: { + advance(-1); // do revert since we have pre-called advance() at the beginning EXPR_TUPLE(); if(!try_compile_assignment()){ ctx()->emit_expr(); @@ -791,7 +791,6 @@ class Compiler { void compile_function(){ // TODO: bug, if there are multiple decorators, will cause error - bool has_decorator = !co()->codes.empty() && co()->codes.back().op == OP_SETUP_DECORATOR; Function func; StrName obj_name; consume(TK("@id")); @@ -812,38 +811,38 @@ class Compiler { func.code = push_context(lexer->src, func.name.str()); compile_block_body(); pop_context(); - emit(OP_LOAD_FUNCTION, co()->add_const(VAR(func))); - if(name_scope() == NAME_LOCAL) emit(OP_SETUP_CLOSURE); + ctx()->emit(OP_LOAD_FUNCTION, ctx()->add_const(VAR(func)), prev().line); + if(name_scope() == NAME_LOCAL) ctx()->emit(OP_SETUP_CLOSURE, BC_NOARG, prev().line); if(!ctx()->is_compiling_class){ if(obj_name.empty()){ - if(has_decorator) emit(OP_CALL, 1); - emit(OP_STORE_NAME, co()->add_name(func.name, name_scope())); + auto e = make_expr(func.name, name_scope()); + e->emit_store(ctx()); } else { - if(has_decorator) SyntaxError("decorator is not supported here"); - emit(OP_LOAD_NAME, co()->add_name(obj_name, name_scope())); - int index = co()->add_name(func.name, NAME_ATTR); - emit(OP_BUILD_ATTR_REF, index); - emit(OP_ROT_TWO); - emit(OP_STORE_REF); + ctx()->emit(OP_LOAD_NAME, ctx()->add_name(obj_name), prev().line); + int index = ctx()->add_name(func.name); + ctx()->emit(OP_STORE_ATTR, index, prev().line); } }else{ - if(has_decorator) emit(OP_CALL, 1); - emit(OP_STORE_CLASS_ATTR, co()->add_name(func.name, name_scope())); + ctx()->emit(OP_STORE_CLASS_ATTR, ctx()->add_name(func.name), BC_KEEPLINE); } } PyObject* read_literal(){ - if(match(TK("-"))){ - consume(TK("@num")); - PyObject* val = get_value(prev()); - return vm->num_negated(val); + advance(); + switch(prev().type){ + case TK("-"): { + consume(TK("@num")); + PyObject* val = LiteralExpr(prev().value).to_object(ctx()); + return vm->num_negated(val); + } + case TK("@num"): return LiteralExpr(prev().value).to_object(ctx()); + case TK("@str"): return LiteralExpr(prev().value).to_object(ctx()); + case TK("True"): return VAR(true); + case TK("False"): return VAR(false); + case TK("None"): return vm->None; + case TK("..."): return vm->Ellipsis; + default: break; } - if(match(TK("@num"))) return get_value(prev()); - if(match(TK("@str"))) return get_value(prev()); - if(match(TK("True"))) return VAR(true); - if(match(TK("False"))) return VAR(false); - if(match(TK("None"))) return vm->None; - if(match(TK("..."))) return vm->Ellipsis; return nullptr; } @@ -858,7 +857,8 @@ public: this->lexer = std::make_unique( make_sp(source, filename, mode) ); - if(rules.empty()) init_pratt_rules(); + // TODO: check if already initialized + init_pratt_rules(); } CodeObject_ compile(){ @@ -883,7 +883,7 @@ public: return code; }else if(mode()==JSON_MODE){ PyObject* value = read_literal(); - if(value != nullptr) emit(OP_LOAD_CONST, code->add_const(value)); + if(value != nullptr) ctx()->emit(OP_LOAD_CONST, ctx()->add_const(value), prev().line); else if(match(TK("{"))) exprMap(); else if(match(TK("["))) exprList(); else SyntaxError("expect a JSON object or array"); diff --git a/src/expr.h b/src/expr.h index 096a3334..cdb476dd 100644 --- a/src/expr.h +++ b/src/expr.h @@ -31,12 +31,7 @@ struct CodeEmitContext{ CodeObject_ co; VM* vm; stack s_expr; - CodeEmitContext(VM* vm, CodeObject_ co): co(co) {} - CodeEmitContext(const CodeEmitContext&) = delete; - CodeEmitContext& operator=(const CodeEmitContext&) = delete; - CodeEmitContext(CodeEmitContext&&) = delete; - CodeEmitContext& operator=(CodeEmitContext&&) = delete; int curr_block_i = 0; bool is_compiling_class = false; @@ -101,12 +96,11 @@ struct CodeEmitContext{ // PASS struct NameExpr: Expr{ - Str name; + StrName name; NameScope scope; - NameExpr(const Str& name, NameScope scope): name(name), scope(scope) {} - NameExpr(Str&& name, NameScope scope): name(std::move(name)), scope(scope) {} + NameExpr(StrName name, NameScope scope): name(name), scope(scope) {} - Str str() const override { return "$" + name; } + Str str() const override { return "$" + name.str(); } void emit(CodeEmitContext* ctx) override { int index = ctx->add_name(name); @@ -246,7 +240,7 @@ struct LiteralExpr: Expr{ UNREACHABLE(); } - void emit(CodeEmitContext* ctx) override { + PyObject* to_object(CodeEmitContext* ctx){ VM* vm = ctx->vm; PyObject* obj = nullptr; if(std::holds_alternative(value)){ @@ -258,6 +252,11 @@ struct LiteralExpr: Expr{ if(std::holds_alternative(value)){ obj = VAR(std::get(value)); } + return obj; + } + + void emit(CodeEmitContext* ctx) override { + PyObject* obj = to_object(ctx); if(obj == nullptr) UNREACHABLE(); int index = ctx->add_const(obj); ctx->emit(OP_LOAD_CONST, index, line); @@ -287,7 +286,7 @@ struct NegatedExpr: Expr{ obj = VAR(std::get(lit->value)); } if(obj != nullptr){ - ctx->emit(OP_LOAD_CONST, ctx()->add_const(obj), line); + ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line); return; } } @@ -362,21 +361,25 @@ struct SequenceExpr: Expr{ }; struct ListExpr: SequenceExpr{ + using SequenceExpr::SequenceExpr; Str str() const override { return "list()"; } Opcode opcode() const override { return OP_BUILD_LIST; } }; struct DictExpr: SequenceExpr{ + using SequenceExpr::SequenceExpr; Str str() const override { return "dict()"; } - Opcode opcode() const override { return OP_BUILD_MAP; } + Opcode opcode() const override { return OP_BUILD_DICT; } }; struct SetExpr: SequenceExpr{ + using SequenceExpr::SequenceExpr; Str str() const override { return "set()"; } Opcode opcode() const override { return OP_BUILD_SET; } }; struct TupleExpr: SequenceExpr{ + using SequenceExpr::SequenceExpr; Str str() const override { return "tuple()"; } Opcode opcode() const override { return OP_BUILD_TUPLE; } @@ -432,7 +435,8 @@ struct CompExpr: Expr{ ctx->enter_block(FOR_LOOP); ctx->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE); bool ok = vars->emit_store(ctx); - if(!ok) SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind + // this error occurs in `vars` instead of this line, but...nevermind + if(!ok) UNREACHABLE(); // TODO: raise a SyntaxError instead if(cond){ cond->emit(ctx); int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); @@ -449,16 +453,19 @@ struct CompExpr: Expr{ struct ListCompExpr: CompExpr{ Opcode op0() override { return OP_BUILD_LIST; } Opcode op1() override { return OP_LIST_APPEND; } + Str str() const override { return "listcomp()"; } }; struct DictCompExpr: CompExpr{ - Opcode op0() override { return OP_BUILD_DI CT; } + Opcode op0() override { return OP_BUILD_DICT; } Opcode op1() override { return OP_DICT_ADD; } + Str str() const override { return "dictcomp()"; } }; struct SetCompExpr: CompExpr{ Opcode op0() override { return OP_BUILD_SET; } Opcode op1() override { return OP_SET_ADD; } + Str str() const override { return "setcomp()"; } }; struct LambdaExpr: Expr{ diff --git a/src/frame.h b/src/frame.h index 072719b8..265255e6 100644 --- a/src/frame.h +++ b/src/frame.h @@ -53,14 +53,14 @@ struct Frame { // } void pop(){ -#if DEBUG_EXTRA_CHECK +#if DEBUG_MODE if(_data.empty()) throw std::runtime_error("_data.empty() is true"); #endif _data.pop_back(); } PyObject* popx(){ -#if DEBUG_EXTRA_CHECK +#if DEBUG_MODE if(_data.empty()) throw std::runtime_error("_data.empty() is true"); #endif PyObject* ret = _data.back(); @@ -69,21 +69,21 @@ struct Frame { } PyObject*& top(){ -#if DEBUG_EXTRA_CHECK +#if DEBUG_MODE if(_data.empty()) throw std::runtime_error("_data.empty() is true"); #endif return _data.back(); } PyObject*& top_1(){ -#if DEBUG_EXTRA_CHECK +#if DEBUG_MODE if(_data.size() < 2) throw std::runtime_error("_data.size() < 2"); #endif return _data[_data.size()-2]; } PyObject*& top_2(){ -#if DEBUG_EXTRA_CHECK +#if DEBUG_MODE if(_data.size() < 3) throw std::runtime_error("_data.size() < 3"); #endif return _data[_data.size()-3]; @@ -115,7 +115,7 @@ struct Frame { } int _exit_block(int i){ - if(co->blocks[i].type == FOR_LOOP) _pop(); + if(co->blocks[i].type == FOR_LOOP) pop(); else if(co->blocks[i].type == TRY_EXCEPT) on_try_block_exit(); return co->blocks[i].parent; } diff --git a/src/iter.h b/src/iter.h index 25b20176..0a9041d6 100644 --- a/src/iter.h +++ b/src/iter.h @@ -26,8 +26,8 @@ public: template class ArrayIter : public BaseIter { - int index; PyObject* ref; + int index; public: ArrayIter(VM* vm, PyObject* ref) : BaseIter(vm), ref(ref), index(0) {} @@ -43,10 +43,10 @@ public: }; class StringIter : public BaseIter { - int index = 0; PyObject* ref; + int index; public: - StringIter(VM* vm, PyObject* ref) : BaseIter(vm), ref(ref) {} + StringIter(VM* vm, PyObject* ref) : BaseIter(vm), ref(ref), index(0) {} PyObject* next() override{ Str* str = &OBJ_GET(Str, ref); diff --git a/src/memory.h b/src/memory.h index 61f6246d..62d9932f 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,7 +1,6 @@ #pragma once #include "common.h" -#include namespace pkpy{ diff --git a/src/obj.h b/src/obj.h index 36d38255..e6f6c180 100644 --- a/src/obj.h +++ b/src/obj.h @@ -3,7 +3,6 @@ #include "common.h" #include "namedict.h" #include "tuplelist.h" -#include namespace pkpy { @@ -142,9 +141,14 @@ struct Py_ : PyObject { }; #define OBJ_GET(T, obj) (((Py_*)(obj))->_value) -#define OBJ_NAME(obj) OBJ_GET(Str, vm->getattr(obj, __name__)) #define OBJ_MARK(obj) if(!is_tagged(obj)) obj->_mark() +#if DEBUG_NO_BUILTIN_MODULES +#define OBJ_NAME(obj) Str("") +#else +#define OBJ_NAME(obj) OBJ_GET(Str, vm->getattr(obj, __name__)) +#endif + const int kTpIntIndex = 2; const int kTpFloatIndex = 3; diff --git a/src/pocketpy.h b/src/pocketpy.h index f5fe5aac..938e92fd 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -542,7 +542,7 @@ inline void init_builtins(VM* _vm) { }); /************ PyBool ************/ - _vm->bind_static_method<1>("bool", "__new__", CPP_LAMBDA(vm->asBool(args[0]))); + _vm->bind_static_method<1>("bool", "__new__", CPP_LAMBDA(VAR(vm->asBool(args[0])))); _vm->bind_method<0>("bool", "__repr__", [](VM* vm, Args& args) { bool val = CAST(bool, args[0]); diff --git a/src/vm.h b/src/vm.h index 778583aa..7e26b37a 100644 --- a/src/vm.h +++ b/src/vm.h @@ -93,7 +93,7 @@ public: } Frame* top_frame() const { -#if DEBUG_EXTRA_CHECK +#if DEBUG_MODE if(callstack.empty()) UNREACHABLE(); #endif return callstack.top().get(); @@ -166,14 +166,18 @@ public: if(_module == nullptr) _module = _main; try { CodeObject_ code = compile(source, filename, mode); - // if(_module == _main) std::cout << disassemble(code) << '\n'; + if(_module == _main) std::cout << disassemble(code) << '\n'; return _exec(code, _module); }catch (const Exception& e){ *_stderr << e.summary() << '\n'; - }catch (const std::exception& e) { + + } +#if !DEBUG_MODE + catch (const std::exception& e) { *_stderr << "An std::exception occurred! It could be a bug.\n"; *_stderr << e.what() << '\n'; } +#endif callstack = {}; return nullptr; } @@ -289,6 +293,7 @@ public: void NameError(StrName name){ _error("NameError", "name " + name.str().escape(true) + " is not defined"); } void AttributeError(PyObject* obj, StrName name){ + // OBJ_NAME calls getattr, which may lead to a infinite recursion _error("AttributeError", "type " + OBJ_NAME(_t(obj)).escape(true) + " has no attribute " + name.str().escape(true)); } @@ -551,74 +556,73 @@ inline PyObject* VM::new_module(StrName name) { } inline Str VM::disassemble(CodeObject_ co){ - return ""; - // auto pad = [](const Str& s, const int n){ - // if(s.size() >= n) return s.substr(0, n); - // return s + std::string(n - s.size(), ' '); - // }; + auto pad = [](const Str& s, const int n){ + if(s.size() >= n) return s.substr(0, n); + return s + std::string(n - s.size(), ' '); + }; - // std::vector jumpTargets; - // for(auto byte : co->codes){ - // if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE){ - // jumpTargets.push_back(byte.arg); - // } - // } - // StrStream ss; - // ss << std::string(54, '-') << '\n'; - // ss << co->name << ":\n"; - // int prev_line = -1; - // for(int i=0; icodes.size(); i++){ - // const Bytecode& byte = co->codes[i]; - // if(byte.op == OP_NO_OP) continue; - // Str line = std::to_string(byte.line); - // if(byte.line == prev_line) line = ""; - // else{ - // if(prev_line != -1) ss << "\n"; - // prev_line = byte.line; - // } + std::vector jumpTargets; + for(auto byte : co->codes){ + if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE){ + jumpTargets.push_back(byte.arg); + } + } + StrStream ss; + ss << std::string(54, '-') << '\n'; + ss << co->name << ":\n"; + int prev_line = -1; + for(int i=0; icodes.size(); i++){ + const Bytecode& byte = co->codes[i]; + if(byte.op == OP_NO_OP) continue; + Str line = std::to_string(byte.line); + if(byte.line == prev_line) line = ""; + else{ + if(prev_line != -1) ss << "\n"; + prev_line = byte.line; + } - // std::string pointer; - // if(std::find(jumpTargets.begin(), jumpTargets.end(), i) != jumpTargets.end()){ - // pointer = "-> "; - // }else{ - // pointer = " "; - // } - // ss << pad(line, 8) << pointer << pad(std::to_string(i), 3); - // ss << " " << pad(OP_NAMES[byte.op], 20) << " "; - // // ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5); - // std::string argStr = byte.arg == -1 ? "" : std::to_string(byte.arg); - // if(byte.op == OP_LOAD_CONST){ - // argStr += " (" + CAST(Str, asRepr(co->consts[byte.arg])) + ")"; - // } - // if(byte.op == OP_LOAD_NAME_REF || byte.op == OP_LOAD_NAME || byte.op == OP_RAISE || byte.op == OP_STORE_NAME){ - // argStr += " (" + co->names[byte.arg].first.str().escape(true) + ")"; - // } - // ss << argStr; - // // ss << pad(argStr, 20); // may overflow - // // ss << co->blocks[byte.block].to_string(); - // if(i != co->codes.size() - 1) ss << '\n'; - // } - // StrStream consts; - // consts << "co_consts: "; - // consts << CAST(Str, asRepr(VAR(co->consts))); + std::string pointer; + if(std::find(jumpTargets.begin(), jumpTargets.end(), i) != jumpTargets.end()){ + pointer = "-> "; + }else{ + pointer = " "; + } + ss << pad(line, 8) << pointer << pad(std::to_string(i), 3); + ss << " " << pad(OP_NAMES[byte.op], 20) << " "; + // ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5); + std::string argStr = byte.arg == -1 ? "" : std::to_string(byte.arg); + if(byte.op == OP_LOAD_CONST){ + argStr += " (" + CAST(Str, asRepr(co->consts[byte.arg])) + ")"; + } + if(byte.op == OP_LOAD_NAME || byte.op == OP_STORE_LOCAL || byte.op == OP_STORE_GLOBAL){ + argStr += " (" + co->names[byte.arg].str().escape(true) + ")"; + } + ss << argStr; + // ss << pad(argStr, 20); // may overflow + // ss << co->blocks[byte.block].to_string(); + if(i != co->codes.size() - 1) ss << '\n'; + } + StrStream consts; + consts << "co_consts: "; + consts << CAST(Str, asRepr(VAR(co->consts))); - // StrStream names; - // names << "co_names: "; - // List list; - // for(int i=0; inames.size(); i++){ - // list.push_back(VAR(co->names[i].first.str())); - // } - // names << CAST(Str, asRepr(VAR(list))); - // ss << '\n' << consts.str() << '\n' << names.str() << '\n'; + StrStream names; + names << "co_names: "; + List list; + for(int i=0; inames.size(); i++){ + list.push_back(VAR(co->names[i].str())); + } + names << CAST(Str, asRepr(VAR(list))); + ss << '\n' << consts.str() << '\n' << names.str() << '\n'; - // for(int i=0; iconsts.size(); i++){ - // PyObject* obj = co->consts[i]; - // if(is_type(obj, tp_function)){ - // const auto& f = CAST(Function&, obj); - // ss << disassemble(f.code); - // } - // } - // return Str(ss.str()); + for(int i=0; iconsts.size(); i++){ + PyObject* obj = co->consts[i]; + if(is_type(obj, tp_function)){ + const auto& f = CAST(Function&, obj); + ss << disassemble(f.code); + } + } + return Str(ss.str()); } inline void VM::init_builtin_types(){ @@ -877,7 +881,7 @@ inline PyObject* VM::_exec(){ }catch(HandledException& e){ continue; }catch(UnhandledException& e){ - PyObject* obj = frame->pop(); + PyObject* obj = frame->popx(); Exception& _e = CAST(Exception&, obj); _e.st_push(frame->snapshot()); callstack.pop();