From 2b940b227d46395663cfa468799ffb7d984ff346 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Fri, 3 Jun 2022 23:07:56 +0530 Subject: [PATCH] minor enhancements/improvements - json print implemented - some bugs in io module fixed --- src/core/vm.c | 10 ++-- src/libs/ext_term.c | 17 +++++++ src/libs/std_io.c | 18 ++++--- src/libs/std_json.c | 111 ++++++++++++++++++++++++++++++++++++++++++ src/libs/std_types.c | 32 ++++++------ tests/modules/json.pk | 2 + 6 files changed, 164 insertions(+), 26 deletions(-) diff --git a/src/core/vm.c b/src/core/vm.c index 3189ac9..62a3f59 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -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) { char buff[STR_INT_BUFF_SIZE]; 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) { @@ -1245,7 +1246,8 @@ L_do_call: // No constructor is defined on the class. Just return self. if (closure == NULL) { 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); } @@ -1265,8 +1267,8 @@ L_do_call: // -1 argument means multiple number of args. if (closure->fn->arity != -1 && closure->fn->arity != argc) { char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", closure->fn->arity); - String* msg = stringFormat(vm, "Expected exactly $ argument(s).", - buff); + String* msg = stringFormat(vm, "Expected exactly $ argument(s) " + "for function $", buff, closure->fn->name); RUNTIME_ERROR(msg); } diff --git a/src/libs/ext_term.c b/src/libs/ext_term.c index 10df26b..eda4a6a 100644 --- a/src/libs/ext_term.c +++ b/src/libs/ext_term.c @@ -9,6 +9,10 @@ #include "gen/ext_term.pk.h" //<< AMALG_INLINE >> #endif +#ifdef _WIN32 +#include +#endif + #define TERM_IMPLEMENT #include "thirdparty/term/term.h" //<< AMALG_INLINE >> #undef TERM_IMPLEMENT @@ -229,6 +233,14 @@ void _termReadEvent(PKVM* vm) { 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 */ /*****************************************************************************/ @@ -252,6 +264,11 @@ void registerModuleTerm(PKVM* vm) { 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); pkReleaseHandle(vm, term); } diff --git a/src/libs/std_io.c b/src/libs/std_io.c index 1cd89c9..b2017a6 100644 --- a/src/libs/std_io.c +++ b/src/libs/std_io.c @@ -35,14 +35,12 @@ DEF(_ioWrite, pkSetRuntimeError(vm, "Cannot write to stdin."); 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: - fprintf(stdout, "%s", bytes); + fwrite(bytes, sizeof(char), length, stdout); return; case 2: - fprintf(stderr, "%s", bytes); + fwrite(bytes, sizeof(char), length, stderr); return; } } @@ -54,6 +52,13 @@ DEF(_ioFlush, 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 */ /*****************************************************************************/ @@ -81,8 +86,8 @@ typedef enum { FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND), FMODE_READ_BIN = (_FMODE_BIN | FMODE_READ), - FMODE_WRITE_BIN = (_FMODE_BIN | FMODE_READ), - FMODE_APPEND_BIN = (_FMODE_BIN | FMODE_READ), + FMODE_WRITE_BIN = (_FMODE_BIN | FMODE_WRITE), + FMODE_APPEND_BIN = (_FMODE_BIN | FMODE_APPEND), FMODE_READ_BIN_EXT = (_FMODE_BIN | FMODE_READ_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, "flush", _ioFlush, 0); + pkModuleAddFunction(vm, io, "getc", _ioGetc, 0); PkHandle* cls_file = pkNewClass(vm, "File", NULL, io, _fileNew, _fileDelete); pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1); diff --git a/src/libs/std_json.c b/src/libs/std_json.c index 0e6d998..50c7f57 100644 --- a/src/libs/std_json.c +++ b/src/libs/std_json.c @@ -82,6 +82,83 @@ static Var _cJsonToPocket(PKVM* vm, cJSON* item) { 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, "json.parse(json_str:String) -> var\n" "Parse a json string into pocket lang object.") { @@ -108,6 +185,39 @@ DEF(_jsonParse, 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 */ /*****************************************************************************/ @@ -116,6 +226,7 @@ void registerModuleJson(PKVM* vm) { PkHandle* json = pkNewModule(vm, "json"); pkModuleAddFunction(vm, json, "parse", _jsonParse, 1); + pkModuleAddFunction(vm, json, "print", _jsonPrint, -1); pkRegisterModule(vm, json); pkReleaseHandle(vm, json); diff --git a/src/libs/std_types.c b/src/libs/std_types.c index 2343701..7ff10db 100644 --- a/src/libs/std_types.c +++ b/src/libs/std_types.c @@ -46,17 +46,17 @@ DEF(_typesHash, /* BYTE BUFFER */ /*****************************************************************************/ -void* _bytebuffNew(PKVM* vm) { +static void* _bytebuffNew(PKVM* vm) { pkByteBuffer* self = pkRealloc(vm, NULL, sizeof(pkByteBuffer)); pkByteBufferInit(self); return self; } -void _bytebuffDelete(PKVM* vm, void* buff) { +static void _bytebuffDelete(PKVM* vm, void* buff) { pkRealloc(vm, buff, 0); } -void _bytebuffReserve(PKVM* vm) { +DEF(_bytebuffReserve, "") { double size; if (!pkValidateSlotNumber(vm, 1, &size)) return; @@ -65,7 +65,7 @@ void _bytebuffReserve(PKVM* vm) { } // buff.fill(data, count) -void _bytebuffFill(PKVM* vm) { +DEF(_bytebuffFill, "") { uint32_t n; if (!pkValidateSlotInteger(vm, 1, &n)) return; if (n < 0x00 || n > 0xff) { @@ -81,14 +81,14 @@ void _bytebuffFill(PKVM* vm) { pkByteBufferFill(self, vm, (uint8_t) n, (int) count); } -void _bytebuffClear(PKVM* vm) { +DEF(_bytebuffClear, "") { // TODO: Should I also zero or reduce the capacity? pkByteBuffer* self = pkGetSelf(vm); self->count = 0; } // Returns the length of bytes were written. -void _bytebuffWrite(PKVM* vm) { +DEF(_bytebuffWrite, "") { pkByteBuffer* self = pkGetSelf(vm); PkVarType type = pkGetSlotType(vm, 1); @@ -135,7 +135,7 @@ void _bytebuffWrite(PKVM* vm) { } -void _bytebuffSubscriptGet(PKVM* vm) { +DEF(_bytebuffSubscriptGet, "") { double index; if (!pkValidateSlotNumber(vm, 1, &index)) return; if (floor(index) != index) { @@ -154,7 +154,7 @@ void _bytebuffSubscriptGet(PKVM* vm) { } -void _bytebuffSubscriptSet(PKVM* vm) { +DEF(_bytebuffSubscriptSet, "") { double index, value; if (!pkValidateSlotNumber(vm, 1, &index)) 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); pkSetSlotStringLength(vm, 0, self->data, self->count); } -void _bytebuffCount(PKVM* vm) { +DEF(_bytebuffCount, "") { pkByteBuffer* self = pkGetSelf(vm); pkSetSlotNumber(vm, 0, self->count); } @@ -202,17 +202,17 @@ typedef struct { double x, y, z; } Vector; -void* _vectorNew(PKVM* vm) { +static void* _vectorNew(PKVM* vm) { Vector* vec = pkRealloc(vm, NULL, sizeof(Vector)); memset(vec, 0, sizeof(Vector)); return vec; } -void _vectorDelete(PKVM* vm, void* vec) { +static void _vectorDelete(PKVM* vm, void* vec) { pkRealloc(vm, vec, 0); } -void _vectorInit(PKVM* vm) { +DEF(_vectorInit, "") { int argc = pkGetArgc(vm); 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; 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; 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); pkSetSlotStringFmt(vm, 0, "[%g, %g, %g]", vec->x, vec->y, vec->z); } diff --git a/tests/modules/json.pk b/tests/modules/json.pk index e2fae82..2b5981f 100644 --- a/tests/modules/json.pk +++ b/tests/modules/json.pk @@ -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. print('All TESTS PASSED')