blueloveTH 3 лет назад
Родитель
Сommit
7079b66a64
8 измененных файлов с 114 добавлено и 8 удалено
  1. 3 1
      src/__stl__.h
  2. 17 0
      src/codeobject.h
  3. 11 0
      src/compiler.h
  4. 2 0
      src/opcodes.h
  5. 1 0
      src/parser.h
  6. 7 0
      src/pocketpy.h
  7. 67 2
      src/vm.h
  8. 6 5
      tests/3.py

+ 3 - 1
src/__stl__.h

@@ -15,4 +15,6 @@
 #include <chrono>
 #include <string_view>
 #include <queue>
-#include <iomanip>
+#include <iomanip>
+
+#include <thread>

+ 17 - 0
src/codeobject.h

@@ -39,6 +39,19 @@ struct CodeObject {
     PyVarList co_consts;
     std::vector<std::shared_ptr<NamePointer>> 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<NamePointer>(name, scope);
         for(int i=0; i<co_names.size(); i++){
@@ -139,6 +152,10 @@ public:
         return v;
     }
 
+    void __clearDataStack(){
+        s_data.clear();
+    }
+
     inline PyVar __deref_pointer(VM*, PyVar);
 
     inline PyVar popValue(VM* vm){

+ 11 - 0
src/compiler.h

@@ -755,6 +755,17 @@ __LISTCOMP:
             EXPR();
             emitCode(OP_ASSERT);
             consumeEndStatement();
+        } else if(match(TK("label"))){
+            if(mode() != EXEC_MODE) syntaxError("'label' is only available in EXEC_MODE");
+            consume(TK("@id"));
+            getCode()->addLabel(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())));

+ 2 - 0
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

+ 1 - 0
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",

+ 7 - 0
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<ThreadedVM*>(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]);

+ 67 - 2
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++));
-}
+}
+
+
+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;
+        }
+    }
+};

+ 6 - 5
tests/3.py

@@ -1,5 +1,6 @@
-k = 0
-for i in range(2, 10000000):
-  if i % 2 == 0:
-    k += 1
-print(k)
+print("Welcome to the Python world!")
+a = input()
+print("You entered: " + a)
+b = input()
+print("You entered: " + b)
+print("END")