From 749456e215590218afa6dead42c1259007728ea3 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Fri, 11 Jun 2021 13:16:55 +0530 Subject: [PATCH] some (minor) code cleanups --- cli/main.c | 5 +- cli/utils.c | 6 +- cli/utils.h | 4 +- docs/TODO.txt | 7 +- src/include/pocketlang.h | 6 +- src/pk_core.c | 226 +++++++++++++++++++++++++++------------ src/pk_core.h | 2 + src/pk_utils.c | 8 +- src/pk_var.c | 18 ++-- src/pk_var.h | 22 +++- src/pk_vm.c | 46 ++++---- src/pk_vm.h | 22 ++-- 12 files changed, 245 insertions(+), 127 deletions(-) diff --git a/cli/main.c b/cli/main.c index b4d4cde..fdb4d9d 100644 --- a/cli/main.c +++ b/cli/main.c @@ -36,9 +36,10 @@ void errorFunction(PKVM* vm, PkErrorType type, const char* file, int line, bool repl = (ud) ? ud->repl_mode : false; if (type == PK_ERROR_COMPILE) { - + if (repl) fprintf(stderr, "Error: %s\n", message); - else fprintf(stderr, "Error: %s\n at \"%s\":%i\n", message, file, line); + else fprintf(stderr, "Error: %s\n at \"%s\":%i\n", + message, file, line); } else if (type == PK_ERROR_RUNTIME) { fprintf(stderr, "Error: %s\n", message); diff --git a/cli/utils.c b/cli/utils.c index 55995e8..1c9bd04 100644 --- a/cli/utils.c +++ b/cli/utils.c @@ -22,9 +22,9 @@ static inline int powerOf2Ceil(int n) { return n; } - /*****************************************************************************/ - /* BYTE BUFFER IMPLEMENTATION */ - /*****************************************************************************/ +/*****************************************************************************/ +/* BYTE BUFFER IMPLEMENTATION */ +/*****************************************************************************/ void byteBufferInit(ByteBuffer* buffer) { buffer->data = NULL; diff --git a/cli/utils.h b/cli/utils.h index 2d64b49..99086e3 100644 --- a/cli/utils.h +++ b/cli/utils.h @@ -21,10 +21,10 @@ void byteBufferInit(ByteBuffer* buffer); // Clears the allocated elements from the VM's realloc function. void byteBufferClear(ByteBuffer* buffer); -// Ensure the capacity is greater than [size], if not resize. +// Ensure the capacity is greater than [size], if not resize. void byteBufferReserve(ByteBuffer* buffer, size_t size); -// Fill the buffer at the end of it with provided data if the capacity +// Fill the buffer at the end of it with provided data if the capacity // isn't enough using VM's realloc function. void byteBufferFill(ByteBuffer* buffer, uint8_t data, int count); diff --git a/docs/TODO.txt b/docs/TODO.txt index 985a17b..5c9fa12 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -1,11 +1,12 @@ // To implement. +- Refactor fiber into a module and is_done as an attribute. + +- move moduleAddGlobalInternal() to var.h (also other var functions). + - refactor build.bat batch script to powershell refactor makefile and setup a github action. - -- check for tabs and trailing white space in source. - they're not allowed. - Hex, binary literals and floats like ".5". diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index faf0be5..f4b47e7 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -222,7 +222,11 @@ PK_PUBLIC PkVar pkGetHandleValue(const PkHandle* handle); // this for every handles before freeing the VM. PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle); -// Add a native function to the given script. If [arity] is -1 that means +// Add a global [value] to the given module. +PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module, + const char* name, PkHandle* value); + +// Add a native function to the given module. If [arity] is -1 that means // The function has variadic parameters and use pkGetArgc() to get the argc. PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, diff --git a/src/pk_core.c b/src/pk_core.c index cd916ac..4855c9b 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -13,6 +13,13 @@ #include "pk_var.h" #include "pk_vm.h" +// M_PI is non standard. The macro _USE_MATH_DEFINES defining before importing +// will define the constants for MSVC. But for a portable solution, +// we're defining it ourselves if it isn't already. +#ifndef M_PI + #define M_PI 3.14159265358979323846 +#endif + /*****************************************************************************/ /* CORE PUBLIC API */ /*****************************************************************************/ @@ -21,6 +28,10 @@ // internal. Which will be wrapped by pkNewModule to return a pkHandle*. static Script* newModuleInternal(PKVM* vm, const char* name); +// The internal function to add global value to a module. +static void moduleAddGlobalInternal(PKVM* vm, Script* script, + const char* name, Var value); + // The internal function to add functions to a module. static void moduleAddFunctionInternal(PKVM* vm, Script* script, const char* name, pkNativeFn fptr, @@ -32,6 +43,18 @@ PkHandle* pkNewModule(PKVM* vm, const char* name) { return vmNewHandle(vm, VAR_OBJ(module)); } +// pkModuleAddGlobal implementation (see pocketlang.h for description). +PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module, + const char* name, PkHandle* value) { + __ASSERT(module != NULL, "Argument module was NULL."); + __ASSERT(value != NULL, "Argument value was NULL."); + Var scr = module->value; + __ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module"); + + moduleAddGlobalInternal(vm, (Script*)AS_OBJ(scr), name, value->value); + +} + // pkModuleAddFunction implementation (see pocketlang.h for description). void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, pkNativeFn fptr, int arity) { @@ -88,7 +111,7 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module, #define RET_ERR(err) \ do { \ - vm->fiber->error = err; \ + VM_SET_ERROR(vm, err); \ RET(VAR_NULL); \ } while(false) @@ -102,12 +125,12 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module, } while (false) // Set error for incompatible type provided as an argument. -#define ERR_INVALID_ARG_TYPE(m_type) \ -do { \ - char buff[STR_INT_BUFF_SIZE]; \ - sprintf(buff, "%d", arg); \ - vm->fiber->error = stringFormat(vm, "Expected a " m_type \ - " at argument $.", buff); \ +#define ERR_INVALID_ARG_TYPE(m_type) \ +do { \ + char buff[STR_INT_BUFF_SIZE]; \ + sprintf(buff, "%d", arg); \ + VM_SET_ERROR(vm, stringFormat(vm, "Expected a " m_type \ + " at argument $.", buff)); \ } while (false) // pkGetArgc implementation (see pocketlang.h for description). @@ -175,8 +198,8 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) { Var val = ARG(arg); if (pkGetValueType((PkVar)&val) != type) { char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", arg); - vm->fiber->error = stringFormat(vm, - "Expected a $ at argument $.", getPkVarTypeName(type), buff); + VM_SET_ERROR(vm, stringFormat(vm, "Expected a $ at argument $.", + getPkVarTypeName(type), buff)); return false; } @@ -240,7 +263,7 @@ bool pkFiberIsDone(const PkHandle* fiber) { /* VALIDATORS */ /*****************************************************************************/ -// Check if a numeric value bool/number and set [value]. +// Check if [var] is a numeric value (bool/number) and set [value]. static inline bool isNumeric(Var var, double* value) { if (IS_NUM(var)) { *value = AS_NUM(var); @@ -253,17 +276,8 @@ static inline bool isNumeric(Var var, double* value) { return false; } -// Check if [var] is bool/number. If not set error and return false. -static inline bool validateNumeric(PKVM* vm, Var var, double* value, - const char* name) { - if (isNumeric(var, value)) return true; - vm->fiber->error = stringFormat(vm, "$ must be a numeric value.", name); - return false; -} - -// Check if [var] is 32 bit integer. If not set error and return false. -static inline bool validateInteger(PKVM* vm, Var var, int64_t* value, - const char* name) { +// Check if [var] is a numeric value (bool/number) and set [value]. +static inline bool isInteger(Var var, int64_t* value) { double number; if (isNumeric(var, &number)) { // TODO: check if the number is larger for a 64 bit integer. @@ -273,8 +287,22 @@ static inline bool validateInteger(PKVM* vm, Var var, int64_t* value, return true; } } + return false; +} - vm->fiber->error = stringFormat(vm, "$ must be a whole number.", name); +// Check if [var] is bool/number. If not set error and return false. +static inline bool validateNumeric(PKVM* vm, Var var, double* value, + const char* name) { + if (isNumeric(var, value)) return true; + VM_SET_ERROR(vm, stringFormat(vm, "$ must be a numeric value.", name)); + return false; +} + +// Check if [var] is 32 bit integer. If not set error and return false. +static inline bool validateInteger(PKVM* vm, Var var, int64_t* value, + const char* name) { + if (isInteger(var, value)) return true; + VM_SET_ERROR(vm, stringFormat(vm, "$ must be a whole number.", name)); return false; } @@ -283,7 +311,7 @@ static inline bool validateInteger(PKVM* vm, Var var, int64_t* value, static inline bool validateIndex(PKVM* vm, int64_t index, uint32_t size, const char* container) { if (index < 0 || size <= index) { - vm->fiber->error = stringFormat(vm, "$ index out of bound.", container); + VM_SET_ERROR(vm, stringFormat(vm, "$ index out of bound.", container)); return false; } return true; @@ -297,8 +325,8 @@ static inline bool validateIndex(PKVM* vm, int64_t index, uint32_t size, ASSERT(arg > 0 && arg <= ARGC, OOPS); \ if (!IS_OBJ(var) || AS_OBJ(var)->type != m_type) { \ char buff[12]; sprintf(buff, "%d", arg); \ - vm->fiber->error = stringFormat(vm, "Expected a " m_name \ - " at argument $.", buff, false); \ + VM_SET_ERROR(vm, stringFormat(vm, "Expected a " m_name \ + " at argument $.", buff, false)); \ } \ *value = (m_class*)AS_OBJ(var); \ return true; \ @@ -413,7 +441,7 @@ static void coreHex(PKVM* vm)) { *ptr++ = '0'; *ptr++ = 'x'; if (value > UINT32_MAX || value < -(int64_t)(UINT32_MAX)) { - vm->fiber->error = newString(vm, "Integer is too large."); + VM_SET_ERROR(vm, newString(vm, "Integer is too large.")); RET(VAR_NULL); } @@ -444,10 +472,10 @@ static void coreAssert(PKVM* vm)) { msg = (String*)AS_OBJ(ARG(2)); } vmPushTempRef(vm, &msg->_super); - vm->fiber->error = stringFormat(vm, "Assertion failed: '@'.", msg); + VM_SET_ERROR(vm, stringFormat(vm, "Assertion failed: '@'.", msg)); vmPopTempRef(vm); } else { - vm->fiber->error = newString(vm, "Assertion failed."); + VM_SET_ERROR(vm, newString(vm, "Assertion failed.")); } } } @@ -558,7 +586,7 @@ static void coreListAppend(PKVM* vm)) { if (!validateArgList(vm, 1, &list)) return; Var elem = ARG(2); - pkVarBufferWrite(&list->elements, vm, elem); + listAppend(vm, list, elem); RET(VAR_OBJ(list)); } @@ -691,11 +719,10 @@ static Script* newModuleInternal(PKVM* vm, const char* name) { return scr; } -// An internal function to add a function to the given [script]. -static void moduleAddFunctionInternal(PKVM* vm, Script* script, - const char* name, pkNativeFn fptr, - int arity) { - +// This will fail an assertion if a function or a global with the [name] +// already exists in the module. +static inline void assertModuleNameDef(PKVM* vm, Script* script, + const char* name) { // Check if function with the same name already exists. if (scriptGetFunc(script, name, (uint32_t)strlen(name)) != -1) { __ASSERT(false, stringFormat(vm, "A function named '$' already esists " @@ -707,6 +734,30 @@ static void moduleAddFunctionInternal(PKVM* vm, Script* script, __ASSERT(false, stringFormat(vm, "A global variable named '$' already " "esists on module '@'", name, script->moudle)->data); } +} + +// The internal function to add global value to a module. +static void moduleAddGlobalInternal(PKVM* vm, Script* script, + const char* name, Var value) { + + // Ensure the name isn't predefined. + assertModuleNameDef(vm, script, name); + + // TODO: move this to pk_var.h and use it in the compilerAddVariable + // function. + uint32_t name_index = scriptAddName(script, vm, name, + (uint32_t)strlen(name)); + pkUintBufferWrite(&script->global_names, vm, name_index); + pkVarBufferWrite(&script->globals, vm, value); +} + +// An internal function to add a function to the given [script]. +static void moduleAddFunctionInternal(PKVM* vm, Script* script, + const char* name, pkNativeFn fptr, + int arity) { + + // Ensure the name isn't predefined. + assertModuleNameDef(vm, script, name); Function* fn = newFunction(vm, name, (int)strlen(name), script, true); fn->native = fptr; @@ -810,8 +861,7 @@ PK_DOC( "hash(value:var) -> num\n" "Return the hash value of the variable, if it's not hashable it'll " "return null.", -static void stdMathHash(PKVM* vm)); -void stdMathHash(PKVM* vm) { +static void stdMathHash(PKVM* vm)) { if (IS_OBJ(ARG(1))) { if (!isObjectHashable(AS_OBJ(ARG(1))->type)) { RET(VAR_NULL); @@ -820,6 +870,16 @@ void stdMathHash(PKVM* vm) { RET(VAR_NUM((double)varHashValue(ARG(1)))); } +PK_DOC( + "sin(rad:num) -> num\n" + "Return the sine value of the argument [rad] which is an angle expressed " + "in radians.", +static void stdMathSine(PKVM* vm)) { + double rad; + if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return; + RET(VAR_NUM(sin(rad))); +} + /*****************************************************************************/ /* CORE INITIALIZATION */ /*****************************************************************************/ @@ -903,6 +963,17 @@ void initializeCore(PKVM* vm) { moduleAddFunctionInternal(vm, math, "abs", stdMathAbs, 1); moduleAddFunctionInternal(vm, math, "sign", stdMathSign, 1); moduleAddFunctionInternal(vm, math, "hash", stdMathHash, 1); + moduleAddFunctionInternal(vm, math, "sin", stdMathSine, 1); + // TODO: add - cos, tan. + // low priority: sinh, cosh, tanh, asin, acos, atan. + + // Note that currently it's mutable (since it's a global variable, not + // constant and pocketlang doesn't support constant) so the user shouldn't + // modify the PI, like in python. + // TODO: at varSetAttrib() we can detect if the user try to change an + // attribute of a core module and we can throw an error. + moduleAddGlobalInternal(vm, math, "PI", VAR_NUM(M_PI)); + } /*****************************************************************************/ @@ -910,8 +981,8 @@ void initializeCore(PKVM* vm) { /*****************************************************************************/ #define UNSUPPORT_OPERAND_TYPES(op) \ - vm->fiber->error = stringFormat(vm, "Unsupported operand types for " \ - "operator '" op "' $ and $", varTypeName(v1), varTypeName(v2)) + VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand types for " \ + "operator '" op "' $ and $", varTypeName(v1), varTypeName(v2))) Var varAdd(PKVM* vm, Var v1, Var v2) { @@ -937,10 +1008,10 @@ Var varAdd(PKVM* vm, Var v1, Var v2) { case OBJ_LIST: { if (o2->type == OBJ_LIST) { + List* l1 = (List*)o1, * l2 = (List*)o2; TODO; } - } - TODO; + } break; case OBJ_MAP: case OBJ_RANGE: @@ -1015,6 +1086,20 @@ Var varModulo(PKVM* vm, Var v1, Var v2) { return VAR_NULL; } +Var varBitAnd(PKVM* vm, Var v1, Var v2) { + + int64_t i1, i2; + if (isInteger(v1, &i1)) { + if (validateInteger(vm, v2, &i2, "Right operand")) { + return VAR_NUM(i1 & i2); + } + return VAR_NULL; + } + + UNSUPPORT_OPERAND_TYPES("&"); + return VAR_NULL; +} + bool varGreater(Var v1, Var v2) { double d1, d2; if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) { @@ -1058,16 +1143,15 @@ bool varLesser(Var v1, Var v2) { #define CASE_ATTRIB(name, hash) case hash // Set error for accessing non-existed attribute. -#define ERR_NO_ATTRIB(on) \ - vm->fiber->error = stringFormat(vm, "'$' object has no attribute " \ - "named '$'", \ - varTypeName(on), attrib->data); +#define ERR_NO_ATTRIB(vm, on, attrib) \ + VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'", \ + varTypeName(on), attrib->data)) Var varGetAttrib(PKVM* vm, Var on, String* attrib) { if (!IS_OBJ(on)) { - vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", - varTypeName(on)); + VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on))); return VAR_NULL; } @@ -1091,7 +1175,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { return VAR_OBJ(stringStrip(vm, str)); default: - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return VAR_NULL; } @@ -1107,7 +1191,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { return VAR_NUM((double)(list->elements.count)); default: - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return VAR_NULL; } @@ -1132,7 +1216,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { return VAR_OBJ(rangeAsList(vm, range)); default: - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return VAR_NULL; } @@ -1156,7 +1240,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { return scr->globals.data[index]; } - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return VAR_NULL; } @@ -1172,7 +1256,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { return VAR_OBJ(newString(vm, fn->name)); default: - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return VAR_NULL; } UNREACHABLE(); @@ -1195,14 +1279,14 @@ void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) { #define ATTRIB_IMMUTABLE(name) \ do { \ if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \ - vm->fiber->error = stringFormat(vm, "'$' attribute is immutable.", name); \ + VM_SET_ERROR(vm, stringFormat(vm, "'$' attribute is immutable.", name)); \ return; \ } \ } while (false) if (!IS_OBJ(on)) { - vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", - varTypeName(on)); + VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on))); return; } @@ -1213,12 +1297,12 @@ do { \ ATTRIB_IMMUTABLE("lower"); ATTRIB_IMMUTABLE("upper"); ATTRIB_IMMUTABLE("strip"); - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return; case OBJ_LIST: ATTRIB_IMMUTABLE("length"); - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return; case OBJ_MAP: @@ -1228,12 +1312,12 @@ do { \ // map.foo = 'bar' TODO; - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return; case OBJ_RANGE: ATTRIB_IMMUTABLE("as_list"); - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return; case OBJ_SCRIPT: { @@ -1255,22 +1339,22 @@ do { \ return; } - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return; } case OBJ_FUNC: ATTRIB_IMMUTABLE("arity"); ATTRIB_IMMUTABLE("name"); - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return; case OBJ_FIBER: - ERR_NO_ATTRIB(on); + ERR_NO_ATTRIB(vm, on, attrib); return; case OBJ_USER: - TODO; //ERR_NO_ATTRIB(on); + TODO; //ERR_NO_ATTRIB(vm, on, attrib); return; default: @@ -1284,8 +1368,8 @@ do { \ Var varGetSubscript(PKVM* vm, Var on, Var key) { if (!IS_OBJ(on)) { - vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", - varTypeName(on)); + VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on))); return VAR_NULL; } @@ -1326,9 +1410,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { String* key_str = toString(vm, key); vmPushTempRef(vm, &key_str->_super); if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) { - vm->fiber->error = stringFormat(vm, "Invalid key '@'.", key_str); + VM_SET_ERROR(vm, stringFormat(vm, "Invalid key '@'.", key_str)); } else { - vm->fiber->error = stringFormat(vm, "Key '@' not exists", key_str); + VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_str)); } vmPopTempRef(vm); return VAR_NULL; @@ -1352,15 +1436,15 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { if (!IS_OBJ(on)) { - vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", - varTypeName(on)); + VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on))); return; } Object* obj = AS_OBJ(on); switch (obj->type) { case OBJ_STRING: - vm->fiber->error = newString(vm, "String objects are immutable."); + VM_SET_ERROR(vm, newString(vm, "String objects are immutable.")); return; case OBJ_LIST: @@ -1376,8 +1460,8 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { case OBJ_MAP: { if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) { - vm->fiber->error = stringFormat(vm, "$ type is not hashable.", - varTypeName(key)); + VM_SET_ERROR(vm, stringFormat(vm, "$ type is not hashable.", + varTypeName(key))); } else { mapSet(vm, (Map*)obj, key, value); } diff --git a/src/pk_core.h b/src/pk_core.h index 33a674c..80abc51 100644 --- a/src/pk_core.h +++ b/src/pk_core.h @@ -36,6 +36,8 @@ Var varMultiply(PKVM* vm, Var v1, Var v2); // Returns v1 * v2. Var varDivide(PKVM* vm, Var v1, Var v2); // Returns v1 / v2. Var varModulo(PKVM* vm, Var v1, Var v2); // Returns v1 % v2. +Var varBitAnd(PKVM* vm, Var v1, Var v2); // Returns v1 & v2. + bool varGreater(Var v1, Var v2); // Returns v1 > v2. bool varLesser(Var v1, Var v2); // Returns v1 < v2. diff --git a/src/pk_utils.c b/src/pk_utils.c index 7f0cc62..66d7a3a 100644 --- a/src/pk_utils.c +++ b/src/pk_utils.c @@ -146,10 +146,10 @@ int utf8_encodeValue(int value, uint8_t* bytes) { // to 4th byte, next 6 bits to 3rd byte, next 6 bits to 2nd byte, 3 bits // first byte. if (value <= 0x10ffff) { - *(bytes++) = (uint8_t)(0b11110000 | ((value & 0b111000000000000000000) >> 18)); - *(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000000000) >> 12)); - *(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000) >> 6)); - *(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111))); + *(bytes++) = (uint8_t)(0b11110000 | ((value & (0b111 << 18)) >> 18)); + *(bytes++) = (uint8_t)(0b10000000 | ((value & (0b111111 << 12)) >> 12)); + *(bytes++) = (uint8_t)(0b10000000 | ((value & (0b111111 << 6)) >> 6)); + *(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111))); return 4; } diff --git a/src/pk_var.c b/src/pk_var.c index d59946a..a596559 100644 --- a/src/pk_var.c +++ b/src/pk_var.c @@ -58,7 +58,7 @@ PkHandle* pkNewMap(PKVM* vm) { PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) { __ASSERT(IS_OBJ_TYPE(fn->value, OBJ_FUNC), "Fn should be of type function."); - + Fiber* fiber = newFiber(vm, (Function*)AS_OBJ(fn->value)); return vmNewHandle(vm, VAR_OBJ(fiber)); } @@ -362,7 +362,7 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner, uint32_t name_index = scriptAddName(owner, vm, name, length); pkUintBufferWrite(&owner->function_names, vm, name_index); vmPopTempRef(vm); - + func->name = owner->names.data[name_index]->data; func->owner = owner; func->arity = -2; // -1 means variadic args. @@ -488,10 +488,10 @@ String* stringUpper(PKVM* vm, String* self) { String* stringStrip(PKVM* vm, String* self) { // Implementation: - // + // // " a string with leading and trailing white space " // ^start >> << end^ - // + // // These 'start' and 'end' pointers will move respectively right and left // while it's a white space and return an allocated string from 'start' with // length of (end - start + 1). For already trimed string it'll not allocate @@ -558,7 +558,7 @@ Var listRemoveAt(PKVM* vm, List* self, uint32_t index) { // Return a has value for the object. static uint32_t _hashObject(Object* obj) { - + ASSERT(isObjectHashable(obj->type), "Check if it's hashable before calling this method."); @@ -646,7 +646,7 @@ static bool _mapFindEntry(Map* self, Var key, MapEntry** result) { *result = entry; return true; } - + index = (index + 1) % self->capacity; } while (index != start_index); @@ -658,7 +658,7 @@ static bool _mapFindEntry(Map* self, Var key, MapEntry** result) { return false; } -// Add the key, value pair to the entries array of the map. Returns true if +// Add the key, value pair to the entries array of the map. Returns true if // the entry added for the first time and false for replaced vlaue. static bool _mapInsertEntry(Map* self, Var key, Var value) { @@ -1198,7 +1198,7 @@ bool toBool(Var v) { String* stringFormat(PKVM* vm, const char* fmt, ...) { va_list arg_list; - // Calculate the total length of the resulting string. This is required to + // Calculate the total length of the resulting string. This is required to // determine the final string size to allocate. va_start(arg_list, fmt); size_t total_length = 0; @@ -1269,7 +1269,7 @@ String* stringJoin(PKVM* vm, String* str1, String* str2) { } uint32_t scriptAddName(Script* self, PKVM* vm, const char* name, - uint32_t length) { + uint32_t length) { for (uint32_t i = 0; i < self->names.count; i++) { String* _name = self->names.data[i]; diff --git a/src/pk_var.h b/src/pk_var.h index c30cb92..e3aa95e 100644 --- a/src/pk_var.h +++ b/src/pk_var.h @@ -17,7 +17,7 @@ * Reference: * https://github.com/wren-lang/wren/blob/main/src/vm/wren_value.h * https://leonardschuetz.ch/blog/nan-boxing/ - * + * * The previous implementation was to add a type field to every var and use * smart pointers(C++17) to object with custom destructors, which makes the * programme inefficient for small types such null, bool, int and float. @@ -56,7 +56,7 @@ * * v~~~~~~~~~~ NaN value * -11111111111---------------------------------------------------- - * + * * We define a our variant \ref var as an unsigned 64 bit integer (we treat it * like a bit array) if the exponent bits were not set, just reinterprit it as * a IEEE 754 double precision 64 bit number. Other wise we there are a lot of @@ -69,14 +69,14 @@ * * v~Highest mestissa bit * -[NaN ]1--------------------------------------------------- - * + * * if sign bit set, it's a heap allocated pointer. * | these 2 bits are type tags representing 8 different types * | vv * S[NaN ]1cXX------------------------------------------------ * | ^~~~~~~~ 48 bits to represent the value (51 for pointer) * '- if this (const) bit set, it's a constant. - * + * * On a 32-bit machine a pointer size is 32 and on a 64-bit machine actually 48 * bits are used for pointers. Ta-da, now we have double precision number, * primitives, pointers all inside a 64 bit sequence and for numbers it doesn't @@ -153,7 +153,7 @@ #else -// TODO: Union tagging implementation of all the above macros ignore macros +// TODO: Union tagging implementation of all the above macros ignore macros // starts with an underscore. typedef enum { @@ -466,6 +466,18 @@ String* stringUpper(PKVM* vm, String* self); // If the string is already trimed it'll return the same string. String* stringStrip(PKVM* vm, String* self); +// An inline function/macro implementation of listAppend(). Set below 0 to 1, +// to make the implementation a static inline function, it's totally okey to +// define a function inside a header as long as it's static (but not a fan). +#if 0 // Function implementation. + static inline void listAppend(PKVM* vm, List* self, Var value) { + pkVarBufferWrite(&self->elements, vm, value); + } +#else // Macro implementation. + #define listAppend(vm, self, value) \ + pkVarBufferWrite(&self->elements, vm, value) +#endif + // Insert [value] to the list at [index] and shift down the rest of the // elements. void listInsert(PKVM* vm, List* self, uint32_t index, Var value); diff --git a/src/pk_vm.c b/src/pk_vm.c index 2a5a9ec..d04c7ea 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -10,9 +10,6 @@ #include "pk_utils.h" #include "pk_debug.h" -// Evaluated to "true" if a runtime error set on the current fiber. -#define HAS_ERROR() (vm->fiber->error != NULL) - /*****************************************************************************/ /* VM PUBLIC API */ /*****************************************************************************/ @@ -127,7 +124,7 @@ void pkReleaseHandle(PKVM* vm, PkHandle* handle) { DEALLOCATE(vm, handle); } -// This function is responsible to call on_done function if it's done with the +// This function is responsible to call on_done function if it's done with the // provided string pointers. PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path, const PkCompileOptions* options) { @@ -195,7 +192,7 @@ PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value) { void pkSetRuntimeError(PKVM* vm, const char* message) { __ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); - vm->fiber->error = newString(vm, message); + VM_SET_ERROR(vm, newString(vm, message)); } /*****************************************************************************/ @@ -217,7 +214,7 @@ void* vmRealloc(PKVM* vm, void* memory, size_t old_size, size_t new_size) { // TODO: Debug trace allocations here. // Track the total allocated memory of the VM to trigger the GC. - // if vmRealloc is called for freeing, the old_size would be 0 since + // if vmRealloc is called for freeing, the old_size would be 0 since // deallocated bytes are traced by garbage collector. vm->bytes_allocated += new_size - old_size; @@ -315,7 +312,7 @@ void vmCollectGarbage(PKVM* vm) { #define _ERR_FAIL(msg) \ do { \ - if (vm->fiber != NULL) vm->fiber->error = msg; \ + if (vm->fiber != NULL) VM_SET_ERROR(vm, msg); \ return false; \ } while (false) @@ -491,7 +488,7 @@ static inline void growStack(PKVM* vm, int size) { sizeof(Var) * fiber->stack_size, sizeof(Var) * new_size); fiber->stack_size = new_size; - + // If the old stack base pointer is the same as the current, that means the // stack hasn't been moved by the reallocation. In that case we're done. if (old_rbp == fiber->stack) return; @@ -549,7 +546,7 @@ static inline void pushCallFrame(PKVM* vm, const Function* fn) { } static void reportError(PKVM* vm) { - ASSERT(HAS_ERROR(), "runtimeError() should be called after an error."); + ASSERT(VM_HAS_ERROR(vm), "runtimeError() should be called after an error."); // TODO: pass the error to the caller of the fiber. // Print the Error message and stack trace. @@ -607,7 +604,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { // Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR. #define CHECK_ERROR() \ do { \ - if (HAS_ERROR()) { \ + if (VM_HAS_ERROR(vm)) { \ UPDATE_FRAME(); \ reportError(vm); \ FIBER_SWITCH_BACK(); \ @@ -618,7 +615,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { // [err_msg] must be of type String. #define RUNTIME_ERROR(err_msg) \ do { \ - vm->fiber->error = err_msg; \ + VM_SET_ERROR(vm, err_msg); \ UPDATE_FRAME(); \ reportError(vm); \ FIBER_SWITCH_BACK(); \ @@ -728,7 +725,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { OPCODE(MAP_INSERT): { Var value = PEEK(-1); // Don't pop yet, we need the reference for gc. - Var key = PEEK(-2); // Don't pop yet, we need the reference for gc. + Var key = PEEK(-2); // Don't pop yet, we need the reference for gc. Var on = PEEK(-3); ASSERT(IS_OBJ_TYPE(on, OBJ_MAP), OOPS); @@ -962,7 +959,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { if (iter >= elems->count) JUMP_ITER_EXIT(); *value = elems->data[iter]; *iterator = VAR_NUM((double)iter + 1); - + } DISPATCH(); case OBJ_MAP: { @@ -978,7 +975,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { *value = map->entries[iter].key; *iterator = VAR_NUM((double)iter + 1); - + } DISPATCH(); case OBJ_RANGE: { @@ -995,7 +992,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { if (current == to) JUMP_ITER_EXIT(); *value = VAR_NUM(current); *iterator = VAR_NUM(it + 1); - + } DISPATCH(); case OBJ_SCRIPT: @@ -1079,7 +1076,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { OPCODE(GET_ATTRIB): { - Var on = PEEK(-1); // Don't pop yet, we need the reference for gc. + Var on = PEEK(-1); // Don't pop yet, we need the reference for gc. String* name = script->names.data[READ_SHORT()]; Var value = varGetAttrib(vm, on, name); DROP(); // on @@ -1100,8 +1097,8 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { OPCODE(SET_ATTRIB): { - Var value = PEEK(-1); // Don't pop yet, we need the reference for gc. - Var on = PEEK(-2); // Don't pop yet, we need the reference for gc. + Var value = PEEK(-1); // Don't pop yet, we need the reference for gc. + Var on = PEEK(-2); // Don't pop yet, we need the reference for gc. String* name = script->names.data[READ_SHORT()]; varSetAttrib(vm, on, name, value); @@ -1233,7 +1230,18 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { DISPATCH(); } - OPCODE(BIT_AND): + OPCODE(BIT_AND) : + { + // Don't pop yet, we need the reference for gc. + Var r = PEEK(-1), l = PEEK(-2); + Var value = varBitAnd(vm, l, r); + DROP(); DROP(); // r, l + PUSH(value); + + CHECK_ERROR(); + DISPATCH(); + } + OPCODE(BIT_OR): OPCODE(BIT_XOR): OPCODE(BIT_LSHIFT): diff --git a/src/pk_vm.h b/src/pk_vm.h index 992ffa4..0105007 100644 --- a/src/pk_vm.h +++ b/src/pk_vm.h @@ -27,7 +27,7 @@ // The allocated size the'll trigger the first GC. (~10MB). #define INITIAL_GC_SIZE (1024 * 1024 * 10) -// The heap size might shrink if the remaining allocated bytes after a GC +// The heap size might shrink if the remaining allocated bytes after a GC // is less than the one before the last GC. So we need a minimum size. #define MIN_HEAP_SIZE (1024 * 1024) @@ -35,6 +35,12 @@ // allocated so far plus the fill factor of it. #define HEAP_FILL_PERCENT 75 +// Evaluated to "true" if a runtime error set on the current fiber. +#define VM_HAS_ERROR(vm) (vm->fiber->error != NULL) + +// Set the error message [err] to the [vm]'s current fiber. +#define VM_SET_ERROR(vm, err) (vm->fiber->error = err) + // Builtin functions are stored in an array in the VM (unlike script functions // they're member of function buffer of the script) and this struct is a single // entry of the array. @@ -130,16 +136,16 @@ PkHandle* vmNewHandle(PKVM* vm, Var value); // Trigger garbage collection. This is an implementation of mark and sweep // garbage collection (https://en.wikipedia.org/wiki/Tracing_garbage_collection). -// +// // 1. MARKING PHASE -// +// // | | // | [obj0] -+---> [obj2] -> [obj6] .------- Garbage --------. // | [obj3] | | | | // | [obj8] | '-----> [obj1] | [obj7] ---> [obj5] | // '----------' | [obj4] | // working set '------------------------' -// +// // First we preform a tree traversel from all the vm's root objects. such as // stack values, temp references, handles, vm's running fiber, current // compiler (if it has any) etc. Mark them (ie. is_marked = true) and add @@ -147,23 +153,23 @@ PkHandle* vmNewHandle(PKVM* vm, Var value); // working set add all of it's referenced objects to the working set and mark // it black (try-color marking) We'll keep doing this till the working set // become empty, at this point any object which isn't marked is a garbage. -// +// // Every single heap allocated objects will be in the VM's link list. Those // objects which are reachable have marked (ie. is_marked = true) once the // marking phase is completed. -// .----------------. +// .----------------. // | VM | // | Object* first -+--------> [obj8] -> [obj7] -> [obj6] ... [obj0] -> NULL // '----------------' marked = true false true true // // 2. SWEEPING PHASE -// +// // .----------------. .-------------. // | VM | | V // | Object* first -+--------> [obj8] [obj7] [obj6] ... [obj0] -> NULL // '----------------' marked = true false true true // '--free()--' -// +// // Once the marking phase is done, we iterate through the objects and remove // the objects which are not marked from the linked list and deallocate them. //