This commit is contained in:
blueloveTH 2022-12-03 19:32:24 +08:00
parent aa4c9ca4ae
commit 9c16aefda3
7 changed files with 132 additions and 69 deletions

View File

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

View File

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

View File

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

View File

@ -222,6 +222,20 @@ struct Parser {
int length = (int)(current_char - token_start); int length = (int)(current_char - token_start);
if(length == 0) return 3; if(length == 0) return 3;
std::string_view name(token_start, length); 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(__KW_MAP.count(name)){
if(name == "not"){ if(name == "not"){
if(strncmp(current_char, " in", 3) == 0){ if(strncmp(current_char, " in", 3) == 0){

View File

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

View File

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

43
tests/json.py Normal file
View File

@ -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