blueloveTH 3 anni fa
parent
commit
9c16aefda3
7 ha cambiato i file con 132 aggiunte e 69 eliminazioni
  1. 16 5
      src/compiler.h
  2. 2 1
      src/error.h
  3. 3 3
      src/main.cpp
  4. 14 0
      src/parser.h
  5. 52 58
      src/pocketpy.h
  6. 2 2
      src/vm.h
  7. 43 0
      tests/json.py

+ 16 - 5
src/compiler.h

@@ -905,11 +905,15 @@ __LISTCOMP:
             {
                 case 0: func->args.push_back(name); break;
                 case 1: func->starredArg = name; state+=1; break;
-                case 2:
+                case 2: {
                     consume(TK("="));
-                    func->kwArgs[name] = consumeLiteral();
+                    PyVarOrNull value = readLiteral();
+                    if(value == nullptr){
+                        syntaxError(_Str("expect a literal, not ") + TK_STR(parser->current.type));
+                    }
+                    func->kwArgs[name] = value;
                     func->kwArgsOrder.push_back(name);
-                    break;
+                } break;
                 case 3: syntaxError("**kwargs is not supported yet"); break;
             }
         } while (match(TK(",")));
@@ -937,7 +941,7 @@ __LISTCOMP:
         if(!isCompilingClass) emitCode(OP_STORE_FUNCTION);
     }
 
