blueloveTH 3 tahun lalu
induk
melakukan
eb58389945
8 mengubah file dengan 75 tambahan dan 87 penghapusan
  1. 0 4
      .github/workflows/main.yml
  2. 1 1
      build_wasm.sh
  3. 7 2
      src/__stl__.h
  4. 1 15
      src/compiler.h
  5. 3 13
      src/main.cpp
  6. 24 32
      src/pocketpy.h
  7. 1 3
      src/repl.h
  8. 38 17
      src/vm.h

+ 0 - 4
.github/workflows/main.yml

@@ -26,10 +26,6 @@ jobs:
     - name: Compiling
       run: |
         mkdir -p output/web/lib
-        mkdir -p output/web/lib_pthread
-        bash build_wasm.sh
-        mv web/lib/* output/web/lib_pthread
-        export PTHREAD_FLAG=" "
         bash build_wasm.sh
         cp web/lib/* output/web/lib
     - uses: crazy-max/ghaction-github-pages@v3

+ 1 - 1
build_wasm.sh

@@ -1,3 +1,3 @@
 rm -rf web/lib/
 mkdir -p web/lib/
-em++ src/main.cpp ${PTHREAD_FLAG:--pthread -sPTHREAD_POOL_SIZE=2} -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_delete,_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_tvm,_pkpy_tvm_exec_async,_pkpy_tvm_get_state,_pkpy_tvm_read_jsonrpc_request,_pkpy_tvm_reset_state,_pkpy_tvm_terminate,_pkpy_tvm_write_jsonrpc_response,_pkpy_new_vm,_pkpy_vm_add_module,_pkpy_vm_eval,_pkpy_vm_exec,_pkpy_vm_get_global,_pkpy_vm_read_output -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js
+em++ src/main.cpp -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_delete,_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_tvm,_pkpy_tvm_exec_async,_pkpy_tvm_get_state,_pkpy_tvm_read_jsonrpc_request,_pkpy_tvm_reset_state,_pkpy_tvm_terminate,_pkpy_tvm_write_jsonrpc_response,_pkpy_new_vm,_pkpy_vm_add_module,_pkpy_vm_eval,_pkpy_vm_exec,_pkpy_vm_get_global,_pkpy_vm_read_output -sASYNCIFY -sEXPORTED_RUNTIME_METHODS=ccall -sASYNCIFY_IMPORTS=pkpy_tvm_exec_async -o web/lib/pocketpy.js

+ 7 - 2
src/__stl__.h

@@ -20,7 +20,6 @@
 #include <iomanip>
 #include <map>
 
-#include <thread>
 #include <atomic>
 #include <iostream>
 
@@ -30,7 +29,13 @@
 #define UNREACHABLE() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " UNREACHABLE()!");
 #endif
 
-#define PK_VERSION "0.5.2"
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#else
+#include <thread>
+#endif
+
+#define PK_VERSION "0.6.0"
 
 //#define PKPY_NO_TYPE_CHECK
 //#define PKPY_NO_INDEX_CHECK

+ 1 - 15
src/compiler.h

@@ -1062,18 +1062,4 @@ __LISTCOMP:
     void syntaxError(_Str msg){ throw CompileError("SyntaxError", msg, getLineSnapshot()); }
     void indentationError(_Str msg){ throw CompileError("IndentationError", msg, getLineSnapshot()); }
     void unexpectedError(_Str msg){ throw CompileError("UnexpectedError", msg, getLineSnapshot()); }
-};
-
-_Code compile(VM* vm, const char* source, _Str filename, CompileMode mode=EXEC_MODE, bool noThrow=true) {
-    Compiler compiler(vm, source, filename, mode);
-    if(!noThrow) return compiler.__fillCode();
-    try{
-        return compiler.__fillCode();
-    }catch(_Error& e){
-        (*vm->_stderr) << e.what() << '\n';
-    }catch(std::exception& e){
-        auto ce = CompileError("UnexpectedError", e.what(), compiler.getLineSnapshot());
-        (*vm->_stderr) << ce.what() << '\n';
-    }
-    return nullptr;
-}
+};

+ 3 - 13
src/main.cpp

@@ -4,7 +4,7 @@
 #include "pocketpy.h"
 
 //#define PK_DEBUG_TIME
-//#define PK_DEBUG_THREADED
+#define PK_DEBUG_THREADED
 
 struct Timer{
     const char* title;
@@ -83,25 +83,15 @@ int main(int argc, char** argv){
         std::string src((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
 
         ThreadedVM* vm = pkpy_new_tvm(true);
-        _Code code = nullptr;
-        Timer("Compile time").run([&]{
-            code = compile(vm, src.c_str(), filename);
-        });
-        if(code == nullptr) return 1;
-
         //std::cout << code->toString() << std::endl;
-
-        // for(auto& kv : _strIntern)
-        //     std::cout << kv.first << ", ";
-        
 #ifdef PK_DEBUG_THREADED
         Timer("Running time").run([=]{
-            vm->execAsync(code);
+            vm->execAsync(src.c_str(), filename, EXEC_MODE);
             _tvm_dispatch(vm);
         });
 #else
         Timer("Running time").run([=]{
-            vm->exec(code);
+            vm->exec(src.c_str(), filename, EXEC_MODE);
         });
 #endif
 

+ 24 - 32
src/pocketpy.h

@@ -4,6 +4,17 @@
 #include "compiler.h"
 #include "repl.h"
 
+_Code VM::compile(const char* source, _Str filename, CompileMode mode) {
+    Compiler compiler(this, source, filename, mode);
+    try{
+        return compiler.__fillCode();
+    }catch(_Error& e){
+        throw e;
+    }catch(std::exception& e){
+        throw CompileError("UnexpectedError", e.what(), compiler.getLineSnapshot());
+    }
+}
+
 #define BIND_NUM_ARITH_OPT(name, op)                                                                    \
     _vm->bindMethodMulti({"int","float"}, #name, [](VM* vm, const pkpy::ArgList& args){                 \
         if(!vm->isIntOrFloat(args[0], args[1]))                                                         \
@@ -55,7 +66,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
     _vm->bindBuiltinFunc("eval", [](VM* vm, const pkpy::ArgList& args) {
         vm->__checkArgSize(args, 1);
         const _Str& expr = vm->PyStr_AS_C(args[0]);
-        _Code code = compile(vm, expr.c_str(), "<eval>", EVAL_MODE, false);
+        _Code code = vm->compile(expr.c_str(), "<eval>", EVAL_MODE);
         return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->copy_f_locals());
     });
 
@@ -589,8 +600,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
 #define __EXPORT __declspec(dllexport)
 #elif __APPLE__
 #define __EXPORT __attribute__((visibility("default"))) __attribute__((used))
-#elif defined(__EMSCRIPTEN__) || defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__)
-#include <emscripten.h>
+#elif __EMSCRIPTEN__
 #define __EXPORT EMSCRIPTEN_KEEPALIVE
 #define __NO_MAIN
 #else
@@ -642,7 +652,7 @@ void __addModuleJson(VM* vm){
     vm->bindFunc(mod, "loads", [](VM* vm, const pkpy::ArgList& args) {
         vm->__checkArgSize(args, 1);
         const _Str& expr = vm->PyStr_AS_C(args[0]);
-        _Code code = compile(vm, expr.c_str(), "<json>", JSON_MODE, false);
+        _Code code = vm->compile(expr.c_str(), "<json>", JSON_MODE);
         return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->copy_f_locals());
     });
 
@@ -846,13 +856,8 @@ extern "C" {
 
     __EXPORT
     /// Run a given source on a virtual machine.
-    /// 
-    /// Return `true` if there is no compile error.
-    bool pkpy_vm_exec(VM* vm, const char* source){
-        _Code code = compile(vm, source, "main.py");
-        if(code == nullptr) return false;
-        vm->exec(code);
-        return true;
+    void pkpy_vm_exec(VM* vm, const char* source){
+        vm->exec(source, "main.py", EXEC_MODE);
     }
 
     __EXPORT
@@ -877,9 +882,7 @@ extern "C" {
     /// Return a json representing the result.
     /// If there is any error, return `nullptr`.
     char* pkpy_vm_eval(VM* vm, const char* source){
-        _Code code = compile(vm, source, "<eval>", EVAL_MODE);
-        if(code == nullptr) return nullptr;
-        PyVarOrNull ret = vm->exec(code);
+        PyVarOrNull ret = vm->exec(source, "<eval>", EVAL_MODE);
         if(ret == nullptr) return nullptr;
         try{
             _Str _json = vm->PyStr_AS_C(vm->asJson(ret));
@@ -907,14 +910,8 @@ extern "C" {
 
     __EXPORT
     /// Add a source module into a virtual machine.
-    ///
-    /// Return `true` if there is no complie error.
-    bool pkpy_vm_add_module(VM* vm, const char* name, const char* source){
-        // compile the module but don't execute it
-        _Code code = compile(vm, source, name + _Str(".py"));
-        if(code == nullptr) return false;
-        vm->addLazyModule(name, code);
-        return true;
+    void pkpy_vm_add_module(VM* vm, const char* name, const char* source){
+        vm->addLazyModule(name, source);
     }
 
     void __vm_init(VM* vm){
@@ -925,9 +922,10 @@ extern "C" {
         __addModuleMath(vm);
         __addModuleRe(vm);
 
-        _Code code = compile(vm, __BUILTINS_CODE, "<builtins>");
-        if(code == nullptr) exit(1);
+        // add builtins | no exception handler | must succeed
+        _Code code = vm->compile(__BUILTINS_CODE, "<builtins>", EXEC_MODE);
         vm->_exec(code, vm->builtins, {});
+
         pkpy_vm_add_module(vm, "random", __RANDOM_CODE);
         pkpy_vm_add_module(vm, "os", __OS_CODE);
     }
@@ -1009,13 +1007,7 @@ extern "C" {
     __EXPORT
     /// Run a given source on a threaded virtual machine.
     /// The excution will be started in a new thread.
-    /// 
-    /// Return `true` if there is no compile error.
-    bool pkpy_tvm_exec_async(VM* vm, const char* source){
-        // although this is a method of VM, it's only used in ThreadedVM
-        _Code code = compile(vm, source, "main.py");
-        if(code == nullptr) return false;
-        vm->execAsync(code);
-        return true;
+    void pkpy_tvm_exec_async(VM* vm, const char* source){
+        vm->execAsync(source, "main.py", EXEC_MODE);
     }
 }

+ 1 - 3
src/repl.h

@@ -54,9 +54,7 @@ __NOT_ENOUGH_LINES:
         }
 
         try{
-            _Code code = compile(vm, line.c_str(), "<stdin>", SINGLE_MODE);
-            if(code == nullptr) return EXEC_SKIPPED;
-            vm->execAsync(code);
+            vm->execAsync(line.c_str(), "<stdin>", SINGLE_MODE);
             return EXEC_DONE;
         }catch(NeedMoreLines& ne){
             buffer += line;

+ 38 - 17
src/vm.h

@@ -26,7 +26,7 @@ class VM {
 protected:
     std::deque< pkpy::unique_ptr<Frame> > callstack;
     PyVarDict _modules;                     // loaded modules
-    std::map<_Str, _Code> _lazyModules;     // lazy loaded modules
+    std::map<_Str, _Str> _lazyModules;     // lazy loaded modules
     PyVar __py2py_call_signal;
     
     void _checkStopFlag(){
@@ -313,7 +313,8 @@ protected:
                         if(it2 == _lazyModules.end()){
                             _error("ImportError", "module '" + name + "' not found");
                         }else{
-                            _Code code = it2->second;
+                            const _Str& source = it2->second;
+                            _Code code = compile(source.c_str(), name, EXEC_MODE);
                             PyVar _m = newModule(name);
                             _exec(code, _m, {});
                             frame->push(_m);
@@ -377,9 +378,14 @@ public:
 
     void sleepForSecs(_Float sec){
         _Int ms = (_Int)(sec * 1000);
-        for(_Int i=0; i<ms; i+=20){
+        const _Int step = 20;
+        for(_Int i=0; i<ms; i+=step){
             _checkStopFlag();
-            std::this_thread::sleep_for(std::chrono::milliseconds(20));
+#ifdef __EMSCRIPTEN__
+            emscripten_sleep(step);
+#else
+            std::this_thread::sleep_for(std::chrono::milliseconds(step));
+#endif
         }
     }
 
@@ -546,9 +552,10 @@ public:
 
 
     // repl mode is only for setting `frame->id` to 0
-    virtual PyVarOrNull exec(const _Code& code, PyVar _module=nullptr){
+    virtual PyVarOrNull exec(const char* source, _Str filename, CompileMode mode, PyVar _module=nullptr){
         if(_module == nullptr) _module = _main;
         try {
+            _Code code = compile(source, filename, mode);
             return _exec(code, _module, {});
         }catch (const _Error& e){
             *_stderr << e.what() << '\n';
@@ -559,8 +566,8 @@ public:
         return nullptr;
     }
 
-    virtual void execAsync(const _Code& code) {
-        exec(code);
+    virtual void execAsync(const char* source, _Str filename, CompileMode mode) {
+        exec(source, filename, mode);
     }
 
     Frame* __pushNewFrame(const _Code& code, PyVar _module, PyVarDict&& locals){
@@ -629,8 +636,8 @@ public:
         return obj;
     }
 
-    void addLazyModule(_Str name, _Code code){
-        _lazyModules[name] = code;
+    void addLazyModule(_Str name, const char* source){
+        _lazyModules[name] = source;
     }
 
     PyVarOrNull getAttr(const PyVar& obj, const _Str& name, bool throw_err=true) {
@@ -949,6 +956,8 @@ public:
             delete _stderr;
         }
     }
+
+    _Code compile(const char* source, _Str filename, CompileMode mode);
 };
 
 /***** Pointers' Impl *****/
