yet another refactor.

This commit doesn't change any behavior, just moving the code
around cleaning things.
This commit is contained in:
Thakee Nathees 2022-04-15 09:49:47 +05:30
parent 6083af6dbe
commit ddc8bd8197
10 changed files with 268 additions and 425 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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}"

View File

@ -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