-    PyVar consumeLiteral(){
+    PyVarOrNull readLiteral(){
         if(match(TK("-"))){
             consume(TK("@num"));
             PyVar val = parser->previous.value;
@@ -949,7 +953,6 @@ __LISTCOMP:
         if(match(TK("False"))) return vm->PyBool(false);
         if(match(TK("None"))) return vm->None;
         if(match(TK("..."))) return vm->Ellipsis;
-        syntaxError(_Str("expect a literal, not ") + TK_STR(parser->current.type));
         return nullptr;
     }
 
@@ -980,6 +983,14 @@ __LISTCOMP:
             EXPR_TUPLE();
             consume(TK("@eof"));
             return code;
+        }else if(mode()==JSON_MODE){
+            PyVarOrNull value = readLiteral();
+            if(value != nullptr) emitCode(OP_LOAD_CONST, code->addConst(value));
+            else if(match(TK("{"))) exprMap();
+            else if(match(TK("["))) exprList();
+            else syntaxError("expect a JSON object or array");
+            consume(TK("@eof"));
+            return code;
         }
 
         while (!match(TK("@eof"))) {

+ 2 - 1
src/error.h

@@ -11,7 +11,8 @@ public:
 enum CompileMode {
     EXEC_MODE,
     EVAL_MODE,
-    SINGLE_MODE     // for REPL
+    SINGLE_MODE,     // for REPL
+    JSON_MODE,
 };
 
 struct SourceMetadata {

+ 3 - 3
src/main.cpp

@@ -42,15 +42,15 @@ extern "C" {
 void _tvm_dispatch(ThreadedVM* vm){
     while(pkpy_tvm_get_state(vm) != THREAD_FINISHED){
         if(pkpy_tvm_get_state(vm) == THREAD_SUSPENDED){
-            PyObjectDump* obj = pkpy_tvm_read_jsonrpc_request(vm);
-            bool is_input_call = INPUT_JSONRPC_STR == obj->json;
+            char* obj = pkpy_tvm_read_jsonrpc_request(vm);
+            bool is_input_call = INPUT_JSONRPC_STR == std::string(obj);
             if(is_input_call){
                 std::string line;
                 std::getline(std::cin, line);
                 pkpy_tvm_resume(vm, line.c_str());
             }else{
                 std::cout << "unknown jsonrpc call" << std::endl;
-                std::cout << obj->json << std::endl;
+                std::cout << obj << std::endl;
                 exit(3);
             }
             pkpy_delete(obj);

+ 14 - 0
src/parser.h

@@ -222,6 +222,20 @@ struct Parser {
         int length = (int)(current_char - token_start);
         if(length == 0) return 3;
         std::string_view name(token_start, length);
+
+        if(src->mode == JSON_MODE){
+            if(name == "true"){
+                setNextToken(TK("True"));
+            } else if(name == "false"){
+                setNextToken(TK("False"));
+            } else if(name == "null"){
+                setNextToken(TK("None"));
+            } else {
+                return 4;
+            }
+            return 0;
+        }
+
         if(__KW_MAP.count(name)){
             if(name == "not"){
                 if(strncmp(current_char, " in", 3) == 0){

+ 52 - 58
src/pocketpy.h

@@ -161,6 +161,10 @@ void __initializeBuiltinFunctions(VM* _vm) {
         return vm->PyStr("null");
     });
 
+    _vm->bindMethod("NoneType", "__eq__", [](VM* vm, const pkpy::ArgList& args) {
+        return vm->PyBool(args[0] == args[1]);
+    });
+
     _vm->bindMethodMulti({"int", "float"}, "__truediv__", [](VM* vm, const pkpy::ArgList& args) {
         if(!vm->isIntOrFloat(args[0], args[1]))
             vm->typeError("unsupported operand type(s) for " "/" );
@@ -630,6 +634,22 @@ void __addModuleSys(VM* vm){
     vm->setAttr(mod, "version", vm->PyStr(PK_VERSION));
 }
 
+void __addModuleJson(VM* vm){
+    PyVar mod = vm->newModule("json");
+    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);
+        if(code == nullptr) return vm->None;
+        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals);
+    });
+
+    vm->bindFunc(mod, "dumps", [](VM* vm, const pkpy::ArgList& args) {
+        vm->__checkArgSize(args, 1);
+        return vm->asJson(args[0]);
+    });
+}
+
 class _PkExported;
 static std::vector<_PkExported*> _pkLookupTable;
 class _PkExported{
@@ -643,7 +663,8 @@ class PkExported : public _PkExported{
     T* _ptr;
 public:
     template<typename... Args>
-    PkExported(Args&&... args) : _ptr(new T(std::forward<Args>(args)...)){
+    PkExported(Args&&... args) {
+        _ptr = new T(std::forward<Args>(args)...);
         _pkLookupTable.push_back(this);
     }
     
@@ -656,91 +677,60 @@ public:
 
 
 extern "C" {
-    struct PyObjectDump {
-        const char* type;   // "int", "str", "float" ...
-        const char* json;   // json representation
-
-        PyObjectDump(_Str _type, _Str _json){
-            type = strdup(_type.c_str());
-            json = strdup(_json.c_str());
-        }
-
-        ~PyObjectDump(){
-            delete[] type;
-            delete[] json;
-        }
-    };
-
-    struct PyOutputDump {
-        const char* _stdout;
-        const char* _stderr;
-
-        PyOutputDump(_Str _stdout, _Str _stderr){
-            this->_stdout = strdup(_stdout.c_str());
-            this->_stderr = strdup(_stderr.c_str());
-        }
-
-        ~PyOutputDump(){
-            delete[] _stdout;
-            delete[] _stderr;
-        }
-    };
-
     __EXPORT
-    /// Delete a pointer allocated by `pkpy_xxx_xxx`.
-    /// It can be `VM*`, `REPL*` or `PyXXXDump*`, etc.
+    /// Delete a class pointer allocated by `pkpy_xxx_xxx`.
+    /// It can be `VM*`, `REPL*`, `ThreadedVM*`, `char*`, etc.
     /// 
     /// !!!
-    /// If the pointer is not allocated by `pkpy_xxx_xxx`, nothing will happen.
+    /// If the pointer is not allocated by `pkpy_xxx_xxx`, the behavior is undefined.
+    /// For char*, you can also use trivial `delete` in your language.
     /// !!!
     void pkpy_delete(void* p){
         for(int i = 0; i < _pkLookupTable.size(); i++){
             if(_pkLookupTable[i]->get() == p){
                 delete _pkLookupTable[i];
                 _pkLookupTable.erase(_pkLookupTable.begin() + i);
+                return;
             }
         }
+        free(p);
     }
 
     __EXPORT
     /// Run a given source on a virtual machine.
     /// 
-    /// Return `true` if there is no error.
+    /// 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;
-        return vm->exec(code) != nullptr;
+        vm->exec(code);
+        return true;
     }
 
     __EXPORT
     /// Get a global variable of a virtual machine.
-    /// Return a `PyObjectDump*` representing the variable.
-    /// You need to call `pkpy_delete` to free the returned `PyObjectDump*` later.
+    /// 
+    /// Return a json representing the result.
     /// If the variable is not found, return `nullptr`.
-    PyObjectDump* pkpy_vm_get_global(VM* vm, const char* name){
+    char* pkpy_vm_get_global(VM* vm, const char* name){
         auto it = vm->_main->attribs.find(name);
         if(it == vm->_main->attribs.end()) return nullptr;
-        return pkpy_allocate(PyObjectDump,
-            it->second->getTypeName().c_str(),
-            vm->PyStr_AS_C(vm->asJson(it->second)).c_str()
-        );
+        _Str _json = vm->PyStr_AS_C(vm->asJson(it->second));
+        return strdup(_json.c_str());
     }
 
     __EXPORT
     /// Evaluate an expression.
     /// 
-    /// Return a `PyObjectDump*` representing the result.
-    /// You need to call `pkpy_delete` to free the returned `PyObjectDump*` later.
+    /// Return a json representing the result.
     /// If there is any error, return `nullptr`.
-    PyObjectDump* pkpy_vm_eval(VM* vm, const char* source){
+    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);
         if(ret == nullptr) return nullptr;
-        return pkpy_allocate(PyObjectDump,
-            ret->getTypeName(),
-            vm->PyStr_AS_C(vm->asJson(ret))
-        );
+        _Str _json = vm->PyStr_AS_C(vm->asJson(ret));
+        return strdup(_json.c_str());
     }
 
     __EXPORT
@@ -779,6 +769,7 @@ extern "C" {
 
         __addModuleSys(vm);
         __addModuleTime(vm);
+        __addModuleJson(vm);
         pkpy_vm_add_module(vm, "random", __RANDOM_CODE);
     }
 
@@ -803,17 +794,20 @@ extern "C" {
     /// The `vm->use_stdio` should be `false`.
     /// After this operation, both stream will be cleared.
     ///
-    /// Return a `PyOutputDump*` representing the result.
-    /// You need to call `pkpy_delete` to free the returned `PyOutputDump*` later.
-    PyOutputDump* pkpy_vm_read_output(VM* vm){
+    /// Return a json representing the result.
+    char* pkpy_vm_read_output(VM* vm){
         if(vm->use_stdio) return nullptr;
         _StrStream* s_out = dynamic_cast<_StrStream*>(vm->_stdout);
         _StrStream* s_err = dynamic_cast<_StrStream*>(vm->_stderr);
-        if(s_out == nullptr || s_err == nullptr) return nullptr;
-        PyOutputDump* dump = pkpy_allocate(PyOutputDump, s_out->str(), s_err->str());
+        _Str _stdout = s_out->str();
+        _Str _stderr = s_err->str();
+        _StrStream ss;
+        ss << '{' << "\"stdout\": " << _stdout.__escape(false);
+        ss << ", ";
+        ss << "\"stderr\": " << _stderr.__escape(false) << '}';
         s_out->str("");
         s_err->str("");
-        return dump;
+        return strdup(ss.str().c_str());
     }
 
     __EXPORT
@@ -840,10 +834,10 @@ extern "C" {
     /// 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`.
-    PyObjectDump* pkpy_tvm_read_jsonrpc_request(ThreadedVM* vm){
+    char* pkpy_tvm_read_jsonrpc_request(ThreadedVM* vm){
         std::optional<_Str> s = vm->readSharedStr();
         if(!s.has_value()) return nullptr;
-        return pkpy_allocate(PyObjectDump, "str"_c, s.value());
+        return strdup(s.value().c_str());
     }
 
     __EXPORT

+ 2 - 2
src/vm.h

@@ -352,8 +352,8 @@ protected:
             }
         }
 
-        if(frame->code->src->mode == EVAL_MODE) {
-            if(frame->stackSize() != 1) systemError("stack size is not 1 in EVAL_MODE");
+        if(frame->code->src->mode == EVAL_MODE || frame->code->src->mode == JSON_MODE){
+            if(frame->stackSize() != 1) systemError("stack size is not 1 in EVAL_MODE/JSON_MODE");
             return frame->popValue(this);
         }
 

+ 43 - 0
tests/json.py

@@ -0,0 +1,43 @@
+a = {
+    'a': 1,
+    'b': 2,
+    'c': None,
+    'd': [1, 2, 3],
+    # 'e': {
+    #     'a': 1,
+    #     'b': 2,
+    #     'c': None,
+    #     'd': [1, 2, 3],
+    # },
+    "f": 'This is a string',
+    'g': [True, False, None],
+    'h': False
+}
+
+import json
+
+_j = json.dumps(a)
+_a = json.loads(_j)
+
+for k, v in a.items():
+    assert a[k] == _a[k]
+
+for k, v in _a.items():
+    assert a[k] == _a[k]
+
+b = [1, 2, True, None, False]
+
+_j = json.dumps(b)
+_b = json.loads(_j)
+
+assert b == _b
+
+c = 1.0
+_j = json.dumps(c)
+_c = json.loads(_j)
+assert c == _c
+
+d = True
+_j = json.dumps(d)
+_d = json.loads(_j)
+assert d == _d