@@ -1077,10 +1086,11 @@ enum ThreadState {
 };
 
 class ThreadedVM : public VM {
-    std::thread* _thread = nullptr;
     std::atomic<ThreadState> _state = THREAD_READY;
     _Str _sharedStr = "";
-    
+
+#ifndef __EMSCRIPTEN__
+    std::thread* _thread = nullptr;
     void __deleteThread(){
         if(_thread != nullptr){
             terminate();
@@ -1089,6 +1099,10 @@ class ThreadedVM : public VM {
             _thread = nullptr;
         }
     }
+#else
+    void __deleteThread(){}
+#endif
+
 public:
     ThreadedVM(bool use_stdio) : VM(use_stdio) {
         bindBuiltinFunc("__string_channel_call", [](VM* vm, const pkpy::ArgList& args){
@@ -1133,21 +1147,28 @@ public:
         _state = THREAD_RUNNING;
     }
 
-    void execAsync(const _Code& code) override {
+    void execAsync(const char* source, _Str filename, CompileMode mode) override {
         if(_state != THREAD_READY) UNREACHABLE();
+
+#ifdef __EMSCRIPTEN__
+        this->_state = THREAD_RUNNING;
+        VM::exec(source, filename, mode);
+        this->_state = THREAD_FINISHED;
+#else
         __deleteThread();
-        _thread = new std::thread([this, code](){
+        _thread = new std::thread([=](){
             this->_state = THREAD_RUNNING;
-            VM::exec(code);
+            VM::exec(source, filename, mode);
             this->_state = THREAD_FINISHED;
         });
+#endif
     }
 
-    PyVarOrNull exec(const _Code& code, PyVar _module = nullptr) override {
-        if(_state == THREAD_READY) return VM::exec(code, _module);
+    PyVarOrNull exec(const char* source, _Str filename, CompileMode mode, PyVar _module=nullptr) override {
+        if(_state == THREAD_READY) return VM::exec(source, filename, mode, _module);
         auto callstackBackup = std::move(callstack);
         callstack.clear();
-        PyVarOrNull ret = VM::exec(code, _module);
+        PyVarOrNull ret = VM::exec(source, filename, mode, _module);
         callstack = std::move(callstackBackup);
         return ret;
     }