mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-03-04 13:15:55 +08:00
minor enhancements/improvements
- json print implemented - some bugs in io module fixed
This commit is contained in:
parent
3b88205b1d
commit
2b940b227d
@ -181,7 +181,8 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv) {
|
|||||||
if ((fiber->closure->fn->arity != -1) && argc != fiber->closure->fn->arity) {
|
if ((fiber->closure->fn->arity != -1) && argc != fiber->closure->fn->arity) {
|
||||||
char buff[STR_INT_BUFF_SIZE];
|
char buff[STR_INT_BUFF_SIZE];
|
||||||
sprintf(buff, "%d", fiber->closure->fn->arity);
|
sprintf(buff, "%d", fiber->closure->fn->arity);
|
||||||
_ERR_FAIL(stringFormat(vm, "Expected exactly $ argument(s).", buff));
|
_ERR_FAIL(stringFormat(vm, "Expected exactly $ argument(s) for "
|
||||||
|
"function $.", buff, fiber->closure->fn->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fiber->state != FIBER_NEW) {
|
if (fiber->state != FIBER_NEW) {
|
||||||
@ -1245,7 +1246,8 @@ L_do_call:
|
|||||||
// No constructor is defined on the class. Just return self.
|
// No constructor is defined on the class. Just return self.
|
||||||
if (closure == NULL) {
|
if (closure == NULL) {
|
||||||
if (argc != 0) {
|
if (argc != 0) {
|
||||||
String* msg = stringFormat(vm, "Expected exactly 0 argument(s).");
|
String* msg = stringFormat(vm, "Expected exactly 0 argument(s) "
|
||||||
|
"for constructor $.", cls->name->data);
|
||||||
RUNTIME_ERROR(msg);
|
RUNTIME_ERROR(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1265,8 +1267,8 @@ L_do_call:
|
|||||||
// -1 argument means multiple number of args.
|
// -1 argument means multiple number of args.
|
||||||
if (closure->fn->arity != -1 && closure->fn->arity != argc) {
|
if (closure->fn->arity != -1 && closure->fn->arity != argc) {
|
||||||
char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", closure->fn->arity);
|
char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", closure->fn->arity);
|
||||||
String* msg = stringFormat(vm, "Expected exactly $ argument(s).",
|
String* msg = stringFormat(vm, "Expected exactly $ argument(s) "
|
||||||
buff);
|
"for function $", buff, closure->fn->name);
|
||||||
RUNTIME_ERROR(msg);
|
RUNTIME_ERROR(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
#include "gen/ext_term.pk.h" //<< AMALG_INLINE >>
|
#include "gen/ext_term.pk.h" //<< AMALG_INLINE >>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TERM_IMPLEMENT
|
#define TERM_IMPLEMENT
|
||||||
#include "thirdparty/term/term.h" //<< AMALG_INLINE >>
|
#include "thirdparty/term/term.h" //<< AMALG_INLINE >>
|
||||||
#undef TERM_IMPLEMENT
|
#undef TERM_IMPLEMENT
|
||||||
@ -229,6 +233,14 @@ void _termReadEvent(PKVM* vm) {
|
|||||||
pkSetSlotBool(vm, 0, term_read_event(event));
|
pkSetSlotBool(vm, 0, term_read_event(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On windows it'll set stdout to binary mode, on other platforms this function
|
||||||
|
// won't make make any difference.
|
||||||
|
void _termBinaryMode(PKVM* vm) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
(void) _setmode(_fileno(stdout), _O_BINARY);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* MODULE REGISTER */
|
/* MODULE REGISTER */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -252,6 +264,11 @@ void registerModuleTerm(PKVM* vm) {
|
|||||||
|
|
||||||
pkModuleAddSource(vm, term, ext_term_pk);
|
pkModuleAddSource(vm, term, ext_term_pk);
|
||||||
|
|
||||||
|
// This is required for language server. Since we need to send '\r\n' to
|
||||||
|
// the lsp client but windows will change '\n' to '\r\n' and it'll become
|
||||||
|
// '\r\r\n', binary mode will prevent this.
|
||||||
|
pkModuleAddFunction(vm, term, "binary_mode", _termBinaryMode, 0);
|
||||||
|
|
||||||
pkRegisterModule(vm, term);
|
pkRegisterModule(vm, term);
|
||||||
pkReleaseHandle(vm, term);
|
pkReleaseHandle(vm, term);
|
||||||
}
|
}
|
||||||
|
@ -35,14 +35,12 @@ DEF(_ioWrite,
|
|||||||
pkSetRuntimeError(vm, "Cannot write to stdin.");
|
pkSetRuntimeError(vm, "Cannot write to stdin.");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: If the string contain null bytes it won't print anything after
|
|
||||||
// that, not sure if that needs to be fixed.
|
|
||||||
case 1:
|
case 1:
|
||||||
fprintf(stdout, "%s", bytes);
|
fwrite(bytes, sizeof(char), length, stdout);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
fprintf(stderr, "%s", bytes);
|
fwrite(bytes, sizeof(char), length, stderr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,6 +52,13 @@ DEF(_ioFlush,
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF(_ioGetc,
|
||||||
|
"io.getc() -> String\n"
|
||||||
|
"Read a single character from stdin and return it.") {
|
||||||
|
char c = (char) fgetc(stdin);
|
||||||
|
pkSetSlotStringLength(vm, 0, &c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* FILE CLASS */
|
/* FILE CLASS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -81,8 +86,8 @@ typedef enum {
|
|||||||
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
|
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
|
||||||
|
|
||||||
FMODE_READ_BIN = (_FMODE_BIN | FMODE_READ),
|
FMODE_READ_BIN = (_FMODE_BIN | FMODE_READ),
|
||||||
FMODE_WRITE_BIN = (_FMODE_BIN | FMODE_READ),
|
FMODE_WRITE_BIN = (_FMODE_BIN | FMODE_WRITE),
|
||||||
FMODE_APPEND_BIN = (_FMODE_BIN | FMODE_READ),
|
FMODE_APPEND_BIN = (_FMODE_BIN | FMODE_APPEND),
|
||||||
|
|
||||||
FMODE_READ_BIN_EXT = (_FMODE_BIN | FMODE_READ_EXT),
|
FMODE_READ_BIN_EXT = (_FMODE_BIN | FMODE_READ_EXT),
|
||||||
FMODE_WRITE_BIN_EXT = (_FMODE_BIN | FMODE_WRITE_EXT),
|
FMODE_WRITE_BIN_EXT = (_FMODE_BIN | FMODE_WRITE_EXT),
|
||||||
@ -439,6 +444,7 @@ void registerModuleIO(PKVM* vm) {
|
|||||||
|
|
||||||
pkModuleAddFunction(vm, io, "write", _ioWrite, 2);
|
pkModuleAddFunction(vm, io, "write", _ioWrite, 2);
|
||||||
pkModuleAddFunction(vm, io, "flush", _ioFlush, 0);
|
pkModuleAddFunction(vm, io, "flush", _ioFlush, 0);
|
||||||
|
pkModuleAddFunction(vm, io, "getc", _ioGetc, 0);
|
||||||
|
|
||||||
PkHandle* cls_file = pkNewClass(vm, "File", NULL, io, _fileNew, _fileDelete);
|
PkHandle* cls_file = pkNewClass(vm, "File", NULL, io, _fileNew, _fileDelete);
|
||||||
pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1);
|
pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1);
|
||||||
|
@ -82,6 +82,83 @@ static Var _cJsonToPocket(PKVM* vm, cJSON* item) {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static cJSON* _pocketToCJson(PKVM* vm, Var item) {
|
||||||
|
PkVarType vt = getVarType(item);
|
||||||
|
switch (vt) {
|
||||||
|
case PK_NULL:
|
||||||
|
return cJSON_CreateNull();
|
||||||
|
|
||||||
|
case PK_BOOL:
|
||||||
|
return cJSON_CreateBool(AS_BOOL(item));
|
||||||
|
|
||||||
|
case PK_NUMBER:
|
||||||
|
return cJSON_CreateNumber(AS_NUM(item));
|
||||||
|
|
||||||
|
case PK_STRING:
|
||||||
|
return cJSON_CreateString(((String*) AS_OBJ(item))->data);
|
||||||
|
|
||||||
|
case PK_LIST: {
|
||||||
|
List* list = (List*) AS_OBJ(item);
|
||||||
|
cJSON* arr = cJSON_CreateArray();
|
||||||
|
|
||||||
|
bool err = false;
|
||||||
|
for (uint32_t i = 0; i < list->elements.count; i++) {
|
||||||
|
cJSON* elem = _pocketToCJson(vm, list->elements.data[i]);
|
||||||
|
if (elem == NULL) {
|
||||||
|
err = true; break;
|
||||||
|
};
|
||||||
|
cJSON_AddItemToArray(arr, elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
cJSON_Delete(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PK_MAP: {
|
||||||
|
Map* map = (Map*) AS_OBJ(item);
|
||||||
|
cJSON* obj = cJSON_CreateObject();
|
||||||
|
|
||||||
|
bool err = false;
|
||||||
|
MapEntry* e = map->entries;
|
||||||
|
for (; e < map->entries + map->capacity; e++) {
|
||||||
|
if (IS_UNDEF(e->key)) continue;
|
||||||
|
|
||||||
|
if (!IS_OBJ_TYPE(e->key, OBJ_STRING)) {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "Expected string as json object key, "
|
||||||
|
"instead got type '%s'.", varTypeName(e->key));
|
||||||
|
err = true; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* value = _pocketToCJson(vm, e->value);
|
||||||
|
if (value == NULL) { err = true; break; }
|
||||||
|
|
||||||
|
cJSON_AddItemToObject(obj, ((String*) AS_OBJ(e->key))->data, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
cJSON_Delete(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
pkSetRuntimeErrorFmt(vm, "Object of type '%s' cannot be serialized "
|
||||||
|
"to json.", varTypeName(item));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
UNREACHABLE();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
DEF(_jsonParse,
|
DEF(_jsonParse,
|
||||||
"json.parse(json_str:String) -> var\n"
|
"json.parse(json_str:String) -> var\n"
|
||||||
"Parse a json string into pocket lang object.") {
|
"Parse a json string into pocket lang object.") {
|
||||||
@ -108,6 +185,39 @@ DEF(_jsonParse,
|
|||||||
vm->fiber->ret[0] = obj;
|
vm->fiber->ret[0] = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF(_jsonPrint,
|
||||||
|
"json.print(value:Var[, pretty:Bool=false])\n"
|
||||||
|
"Render a pocketlang value into text. Takes an optional argument pretty, if "
|
||||||
|
"true it'll pretty print the output.") {
|
||||||
|
|
||||||
|
int argc = pkGetArgc(vm);
|
||||||
|
if (!pkCheckArgcRange(vm, argc, 1, 2)) return;
|
||||||
|
|
||||||
|
bool pretty = false;
|
||||||
|
if (argc == 2) {
|
||||||
|
if (!pkValidateSlotBool(vm, 2, &pretty)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Var value = vm->fiber->ret[1];
|
||||||
|
cJSON* json = _pocketToCJson(vm, value);
|
||||||
|
|
||||||
|
// A runtime error already set.
|
||||||
|
if (json == NULL) return;
|
||||||
|
|
||||||
|
char* string = (pretty)
|
||||||
|
? cJSON_Print(json)
|
||||||
|
: cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
|
||||||
|
if (string == NULL) {
|
||||||
|
pkSetRuntimeError(vm, "Failed to print json.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkSetSlotString(vm, 0, string);
|
||||||
|
free(string);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* MODULE REGISTER */
|
/* MODULE REGISTER */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -116,6 +226,7 @@ void registerModuleJson(PKVM* vm) {
|
|||||||
PkHandle* json = pkNewModule(vm, "json");
|
PkHandle* json = pkNewModule(vm, "json");
|
||||||
|
|
||||||
pkModuleAddFunction(vm, json, "parse", _jsonParse, 1);
|
pkModuleAddFunction(vm, json, "parse", _jsonParse, 1);
|
||||||
|
pkModuleAddFunction(vm, json, "print", _jsonPrint, -1);
|
||||||
|
|
||||||
pkRegisterModule(vm, json);
|
pkRegisterModule(vm, json);
|
||||||
pkReleaseHandle(vm, json);
|
pkReleaseHandle(vm, json);
|
||||||
|
@ -46,17 +46,17 @@ DEF(_typesHash,
|
|||||||
/* BYTE BUFFER */
|
/* BYTE BUFFER */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void* _bytebuffNew(PKVM* vm) {
|
static void* _bytebuffNew(PKVM* vm) {
|
||||||
pkByteBuffer* self = pkRealloc(vm, NULL, sizeof(pkByteBuffer));
|
pkByteBuffer* self = pkRealloc(vm, NULL, sizeof(pkByteBuffer));
|
||||||
pkByteBufferInit(self);
|
pkByteBufferInit(self);
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bytebuffDelete(PKVM* vm, void* buff) {
|
static void _bytebuffDelete(PKVM* vm, void* buff) {
|
||||||
pkRealloc(vm, buff, 0);
|
pkRealloc(vm, buff, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bytebuffReserve(PKVM* vm) {
|
DEF(_bytebuffReserve, "") {
|
||||||
double size;
|
double size;
|
||||||
if (!pkValidateSlotNumber(vm, 1, &size)) return;
|
if (!pkValidateSlotNumber(vm, 1, &size)) return;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ void _bytebuffReserve(PKVM* vm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buff.fill(data, count)
|
// buff.fill(data, count)
|
||||||
void _bytebuffFill(PKVM* vm) {
|
DEF(_bytebuffFill, "") {
|
||||||
uint32_t n;
|
uint32_t n;
|
||||||
if (!pkValidateSlotInteger(vm, 1, &n)) return;
|
if (!pkValidateSlotInteger(vm, 1, &n)) return;
|
||||||
if (n < 0x00 || n > 0xff) {
|
if (n < 0x00 || n > 0xff) {
|
||||||
@ -81,14 +81,14 @@ void _bytebuffFill(PKVM* vm) {
|
|||||||
pkByteBufferFill(self, vm, (uint8_t) n, (int) count);
|
pkByteBufferFill(self, vm, (uint8_t) n, (int) count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bytebuffClear(PKVM* vm) {
|
DEF(_bytebuffClear, "") {
|
||||||
// TODO: Should I also zero or reduce the capacity?
|
// TODO: Should I also zero or reduce the capacity?
|
||||||
pkByteBuffer* self = pkGetSelf(vm);
|
pkByteBuffer* self = pkGetSelf(vm);
|
||||||
self->count = 0;
|
self->count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the length of bytes were written.
|
// Returns the length of bytes were written.
|
||||||
void _bytebuffWrite(PKVM* vm) {
|
DEF(_bytebuffWrite, "") {
|
||||||
pkByteBuffer* self = pkGetSelf(vm);
|
pkByteBuffer* self = pkGetSelf(vm);
|
||||||
|
|
||||||
PkVarType type = pkGetSlotType(vm, 1);
|
PkVarType type = pkGetSlotType(vm, 1);
|
||||||
@ -135,7 +135,7 @@ void _bytebuffWrite(PKVM* vm) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bytebuffSubscriptGet(PKVM* vm) {
|
DEF(_bytebuffSubscriptGet, "") {
|
||||||
double index;
|
double index;
|
||||||
if (!pkValidateSlotNumber(vm, 1, &index)) return;
|
if (!pkValidateSlotNumber(vm, 1, &index)) return;
|
||||||
if (floor(index) != index) {
|
if (floor(index) != index) {
|
||||||
@ -154,7 +154,7 @@ void _bytebuffSubscriptGet(PKVM* vm) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bytebuffSubscriptSet(PKVM* vm) {
|
DEF(_bytebuffSubscriptSet, "") {
|
||||||
double index, value;
|
double index, value;
|
||||||
if (!pkValidateSlotNumber(vm, 1, &index)) return;
|
if (!pkValidateSlotNumber(vm, 1, &index)) return;
|
||||||
if (!pkValidateSlotNumber(vm, 2, &value)) return;
|
if (!pkValidateSlotNumber(vm, 2, &value)) return;
|
||||||
@ -184,12 +184,12 @@ void _bytebuffSubscriptSet(PKVM* vm) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bytebuffString(PKVM* vm) {
|
DEF(_bytebuffString, "") {
|
||||||
pkByteBuffer* self = pkGetSelf(vm);
|
pkByteBuffer* self = pkGetSelf(vm);
|
||||||
pkSetSlotStringLength(vm, 0, self->data, self->count);
|
pkSetSlotStringLength(vm, 0, self->data, self->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bytebuffCount(PKVM* vm) {
|
DEF(_bytebuffCount, "") {
|
||||||
pkByteBuffer* self = pkGetSelf(vm);
|
pkByteBuffer* self = pkGetSelf(vm);
|
||||||
pkSetSlotNumber(vm, 0, self->count);
|
pkSetSlotNumber(vm, 0, self->count);
|
||||||
}
|
}
|
||||||
@ -202,17 +202,17 @@ typedef struct {
|
|||||||
double x, y, z;
|
double x, y, z;
|
||||||
} Vector;
|
} Vector;
|
||||||
|
|
||||||
void* _vectorNew(PKVM* vm) {
|
static void* _vectorNew(PKVM* vm) {
|
||||||
Vector* vec = pkRealloc(vm, NULL, sizeof(Vector));
|
Vector* vec = pkRealloc(vm, NULL, sizeof(Vector));
|
||||||
memset(vec, 0, sizeof(Vector));
|
memset(vec, 0, sizeof(Vector));
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _vectorDelete(PKVM* vm, void* vec) {
|
static void _vectorDelete(PKVM* vm, void* vec) {
|
||||||
pkRealloc(vm, vec, 0);
|
pkRealloc(vm, vec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _vectorInit(PKVM* vm) {
|
DEF(_vectorInit, "") {
|
||||||
int argc = pkGetArgc(vm);
|
int argc = pkGetArgc(vm);
|
||||||
if (!pkCheckArgcRange(vm, argc, 0, 3)) return;
|
if (!pkCheckArgcRange(vm, argc, 0, 3)) return;
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ void _vectorInit(PKVM* vm) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _vectorGetter(PKVM* vm) {
|
DEF(_vectorGetter, "") {
|
||||||
const char* name; uint32_t length;
|
const char* name; uint32_t length;
|
||||||
if (!pkValidateSlotString(vm, 1, &name, &length)) return;
|
if (!pkValidateSlotString(vm, 1, &name, &length)) return;
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ void _vectorGetter(PKVM* vm) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _vectorSetter(PKVM* vm) {
|
DEF(_vectorSetter, "") {
|
||||||
const char* name; uint32_t length;
|
const char* name; uint32_t length;
|
||||||
if (!pkValidateSlotString(vm, 1, &name, &length)) return;
|
if (!pkValidateSlotString(vm, 1, &name, &length)) return;
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ void _vectorSetter(PKVM* vm) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _vectorRepr(PKVM* vm) {
|
DEF(_vectorRepr, "") {
|
||||||
Vector* vec = pkGetSelf(vm);
|
Vector* vec = pkGetSelf(vm);
|
||||||
pkSetSlotStringFmt(vm, 0, "[%g, %g, %g]", vec->x, vec->y, vec->z);
|
pkSetSlotStringFmt(vm, 0, "[%g, %g, %g]", vec->x, vec->y, vec->z);
|
||||||
}
|
}
|
||||||
|
@ -42,5 +42,7 @@ assert(obj['quiz']['maths'] ==
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
assert(json.parse(json.print(obj)) == obj)
|
||||||
|
|
||||||
# If we got here, that means all test were passed.
|
# If we got here, that means all test were passed.
|
||||||
print('All TESTS PASSED')
|
print('All TESTS PASSED')
|
||||||
|
Loading…
Reference in New Issue
Block a user