mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
add json
This commit is contained in:
parent
aa4c9ca4ae
commit
9c16aefda3
@ -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"))) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
14
src/parser.h
14
src/parser.h
@ -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){
|
||||||
|
110
src/pocketpy.h
110
src/pocketpy.h
@ -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
|
||||||
|
4
src/vm.h
4
src/vm.h
@ -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
43
tests/json.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user