diff --git a/src/__stl__.h b/src/__stl__.h index a0fb2c48..321bc764 100644 --- a/src/__stl__.h +++ b/src/__stl__.h @@ -18,4 +18,5 @@ #include #include -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/src/compiler.h b/src/compiler.h index 2f6fb423..a7877674 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -941,12 +941,11 @@ _Code compile(VM* vm, const char* source, _Str filename, CompileMode mode=EXEC_M return compiler.__fillCode(); }catch(std::exception& e){ if(const _Error* _ = dynamic_cast(&e)){ - vm->_stderr(vm, e.what()); + (*vm->_stderr) << e.what() << '\n'; }else{ auto ce = CompileError("UnexpectedError", e.what(), compiler.getLineSnapshot()); - vm->_stderr(vm, ce.what()); + (*vm->_stderr) << ce.what() << '\n'; } - vm->_stderr(vm, "\n"); return nullptr; } } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f13da9b3..4a920cda 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include "pocketpy.h" @@ -19,25 +18,6 @@ struct Timer{ } }; -ThreadedVM* new_tvm_with_callbacks(){ - ThreadedVM* vm = pkpy_new_tvm([](const VM* vm, const char* str) { - std::cout << str; std::cout.flush(); - }, [](const VM* vm, const char* str) { - std::cerr << str; std::cerr.flush(); - }); - return vm; -} - -VM* new_vm_with_callbacks(){ - VM* vm = pkpy_new_vm([](const VM* vm, const char* str) { - std::cout << str; std::cout.flush(); - }, [](const VM* vm, const char* str) { - std::cerr << str; std::cerr.flush(); - }); - return vm; -} - - #if defined(__EMSCRIPTEN__) || defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__) // these code is for demo use, feel free to modify it @@ -47,7 +27,9 @@ REPL* _repl; extern "C" { __EXPORT void repl_start(){ - _repl = pkpy_new_repl(new_vm_with_callbacks(), false); + VM* vm = pkpy_new_vm(true); + useStandardBuffer(vm); + _repl = pkpy_new_repl(vm, false); } __EXPORT @@ -58,10 +40,10 @@ extern "C" { #else - int main(int argc, char** argv){ if(argc == 1){ - REPL repl(new_vm_with_callbacks()); + VM* vm = pkpy_new_vm(true); + REPL repl(vm); while(true){ std::string line; std::getline(std::cin, line); @@ -81,7 +63,7 @@ int main(int argc, char** argv){ } std::string src((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - ThreadedVM* vm = new_tvm_with_callbacks(); + ThreadedVM* vm = pkpy_new_tvm(true); _Code code; Timer("Compile time").run([&]{ code = compile(vm, src.c_str(), filename); diff --git a/src/obj.h b/src/obj.h index ec7c6f2f..a326f618 100644 --- a/src/obj.h +++ b/src/obj.h @@ -10,7 +10,7 @@ const _Int _Int_MAX_NEG = -9223372036854775807LL; const _Float _FLOAT_INF_POS = INFINITY; const _Float _FLOAT_INF_NEG = -INFINITY; -#define PK_VERSION "0.2.3" +#define PK_VERSION "0.2.4" class CodeObject; class BasePointer; diff --git a/src/pocketpy.h b/src/pocketpy.h index 38d1b504..05415812 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -42,8 +42,7 @@ void __initializeBuiltinFunctions(VM* _vm) { _vm->bindBuiltinFunc("print", [](VM* vm, PyVarList args) { _StrStream ss; for (auto& arg : args) ss << vm->PyStr_AS_C(vm->asStr(arg)) << " "; - vm->_stdout(vm, ss.str().c_str()); - vm->_stdout(vm, "\n"); + (*vm->_stdout) << ss.str() << '\n'; return vm->None; }); @@ -593,6 +592,22 @@ extern "C" { } }; + __EXPORT + struct PyOutputDump: public PkExportedResource{ + const char* stdout; + const char* stderr; + + PyOutputDump(const char* _stdout, const char* _stderr){ + stdout = strdup(_stdout); + stderr = strdup(_stderr); + } + + ~PyOutputDump(){ + delete[] stdout; + delete[] stderr; + } + }; + __EXPORT void pkpy_delete(PkExportedResource* p){ delete p; @@ -665,11 +680,8 @@ extern "C" { return vm->exec(code, _m) != nullptr; } - void __vm_init(VM* vm, PrintFn _stdout, PrintFn _stderr){ + void __vm_init(VM* vm){ __initializeBuiltinFunctions(vm); - vm->_stdout = _stdout; - vm->_stderr = _stderr; - _Code code = compile(vm, __BUILTINS_CODE, ""); if(code == nullptr) exit(1); vm->_exec(code, vm->builtins); @@ -680,19 +692,34 @@ extern "C" { } __EXPORT - VM* pkpy_new_vm(PrintFn _stdout, PrintFn _stderr){ - VM* vm = new VM(); - __vm_init(vm, _stdout, _stderr); + VM* pkpy_new_vm(bool use_stdio){ + VM* vm = new VM(use_stdio); + __vm_init(vm); return vm; } __EXPORT - ThreadedVM* pkpy_new_tvm(PrintFn _stdout, PrintFn _stderr){ - ThreadedVM* vm = new ThreadedVM(); - __vm_init(vm, _stdout, _stderr); + ThreadedVM* pkpy_new_tvm(bool use_stdio){ + ThreadedVM* vm = new ThreadedVM(use_stdio); + __vm_init(vm); return vm; } + __EXPORT + PyOutputDump* pkpy_vm_read_output(VM* vm){ + if(vm->use_stdio) UNREACHABLE(); + _StrStream* s_out = dynamic_cast<_StrStream*>(vm->_stdout); + _StrStream* s_err = dynamic_cast<_StrStream*>(vm->_stderr); + if(s_out == nullptr || s_err == nullptr) UNREACHABLE(); + PyOutputDump* dump = new PyOutputDump( + s_out->str().c_str(), + s_out->str().c_str() + ); + s_out->str(""); + s_err->str(""); + return dump; + } + __EXPORT int pkpy_tvm_get_state(ThreadedVM* vm){ return vm->getState(); diff --git a/src/repl.h b/src/repl.h index 87b1dfd1..fa485c9e 100644 --- a/src/repl.h +++ b/src/repl.h @@ -21,15 +21,15 @@ class REPL: public PkExportedResource { void _loop_start(){ mode = SINGLE_MODE; if(use_prompt){ - vm->_stdout(vm, need_more_lines ? "... " : ">>> "); + (*vm->_stdout) << (need_more_lines ? "... " : ">>> "); } } public: REPL(VM* vm, bool use_prompt=true) : vm(vm), use_prompt(use_prompt) { - vm->_stdout(vm, "pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ")\n"); - vm->_stdout(vm, "https://github.com/blueloveTH/pocketpy" "\n"); - vm->_stdout(vm, "Type \"exit()\" to exit." "\n"); + (*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(); } diff --git a/src/vm.h b/src/vm.h index ebbf7018..2bcec079 100644 --- a/src/vm.h +++ b/src/vm.h @@ -48,7 +48,6 @@ class VM: public PkExportedResource{ private: std::stack< std::unique_ptr > callstack; PyVarDict _modules; // 3rd modules - PyVar __py2py_call_signal; PyVar runFrame(Frame* frame){ @@ -155,8 +154,7 @@ private: { const PyVar& expr = frame->topValue(this); if(expr == None) break; - _stdout(this, PyStr_AS_C(asRepr(expr)).c_str()); - _stdout(this, "\n"); + *_stdout << PyStr_AS_C(asRepr(expr)) << '\n'; } break; case OP_POP_TOP: frame->popValue(this); break; case OP_BINARY_OP: @@ -328,15 +326,26 @@ public: PyVarDict _types; PyVar None, True, False; - PrintFn _stdout = [](const VM* vm, auto s){}; - PrintFn _stderr = [](const VM* vm, auto s){}; + bool use_stdio; + std::ostream* _stdout; + std::ostream* _stderr; PyVar builtins; // builtins module PyVar _main; // __main__ module int maxRecursionDepth = 1000; - VM(){ + VM(bool use_stdio){ + this->use_stdio = use_stdio; + if(use_stdio){ + std::cout.setf(std::ios::unitbuf); + std::cerr.setf(std::ios::unitbuf); + this->_stdout = &std::cout; + this->_stderr = &std::cerr; + }else{ + this->_stdout = new _StrStream(); + this->_stderr = new _StrStream(); + } initializeBuiltinClasses(); } @@ -462,12 +471,11 @@ public: return _exec(code, _module); } catch (const std::exception& e) { if(const _Error* _ = dynamic_cast(&e)){ - _stderr(this, e.what()); + *_stderr << e.what() << '\n'; }else{ auto re = RuntimeError("UnexpectedError", e.what(), _cleanErrorAndGetSnapshots()); - _stderr(this, re.what()); + *_stderr << re.what() << '\n'; } - _stderr(this, "\n"); return nullptr; } } @@ -785,7 +793,12 @@ public: if (!val) _error("AssertionError", msg); } - virtual ~VM() = default; + virtual ~VM() { + if(!use_stdio){ + delete _stdout; + delete _stderr; + } + } }; /***** Pointers' Impl *****/ @@ -916,10 +929,11 @@ enum ThreadState { class ThreadedVM : public VM { std::thread* _thread; std::atomic state = THREAD_READY; - public: + ThreadedVM(bool use_stdio) : VM(use_stdio) {} + _Str _stdin; - + void suspend(){ if(_thread == nullptr) UNREACHABLE(); if(state != THREAD_RUNNING) UNREACHABLE(); @@ -938,8 +952,7 @@ public: /***** For outer use *****/ ThreadState getState(){ - if(_thread == nullptr) UNREACHABLE(); - return state; + return state.load(); } void resume(){ @@ -950,6 +963,7 @@ public: void startExec(const _Code& code){ if(_thread != nullptr) UNREACHABLE(); + if(state != THREAD_READY) UNREACHABLE(); _thread = new std::thread([this, code](){ this->state = THREAD_RUNNING; this->exec(code);