From ddc8bd8197c43cd0d9908b7d64dda53e454d3446 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Fri, 15 Apr 2022 09:49:47 +0530 Subject: [PATCH] yet another refactor. This commit doesn't change any behavior, just moving the code around cleaning things. --- cli/common.h | 44 +--- src/include/pocketlang.h | 7 + src/pk_common.h | 44 +--- src/pk_compiler.c | 26 ++- src/pk_core.c | 482 ++++++++++++++++----------------------- src/pk_internal.h | 43 +++- src/pk_value.c | 4 +- src/pk_value.h | 9 +- tests/check.py | 33 +-- tests/tests.py | 1 + 10 files changed, 268 insertions(+), 425 deletions(-) diff --git a/cli/common.h b/cli/common.h index 7cd37b2..7b50d00 100644 --- a/cli/common.h +++ b/cli/common.h @@ -28,8 +28,7 @@ // The internal assertion macro, this will print error and break regardless of // the build target (debug or release). Use ASSERT() for debug assertion and -// use __ASSERT() for TODOs and assertions in public methods (to indicate that -// the host application did something wrong). +// use __ASSERT() for TODOs. #define __ASSERT(condition, message) \ do { \ if (!(condition)) { \ @@ -111,45 +110,4 @@ #define TODO __ASSERT(false, "TODO: It hasn't implemented yet.") #define OOPS "Oops a bug!! report please." -// The formated string to convert double to string. It'll be with the minimum -// length string representation of either a regular float or a scientific -// notation (at most 15 decimal points). -// Reference: https://www.cplusplus.com/reference/cstdio/printf/ -#define DOUBLE_FMT "%.16g" - -// Double number to string buffer size, used in sprintf with DOUBLE_FMT. -// A largest number : "-1.234567890123456e+308" -// + 1 fot sign '+' or '-' -// + 16 fot significant digits -// + 1 for decimal point '.' -// + 1 for exponent char 'e' -// + 1 for sign of exponent -// + 3 for the exponent digits -// + 1 for null byte '\0' -#define STR_DBL_BUFF_SIZE 24 - -// Integer number to string buffer size, used in sprintf with format "%d". -// The minimum 32 bit integer = -2147483648 -// + 1 for sign '-' -// + 10 for digits -// + 1 for null byte '\0' -#define STR_INT_BUFF_SIZE 12 - -// Integer number (double) to hex string buffer size. -// The maximum value an unsigned 64 bit integer can get is -// 0xffffffffffffffff which is 16 characters. -// + 16 for hex digits -// + 1 for sign '-' -// + 2 for '0x' prefix -// + 1 for null byte '\0' -#define STR_HEX_BUFF_SIZE 20 - -// Integer number (double) to bin string buffer size. -// The maximum value an unsigned 64 bit integer can get is 0b11111... 64 1s. -// + 64 for bin digits -// + 1 for sign '-' -// + 2 for '0b' prefix -// + 1 for null byte '\0' -#define STR_BIN_BUFF_SIZE 68 - #endif //PK_COMMON_H diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index c742d25..abf4d69 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -242,8 +242,15 @@ PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle); PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module, const char* name, PkHandle* value); +// Returns the global value with the [name] in the given [module], if the name +// not exists in the globals of the module, it'll return NULL. +PK_PUBLIC PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module, + const char* name); + // 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. +// Note that the function will be added as a global variable of the module, +// to retrieve the function use pkModuleGetGlobal(). PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, pkNativeFn fptr, int arity); diff --git a/src/pk_common.h b/src/pk_common.h index 7cd37b2..7b50d00 100644 --- a/src/pk_common.h +++ b/src/pk_common.h @@ -28,8 +28,7 @@ // The internal assertion macro, this will print error and break regardless of // the build target (debug or release). Use ASSERT() for debug assertion and -// use __ASSERT() for TODOs and assertions in public methods (to indicate that -// the host application did something wrong). +// use __ASSERT() for TODOs. #define __ASSERT(condition, message) \ do { \ if (!(condition)) { \ @@ -111,45 +110,4 @@ #define TODO __ASSERT(false, "TODO: It hasn't implemented yet.") #define OOPS "Oops a bug!! report please." -// The formated string to convert double to string. It'll be with the minimum -// length string representation of either a regular float or a scientific -// notation (at most 15 decimal points). -// Reference: https://www.cplusplus.com/reference/cstdio/printf/ -#define DOUBLE_FMT "%.16g" - -// Double number to string buffer size, used in sprintf with DOUBLE_FMT. -// A largest number : "-1.234567890123456e+308" -// + 1 fot sign '+' or '-' -// + 16 fot significant digits -// + 1 for decimal point '.' -// + 1 for exponent char 'e' -// + 1 for sign of exponent -// + 3 for the exponent digits -// + 1 for null byte '\0' -#define STR_DBL_BUFF_SIZE 24 - -// Integer number to string buffer size, used in sprintf with format "%d". -// The minimum 32 bit integer = -2147483648 -// + 1 for sign '-' -// + 10 for digits -// + 1 for null byte '\0' -#define STR_INT_BUFF_SIZE 12 - -// Integer number (double) to hex string buffer size. -// The maximum value an unsigned 64 bit integer can get is -// 0xffffffffffffffff which is 16 characters. -// + 16 for hex digits -// + 1 for sign '-' -// + 2 for '0x' prefix -// + 1 for null byte '\0' -#define STR_HEX_BUFF_SIZE 20 - -// Integer number (double) to bin string buffer size. -// The maximum value an unsigned 64 bit integer can get is 0b11111... 64 1s. -// + 64 for bin digits -// + 1 for sign '-' -// + 2 for '0b' prefix -// + 1 for null byte '\0' -#define STR_BIN_BUFF_SIZE 68 - #endif //PK_COMMON_H diff --git a/src/pk_compiler.c b/src/pk_compiler.c index cded684..cb4a16e 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -607,6 +607,16 @@ static void resolveError(Compiler* compiler, int line, const char* fmt, ...) { va_end(args); } +// Check if the given [index] is greater than or equal to the maximum constants +// that a module can contain and report an error. +static void checkMaxConstantsReached(Compiler* compiler, int index) { + ASSERT(index >= 0, OOPS); + if (index >= MAX_CONSTANTS) { + parseError(compiler, "A module should contain at most %d " + "unique constants.", MAX_CONSTANTS); + } +} + /*****************************************************************************/ /* LEXING */ /*****************************************************************************/ @@ -2085,10 +2095,7 @@ static int compilerAddConstant(Compiler* compiler, Var value) { uint32_t index = moduleAddConstant(compiler->parser.vm, compiler->module, value); - if (index >= MAX_CONSTANTS) { - parseError(compiler, "A module should contain at most %d " - "unique constants.", MAX_CONSTANTS); - } + checkMaxConstantsReached(compiler, index); return (int)index; } @@ -2289,10 +2296,8 @@ static void compileClass(Compiler* compiler) { moduleSetGlobal(compiler->module, index, VAR_OBJ(cls)); // Check count exceeded. - if (cls_index >= MAX_CONSTANTS || ctor_index >= MAX_CONSTANTS) { - parseError(compiler, "A module should contain at most %d " - "unique constants.", MAX_CONSTANTS); - } + checkMaxConstantsReached(compiler, cls_index); + checkMaxConstantsReached(compiler, ctor_index); // Compile the constructor function. ASSERT(compiler->func->ptr == compiler->module->body->fn, OOPS); @@ -2371,10 +2376,7 @@ static void compileFunction(Compiler* compiler, bool is_literal) { int fn_index; Function* func = newFunction(compiler->parser.vm, name, name_length, compiler->module, false, NULL, &fn_index); - if (fn_index >= MAX_CONSTANTS) { - parseError(compiler, "A module should contain at most %d " - "unique constants.", MAX_CONSTANTS); - } + checkMaxConstantsReached(compiler, fn_index); if (!is_literal) { ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS); diff --git a/src/pk_core.c b/src/pk_core.c index 20071b3..2b8847d 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -32,10 +32,6 @@ static const char* DOCSTRING(fn) = docstring; \ static void fn(PKVM* vm) -/*****************************************************************************/ -/* CORE PUBLIC API */ -/*****************************************************************************/ - // Create a new module with the given [name] and returns as a Module* for // internal. Which will be wrapped by pkNewModule to return a pkHandle*. static Module* newModuleInternal(PKVM* vm, const char* name); @@ -46,42 +42,58 @@ static void moduleAddFunctionInternal(PKVM* vm, Module* module, const char* name, pkNativeFn fptr, int arity, const char* docstring); +/*****************************************************************************/ +/* CORE PUBLIC API */ +/*****************************************************************************/ + PkHandle* pkNewModule(PKVM* vm, const char* name) { Module* module = newModuleInternal(vm, name); return vmNewHandle(vm, VAR_OBJ(module)); } -PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module, +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."); - __ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), - "Given handle is not a module."); + ASSERT(module != NULL, "Argument module was NULL."); + ASSERT(value != NULL, "Argument value was NULL."); + ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), + "Given handle is not a module."); moduleAddGlobal(vm, (Module*)AS_OBJ(module->value), name, (uint32_t)strlen(name), value->value); } +PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module, const char* name) { + ASSERT(module != NULL, "Argument module was NULL."); + ASSERT(name != NULL, "Argument name was NULL."); + ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), + "Given handle is not a module."); + + Module* module_ = (Module*)AS_OBJ(module->value); + int index = moduleGetGlobalIndex(module_, name, (uint32_t)strlen(name)); + if (index == -1) return NULL; + return vmNewHandle(vm, module_->globals.data[index]); +} + void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, pkNativeFn fptr, int arity) { - __ASSERT(module != NULL, "Argument module was NULL."); - __ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), - "Given handle is not a module."); + ASSERT(module != NULL, "Argument module was NULL."); + ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), + "Given handle is not a module."); moduleAddFunctionInternal(vm, (Module*)AS_OBJ(module->value), name, fptr, arity, NULL /*TODO: Public API for function docstring.*/); } PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module) { - __ASSERT(module != NULL, "Argument module was NULL."); - __ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), - "Given handle is not a module."); + ASSERT(module != NULL, "Argument module was NULL."); + ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), + "Given handle is not a module."); Module* _module = (Module*)AS_OBJ(module->value); int main_index = moduleGetGlobalIndex(_module, IMPLICIT_MAIN_NAME, (uint32_t)strlen(IMPLICIT_MAIN_NAME)); if (main_index == -1) return NULL; - ASSERT_INDEX(main_index, _module->globals.count); + ASSERT_INDEX(main_index, (int)_module->globals.count); Var main_fn = _module->globals.data[main_index]; ASSERT(IS_OBJ_TYPE(main_fn, OBJ_CLOSURE), OOPS); return vmNewHandle(vm, main_fn); @@ -110,12 +122,12 @@ PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module) { // Check for errors in before calling the get arg public api function. #define CHECK_GET_ARG_API_ERRORS() \ do { \ - __ASSERT(vm->fiber != NULL, \ + ASSERT(vm->fiber != NULL, \ "This function can only be called at runtime."); \ if (arg != 0) {/* If Native setter, the value would be at fiber->ret */ \ - __ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index."); \ + ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index."); \ } \ - __ASSERT(value != NULL, "Argument [value] was NULL."); \ + ASSERT(value != NULL, "Argument [value] was NULL."); \ } while (false) // Set error for incompatible type provided as an argument. (TODO: got type). @@ -132,7 +144,7 @@ do { \ } while (false) int pkGetArgc(const PKVM* vm) { - __ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); + ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); return ARGC; } @@ -156,8 +168,8 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) { } PkVar pkGetArg(const PKVM* vm, int arg) { - __ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); - __ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); + ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); + ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); return &(ARG(arg)); } @@ -281,22 +293,22 @@ void pkReturnInstNative(PKVM* vm, void* data, uint32_t id) { const char* pkStringGetData(const PkVar value) { const Var str = (*(const Var*)value); - __ASSERT(IS_OBJ_TYPE(str, OBJ_STRING), "Value should be of type string."); + ASSERT(IS_OBJ_TYPE(str, OBJ_STRING), "Value should be of type string."); return ((String*)AS_OBJ(str))->data; } PkVar pkFiberGetReturnValue(const PkHandle* fiber) { - __ASSERT(fiber != NULL, "Handle fiber was NULL."); + ASSERT(fiber != NULL, "Handle fiber was NULL."); Var fb = fiber->value; - __ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber"); + ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber"); Fiber* _fiber = (Fiber*)AS_OBJ(fb); return (PkVar)_fiber->ret; } bool pkFiberIsDone(const PkHandle* fiber) { - __ASSERT(fiber != NULL, "Handle fiber was NULL."); + ASSERT(fiber != NULL, "Handle fiber was NULL."); Var fb = fiber->value; - __ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber"); + ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber"); Fiber* _fiber = (Fiber*)AS_OBJ(fb); return _fiber->state == FIBER_DONE; } @@ -399,6 +411,14 @@ static inline bool validateCond(PKVM* vm, bool condition, const char* err) { /* SHARED FUNCTIONS */ /*****************************************************************************/ +static void initializeBuiltinFunctions(PKVM* vm); +static void initializeCoreModules(PKVM* vm); + +void initializeCore(PKVM* vm) { + initializeBuiltinFunctions(vm); + initializeCoreModules(vm); +} + Module* getCoreLib(const PKVM* vm, String* name) { Var lib = mapGet(vm->core_libs, VAR_OBJ(name)); if (IS_UNDEF(lib)) return NULL; @@ -731,6 +751,48 @@ DEF(coreMapRemove, RET(mapRemoveKey(vm, map, key)); } +static void initializeBuiltinFN(PKVM* vm, Closure** bfn, const char* name, + int length, int arity, pkNativeFn ptr, + const char* docstring) { + Function* fn = newFunction(vm, name, length, NULL, true, docstring, NULL); + fn->arity = arity; + fn->native = ptr; + vmPushTempRef(vm, &fn->_super); // fn. + *bfn = newClosure(vm, fn); + vmPopTempRef(vm); // fn. +} + +static void initializeBuiltinFunctions(PKVM* vm) { +#define INITIALIZE_BUILTIN_FN(name, fn, argc) \ + initializeBuiltinFN(vm, &vm->builtins[vm->builtins_count++], name, \ + (int)strlen(name), argc, fn, DOCSTRING(fn)); + // General functions. + INITIALIZE_BUILTIN_FN("type_name", coreTypeName, 1); + INITIALIZE_BUILTIN_FN("help", coreHelp, -1); + INITIALIZE_BUILTIN_FN("assert", coreAssert, -1); + INITIALIZE_BUILTIN_FN("bin", coreBin, 1); + INITIALIZE_BUILTIN_FN("hex", coreHex, 1); + INITIALIZE_BUILTIN_FN("yield", coreYield, -1); + INITIALIZE_BUILTIN_FN("to_string", coreToString, 1); + INITIALIZE_BUILTIN_FN("print", corePrint, -1); + INITIALIZE_BUILTIN_FN("input", coreInput, -1); + INITIALIZE_BUILTIN_FN("exit", coreExit, -1); + + // String functions. + INITIALIZE_BUILTIN_FN("str_sub", coreStrSub, 3); + INITIALIZE_BUILTIN_FN("str_chr", coreStrChr, 1); + INITIALIZE_BUILTIN_FN("str_ord", coreStrOrd, 1); + + // List functions. + INITIALIZE_BUILTIN_FN("list_append", coreListAppend, 2); + INITIALIZE_BUILTIN_FN("list_join", coreListJoin, 1); + + // Map functions. + INITIALIZE_BUILTIN_FN("map_remove", coreMapRemove, 2); + +#undef INITIALIZE_BUILTIN_FN +} + /*****************************************************************************/ /* CORE MODULE METHODS */ /*****************************************************************************/ @@ -744,8 +806,8 @@ static Module* newModuleInternal(PKVM* vm, const char* name) { // Check if any module with the same name already exists and assert to the // hosting application. if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(_name)))) { - __ASSERT(false, stringFormat(vm, - "A module named '$' already exists", name)->data); + ASSERT(false, stringFormat(vm, + "A module named '$' already exists", name)->data); } Module* module = newModule(vm, _name, true); @@ -1165,64 +1227,10 @@ DEF(stdFiberResume, } } -/*****************************************************************************/ -/* CORE INITIALIZATION */ -/*****************************************************************************/ - -static void initializeBuiltinFN(PKVM* vm, Closure** bfn, const char* name, - int length, int arity, pkNativeFn ptr, - const char* docstring) { - - Function* fn = newFunction(vm, name, length, NULL, true, docstring, NULL); - fn->arity = arity; - fn->native = ptr; - - vmPushTempRef(vm, &fn->_super); // fn. - *bfn = newClosure(vm, fn); - vmPopTempRef(vm); // fn. -} - -void initializeCore(PKVM* vm) { - -#define INITIALIZE_BUILTIN_FN(name, fn, argc) \ - initializeBuiltinFN(vm, &vm->builtins[vm->builtins_count++], name, \ - (int)strlen(name), argc, fn, DOCSTRING(fn)); - +static void initializeCoreModules(PKVM* vm) { #define MODULE_ADD_FN(module, name, fn, argc) \ moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn)) - // Initialize builtin functions. - INITIALIZE_BUILTIN_FN("type_name", coreTypeName, 1); - - // TODO: Add 'is' keyword with modules for builtin types. - // ex: val is Num; val is null; val is List; val is Range - // List.append(l, e) # List is implicitly imported core module. - // String.lower(s) - - INITIALIZE_BUILTIN_FN("help", coreHelp, -1); - INITIALIZE_BUILTIN_FN("assert", coreAssert, -1); - INITIALIZE_BUILTIN_FN("bin", coreBin, 1); - INITIALIZE_BUILTIN_FN("hex", coreHex, 1); - INITIALIZE_BUILTIN_FN("yield", coreYield, -1); - INITIALIZE_BUILTIN_FN("to_string", coreToString, 1); - INITIALIZE_BUILTIN_FN("print", corePrint, -1); - INITIALIZE_BUILTIN_FN("input", coreInput, -1); - INITIALIZE_BUILTIN_FN("exit", coreExit, -1); - - // String functions. - INITIALIZE_BUILTIN_FN("str_sub", coreStrSub, 3); - INITIALIZE_BUILTIN_FN("str_chr", coreStrChr, 1); - INITIALIZE_BUILTIN_FN("str_ord", coreStrOrd, 1); - - // List functions. - INITIALIZE_BUILTIN_FN("list_append", coreListAppend, 2); - INITIALIZE_BUILTIN_FN("list_join", coreListJoin, 1); - - // Map functions. - INITIALIZE_BUILTIN_FN("map_remove", coreMapRemove, 2); - - // Core Modules ///////////////////////////////////////////////////////////// - Module* lang = newModuleInternal(vm, "lang"); MODULE_ADD_FN(lang, "clock", stdLangClock, 0); MODULE_ADD_FN(lang, "gc", stdLangGC, 0); @@ -1233,31 +1241,31 @@ void initializeCore(PKVM* vm) { #endif Module* math = newModuleInternal(vm, "math"); - MODULE_ADD_FN(math, "floor", stdMathFloor, 1); - MODULE_ADD_FN(math, "ceil", stdMathCeil, 1); - MODULE_ADD_FN(math, "pow", stdMathPow, 2); - MODULE_ADD_FN(math, "sqrt", stdMathSqrt, 1); - MODULE_ADD_FN(math, "abs", stdMathAbs, 1); - MODULE_ADD_FN(math, "sign", stdMathSign, 1); - MODULE_ADD_FN(math, "hash", stdMathHash, 1); - MODULE_ADD_FN(math, "sin", stdMathSine, 1); - MODULE_ADD_FN(math, "cos", stdMathCosine, 1); - MODULE_ADD_FN(math, "tan", stdMathTangent, 1); - MODULE_ADD_FN(math, "sinh", stdMathSinh, 1); - MODULE_ADD_FN(math, "cosh", stdMathCosh, 1); - MODULE_ADD_FN(math, "tanh", stdMathTanh, 1); - MODULE_ADD_FN(math, "asin", stdMathArcSine, 1); - MODULE_ADD_FN(math, "acos", stdMathArcCosine, 1); - MODULE_ADD_FN(math, "atan", stdMathArcTangent, 1); - MODULE_ADD_FN(math, "log10", stdMathLog10, 1); - MODULE_ADD_FN(math, "round", stdMathRound, 1); - MODULE_ADD_FN(math, "log2", stdMathLog2, 1); - MODULE_ADD_FN(math, "hypot", stdMathHypot, 2); - MODULE_ADD_FN(math, "cbrt", stdMathCbrt, 1); - MODULE_ADD_FN(math, "gamma", stdMathGamma, 1); - MODULE_ADD_FN(math, "lgamma",stdMathLgamma, 1); - MODULE_ADD_FN(math, "erf", stdMathErf, 1); - MODULE_ADD_FN(math, "erfc", stdMathErfc, 1); + MODULE_ADD_FN(math, "floor", stdMathFloor, 1); + MODULE_ADD_FN(math, "ceil", stdMathCeil, 1); + MODULE_ADD_FN(math, "pow", stdMathPow, 2); + MODULE_ADD_FN(math, "sqrt", stdMathSqrt, 1); + MODULE_ADD_FN(math, "abs", stdMathAbs, 1); + MODULE_ADD_FN(math, "sign", stdMathSign, 1); + MODULE_ADD_FN(math, "hash", stdMathHash, 1); + MODULE_ADD_FN(math, "sin", stdMathSine, 1); + MODULE_ADD_FN(math, "cos", stdMathCosine, 1); + MODULE_ADD_FN(math, "tan", stdMathTangent, 1); + MODULE_ADD_FN(math, "sinh", stdMathSinh, 1); + MODULE_ADD_FN(math, "cosh", stdMathCosh, 1); + MODULE_ADD_FN(math, "tanh", stdMathTanh, 1); + MODULE_ADD_FN(math, "asin", stdMathArcSine, 1); + MODULE_ADD_FN(math, "acos", stdMathArcCosine, 1); + MODULE_ADD_FN(math, "atan", stdMathArcTangent, 1); + MODULE_ADD_FN(math, "log10", stdMathLog10, 1); + MODULE_ADD_FN(math, "round", stdMathRound, 1); + MODULE_ADD_FN(math, "log2", stdMathLog2, 1); + MODULE_ADD_FN(math, "hypot", stdMathHypot, 2); + MODULE_ADD_FN(math, "cbrt", stdMathCbrt, 1); + MODULE_ADD_FN(math, "gamma", stdMathGamma, 1); + MODULE_ADD_FN(math, "lgamma", stdMathLgamma, 1); + MODULE_ADD_FN(math, "erf", stdMathErf, 1); + MODULE_ADD_FN(math, "erfc", stdMathErfc, 1); // 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 @@ -1265,12 +1273,17 @@ void initializeCore(PKVM* vm) { moduleAddGlobal(vm, math, "PI", 2, VAR_NUM(M_PI)); Module* fiber = newModuleInternal(vm, "Fiber"); - MODULE_ADD_FN(fiber, "new", stdFiberNew, 1); - MODULE_ADD_FN(fiber, "run", stdFiberRun, -1); - MODULE_ADD_FN(fiber, "resume", stdFiberResume, -1); + MODULE_ADD_FN(fiber, "new", stdFiberNew, 1); + MODULE_ADD_FN(fiber, "run", stdFiberRun, -1); + MODULE_ADD_FN(fiber, "resume", stdFiberResume, -1); +#undef MODULE_ADD_FN } +#undef IS_NUM_BYTE +#undef DOCSTRING +#undef DEF + /*****************************************************************************/ /* OPERATORS */ /*****************************************************************************/ @@ -1308,17 +1321,6 @@ Var varAdd(PKVM* vm, Var v1, Var v2) { return VAR_OBJ(listJoin(vm, (List*)o1, (List*)o2)); } } break; - - case OBJ_MAP: - case OBJ_RANGE: - case OBJ_MODULE: - case OBJ_FUNC: - case OBJ_CLOSURE: - case OBJ_UPVALUE: - case OBJ_FIBER: - case OBJ_CLASS: - case OBJ_INST: - break; } } @@ -1522,32 +1524,19 @@ bool varContains(PKVM* vm, Var elem, Var container) { Map* map = (Map*)AS_OBJ(container); return !IS_UNDEF(mapGet(map, elem)); } break; - - case OBJ_RANGE: - case OBJ_MODULE: - case OBJ_FUNC: - case OBJ_CLOSURE: - case OBJ_UPVALUE: - case OBJ_FIBER: - case OBJ_CLASS: - case OBJ_INST: - TODO; } - UNREACHABLE(); + + VM_SET_ERROR(vm, stringFormat(vm, "Argument of type $ is not iterable.", + varTypeName(container))); + return VAR_NULL; } -// TODO: The ERR_NO_ATTRIB() macro should splitted into 2 to for setter and -// getter and for the setter, the error message should be "object has no -// mutable attribute", to indicate that there might be an attribute with the -// name might be exists (but not accessed in a setter). +Var varGetAttrib(PKVM* vm, Var on, String* attrib) { -// Set error for accessing non-existed attribute. #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_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", varTypeName(on))); @@ -1556,8 +1545,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { Object* obj = AS_OBJ(on); switch (obj->type) { - case OBJ_STRING: - { + case OBJ_STRING: { String* str = (String*)obj; switch (attrib->hash) { @@ -1572,42 +1560,26 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { case CHECK_HASH("strip", 0xfd1b18d1): return VAR_OBJ(stringStrip(vm, str)); - - default: - ERR_NO_ATTRIB(vm, on, attrib); - return VAR_NULL; } + } break; - UNREACHABLE(); - } - - case OBJ_LIST: - { + case OBJ_LIST: { List* list = (List*)obj; switch (attrib->hash) { case CHECK_HASH("length", 0x83d03615): return VAR_NUM((double)(list->elements.count)); - - default: - ERR_NO_ATTRIB(vm, on, attrib); - return VAR_NULL; } + } break; - UNREACHABLE(); - } - - case OBJ_MAP: - { + case OBJ_MAP: { // map = { "foo" : 42, "can't access" : 32 } // val = map.foo ## <-- This should be error // Only the map's attributes are accessed here. TODO; - UNREACHABLE(); - } + } break; - case OBJ_RANGE: - { + case OBJ_RANGE: { Range* range = (Range*)obj; switch (attrib->hash) { @@ -1623,17 +1595,10 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { case CHECK_HASH("last", 0x63e1d819): return VAR_NUM(range->to); - - default: - ERR_NO_ATTRIB(vm, on, attrib); - return VAR_NULL; } + } break; - UNREACHABLE(); - } - - case OBJ_MODULE: - { + case OBJ_MODULE: { Module* module = (Module*)obj; // Search in globals. @@ -1642,19 +1607,12 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { ASSERT_INDEX((uint32_t)index, module->globals.count); return module->globals.data[index]; } - - ERR_NO_ATTRIB(vm, on, attrib); - return VAR_NULL; - } + } break; case OBJ_FUNC: - { - // Functions aren't first class objects. - UNREACHABLE(); - } + break; - case OBJ_CLOSURE: - { + case OBJ_CLOSURE: { Closure* closure = (Closure*)obj; switch (attrib->hash) { @@ -1663,58 +1621,51 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { case CHECK_HASH("name", 0x8d39bde6): return VAR_OBJ(newString(vm, closure->fn->name)); - - default: - ERR_NO_ATTRIB(vm, on, attrib); - return VAR_NULL; } - } + } break; case OBJ_UPVALUE: - // Upvalues aren't first class objects. - UNREACHABLE(); + UNREACHABLE(); // Upvalues aren't first class objects. + break; - case OBJ_FIBER: - { - Fiber* fb = (Fiber*)obj; - switch (attrib->hash) { + case OBJ_FIBER: { + Fiber* fb = (Fiber*)obj; + switch (attrib->hash) { - case CHECK_HASH("is_done", 0x789c2706): - return VAR_BOOL(fb->state == FIBER_DONE); + case CHECK_HASH("is_done", 0x789c2706): + return VAR_BOOL(fb->state == FIBER_DONE); - case CHECK_HASH("function", 0x9ed64249): - return VAR_OBJ(fb->closure); - - default: - ERR_NO_ATTRIB(vm, on, attrib); - return VAR_NULL; - } - UNREACHABLE(); + case CHECK_HASH("function", 0x9ed64249): + return VAR_OBJ(fb->closure); } + } break; case OBJ_CLASS: TODO; - UNREACHABLE(); + break; - case OBJ_INST: - { + case OBJ_INST: { Var value; - if (!instGetAttrib(vm, (Instance*)obj, attrib, &value)) { - ERR_NO_ATTRIB(vm, on, attrib); - return VAR_NULL; + if (instGetAttrib(vm, (Instance*)obj, attrib, &value)) { + return value; } - return value; - } - - default: - UNREACHABLE(); + } break; } - UNREACHABLE(); + ERR_NO_ATTRIB(vm, on, attrib); + return VAR_NULL; + +#undef ERR_NO_ATTRIB } void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) { +// Set error for accessing non-existed attribute. +#define ERR_NO_ATTRIB(vm, on, attrib) \ + VM_SET_ERROR(vm, stringFormat(vm, \ + "'$' object has no mutable attribute named '$'", \ + varTypeName(on), attrib->data)) + #define ATTRIB_IMMUTABLE(name) \ do { \ if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \ @@ -1745,12 +1696,7 @@ do { \ return; case OBJ_MAP: - // Not sure should I allow string values could be accessed with - // this way. ex: - // map = { "foo" : 42, "can't access" : 32 } - // map.foo = 'bar' TODO; - ERR_NO_ATTRIB(vm, on, attrib); return; @@ -1763,22 +1709,16 @@ do { \ case OBJ_MODULE: { Module* module = (Module*)obj; - - // Check globals. int index = moduleGetGlobalIndex(module, attrib->data, attrib->length); if (index != -1) { ASSERT_INDEX((uint32_t)index, module->globals.count); module->globals.data[index] = value; return; } - - ERR_NO_ATTRIB(vm, on, attrib); - return; - } + } break; case OBJ_FUNC: - // Functions aren't first class objects. - UNREACHABLE(); + UNREACHABLE(); // Functions aren't first class objects. return; case OBJ_CLOSURE: @@ -1788,8 +1728,7 @@ do { \ return; case OBJ_UPVALUE: - // Upvalues aren't first class objects. - UNREACHABLE(); + UNREACHABLE(); // Upvalues aren't first class objects. return; case OBJ_FIBER: @@ -1800,8 +1739,7 @@ do { \ ERR_NO_ATTRIB(vm, on, attrib); return; - case OBJ_INST: - { + case OBJ_INST: { if (!instSetAttrib(vm, (Instance*)obj, attrib, value)) { // If we has error by now, that means the set value type is // incompatible. No need for us to set an other error, just return. @@ -1812,17 +1750,15 @@ do { \ // If we reached here, that means the attribute exists and we have // updated the value. return; - } - - default: - UNREACHABLE(); + } break; } - UNREACHABLE(); + + ERR_NO_ATTRIB(vm, on, attrib); + return; #undef ATTRIB_IMMUTABLE -} - #undef ERR_NO_ATTRIB +} Var varGetSubscript(PKVM* vm, Var on, Var key) { if (!IS_OBJ(on)) { @@ -1833,8 +1769,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { Object* obj = AS_OBJ(on); switch (obj->type) { - case OBJ_STRING: - { + case OBJ_STRING: { int64_t index; String* str = ((String*)obj); if (!validateInteger(vm, key, &index, "List index")) { @@ -1845,10 +1780,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { } String* c = newStringLength(vm, str->data + index, 1); return VAR_OBJ(c); - } + } break; - case OBJ_LIST: - { + case OBJ_LIST: { int64_t index; pkVarBuffer* elems = &((List*)obj)->elements; if (!validateInteger(vm, key, &index, "List index")) { @@ -1858,10 +1792,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { return VAR_NULL; } return elems->data[index]; - } + } break; - case OBJ_MAP: - { + case OBJ_MAP: { Var value = mapGet((Map*)obj, key); if (IS_UNDEF(value)) { @@ -1876,24 +1809,16 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { return VAR_NULL; } return value; - } + } break; - case OBJ_RANGE: - case OBJ_MODULE: case OBJ_FUNC: - case OBJ_CLOSURE: case OBJ_UPVALUE: - case OBJ_FIBER: - case OBJ_CLASS: - case OBJ_INST: - TODO; - UNREACHABLE(); - - default: - UNREACHABLE(); + UNREACHABLE(); // Not first class objects. } - UNREACHABLE(); + VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on))); + return VAR_NULL; } void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { @@ -1905,22 +1830,16 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { Object* obj = AS_OBJ(on); switch (obj->type) { - case OBJ_STRING: - VM_SET_ERROR(vm, newString(vm, "String objects are immutable.")); - return; - - case OBJ_LIST: - { + case OBJ_LIST: { int64_t index; pkVarBuffer* elems = &((List*)obj)->elements; if (!validateInteger(vm, key, &index, "List index")) return; if (!validateIndex(vm, index, elems->count, "List")) return; elems->data[index] = value; return; - } + } break; - case OBJ_MAP: - { + case OBJ_MAP: { if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) { VM_SET_ERROR(vm, stringFormat(vm, "$ type is not hashable.", varTypeName(key))); @@ -1928,27 +1847,14 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { mapSet(vm, (Map*)obj, key, value); } return; - } + } break; - case OBJ_RANGE: - case OBJ_MODULE: case OBJ_FUNC: - case OBJ_CLOSURE: case OBJ_UPVALUE: - case OBJ_FIBER: - case OBJ_CLASS: - case OBJ_INST: - TODO; - UNREACHABLE(); - - default: UNREACHABLE(); } - UNREACHABLE(); + VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on))); + return; } - -#undef IS_NUM_BYTE - -#undef DOCSTRING -#undef DEF diff --git a/src/pk_internal.h b/src/pk_internal.h index 69fcbbe..1ae739a 100644 --- a/src/pk_internal.h +++ b/src/pk_internal.h @@ -11,7 +11,7 @@ #include "pk_common.h" -// Commonly used c standard headers across the sources. Don't include any +// Commonly used C standard headers across the sources. Don't include any // headers that are specific to a single source here, instead include them in // their source files explicitly (can not be implicitly included by another // header). And don't include any C standard headers in any of the pocketlang @@ -125,4 +125,45 @@ // #define CHECK_HASH(name, hash) hash +// The formated string to convert double to string. It'll be with the minimum +// length string representation of either a regular float or a scientific +// notation (at most 15 decimal points). +// Reference: https://www.cplusplus.com/reference/cstdio/printf/ +#define DOUBLE_FMT "%.16g" + +// Double number to string buffer size, used in sprintf with DOUBLE_FMT. +// A largest number : "-1.234567890123456e+308" +// + 1 fot sign '+' or '-' +// + 16 fot significant digits +// + 1 for decimal point '.' +// + 1 for exponent char 'e' +// + 1 for sign of exponent +// + 3 for the exponent digits +// + 1 for null byte '\0' +#define STR_DBL_BUFF_SIZE 24 + +// Integer number to string buffer size, used in sprintf with format "%d". +// The minimum 32 bit integer = -2147483648 +// + 1 for sign '-' +// + 10 for digits +// + 1 for null byte '\0' +#define STR_INT_BUFF_SIZE 12 + +// Integer number (double) to hex string buffer size. +// The maximum value an unsigned 64 bit integer can get is +// 0xffffffffffffffff which is 16 characters. +// + 16 for hex digits +// + 1 for sign '-' +// + 2 for '0x' prefix +// + 1 for null byte '\0' +#define STR_HEX_BUFF_SIZE 20 + +// Integer number (double) to bin string buffer size. +// The maximum value an unsigned 64 bit integer can get is 0b11111... 64 1s. +// + 64 for bin digits +// + 1 for sign '-' +// + 2 for '0b' prefix +// + 1 for null byte '\0' +#define STR_BIN_BUFF_SIZE 68 + #endif // PK_INTERNAL diff --git a/src/pk_value.c b/src/pk_value.c index 4695f70..ca2c7d4 100644 --- a/src/pk_value.c +++ b/src/pk_value.c @@ -1242,8 +1242,8 @@ bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) { if (IS_UNDEF(val)) { // FIXME: add a list of attribute overrides. - if (IS_CSTR_EQ(attrib, "as_string", 9, - CHECK_HASH("as_string", 0xbdef4147))) { + if ((CHECK_HASH("as_string", 0xbdef4147) == attrib->hash) && + IS_CSTR_EQ(attrib, "as_string", 9)) { *value = VAR_OBJ(toRepr(vm, VAR_OBJ(inst))); return true; } diff --git a/src/pk_value.h b/src/pk_value.h index ae310dc..7369e4b 100644 --- a/src/pk_value.h +++ b/src/pk_value.h @@ -137,11 +137,10 @@ ((s1)->length == (s2)->length) && \ (memcmp((const void*)(s1)->data, (const void*)(s2)->data, (s1)->length) == 0)) -// Compare pocket string with c string. -#define IS_CSTR_EQ(str, cstr, len, chash) \ - (((str)->hash == chash) && \ - ((str)->length == len) && \ - (memcmp((const void*)(str)->data, (const void*)(cstr), len) == 0)) +// Compare pocket string with C string. +#define IS_CSTR_EQ(str, cstr, len) \ + (((str)->length == len) && \ + (memcmp((const void*)(str)->data, (const void*)(cstr), len) == 0)) // Decode types. #define AS_BOOL(value) ((value) == VAR_TRUE) diff --git a/tests/check.py b/tests/check.py index 9b86ccc..d6a3cc7 100644 --- a/tests/check.py +++ b/tests/check.py @@ -1,5 +1,6 @@ #!python ## Copyright (c) 2020-2021 Thakee Nathees +## Copyright (c) 2021-2022 Pocketlang Contributors ## Distributed Under The MIT License ## This will run static checks on the source files, for line length, @@ -7,8 +8,7 @@ import os, sys, re from os import listdir -from os.path import ( - join, abspath, dirname, relpath) +from os.path import join, abspath, dirname, relpath ## The absolute path of this file, when run as a script. ## This file is not intended to be included in other files at the moment. @@ -49,21 +49,12 @@ SOURCE_DIRS = [ "../docs/wasm/", ] -## A list of common header that just copied in different projects. -## These common header cannot be re-used because we're trying to achieve -## minimalistic with the count of the sources in pocketlang. -COMMON_HEADERS = [ - "../src/pk_common.h", - "../cli/common.h", -] - ## This global variable will be set to true if any check failed. checks_failed = False def main(): check_fnv1_hash(to_abs_paths(HASH_CHECK_LIST)) check_static(to_abs_paths(SOURCE_DIRS)) - check_common_header_match(to_abs_paths(COMMON_HEADERS)) if checks_failed: sys.exit(1) print("Static checks were passed.") @@ -141,26 +132,6 @@ def check_static(dirs): else: is_last_empty = False -## TODO: Remove this check and resolve the cause. -## Assert all the content of the headers list below are the same. -## some header are re-used by copying, so changes must be reflect. -def check_common_header_match(headers): - headers = list(headers) - assert len(headers) >= 1 - - content = '' - with open(headers[0], 'r') as f: - content = f.read() - - for i in range(1, len(headers)): - with open(headers[i], 'r') as f: - if f.read() != content: - main_header = to_rel_path(headers[0]) - curr_header = to_rel_path(headers[i]) - report_error("File content mismatch: \"%s\" and \"%s\"\n" - " These files contants should be the same." - %(main_header, curr_header)) - ## Returns a formated string of the error location. def location(file, line): return f"{'%-17s'%file} : {'%4s'%line}" diff --git a/tests/tests.py b/tests/tests.py index 0777118..bfdabf1 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,6 @@ #!python ## Copyright (c) 2020-2021 Thakee Nathees +## Copyright (c) 2021-2022 Pocketlang Contributors ## Distributed Under The MIT License import os, sys, platform