diff --git a/src/__stl__.h b/src/__stl__.h index f75d1b7d..897267ba 100644 --- a/src/__stl__.h +++ b/src/__stl__.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/src/builtins.h b/src/builtins.h index e53fe19f..927f16d2 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -229,6 +229,28 @@ def sorted(iterable, key=None, reverse=False): b[i], b[j] = b[j], b[i] return b +import json as _json + +def jsonrpc(method, params, raw=False): + assert type(method) is str + assert type(params) is list + data = { + 'jsonrpc': '2.0', + 'method': method, + 'params': params, + } + ret = __string_channel_call(_json.dumps(data)) + ret = _json.loads(ret) + if raw: + return ret + assert type(ret) is dict + if 'result' in ret: + return ret['result'] + raise JsonRpcError(ret['error']['message']) + +def input(prompt=None): + return jsonrpc('input', [prompt]) + class FileIO: def __init__(self, path, mode): assert type(path) is str @@ -236,19 +258,19 @@ class FileIO: assert mode in ['r', 'w'] self.path = path self.mode = mode - self.fp = jsonrpc({"method": "fopen", "params": [path, mode]}) + self.fp = jsonrpc('fopen', [path, mode]) def read(self): assert self.mode == 'r' - return jsonrpc({"method": "fread", "params": [self.fp]}) + return jsonrpc('fread', [self.fp]) def write(self, s): assert self.mode == 'w' assert type(s) is str - jsonrpc({"method": "fwrite", "params": [self.fp, s]}) + jsonrpc('fwrite', [self.fp, s]) def close(self): - jsonrpc({"method": "fclose", "params": [self.fp]}) + jsonrpc('fclose', [self.fp]) def __enter__(self): pass diff --git a/src/compiler.h b/src/compiler.h index 92bc5880..30327b03 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -1020,8 +1020,9 @@ __LISTCOMP: } }; -_Code compile(VM* vm, const char* source, _Str filename, CompileMode mode=EXEC_MODE) { +_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(std::exception& e){ diff --git a/src/main.cpp b/src/main.cpp index eda9e4ba..5810f1c9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,11 +43,15 @@ void _tvm_dispatch(ThreadedVM* vm){ while(pkpy_tvm_get_state(vm) != THREAD_FINISHED){ if(pkpy_tvm_get_state(vm) == THREAD_SUSPENDED){ char* obj = pkpy_tvm_read_jsonrpc_request(vm); - bool is_input_call = INPUT_JSONRPC_STR == std::string(obj); + bool is_input_call = std::string_view(obj).find("\"input\"") != std::string::npos; if(is_input_call){ std::string line; std::getline(std::cin, line); - pkpy_tvm_resume(vm, line.c_str()); + _StrStream ss; + ss << '{'; + ss << "\"result\": " << _Str(line).__escape(false); + ss << '}'; + pkpy_tvm_jsonrpc_response(vm, ss.str().c_str()); }else{ std::cout << "unknown jsonrpc call" << std::endl; std::cout << obj << std::endl; diff --git a/src/pocketpy.h b/src/pocketpy.h index 1823a804..40897892 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -58,8 +58,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_MODE); - if(code == nullptr) return vm->None; + _Code code = compile(vm, expr.c_str(), "", EVAL_MODE, false); return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals); }); @@ -639,8 +638,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_MODE); - if(code == nullptr) return vm->None; + _Code code = compile(vm, expr.c_str(), "", JSON_MODE, false); return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals); }); @@ -650,14 +648,14 @@ void __addModuleJson(VM* vm){ }); } -class _PkExported; -static std::vector<_PkExported*> _pkLookupTable; class _PkExported{ public: virtual ~_PkExported() = default; virtual void* get() = 0; }; +static std::vector<_PkExported*> _pkLookupTable; + template class PkExported : public _PkExported{ T* _ptr; @@ -763,13 +761,13 @@ extern "C" { void __vm_init(VM* vm){ __initializeBuiltinFunctions(vm); - _Code code = compile(vm, __BUILTINS_CODE, ""); - if(code == nullptr) exit(1); - vm->_exec(code, vm->builtins, {}); - __addModuleSys(vm); __addModuleTime(vm); __addModuleJson(vm); + + _Code code = compile(vm, __BUILTINS_CODE, ""); + if(code == nullptr) exit(1); + vm->_exec(code, vm->builtins, {}); pkpy_vm_add_module(vm, "random", __RANDOM_CODE); } @@ -830,22 +828,15 @@ extern "C" { __EXPORT /// Read the current JSONRPC request from shared string buffer. - /// - /// Return a `PyObjectDump*` representing the string. - /// You need to call `pkpy_delete` to free the returned `PyObjectDump*` later. - /// If the buffer is empty, return `nullptr`. char* pkpy_tvm_read_jsonrpc_request(ThreadedVM* vm){ - std::optional<_Str> s = vm->readSharedStr(); - if(!s.has_value()) return nullptr; - return strdup(s.value().c_str()); + _Str s = vm->readSharedStr(); + return strdup(s.c_str()); } __EXPORT - /// Resume a suspended threaded virtual machine - /// and put the given string into the shared string buffer. - /// It is usually used for JSONRPC. - void pkpy_tvm_resume(ThreadedVM* vm, const char* value){ - vm->resume(value); + /// Write a JSONRPC response to shared string buffer. + void pkpy_tvm_jsonrpc_response(ThreadedVM* vm, const char* value){ + vm->jsonrpcResponse(value); } __EXPORT diff --git a/src/vm.h b/src/vm.h index e67cd7d9..113f42d7 100644 --- a/src/vm.h +++ b/src/vm.h @@ -40,7 +40,8 @@ protected: PyVar runFrame(Frame* frame){ while(!frame->isCodeEnd()){ const ByteCode& byte = frame->readCode(); - //printf("%s (%d) stack_size: %d\n", OP_NAMES[byte.op], byte.arg, frame->stackSize()); + //printf("[%d] %s (%d)\n", frame->stackSize(), OP_NAMES[byte.op], byte.arg); + //printf("%s\n", frame->code->src->getLine(byte.line).c_str()); _checkStopFlag(); @@ -466,8 +467,6 @@ public: obj = call(it->second, args); }else{ obj = newObject(_callable, (_Int)-1); - } - if(obj->isType(_callable)){ PyVarOrNull init_fn = getAttr(obj, __init__, false); if (init_fn != nullptr) call(init_fn, args); } @@ -1082,21 +1081,11 @@ enum ThreadState { THREAD_FINISHED }; -const _Str INPUT_JSONRPC_STR = "{\"method\": \"input\", \"params\": []}"; - class ThreadedVM : public VM { std::thread* _thread = nullptr; std::atomic _state = THREAD_READY; - std::optional<_Str> _sharedStr = {}; + _Str _sharedStr = ""_c; - PyVar jsonRpc(const _Str& _json){ - _sharedStr = _json; - suspend(); - std::optional<_Str> ret = readSharedStr(); - if(ret.has_value()) return PyStr(ret.value()); - return None; - } - void __deleteThread(){ if(_thread != nullptr){ if(_state == THREAD_RUNNING || _state == THREAD_SUSPENDED){ @@ -1109,20 +1098,14 @@ class ThreadedVM : public VM { } public: ThreadedVM(bool use_stdio) : VM(use_stdio) { - bindBuiltinFunc("jsonrpc", [](VM* vm, const pkpy::ArgList& args){ - ThreadedVM *tvm = dynamic_cast(vm); - if(tvm == nullptr) UNREACHABLE(); - tvm->__checkArgSize(args, 1); - tvm->__checkType(args[0], vm->builtins->attribs["dict"_c]); - _Str _json = tvm->PyStr_AS_C(tvm->asJson(args[0])); - return tvm->jsonRpc(_json); - }); + bindBuiltinFunc("__string_channel_call", [](VM* vm, const pkpy::ArgList& args){ + vm->__checkArgSize(args, 1); + _Str data = vm->PyStr_AS_C(args[0]); - bindBuiltinFunc("input", [](VM* vm, const pkpy::ArgList& args) { - ThreadedVM *tvm = dynamic_cast(vm); - if(tvm == nullptr) UNREACHABLE(); - tvm->__checkArgSize(args, 0); - return tvm->jsonRpc(INPUT_JSONRPC_STR); + ThreadedVM* tvm = (ThreadedVM*)vm; + tvm->_sharedStr = data; + tvm->suspend(); + return tvm->PyStr(tvm->readSharedStr()); }); } @@ -1136,9 +1119,9 @@ public: } } - std::optional<_Str> readSharedStr(){ - std::optional<_Str> copy = _sharedStr; - _sharedStr = {}; + _Str readSharedStr(){ + _Str copy = _sharedStr; + _sharedStr = ""_c; return copy; } @@ -1148,14 +1131,10 @@ public: return _state; } - void resume(const char* value=nullptr){ + void jsonrpcResponse(const char* value){ if(_state != THREAD_SUSPENDED) UNREACHABLE(); _state = THREAD_RUNNING; - if(value == nullptr){ - _sharedStr = {}; - }else{ - _sharedStr = _Str(value); - } + _sharedStr = _Str(value); } void execAsync(const _Code& code) override {