diff --git a/src/codeobject.h b/src/codeobject.h index a949f5bc..3d47ea3c 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -30,11 +30,15 @@ struct CodeObject { _Source src; _Str name; - CodeObject(_Source src, _Str name, CompileMode mode=EXEC_MODE) { + CodeObject(_Source src, _Str name) { this->src = src; this->name = name; } + CompileMode mode() const { + return src->mode; + } + std::vector co_code; PyVarList co_consts; std::vector> co_names; diff --git a/src/compiler.h b/src/compiler.h index 4e98ddfd..5575851e 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -931,7 +931,7 @@ __LISTCOMP: } _Code __fillCode(){ - _Code code = std::make_shared(parser->src, _Str(""), mode()); + _Code code = std::make_shared(parser->src, _Str("")); codes.push(code); // Lex initial tokens. current <-- next. diff --git a/src/error.h b/src/error.h index d6e0f732..2ac58fcc 100644 --- a/src/error.h +++ b/src/error.h @@ -11,7 +11,7 @@ public: enum CompileMode { EXEC_MODE, EVAL_MODE, - SINGLE_MODE + SINGLE_MODE // for REPL }; struct SourceMetadata { diff --git a/src/main.cpp b/src/main.cpp index c65c6f36..5b12fcd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,19 +21,21 @@ struct Timer{ #if defined(__EMSCRIPTEN__) || defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__) // these code is for demo use, feel free to modify it - REPL* _repl; +VM* _vm; extern "C" { __EXPORT void repl_start(){ - VM* vm = pkpy_new_vm(true); - _repl = pkpy_new_repl(vm, false); + _vm = pkpy_new_vm(true); + _repl = pkpy_new_repl(_vm); } __EXPORT bool repl_input(const char* line){ - return pkpy_repl_input(_repl, line); + bool need_more_lines = pkpy_repl_input(_repl, line); + if(!need_more_lines) pkpy_exec_repl(_repl); + return need_more_lines; } } @@ -44,9 +46,14 @@ int main(int argc, char** argv){ VM* vm = pkpy_new_vm(true); REPL repl(vm); while(true){ + (*vm->_stdout) << (repl.is_need_more_lines() ? "... " : ">>> "); std::string line; std::getline(std::cin, line); - repl.input(line); + if(repl.input(line) == false){ // do not need more lines + _Code code = repl.readBufferCode(); + if(code == nullptr) continue; + vm->exec(code); + } } return 0; } @@ -82,11 +89,15 @@ int main(int argc, char** argv){ while(pkpy_tvm_get_state(vm) != THREAD_FINISHED){ if(pkpy_tvm_get_state(vm) == THREAD_SUSPENDED){ PyObjectDump* obj = pkpy_tvm_read_json(vm); - if(INPUT_JSONRPC_STR != obj->json) UNREACHABLE(); + bool is_input_call = INPUT_JSONRPC_STR != obj->json; pkpy_delete(obj); - std::string line; - std::getline(std::cin, line); - pkpy_tvm_resume(vm, line.c_str()); + if(is_input_call){ + std::string line; + std::getline(std::cin, line); + pkpy_tvm_resume(vm, line.c_str()); + }else{ + pkpy_tvm_resume(vm, nullptr); + } } } }); diff --git a/src/pocketpy.h b/src/pocketpy.h index 256b90b3..a7549a7d 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -657,6 +657,13 @@ extern "C" { return vm->exec(code) != nullptr; } + __EXPORT + bool pkpy_exec_repl(REPL* r){ + _Code code = r->readBufferCode(); + if(code == nullptr) return false; + return r->getVM()->exec(code) != nullptr; + } + __EXPORT PyObjectDump* pkpy_get_global(VM* vm, const char* name){ auto it = vm->_main->attribs.find(name); @@ -667,31 +674,11 @@ extern "C" { ); } - __EXPORT - void pkpy_set_global_int(VM* vm, const char* name, _Int value){ - vm->setAttr(vm->_main, name, vm->PyInt(value)); - } - - __EXPORT - void pkpy_set_global_float(VM* vm, const char* name, _Float value){ - vm->setAttr(vm->_main, name, vm->PyFloat(value)); - } - - __EXPORT - void pkpy_set_global_str(VM* vm, const char* name, const char* value){ - vm->setAttr(vm->_main, name, vm->PyStr(value)); - } - - __EXPORT - void pkpy_set_global_bool(VM* vm, const char* name, bool value){ - vm->setAttr(vm->_main, name, vm->PyBool(value)); - } - __EXPORT PyObjectDump* pkpy_eval(VM* vm, const char* source){ _Code code = compile(vm, source, "", EVAL_MODE); if(code == nullptr) return nullptr; - PyVar ret = vm->exec(code); + PyVarOrNull ret = vm->exec(code); if(ret == nullptr) return nullptr; return new PyObjectDump( ret->getTypeName(), @@ -700,8 +687,8 @@ extern "C" { } __EXPORT - REPL* pkpy_new_repl(VM* vm, bool use_prompt){ - return new REPL(vm, use_prompt); + REPL* pkpy_new_repl(VM* vm){ + return new REPL(vm); } __EXPORT @@ -767,11 +754,25 @@ extern "C" { return true; } + __EXPORT + void pkpy_tvm_reset_state(ThreadedVM* vm){ + vm->resetState(); + } + + __EXPORT + bool pkpy_tvm_start_exec_repl(REPL* r){ + _Code code = r->readBufferCode(); + if(code == nullptr) return false; + ThreadedVM* vm = dynamic_cast(r->getVM()); + vm->startExec(code); + return true; + } + __EXPORT PyObjectDump* pkpy_tvm_read_json(ThreadedVM* vm){ std::optional<_Str> s = vm->readSharedStr(); if(!s.has_value()) return nullptr; - return new PyObjectDump("str", s.value()); + return new PyObjectDump("str"_c, s.value()); } __EXPORT diff --git a/src/repl.h b/src/repl.h index 7b89e5bc..06f1111b 100644 --- a/src/repl.h +++ b/src/repl.h @@ -4,33 +4,28 @@ #include "vm.h" class REPL: public PkExportedResource { +protected: int need_more_lines = 0; std::string buffer; CompileMode mode; VM* vm; - - bool use_prompt; // whether to print >>> or ... - bool exited = false; void _exit(){ exited = true; exit(0); } - - void _loop_start(){ - mode = SINGLE_MODE; - if(use_prompt){ - (*vm->_stdout) << (need_more_lines ? "... " : ">>> "); - } - } - public: - REPL(VM* vm, bool use_prompt=true) : vm(vm), use_prompt(use_prompt) { + REPL(VM* vm) : vm(vm){ (*vm->_stdout) << ("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ")\n"); (*vm->_stdout) << ("https://github.com/blueloveTH/pocketpy" "\n"); (*vm->_stdout) << ("Type \"exit()\" to exit." "\n"); - _loop_start(); + } + + VM* getVM() { return vm; } + + bool is_need_more_lines() const { + return need_more_lines; } bool input(const char* line){ @@ -39,6 +34,7 @@ public: bool input(std::string line){ if(exited) return false; + mode = SINGLE_MODE; if(need_more_lines){ buffer += line; buffer += '\n'; @@ -62,14 +58,30 @@ __NOT_ENOUGH_LINES: try{ _Code code = compile(vm, line.c_str(), "", mode); - if(code != nullptr) vm->exec(code, nullptr, true); + this->onCompiled(code); }catch(NeedMoreLines& ne){ buffer += line; buffer += '\n'; need_more_lines = ne.isClassDef ? 3 : 2; } __LOOP_CONTINUE: - _loop_start(); - return need_more_lines > 0; + return is_need_more_lines(); + } + + _Code readBufferCode(){ + auto copy = std::move(bufferCode); + bufferCode = nullptr; + return copy; + } + +protected: + _Code bufferCode = nullptr; + + void onCompiled(_Code code){ + if(code == nullptr){ + bufferCode = nullptr; + }else{ + bufferCode = std::move(code); + } } }; \ No newline at end of file diff --git a/src/vm.h b/src/vm.h index 58a791cd..a91861cc 100644 --- a/src/vm.h +++ b/src/vm.h @@ -507,10 +507,11 @@ public: return call(getAttr(obj, func), args); } - PyVarOrNull exec(const _Code& code, PyVar _module=nullptr, bool repl_mode=false){ + // repl mode is only for setting `frame->id` to 0 + virtual PyVarOrNull exec(const _Code& code, PyVar _module=nullptr){ if(_module == nullptr) _module = _main; try { - return _exec(code, _module, {}, repl_mode); + return _exec(code, _module, {}); } catch (const std::exception& e) { if(const _Error* _ = dynamic_cast(&e)){ *_stderr << e.what() << '\n'; @@ -532,9 +533,9 @@ public: return frame; } - PyVar _exec(const _Code& code, PyVar _module, const PyVarDict& locals, bool repl_mode=false){ + PyVar _exec(const _Code& code, PyVar _module, const PyVarDict& locals){ Frame* frame = __pushNewFrame(code, _module, locals); - if(repl_mode) frame->id = 0; + if(code->mode() == SINGLE_MODE) frame->id = 0; Frame* frameBase = frame; PyVar ret = nullptr; @@ -1120,6 +1121,20 @@ public: }); } + PyVarOrNull exec(const _Code& code, PyVar _module = nullptr) override { + if(_state == THREAD_READY) return VM::exec(code, _module); + auto callstackBackup = std::move(callstack); + callstack.clear(); + PyVarOrNull ret = VM::exec(code, _module); + callstack = std::move(callstackBackup); + return ret; + } + + void resetState(){ + if(this->_state != THREAD_FINISHED) UNREACHABLE(); + this->_state = THREAD_READY; + } + ~ThreadedVM(){ if(_thread != nullptr){ _thread->join();