mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-03-04 05:05:57 +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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,10 @@
|
||||
#include "gen/ext_term.pk.h" //<< AMALG_INLINE >>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <fcntl.h>
|
||||
#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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user