mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
Merge pull request #198 from ThakeeNathees/minor-refactor
Yet another refactor.
This commit is contained in:
commit
ec569aba74
44
cli/common.h
44
cli/common.h
@ -28,8 +28,7 @@
|
|||||||
|
|
||||||
// The internal assertion macro, this will print error and break regardless of
|
// The internal assertion macro, this will print error and break regardless of
|
||||||
// the build target (debug or release). Use ASSERT() for debug assertion and
|
// the build target (debug or release). Use ASSERT() for debug assertion and
|
||||||
// use __ASSERT() for TODOs and assertions in public methods (to indicate that
|
// use __ASSERT() for TODOs.
|
||||||
// the host application did something wrong).
|
|
||||||
#define __ASSERT(condition, message) \
|
#define __ASSERT(condition, message) \
|
||||||
do { \
|
do { \
|
||||||
if (!(condition)) { \
|
if (!(condition)) { \
|
||||||
@ -111,45 +110,4 @@
|
|||||||
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
|
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
|
||||||
#define OOPS "Oops a bug!! report please."
|
#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
|
#endif //PK_COMMON_H
|
||||||
|
@ -242,8 +242,15 @@ PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle);
|
|||||||
PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
|
PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
|
||||||
const char* name, PkHandle* value);
|
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
|
// 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.
|
// 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,
|
PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
|
||||||
const char* name,
|
const char* name,
|
||||||
pkNativeFn fptr, int arity);
|
pkNativeFn fptr, int arity);
|
||||||
|
@ -28,8 +28,7 @@
|
|||||||
|
|
||||||
// The internal assertion macro, this will print error and break regardless of
|
// The internal assertion macro, this will print error and break regardless of
|
||||||
// the build target (debug or release). Use ASSERT() for debug assertion and
|
// the build target (debug or release). Use ASSERT() for debug assertion and
|
||||||
// use __ASSERT() for TODOs and assertions in public methods (to indicate that
|
// use __ASSERT() for TODOs.
|
||||||
// the host application did something wrong).
|
|
||||||
#define __ASSERT(condition, message) \
|
#define __ASSERT(condition, message) \
|
||||||
do { \
|
do { \
|
||||||
if (!(condition)) { \
|
if (!(condition)) { \
|
||||||
@ -111,45 +110,4 @@
|
|||||||
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
|
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
|
||||||
#define OOPS "Oops a bug!! report please."
|
#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
|
#endif //PK_COMMON_H
|
||||||
|
@ -607,6 +607,16 @@ static void resolveError(Compiler* compiler, int line, const char* fmt, ...) {
|
|||||||
va_end(args);
|
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 */
|
/* LEXING */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -2085,10 +2095,7 @@ static int compilerAddConstant(Compiler* compiler, Var value) {
|
|||||||
|
|
||||||
uint32_t index = moduleAddConstant(compiler->parser.vm,
|
uint32_t index = moduleAddConstant(compiler->parser.vm,
|
||||||
compiler->module, value);
|
compiler->module, value);
|
||||||
if (index >= MAX_CONSTANTS) {
|
checkMaxConstantsReached(compiler, index);
|
||||||
parseError(compiler, "A module should contain at most %d "
|
|
||||||
"unique constants.", MAX_CONSTANTS);
|
|
||||||
}
|
|
||||||
return (int)index;
|
return (int)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2289,10 +2296,8 @@ static void compileClass(Compiler* compiler) {
|
|||||||
moduleSetGlobal(compiler->module, index, VAR_OBJ(cls));
|
moduleSetGlobal(compiler->module, index, VAR_OBJ(cls));
|
||||||
|
|
||||||
// Check count exceeded.
|
// Check count exceeded.
|
||||||
if (cls_index >= MAX_CONSTANTS || ctor_index >= MAX_CONSTANTS) {
|
checkMaxConstantsReached(compiler, cls_index);
|
||||||
parseError(compiler, "A module should contain at most %d "
|
checkMaxConstantsReached(compiler, ctor_index);
|
||||||
"unique constants.", MAX_CONSTANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile the constructor function.
|
// Compile the constructor function.
|
||||||
ASSERT(compiler->func->ptr == compiler->module->body->fn, OOPS);
|
ASSERT(compiler->func->ptr == compiler->module->body->fn, OOPS);
|
||||||
@ -2371,10 +2376,7 @@ static void compileFunction(Compiler* compiler, bool is_literal) {
|
|||||||
int fn_index;
|
int fn_index;
|
||||||
Function* func = newFunction(compiler->parser.vm, name, name_length,
|
Function* func = newFunction(compiler->parser.vm, name, name_length,
|
||||||
compiler->module, false, NULL, &fn_index);
|
compiler->module, false, NULL, &fn_index);
|
||||||
if (fn_index >= MAX_CONSTANTS) {
|
checkMaxConstantsReached(compiler, fn_index);
|
||||||
parseError(compiler, "A module should contain at most %d "
|
|
||||||
"unique constants.", MAX_CONSTANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_literal) {
|
if (!is_literal) {
|
||||||
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
|
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
|
||||||
|
482
src/pk_core.c
482
src/pk_core.c
@ -32,10 +32,6 @@
|
|||||||
static const char* DOCSTRING(fn) = docstring; \
|
static const char* DOCSTRING(fn) = docstring; \
|
||||||
static void fn(PKVM* vm)
|
static void fn(PKVM* vm)
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* CORE PUBLIC API */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
// Create a new module with the given [name] and returns as a Module* for
|
// 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*.
|
// internal. Which will be wrapped by pkNewModule to return a pkHandle*.
|
||||||
static Module* newModuleInternal(PKVM* vm, const char* name);
|
static Module* newModuleInternal(PKVM* vm, const char* name);
|
||||||
@ -46,42 +42,58 @@ static void moduleAddFunctionInternal(PKVM* vm, Module* module,
|
|||||||
const char* name, pkNativeFn fptr,
|
const char* name, pkNativeFn fptr,
|
||||||
int arity, const char* docstring);
|
int arity, const char* docstring);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* CORE PUBLIC API */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
PkHandle* pkNewModule(PKVM* vm, const char* name) {
|
PkHandle* pkNewModule(PKVM* vm, const char* name) {
|
||||||
Module* module = newModuleInternal(vm, name);
|
Module* module = newModuleInternal(vm, name);
|
||||||
return vmNewHandle(vm, VAR_OBJ(module));
|
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) {
|
const char* name, PkHandle* value) {
|
||||||
__ASSERT(module != NULL, "Argument module was NULL.");
|
ASSERT(module != NULL, "Argument module was NULL.");
|
||||||
__ASSERT(value != NULL, "Argument value was NULL.");
|
ASSERT(value != NULL, "Argument value was NULL.");
|
||||||
__ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
|
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
|
||||||
"Given handle is not a module.");
|
"Given handle is not a module.");
|
||||||
|
|
||||||
moduleAddGlobal(vm, (Module*)AS_OBJ(module->value),
|
moduleAddGlobal(vm, (Module*)AS_OBJ(module->value),
|
||||||
name, (uint32_t)strlen(name), value->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,
|
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
||||||
pkNativeFn fptr, int arity) {
|
pkNativeFn fptr, int arity) {
|
||||||
__ASSERT(module != NULL, "Argument module was NULL.");
|
ASSERT(module != NULL, "Argument module was NULL.");
|
||||||
__ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
|
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
|
||||||
"Given handle is not a module.");
|
"Given handle is not a module.");
|
||||||
moduleAddFunctionInternal(vm, (Module*)AS_OBJ(module->value),
|
moduleAddFunctionInternal(vm, (Module*)AS_OBJ(module->value),
|
||||||
name, fptr, arity,
|
name, fptr, arity,
|
||||||
NULL /*TODO: Public API for function docstring.*/);
|
NULL /*TODO: Public API for function docstring.*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module) {
|
PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module) {
|
||||||
__ASSERT(module != NULL, "Argument module was NULL.");
|
ASSERT(module != NULL, "Argument module was NULL.");
|
||||||
__ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
|
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
|
||||||
"Given handle is not a module.");
|
"Given handle is not a module.");
|
||||||
Module* _module = (Module*)AS_OBJ(module->value);
|
Module* _module = (Module*)AS_OBJ(module->value);
|
||||||
|
|
||||||
int main_index = moduleGetGlobalIndex(_module, IMPLICIT_MAIN_NAME,
|
int main_index = moduleGetGlobalIndex(_module, IMPLICIT_MAIN_NAME,
|
||||||
(uint32_t)strlen(IMPLICIT_MAIN_NAME));
|
(uint32_t)strlen(IMPLICIT_MAIN_NAME));
|
||||||
if (main_index == -1) return NULL;
|
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];
|
Var main_fn = _module->globals.data[main_index];
|
||||||
ASSERT(IS_OBJ_TYPE(main_fn, OBJ_CLOSURE), OOPS);
|
ASSERT(IS_OBJ_TYPE(main_fn, OBJ_CLOSURE), OOPS);
|
||||||
return vmNewHandle(vm, main_fn);
|
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.
|
// Check for errors in before calling the get arg public api function.
|
||||||
#define CHECK_GET_ARG_API_ERRORS() \
|
#define CHECK_GET_ARG_API_ERRORS() \
|
||||||
do { \
|
do { \
|
||||||
__ASSERT(vm->fiber != NULL, \
|
ASSERT(vm->fiber != NULL, \
|
||||||
"This function can only be called at runtime."); \
|
"This function can only be called at runtime."); \
|
||||||
if (arg != 0) {/* If Native setter, the value would be at fiber->ret */ \
|
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)
|
} while (false)
|
||||||
|
|
||||||
// Set error for incompatible type provided as an argument. (TODO: got type).
|
// Set error for incompatible type provided as an argument. (TODO: got type).
|
||||||
@ -132,7 +144,7 @@ do { \
|
|||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
int pkGetArgc(const PKVM* vm) {
|
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;
|
return ARGC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,8 +168,8 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PkVar pkGetArg(const PKVM* vm, int arg) {
|
PkVar pkGetArg(const PKVM* vm, int arg) {
|
||||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
||||||
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index.");
|
ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index.");
|
||||||
|
|
||||||
return &(ARG(arg));
|
return &(ARG(arg));
|
||||||
}
|
}
|
||||||
@ -281,22 +293,22 @@ void pkReturnInstNative(PKVM* vm, void* data, uint32_t id) {
|
|||||||
|
|
||||||
const char* pkStringGetData(const PkVar value) {
|
const char* pkStringGetData(const PkVar value) {
|
||||||
const Var str = (*(const Var*)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;
|
return ((String*)AS_OBJ(str))->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
PkVar pkFiberGetReturnValue(const PkHandle* fiber) {
|
PkVar pkFiberGetReturnValue(const PkHandle* fiber) {
|
||||||
__ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
||||||
Var fb = fiber->value;
|
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);
|
Fiber* _fiber = (Fiber*)AS_OBJ(fb);
|
||||||
return (PkVar)_fiber->ret;
|
return (PkVar)_fiber->ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pkFiberIsDone(const PkHandle* fiber) {
|
bool pkFiberIsDone(const PkHandle* fiber) {
|
||||||
__ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
||||||
Var fb = fiber->value;
|
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);
|
Fiber* _fiber = (Fiber*)AS_OBJ(fb);
|
||||||
return _fiber->state == FIBER_DONE;
|
return _fiber->state == FIBER_DONE;
|
||||||
}
|
}
|
||||||
@ -399,6 +411,14 @@ static inline bool validateCond(PKVM* vm, bool condition, const char* err) {
|
|||||||
/* SHARED FUNCTIONS */
|
/* 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) {
|
Module* getCoreLib(const PKVM* vm, String* name) {
|
||||||
Var lib = mapGet(vm->core_libs, VAR_OBJ(name));
|
Var lib = mapGet(vm->core_libs, VAR_OBJ(name));
|
||||||
if (IS_UNDEF(lib)) return NULL;
|
if (IS_UNDEF(lib)) return NULL;
|
||||||
@ -731,6 +751,48 @@ DEF(coreMapRemove,
|
|||||||
RET(mapRemoveKey(vm, map, key));
|
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 */
|
/* 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
|
// Check if any module with the same name already exists and assert to the
|
||||||
// hosting application.
|
// hosting application.
|
||||||
if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(_name)))) {
|
if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(_name)))) {
|
||||||
__ASSERT(false, stringFormat(vm,
|
ASSERT(false, stringFormat(vm,
|
||||||
"A module named '$' already exists", name)->data);
|
"A module named '$' already exists", name)->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Module* module = newModule(vm, _name, true);
|
Module* module = newModule(vm, _name, true);
|
||||||
@ -1165,64 +1227,10 @@ DEF(stdFiberResume,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
static void initializeCoreModules(PKVM* vm) {
|
||||||
/* 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));
|
|
||||||
|
|
||||||
#define MODULE_ADD_FN(module, name, fn, argc) \
|
#define MODULE_ADD_FN(module, name, fn, argc) \
|
||||||
moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn))
|
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* lang = newModuleInternal(vm, "lang");
|
||||||
MODULE_ADD_FN(lang, "clock", stdLangClock, 0);
|
MODULE_ADD_FN(lang, "clock", stdLangClock, 0);
|
||||||
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
|
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
|
||||||
@ -1233,31 +1241,31 @@ void initializeCore(PKVM* vm) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
Module* math = newModuleInternal(vm, "math");
|
Module* math = newModuleInternal(vm, "math");
|
||||||
MODULE_ADD_FN(math, "floor", stdMathFloor, 1);
|
MODULE_ADD_FN(math, "floor", stdMathFloor, 1);
|
||||||
MODULE_ADD_FN(math, "ceil", stdMathCeil, 1);
|
MODULE_ADD_FN(math, "ceil", stdMathCeil, 1);
|
||||||
MODULE_ADD_FN(math, "pow", stdMathPow, 2);
|
MODULE_ADD_FN(math, "pow", stdMathPow, 2);
|
||||||
MODULE_ADD_FN(math, "sqrt", stdMathSqrt, 1);
|
MODULE_ADD_FN(math, "sqrt", stdMathSqrt, 1);
|
||||||
MODULE_ADD_FN(math, "abs", stdMathAbs, 1);
|
MODULE_ADD_FN(math, "abs", stdMathAbs, 1);
|
||||||
MODULE_ADD_FN(math, "sign", stdMathSign, 1);
|
MODULE_ADD_FN(math, "sign", stdMathSign, 1);
|
||||||
MODULE_ADD_FN(math, "hash", stdMathHash, 1);
|
MODULE_ADD_FN(math, "hash", stdMathHash, 1);
|
||||||
MODULE_ADD_FN(math, "sin", stdMathSine, 1);
|
MODULE_ADD_FN(math, "sin", stdMathSine, 1);
|
||||||
MODULE_ADD_FN(math, "cos", stdMathCosine, 1);
|
MODULE_ADD_FN(math, "cos", stdMathCosine, 1);
|
||||||
MODULE_ADD_FN(math, "tan", stdMathTangent, 1);
|
MODULE_ADD_FN(math, "tan", stdMathTangent, 1);
|
||||||
MODULE_ADD_FN(math, "sinh", stdMathSinh, 1);
|
MODULE_ADD_FN(math, "sinh", stdMathSinh, 1);
|
||||||
MODULE_ADD_FN(math, "cosh", stdMathCosh, 1);
|
MODULE_ADD_FN(math, "cosh", stdMathCosh, 1);
|
||||||
MODULE_ADD_FN(math, "tanh", stdMathTanh, 1);
|
MODULE_ADD_FN(math, "tanh", stdMathTanh, 1);
|
||||||
MODULE_ADD_FN(math, "asin", stdMathArcSine, 1);
|
MODULE_ADD_FN(math, "asin", stdMathArcSine, 1);
|
||||||
MODULE_ADD_FN(math, "acos", stdMathArcCosine, 1);
|
MODULE_ADD_FN(math, "acos", stdMathArcCosine, 1);
|
||||||
MODULE_ADD_FN(math, "atan", stdMathArcTangent, 1);
|
MODULE_ADD_FN(math, "atan", stdMathArcTangent, 1);
|
||||||
MODULE_ADD_FN(math, "log10", stdMathLog10, 1);
|
MODULE_ADD_FN(math, "log10", stdMathLog10, 1);
|
||||||
MODULE_ADD_FN(math, "round", stdMathRound, 1);
|
MODULE_ADD_FN(math, "round", stdMathRound, 1);
|
||||||
MODULE_ADD_FN(math, "log2", stdMathLog2, 1);
|
MODULE_ADD_FN(math, "log2", stdMathLog2, 1);
|
||||||
MODULE_ADD_FN(math, "hypot", stdMathHypot, 2);
|
MODULE_ADD_FN(math, "hypot", stdMathHypot, 2);
|
||||||
MODULE_ADD_FN(math, "cbrt", stdMathCbrt, 1);
|
MODULE_ADD_FN(math, "cbrt", stdMathCbrt, 1);
|
||||||
MODULE_ADD_FN(math, "gamma", stdMathGamma, 1);
|
MODULE_ADD_FN(math, "gamma", stdMathGamma, 1);
|
||||||
MODULE_ADD_FN(math, "lgamma",stdMathLgamma, 1);
|
MODULE_ADD_FN(math, "lgamma", stdMathLgamma, 1);
|
||||||
MODULE_ADD_FN(math, "erf", stdMathErf, 1);
|
MODULE_ADD_FN(math, "erf", stdMathErf, 1);
|
||||||
MODULE_ADD_FN(math, "erfc", stdMathErfc, 1);
|
MODULE_ADD_FN(math, "erfc", stdMathErfc, 1);
|
||||||
|
|
||||||
// Note that currently it's mutable (since it's a global variable, not
|
// 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
|
// 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));
|
moduleAddGlobal(vm, math, "PI", 2, VAR_NUM(M_PI));
|
||||||
|
|
||||||
Module* fiber = newModuleInternal(vm, "Fiber");
|
Module* fiber = newModuleInternal(vm, "Fiber");
|
||||||
MODULE_ADD_FN(fiber, "new", stdFiberNew, 1);
|
MODULE_ADD_FN(fiber, "new", stdFiberNew, 1);
|
||||||
MODULE_ADD_FN(fiber, "run", stdFiberRun, -1);
|
MODULE_ADD_FN(fiber, "run", stdFiberRun, -1);
|
||||||
MODULE_ADD_FN(fiber, "resume", stdFiberResume, -1);
|
MODULE_ADD_FN(fiber, "resume", stdFiberResume, -1);
|
||||||
|
|
||||||
|
#undef MODULE_ADD_FN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef IS_NUM_BYTE
|
||||||
|
#undef DOCSTRING
|
||||||
|
#undef DEF
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* OPERATORS */
|
/* OPERATORS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -1308,17 +1321,6 @@ Var varAdd(PKVM* vm, Var v1, Var v2) {
|
|||||||
return VAR_OBJ(listJoin(vm, (List*)o1, (List*)o2));
|
return VAR_OBJ(listJoin(vm, (List*)o1, (List*)o2));
|
||||||
}
|
}
|
||||||
} break;
|
} 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);
|
Map* map = (Map*)AS_OBJ(container);
|
||||||
return !IS_UNDEF(mapGet(map, elem));
|
return !IS_UNDEF(mapGet(map, elem));
|
||||||
} break;
|
} 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
|
Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||||
// 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).
|
|
||||||
|
|
||||||
// Set error for accessing non-existed attribute.
|
|
||||||
#define ERR_NO_ATTRIB(vm, on, attrib) \
|
#define ERR_NO_ATTRIB(vm, on, attrib) \
|
||||||
VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'", \
|
VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'", \
|
||||||
varTypeName(on), attrib->data))
|
varTypeName(on), attrib->data))
|
||||||
|
|
||||||
Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|
||||||
|
|
||||||
if (!IS_OBJ(on)) {
|
if (!IS_OBJ(on)) {
|
||||||
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
|
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
|
||||||
varTypeName(on)));
|
varTypeName(on)));
|
||||||
@ -1556,8 +1545,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
|
|
||||||
Object* obj = AS_OBJ(on);
|
Object* obj = AS_OBJ(on);
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case OBJ_STRING:
|
case OBJ_STRING: {
|
||||||
{
|
|
||||||
String* str = (String*)obj;
|
String* str = (String*)obj;
|
||||||
switch (attrib->hash) {
|
switch (attrib->hash) {
|
||||||
|
|
||||||
@ -1572,42 +1560,26 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
|
|
||||||
case CHECK_HASH("strip", 0xfd1b18d1):
|
case CHECK_HASH("strip", 0xfd1b18d1):
|
||||||
return VAR_OBJ(stringStrip(vm, str));
|
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;
|
List* list = (List*)obj;
|
||||||
switch (attrib->hash) {
|
switch (attrib->hash) {
|
||||||
|
|
||||||
case CHECK_HASH("length", 0x83d03615):
|
case CHECK_HASH("length", 0x83d03615):
|
||||||
return VAR_NUM((double)(list->elements.count));
|
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 }
|
// map = { "foo" : 42, "can't access" : 32 }
|
||||||
// val = map.foo ## <-- This should be error
|
// val = map.foo ## <-- This should be error
|
||||||
// Only the map's attributes are accessed here.
|
// Only the map's attributes are accessed here.
|
||||||
TODO;
|
TODO;
|
||||||
UNREACHABLE();
|
} break;
|
||||||
}
|
|
||||||
|
|
||||||
case OBJ_RANGE:
|
case OBJ_RANGE: {
|
||||||
{
|
|
||||||
Range* range = (Range*)obj;
|
Range* range = (Range*)obj;
|
||||||
switch (attrib->hash) {
|
switch (attrib->hash) {
|
||||||
|
|
||||||
@ -1623,17 +1595,10 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
|
|
||||||
case CHECK_HASH("last", 0x63e1d819):
|
case CHECK_HASH("last", 0x63e1d819):
|
||||||
return VAR_NUM(range->to);
|
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;
|
Module* module = (Module*)obj;
|
||||||
|
|
||||||
// Search in globals.
|
// Search in globals.
|
||||||
@ -1642,19 +1607,12 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
ASSERT_INDEX((uint32_t)index, module->globals.count);
|
ASSERT_INDEX((uint32_t)index, module->globals.count);
|
||||||
return module->globals.data[index];
|
return module->globals.data[index];
|
||||||
}
|
}
|
||||||
|
} break;
|
||||||
ERR_NO_ATTRIB(vm, on, attrib);
|
|
||||||
return VAR_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OBJ_FUNC:
|
case OBJ_FUNC:
|
||||||
{
|
break;
|
||||||
// Functions aren't first class objects.
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
case OBJ_CLOSURE:
|
case OBJ_CLOSURE: {
|
||||||
{
|
|
||||||
Closure* closure = (Closure*)obj;
|
Closure* closure = (Closure*)obj;
|
||||||
switch (attrib->hash) {
|
switch (attrib->hash) {
|
||||||
|
|
||||||
@ -1663,58 +1621,51 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
|
|
||||||
case CHECK_HASH("name", 0x8d39bde6):
|
case CHECK_HASH("name", 0x8d39bde6):
|
||||||
return VAR_OBJ(newString(vm, closure->fn->name));
|
return VAR_OBJ(newString(vm, closure->fn->name));
|
||||||
|
|
||||||
default:
|
|
||||||
ERR_NO_ATTRIB(vm, on, attrib);
|
|
||||||
return VAR_NULL;
|
|
||||||
}
|
}
|
||||||
}
|
} break;
|
||||||
|
|
||||||
case OBJ_UPVALUE:
|
case OBJ_UPVALUE:
|
||||||
// Upvalues aren't first class objects.
|
UNREACHABLE(); // Upvalues aren't first class objects.
|
||||||
UNREACHABLE();
|
break;
|
||||||
|
|
||||||
case OBJ_FIBER:
|
case OBJ_FIBER: {
|
||||||
{
|
Fiber* fb = (Fiber*)obj;
|
||||||
Fiber* fb = (Fiber*)obj;
|
switch (attrib->hash) {
|
||||||
switch (attrib->hash) {
|
|
||||||
|
|
||||||
case CHECK_HASH("is_done", 0x789c2706):
|
case CHECK_HASH("is_done", 0x789c2706):
|
||||||
return VAR_BOOL(fb->state == FIBER_DONE);
|
return VAR_BOOL(fb->state == FIBER_DONE);
|
||||||
|
|
||||||
case CHECK_HASH("function", 0x9ed64249):
|
case CHECK_HASH("function", 0x9ed64249):
|
||||||
return VAR_OBJ(fb->closure);
|
return VAR_OBJ(fb->closure);
|
||||||
|
|
||||||
default:
|
|
||||||
ERR_NO_ATTRIB(vm, on, attrib);
|
|
||||||
return VAR_NULL;
|
|
||||||
}
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case OBJ_CLASS:
|
case OBJ_CLASS:
|
||||||
TODO;
|
TODO;
|
||||||
UNREACHABLE();
|
break;
|
||||||
|
|
||||||
case OBJ_INST:
|
case OBJ_INST: {
|
||||||
{
|
|
||||||
Var value;
|
Var value;
|
||||||
if (!instGetAttrib(vm, (Instance*)obj, attrib, &value)) {
|
if (instGetAttrib(vm, (Instance*)obj, attrib, &value)) {
|
||||||
ERR_NO_ATTRIB(vm, on, attrib);
|
return value;
|
||||||
return VAR_NULL;
|
|
||||||
}
|
}
|
||||||
return value;
|
} break;
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UNREACHABLE();
|
ERR_NO_ATTRIB(vm, on, attrib);
|
||||||
|
return VAR_NULL;
|
||||||
|
|
||||||
|
#undef ERR_NO_ATTRIB
|
||||||
}
|
}
|
||||||
|
|
||||||
void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
|
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) \
|
#define ATTRIB_IMMUTABLE(name) \
|
||||||
do { \
|
do { \
|
||||||
if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \
|
if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \
|
||||||
@ -1745,12 +1696,7 @@ do { \
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case OBJ_MAP:
|
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;
|
TODO;
|
||||||
|
|
||||||
ERR_NO_ATTRIB(vm, on, attrib);
|
ERR_NO_ATTRIB(vm, on, attrib);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1763,22 +1709,16 @@ do { \
|
|||||||
|
|
||||||
case OBJ_MODULE: {
|
case OBJ_MODULE: {
|
||||||
Module* module = (Module*)obj;
|
Module* module = (Module*)obj;
|
||||||
|
|
||||||
// Check globals.
|
|
||||||
int index = moduleGetGlobalIndex(module, attrib->data, attrib->length);
|
int index = moduleGetGlobalIndex(module, attrib->data, attrib->length);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
ASSERT_INDEX((uint32_t)index, module->globals.count);
|
ASSERT_INDEX((uint32_t)index, module->globals.count);
|
||||||
module->globals.data[index] = value;
|
module->globals.data[index] = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} break;
|
||||||
ERR_NO_ATTRIB(vm, on, attrib);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OBJ_FUNC:
|
case OBJ_FUNC:
|
||||||
// Functions aren't first class objects.
|
UNREACHABLE(); // Functions aren't first class objects.
|
||||||
UNREACHABLE();
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case OBJ_CLOSURE:
|
case OBJ_CLOSURE:
|
||||||
@ -1788,8 +1728,7 @@ do { \
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case OBJ_UPVALUE:
|
case OBJ_UPVALUE:
|
||||||
// Upvalues aren't first class objects.
|
UNREACHABLE(); // Upvalues aren't first class objects.
|
||||||
UNREACHABLE();
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case OBJ_FIBER:
|
case OBJ_FIBER:
|
||||||
@ -1800,8 +1739,7 @@ do { \
|
|||||||
ERR_NO_ATTRIB(vm, on, attrib);
|
ERR_NO_ATTRIB(vm, on, attrib);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case OBJ_INST:
|
case OBJ_INST: {
|
||||||
{
|
|
||||||
if (!instSetAttrib(vm, (Instance*)obj, attrib, value)) {
|
if (!instSetAttrib(vm, (Instance*)obj, attrib, value)) {
|
||||||
// If we has error by now, that means the set value type is
|
// 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.
|
// 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
|
// If we reached here, that means the attribute exists and we have
|
||||||
// updated the value.
|
// updated the value.
|
||||||
return;
|
return;
|
||||||
}
|
} break;
|
||||||
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
|
||||||
|
ERR_NO_ATTRIB(vm, on, attrib);
|
||||||
|
return;
|
||||||
|
|
||||||
#undef ATTRIB_IMMUTABLE
|
#undef ATTRIB_IMMUTABLE
|
||||||
}
|
|
||||||
|
|
||||||
#undef ERR_NO_ATTRIB
|
#undef ERR_NO_ATTRIB
|
||||||
|
}
|
||||||
|
|
||||||
Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||||
if (!IS_OBJ(on)) {
|
if (!IS_OBJ(on)) {
|
||||||
@ -1833,8 +1769,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
|||||||
|
|
||||||
Object* obj = AS_OBJ(on);
|
Object* obj = AS_OBJ(on);
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case OBJ_STRING:
|
case OBJ_STRING: {
|
||||||
{
|
|
||||||
int64_t index;
|
int64_t index;
|
||||||
String* str = ((String*)obj);
|
String* str = ((String*)obj);
|
||||||
if (!validateInteger(vm, key, &index, "List index")) {
|
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);
|
String* c = newStringLength(vm, str->data + index, 1);
|
||||||
return VAR_OBJ(c);
|
return VAR_OBJ(c);
|
||||||
}
|
} break;
|
||||||
|
|
||||||
case OBJ_LIST:
|
case OBJ_LIST: {
|
||||||
{
|
|
||||||
int64_t index;
|
int64_t index;
|
||||||
pkVarBuffer* elems = &((List*)obj)->elements;
|
pkVarBuffer* elems = &((List*)obj)->elements;
|
||||||
if (!validateInteger(vm, key, &index, "List index")) {
|
if (!validateInteger(vm, key, &index, "List index")) {
|
||||||
@ -1858,10 +1792,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
|||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
return elems->data[index];
|
return elems->data[index];
|
||||||
}
|
} break;
|
||||||
|
|
||||||
case OBJ_MAP:
|
case OBJ_MAP: {
|
||||||
{
|
|
||||||
Var value = mapGet((Map*)obj, key);
|
Var value = mapGet((Map*)obj, key);
|
||||||
if (IS_UNDEF(value)) {
|
if (IS_UNDEF(value)) {
|
||||||
|
|
||||||
@ -1876,24 +1809,16 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
|||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
} break;
|
||||||
|
|
||||||
case OBJ_RANGE:
|
|
||||||
case OBJ_MODULE:
|
|
||||||
case OBJ_FUNC:
|
case OBJ_FUNC:
|
||||||
case OBJ_CLOSURE:
|
|
||||||
case OBJ_UPVALUE:
|
case OBJ_UPVALUE:
|
||||||
case OBJ_FIBER:
|
UNREACHABLE(); // Not first class objects.
|
||||||
case OBJ_CLASS:
|
|
||||||
case OBJ_INST:
|
|
||||||
TODO;
|
|
||||||
UNREACHABLE();
|
|
||||||
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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);
|
Object* obj = AS_OBJ(on);
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case OBJ_STRING:
|
case OBJ_LIST: {
|
||||||
VM_SET_ERROR(vm, newString(vm, "String objects are immutable."));
|
|
||||||
return;
|
|
||||||
|
|
||||||
case OBJ_LIST:
|
|
||||||
{
|
|
||||||
int64_t index;
|
int64_t index;
|
||||||
pkVarBuffer* elems = &((List*)obj)->elements;
|
pkVarBuffer* elems = &((List*)obj)->elements;
|
||||||
if (!validateInteger(vm, key, &index, "List index")) return;
|
if (!validateInteger(vm, key, &index, "List index")) return;
|
||||||
if (!validateIndex(vm, index, elems->count, "List")) return;
|
if (!validateIndex(vm, index, elems->count, "List")) return;
|
||||||
elems->data[index] = value;
|
elems->data[index] = value;
|
||||||
return;
|
return;
|
||||||
}
|
} break;
|
||||||
|
|
||||||
case OBJ_MAP:
|
case OBJ_MAP: {
|
||||||
{
|
|
||||||
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
||||||
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not hashable.",
|
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not hashable.",
|
||||||
varTypeName(key)));
|
varTypeName(key)));
|
||||||
@ -1928,27 +1847,14 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
|||||||
mapSet(vm, (Map*)obj, key, value);
|
mapSet(vm, (Map*)obj, key, value);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
} break;
|
||||||
|
|
||||||
case OBJ_RANGE:
|
|
||||||
case OBJ_MODULE:
|
|
||||||
case OBJ_FUNC:
|
case OBJ_FUNC:
|
||||||
case OBJ_CLOSURE:
|
|
||||||
case OBJ_UPVALUE:
|
case OBJ_UPVALUE:
|
||||||
case OBJ_FIBER:
|
|
||||||
case OBJ_CLASS:
|
|
||||||
case OBJ_INST:
|
|
||||||
TODO;
|
|
||||||
UNREACHABLE();
|
|
||||||
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
UNREACHABLE();
|
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
|
||||||
|
varTypeName(on)));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef IS_NUM_BYTE
|
|
||||||
|
|
||||||
#undef DOCSTRING
|
|
||||||
#undef DEF
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#include "pk_common.h"
|
#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
|
// headers that are specific to a single source here, instead include them in
|
||||||
// their source files explicitly (can not be implicitly included by another
|
// 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
|
// header). And don't include any C standard headers in any of the pocketlang
|
||||||
@ -125,4 +125,45 @@
|
|||||||
//
|
//
|
||||||
#define CHECK_HASH(name, hash) hash
|
#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
|
#endif // PK_INTERNAL
|
||||||
|
@ -1242,8 +1242,8 @@ bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) {
|
|||||||
if (IS_UNDEF(val)) {
|
if (IS_UNDEF(val)) {
|
||||||
|
|
||||||
// FIXME: add a list of attribute overrides.
|
// FIXME: add a list of attribute overrides.
|
||||||
if (IS_CSTR_EQ(attrib, "as_string", 9,
|
if ((CHECK_HASH("as_string", 0xbdef4147) == attrib->hash) &&
|
||||||
CHECK_HASH("as_string", 0xbdef4147))) {
|
IS_CSTR_EQ(attrib, "as_string", 9)) {
|
||||||
*value = VAR_OBJ(toRepr(vm, VAR_OBJ(inst)));
|
*value = VAR_OBJ(toRepr(vm, VAR_OBJ(inst)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -137,11 +137,10 @@
|
|||||||
((s1)->length == (s2)->length) && \
|
((s1)->length == (s2)->length) && \
|
||||||
(memcmp((const void*)(s1)->data, (const void*)(s2)->data, (s1)->length) == 0))
|
(memcmp((const void*)(s1)->data, (const void*)(s2)->data, (s1)->length) == 0))
|
||||||
|
|
||||||
// Compare pocket string with c string.
|
// Compare pocket string with C string.
|
||||||
#define IS_CSTR_EQ(str, cstr, len, chash) \
|
#define IS_CSTR_EQ(str, cstr, len) \
|
||||||
(((str)->hash == chash) && \
|
(((str)->length == len) && \
|
||||||
((str)->length == len) && \
|
(memcmp((const void*)(str)->data, (const void*)(cstr), len) == 0))
|
||||||
(memcmp((const void*)(str)->data, (const void*)(cstr), len) == 0))
|
|
||||||
|
|
||||||
// Decode types.
|
// Decode types.
|
||||||
#define AS_BOOL(value) ((value) == VAR_TRUE)
|
#define AS_BOOL(value) ((value) == VAR_TRUE)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#!python
|
#!python
|
||||||
## Copyright (c) 2020-2021 Thakee Nathees
|
## Copyright (c) 2020-2021 Thakee Nathees
|
||||||
|
## Copyright (c) 2021-2022 Pocketlang Contributors
|
||||||
## Distributed Under The MIT License
|
## Distributed Under The MIT License
|
||||||
|
|
||||||
## This will run static checks on the source files, for line length,
|
## This will run static checks on the source files, for line length,
|
||||||
@ -7,8 +8,7 @@
|
|||||||
|
|
||||||
import os, sys, re
|
import os, sys, re
|
||||||
from os import listdir
|
from os import listdir
|
||||||
from os.path import (
|
from os.path import join, abspath, dirname, relpath
|
||||||
join, abspath, dirname, relpath)
|
|
||||||
|
|
||||||
## The absolute path of this file, when run as a script.
|
## 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.
|
## This file is not intended to be included in other files at the moment.
|
||||||
@ -49,21 +49,12 @@ SOURCE_DIRS = [
|
|||||||
"../docs/wasm/",
|
"../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.
|
## This global variable will be set to true if any check failed.
|
||||||
checks_failed = False
|
checks_failed = False
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
check_fnv1_hash(to_abs_paths(HASH_CHECK_LIST))
|
check_fnv1_hash(to_abs_paths(HASH_CHECK_LIST))
|
||||||
check_static(to_abs_paths(SOURCE_DIRS))
|
check_static(to_abs_paths(SOURCE_DIRS))
|
||||||
check_common_header_match(to_abs_paths(COMMON_HEADERS))
|
|
||||||
if checks_failed:
|
if checks_failed:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
print("Static checks were passed.")
|
print("Static checks were passed.")
|
||||||
@ -141,26 +132,6 @@ def check_static(dirs):
|
|||||||
else:
|
else:
|
||||||
is_last_empty = False
|
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.
|
## Returns a formated string of the error location.
|
||||||
def location(file, line):
|
def location(file, line):
|
||||||
return f"{'%-17s'%file} : {'%4s'%line}"
|
return f"{'%-17s'%file} : {'%4s'%line}"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#!python
|
#!python
|
||||||
## Copyright (c) 2020-2021 Thakee Nathees
|
## Copyright (c) 2020-2021 Thakee Nathees
|
||||||
|
## Copyright (c) 2021-2022 Pocketlang Contributors
|
||||||
## Distributed Under The MIT License
|
## Distributed Under The MIT License
|
||||||
|
|
||||||
import os, sys, platform
|
import os, sys, platform
|
||||||
|
Loading…
Reference in New Issue
Block a user