From 7079b66a64029fe02c44eee67e7ac7799592000c Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 14 Nov 2022 15:19:39 +0800 Subject: [PATCH] add goto & Threaded VM --- src/__stl__.h | 4 ++- src/codeobject.h | 17 ++++++++++++ src/compiler.h | 11 ++++++++ src/opcodes.h | 2 ++ src/parser.h | 1 + src/pocketpy.h | 7 +++++ src/vm.h | 69 ++++++++++++++++++++++++++++++++++++++++++++++-- tests/3.py | 11 ++++---- 8 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/__stl__.h b/src/__stl__.h index 2172f3bc..6157f02d 100644 --- a/src/__stl__.h +++ b/src/__stl__.h @@ -15,4 +15,6 @@ #include #include #include -#include \ No newline at end of file +#include + +#include \ No newline at end of file diff --git a/src/codeobject.h b/src/codeobject.h index 1b5416bc..25d2270b 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -39,6 +39,19 @@ struct CodeObject { PyVarList co_consts; std::vector> co_names; + // for goto use + // note: some opcodes moves the bytecode, such as listcomp + // goto/label should be put at toplevel statements + std::unordered_map<_Str, int> co_labels; + + void addLabel(const _Str& label){ + if(co_labels.find(label) != co_labels.end()){ + _Str msg = "label '" + label + "' already exists"; + throw std::runtime_error(msg.c_str()); + } + co_labels[label] = co_code.size(); + } + int addName(const _Str& name, NameScope scope){ auto p = std::make_shared(name, scope); for(int i=0; iaddLabel(parser->previous.str()); + consumeEndStatement(); + } else if(match(TK("goto"))){ + if(mode() != EXEC_MODE) syntaxError("'goto' is only available in EXEC_MODE"); + consume(TK("@id")); + emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(parser->previous.str()))); + emitCode(OP_GOTO); + consumeEndStatement(); } else if(match(TK("raise"))){ consume(TK("@id")); // dummy exception type emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(parser->previous.str()))); diff --git a/src/opcodes.h b/src/opcodes.h index db6677cd..e9d143ac 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -56,4 +56,6 @@ OPCODE(DELETE_PTR) // no arg, [ptr] -> [] -> delete ptr OPCODE(BUILD_SMART_TUPLE) // if all elements are pointers, build a compound pointer, otherwise build a tuple OPCODE(BUILD_STRING) // arg is the expr count, build a string from the top of the stack +OPCODE(GOTO) + #endif \ No newline at end of file diff --git a/src/parser.h b/src/parser.h index 38ff8ca4..43e3df2e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -14,6 +14,7 @@ constexpr const char* __TOKENS[] = { /** KW_BEGIN **/ "class", "import", "as", "def", "lambda", "pass", "del", "None", "in", "is", "and", "or", "not", "True", "False", "global", + "goto", "label", // extended keywords, not available in cpython "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise", /** KW_END **/ "is not", "not in", diff --git a/src/pocketpy.h b/src/pocketpy.h index 448c8a28..1d581901 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -47,6 +47,13 @@ void __initializeBuiltinFunctions(VM* _vm) { return vm->None; }); + _vm->bindBuiltinFunc("input", [](VM* vm, PyVarList args) { + vm->__checkArgSize(args, 0); + ThreadedVM* tvm = dynamic_cast(vm); + if(tvm == nullptr) vm->typeError("input() can only be called in threaded mode"); + return vm->PyStr(tvm->readStdin()); + }); + _vm->bindBuiltinFunc("eval", [](VM* vm, PyVarList args) { vm->__checkArgSize(args, 1); const _Str& expr = vm->PyStr_AS_C(args[0]); diff --git a/src/vm.h b/src/vm.h index 7a57eee8..64b472ec 100644 --- a/src/vm.h +++ b/src/vm.h @@ -247,6 +247,16 @@ private: frame->push(ret); } break; case OP_JUMP_ABSOLUTE: frame->jumpTo(byte.arg); break; + case OP_GOTO: { + PyVar obj = frame->popValue(this); + const _Str& label = PyStr_AS_C(obj); + auto it = frame->code->co_labels.find(label); + if(it == frame->code->co_labels.end()){ + _error("KeyError", "label '" + label + "' not found"); + } + frame->__clearDataStack(); + frame->jumpTo(it->second); + } break; case OP_GET_ITER: { PyVar obj = frame->popValue(this); @@ -315,7 +325,7 @@ private: } public: - PyVarDict _types; // builtin types + PyVarDict _types; PyVar None, True, False; PrintFn _stdout = [](const VM* vm, auto s){}; @@ -774,6 +784,8 @@ public: void _assert(bool val, const _Str& msg){ if (!val) _error("AssertionError", msg); } + + virtual ~VM() = default; }; /***** Pointers' Impl *****/ @@ -892,4 +904,57 @@ PyVar RangeIterator::next(){ PyVar StringIterator::next(){ return vm->PyStr(str->u8_getitem(index++)); -} \ No newline at end of file +} + + +class ThreadedVM : public VM { + std::thread* _thread; + bool _flag_thread_sleeping = false; + +public: + _Str _stdin; + + void sleep(){ + if(_thread == nullptr) UNREACHABLE(); + _flag_thread_sleeping = true; + // 50 fps is enough + while(!_flag_thread_sleeping) std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + void writeStdin(const _Str& s){ + if(_thread == nullptr) UNREACHABLE(); + _stdin = s; + wakeUp(); + } + + _Str readStdin(){ + if(_thread == nullptr) UNREACHABLE(); + _Str copy = _stdin; + _stdin = ""; + return copy; + } + + bool isSleeping(){ + if(_thread == nullptr) UNREACHABLE(); + return _flag_thread_sleeping; + } + + void wakeUp(){ + if(_thread == nullptr) UNREACHABLE(); + _flag_thread_sleeping = false; + } + + void startExec(const _Code& code){ + if(_thread != nullptr) UNREACHABLE(); + _thread = new std::thread([this, code](){ + this->exec(code); + }); + } + + ~ThreadedVM(){ + if(_thread != nullptr){ + _thread->join(); + delete _thread; + } + } +}; \ No newline at end of file diff --git a/tests/3.py b/tests/3.py index 963023fa..1c2b8858 100644 --- a/tests/3.py +++ b/tests/3.py @@ -1,5 +1,6 @@ -k = 0 -for i in range(2, 10000000): - if i % 2 == 0: - k += 1 -print(k) \ No newline at end of file +print("Welcome to the Python world!") +a = input() +print("You entered: " + a) +b = input() +print("You entered: " + b) +print("END") \ No newline at end of file