2021-02-11 01:23:48 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021 Thakee Nathees
|
|
|
|
* Licensed under: MIT License
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "core.h"
|
2021-02-13 01:40:19 +08:00
|
|
|
|
2021-05-16 15:05:54 +08:00
|
|
|
#include <ctype.h>
|
2021-02-13 01:40:19 +08:00
|
|
|
#include <math.h>
|
2021-02-15 20:49:19 +08:00
|
|
|
#include <time.h>
|
|
|
|
|
2021-05-16 15:05:54 +08:00
|
|
|
#include "utils.h"
|
2021-02-18 02:27:24 +08:00
|
|
|
#include "var.h"
|
2021-02-11 01:23:48 +08:00
|
|
|
#include "vm.h"
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
// Convert number var as int32_t. Check if it's number before using it.
|
|
|
|
#define _AS_INTEGER(var) (int32_t)trunc(AS_NUM(var))
|
|
|
|
|
2021-05-15 17:29:44 +08:00
|
|
|
static void initializeBuiltinFN(PKVM* vm, BuiltinFn* bfn, const char* name,
|
2021-05-09 18:28:00 +08:00
|
|
|
int length, int arity, pkNativeFn ptr) {
|
2021-02-12 01:35:43 +08:00
|
|
|
bfn->name = name;
|
2021-02-18 02:27:24 +08:00
|
|
|
bfn->length = length;
|
2021-02-12 01:35:43 +08:00
|
|
|
|
2021-04-26 17:34:30 +08:00
|
|
|
bfn->fn = newFunction(vm, name, length, NULL, true);
|
|
|
|
bfn->fn->arity = arity;
|
|
|
|
bfn->fn->native = ptr;
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
|
|
|
|
2021-05-15 17:29:44 +08:00
|
|
|
int findBuiltinFunction(PKVM* vm, const char* name, uint32_t length) {
|
|
|
|
for (int i = 0; i < vm->builtins_count; i++) {
|
|
|
|
if (length == vm->builtins[i].length &&
|
|
|
|
strncmp(name, vm->builtins[i].name, length) == 0) {
|
2021-02-12 01:35:43 +08:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* VALIDATORS */
|
|
|
|
/*****************************************************************************/
|
2021-02-13 01:40:19 +08:00
|
|
|
|
|
|
|
// Check if a numeric value bool/number and set [value].
|
2021-02-15 20:49:19 +08:00
|
|
|
static inline bool isNumeric(Var var, double* value) {
|
2021-02-13 01:40:19 +08:00
|
|
|
if (IS_BOOL(var)) {
|
|
|
|
*value = AS_BOOL(var);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (IS_NUM(var)) {
|
|
|
|
*value = AS_NUM(var);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if [var] is bool/number. If not set error and return false.
|
2021-05-09 18:28:00 +08:00
|
|
|
static inline bool validateNumeric(PKVM* vm, Var var, double* value,
|
2021-02-13 01:40:19 +08:00
|
|
|
const char* name) {
|
|
|
|
if (isNumeric(var, value)) return true;
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ must be a numeric value.", name);
|
2021-02-13 01:40:19 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if [var] is integer. If not set error and return false.
|
2021-05-09 18:28:00 +08:00
|
|
|
static inline bool validateIngeger(PKVM* vm, Var var, int32_t* value,
|
2021-02-13 01:40:19 +08:00
|
|
|
const char* name) {
|
|
|
|
double number;
|
|
|
|
if (isNumeric(var, &number)) {
|
|
|
|
double truncated = trunc(number);
|
|
|
|
if (truncated == number) {
|
|
|
|
*value = (int32_t)(truncated);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ must be an integer.", name);
|
2021-02-13 01:40:19 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
static inline bool validateIndex(PKVM* vm, int32_t index, int32_t size,
|
2021-02-13 01:40:19 +08:00
|
|
|
const char* container) {
|
|
|
|
if (index < 0 || size <= index) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ index out of range.", container);
|
2021-02-13 01:40:19 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-05-16 15:05:54 +08:00
|
|
|
// Check if [var] is string for argument at [arg_ind]. If not set error and
|
|
|
|
// return false.
|
|
|
|
static bool validateArgString(PKVM* vm, Var var, String** value, int arg_ind) {
|
|
|
|
if (!IS_OBJ(var) || AS_OBJ(var)->type != OBJ_STRING) {
|
|
|
|
String* str_arg = toString(vm, VAR_NUM((double)arg_ind), false);
|
|
|
|
vmPushTempRef(vm, &str_arg->_super);
|
|
|
|
vm->fiber->error = stringFormat(vm, "Expected a string at argument @.",
|
|
|
|
str_arg, false);
|
|
|
|
vmPopTempRef(vm);
|
|
|
|
}
|
|
|
|
*value = (String*)AS_OBJ(var);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
/*****************************************************************************/
|
2021-05-15 18:46:55 +08:00
|
|
|
/* BUILTIN FUNCTIONS API */
|
2021-02-16 02:51:00 +08:00
|
|
|
/*****************************************************************************/
|
2021-02-13 01:40:19 +08:00
|
|
|
|
|
|
|
// Argument getter (1 based).
|
2021-02-25 17:03:06 +08:00
|
|
|
#define ARG(n) vm->fiber->ret[n]
|
2021-02-13 01:40:19 +08:00
|
|
|
|
2021-05-16 15:05:54 +08:00
|
|
|
// Convinent macros.
|
2021-05-11 14:38:23 +08:00
|
|
|
#define ARG1 ARG(1)
|
|
|
|
#define ARG2 ARG(2)
|
|
|
|
#define ARG3 ARG(3)
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
// Argument count used in variadic functions.
|
2021-02-25 17:03:06 +08:00
|
|
|
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
|
2021-02-13 01:40:19 +08:00
|
|
|
|
|
|
|
// Set return value.
|
2021-02-25 17:03:06 +08:00
|
|
|
#define RET(value) \
|
|
|
|
do { \
|
|
|
|
*(vm->fiber->ret) = value; \
|
|
|
|
return; \
|
2021-02-16 02:51:00 +08:00
|
|
|
} while (false)
|
2021-02-13 01:40:19 +08:00
|
|
|
|
2021-05-15 17:29:44 +08:00
|
|
|
Function* getBuiltinFunction(PKVM* vm, int index) {
|
|
|
|
ASSERT_INDEX(index, vm->builtins_count);
|
|
|
|
return vm->builtins[index].fn;
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
|
|
|
|
2021-05-15 17:29:44 +08:00
|
|
|
const char* getBuiltinFunctionName(PKVM* vm, int index) {
|
|
|
|
ASSERT_INDEX(index, vm->builtins_count);
|
|
|
|
return vm->builtins[index].name;
|
2021-02-18 02:27:24 +08:00
|
|
|
}
|
|
|
|
|
2021-05-15 17:29:44 +08:00
|
|
|
Script* getCoreLib(PKVM* vm, String* name) {
|
|
|
|
Var lib = mapGet(vm->core_libs, VAR_OBJ(&name->_super));
|
2021-05-07 17:41:19 +08:00
|
|
|
if (IS_UNDEF(lib)) return NULL;
|
|
|
|
ASSERT(IS_OBJ(lib) && AS_OBJ(lib)->type == OBJ_SCRIPT, OOPS);
|
|
|
|
return (Script*)AS_OBJ(lib);
|
|
|
|
}
|
|
|
|
|
2021-05-15 18:46:55 +08:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* CORE BUILTIN FUNCTIONS */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
2021-02-12 14:53:52 +08:00
|
|
|
#define FN_IS_PRIMITE_TYPE(name, check) \
|
2021-05-09 18:28:00 +08:00
|
|
|
void coreIs##name(PKVM* vm) { \
|
2021-05-15 17:29:44 +08:00
|
|
|
RET(VAR_BOOL(check(ARG1))); \
|
2021-02-12 14:53:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define FN_IS_OBJ_TYPE(name, _enum) \
|
2021-05-09 18:28:00 +08:00
|
|
|
void coreIs##name(PKVM* vm) { \
|
2021-05-15 17:29:44 +08:00
|
|
|
Var arg1 = ARG1; \
|
2021-02-12 14:53:52 +08:00
|
|
|
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == _enum) { \
|
2021-02-13 01:40:19 +08:00
|
|
|
RET(VAR_TRUE); \
|
2021-02-12 14:53:52 +08:00
|
|
|
} else { \
|
2021-02-13 01:40:19 +08:00
|
|
|
RET(VAR_FALSE); \
|
2021-02-12 14:53:52 +08:00
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
FN_IS_PRIMITE_TYPE(Null, IS_NULL)
|
|
|
|
FN_IS_PRIMITE_TYPE(Bool, IS_BOOL)
|
|
|
|
FN_IS_PRIMITE_TYPE(Num, IS_NUM)
|
|
|
|
|
|
|
|
FN_IS_OBJ_TYPE(String, OBJ_STRING)
|
2021-02-13 01:40:19 +08:00
|
|
|
FN_IS_OBJ_TYPE(List, OBJ_LIST)
|
2021-02-12 14:53:52 +08:00
|
|
|
FN_IS_OBJ_TYPE(Map, OBJ_MAP)
|
|
|
|
FN_IS_OBJ_TYPE(Range, OBJ_RANGE)
|
|
|
|
FN_IS_OBJ_TYPE(Function, OBJ_FUNC)
|
|
|
|
FN_IS_OBJ_TYPE(Script, OBJ_SCRIPT)
|
|
|
|
FN_IS_OBJ_TYPE(UserObj, OBJ_USER)
|
|
|
|
|
2021-05-11 14:38:23 +08:00
|
|
|
void coreAssert(PKVM* vm) {
|
2021-05-13 17:10:57 +08:00
|
|
|
int argc = ARGC;
|
|
|
|
if (argc != 1 && argc != 2) {
|
|
|
|
vm->fiber->error = newString(vm, "Invalid argument count.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 14:38:23 +08:00
|
|
|
if (!toBool(ARG1)) {
|
|
|
|
String* msg = NULL;
|
2021-05-13 17:10:57 +08:00
|
|
|
|
|
|
|
if (argc == 2) {
|
|
|
|
if (AS_OBJ(ARG2)->type != OBJ_STRING) {
|
|
|
|
msg = toString(vm, ARG2, false);
|
|
|
|
} else {
|
|
|
|
msg = (String*)AS_OBJ(ARG2);
|
|
|
|
}
|
|
|
|
vmPushTempRef(vm, &msg->_super);
|
2021-05-16 01:57:34 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "Assertion failed: '@'.", msg);
|
2021-05-13 17:10:57 +08:00
|
|
|
vmPopTempRef(vm);
|
2021-05-11 14:38:23 +08:00
|
|
|
} else {
|
2021-05-13 17:10:57 +08:00
|
|
|
vm->fiber->error = newString(vm, "Assertion failed.");
|
2021-05-11 14:38:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the has value of the variable, if it's not hashable it'll return null.
|
|
|
|
void coreHash(PKVM* vm) {
|
|
|
|
if (IS_OBJ(ARG1)) {
|
|
|
|
if (!isObjectHashable(AS_OBJ(ARG1)->type)) {
|
|
|
|
RET(VAR_NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RET(VAR_NUM((double)varHashValue(ARG1)));
|
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
void coreToString(PKVM* vm) {
|
2021-05-11 14:38:23 +08:00
|
|
|
RET(VAR_OBJ(&toString(vm, ARG1, false)->_super));
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
void corePrint(PKVM* vm) {
|
2021-05-14 18:44:57 +08:00
|
|
|
// If the host appliaction donesn't provide any write function, discard the
|
|
|
|
// output.
|
|
|
|
if (vm->config.write_fn == NULL) return;
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
String* str; //< Will be cleaned by garbage collector;
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
for (int i = 1; i <= ARGC; i++) {
|
|
|
|
Var arg = ARG(i);
|
|
|
|
// If it's already a string don't allocate a new string instead use it.
|
|
|
|
if (IS_OBJ(arg) && AS_OBJ(arg)->type == OBJ_STRING) {
|
|
|
|
str = (String*)AS_OBJ(arg);
|
|
|
|
} else {
|
|
|
|
str = toString(vm, arg, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != 1) vm->config.write_fn(vm, " ");
|
|
|
|
vm->config.write_fn(vm, str->data);
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-13 01:40:19 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
vm->config.write_fn(vm, "\n");
|
|
|
|
}
|
|
|
|
|
2021-05-16 15:05:54 +08:00
|
|
|
// string functions
|
|
|
|
// ----------------
|
|
|
|
|
|
|
|
void coreStrLower(PKVM* vm) {
|
|
|
|
String* str;
|
|
|
|
if (!validateArgString(vm, ARG1, &str, 1)) return;
|
|
|
|
|
|
|
|
String* result = newStringLength(vm, str->data, str->length);
|
|
|
|
char* data = result->data;
|
|
|
|
for (; *data; ++data) *data = tolower(*data);
|
|
|
|
// Since the string is modified re-hash it.
|
|
|
|
result->hash = utilHashString(result->data);
|
|
|
|
|
|
|
|
RET(VAR_OBJ(&result->_super));
|
|
|
|
}
|
|
|
|
|
|
|
|
void coreStrUpper(PKVM* vm) {
|
|
|
|
String* str;
|
|
|
|
if (!validateArgString(vm, ARG1, &str, 1)) return;
|
|
|
|
|
|
|
|
String* result = newStringLength(vm, str->data, str->length);
|
|
|
|
char* data = result->data;
|
|
|
|
for (; *data; ++data) *data = toupper(*data);
|
|
|
|
// Since the string is modified re-hash it.
|
|
|
|
result->hash = utilHashString(result->data);
|
|
|
|
|
|
|
|
RET(VAR_OBJ(&result->_super));
|
|
|
|
}
|
|
|
|
|
|
|
|
void coreStrStrip(PKVM* vm) {
|
|
|
|
String* str;
|
|
|
|
if (!validateArgString(vm, ARG1, &str, 1)) return;
|
|
|
|
|
|
|
|
const char* start = str->data;
|
|
|
|
while (*start && isspace(*start)) start++;
|
|
|
|
if (*start == '\0') RET(VAR_OBJ(&newStringLength(vm, NULL, 0)->_super));
|
|
|
|
|
|
|
|
const char* end = str->data + str->length - 1;
|
|
|
|
while (isspace(*end)) end--;
|
|
|
|
|
2021-05-18 19:21:38 +08:00
|
|
|
RET(VAR_OBJ(&newStringLength(vm, start, (uint32_t)(end - start + 1))->_super));
|
2021-05-16 15:05:54 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
/*****************************************************************************/
|
2021-05-15 18:46:55 +08:00
|
|
|
/* CORE LIBRARY METHODS */
|
2021-02-16 02:51:00 +08:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
2021-05-15 18:46:55 +08:00
|
|
|
// 'path' library methods.
|
|
|
|
// -----------------------
|
|
|
|
|
2021-05-11 14:38:23 +08:00
|
|
|
// TODO: path library should be added by the cli (or the hosting application).
|
|
|
|
void stdPathAbspath(PKVM* vm) {
|
|
|
|
Var relpath = ARG1;
|
|
|
|
if (!IS_OBJ(relpath) || AS_OBJ(relpath)->type != OBJ_STRING) {
|
|
|
|
vm->fiber->error = newString(vm, "Expected a string at argument 1.");
|
2021-02-16 02:51:00 +08:00
|
|
|
}
|
|
|
|
|
2021-05-11 14:38:23 +08:00
|
|
|
// TODO: abspath.
|
|
|
|
RET(VAR_OBJ(newString(vm, "TODO: abspath")));
|
|
|
|
}
|
|
|
|
|
|
|
|
void stdPathCurdir(PKVM* vm) {
|
|
|
|
RET(VAR_OBJ(newString(vm, "TODO: curdir")));
|
2021-02-16 02:51:00 +08:00
|
|
|
}
|
2021-02-15 20:49:19 +08:00
|
|
|
|
2021-05-15 18:46:55 +08:00
|
|
|
// 'lang' library methods.
|
|
|
|
// -----------------------
|
|
|
|
|
|
|
|
// Returns the number of seconds since the application started.
|
|
|
|
void stdLangClock(PKVM* vm) {
|
2021-02-16 02:51:00 +08:00
|
|
|
RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC));
|
|
|
|
}
|
2021-02-15 20:49:19 +08:00
|
|
|
|
2021-05-15 18:46:55 +08:00
|
|
|
// Trigger garbage collection and return the ammount of bytes cleaned.
|
|
|
|
void stdLangGC(PKVM* vm) {
|
|
|
|
size_t bytes_before = vm->bytes_allocated;
|
|
|
|
vmCollectGarbage(vm);
|
|
|
|
size_t garbage = bytes_before - vm->bytes_allocated;
|
|
|
|
RET(VAR_NUM((double)garbage));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write function, just like print function but it wont put space between args
|
|
|
|
// and write a new line at the end.
|
|
|
|
void stdLangWrite(PKVM* vm) {
|
|
|
|
// If the host appliaction donesn't provide any write function, discard the
|
|
|
|
// output.
|
|
|
|
if (vm->config.write_fn == NULL) return;
|
|
|
|
|
|
|
|
String* str; //< Will be cleaned by garbage collector;
|
|
|
|
|
|
|
|
for (int i = 1; i <= ARGC; i++) {
|
|
|
|
Var arg = ARG(i);
|
|
|
|
// If it's already a string don't allocate a new string instead use it.
|
|
|
|
if (IS_OBJ(arg) && AS_OBJ(arg)->type == OBJ_STRING) {
|
|
|
|
str = (String*)AS_OBJ(arg);
|
|
|
|
} else {
|
|
|
|
str = toString(vm, arg, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
vm->config.write_fn(vm, str->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* CORE INITIALIZATION */
|
|
|
|
/*****************************************************************************/
|
2021-05-09 18:28:00 +08:00
|
|
|
void initializeCore(PKVM* vm) {
|
2021-02-12 14:53:52 +08:00
|
|
|
|
2021-05-15 17:29:44 +08:00
|
|
|
#define INITALIZE_BUILTIN_FN(name, fn, argc) \
|
|
|
|
initializeBuiltinFN(vm, &vm->builtins[vm->builtins_count++], name, \
|
2021-04-26 17:34:30 +08:00
|
|
|
(int)strlen(name), argc, fn);
|
2021-02-18 02:27:24 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Initialize builtin functions.
|
2021-02-18 02:27:24 +08:00
|
|
|
INITALIZE_BUILTIN_FN("is_null", coreIsNull, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_bool", coreIsBool, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_num", coreIsNum, 1);
|
2021-04-26 17:34:30 +08:00
|
|
|
|
2021-02-18 02:27:24 +08:00
|
|
|
INITALIZE_BUILTIN_FN("is_string", coreIsString, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_list", coreIsList, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_map", coreIsMap, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_range", coreIsRange, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_function", coreIsFunction, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_script", coreIsScript, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("is_userobj", coreIsUserObj, 1);
|
2021-04-26 17:34:30 +08:00
|
|
|
|
2021-05-13 17:10:57 +08:00
|
|
|
INITALIZE_BUILTIN_FN("assert", coreAssert, -1);
|
2021-05-11 14:38:23 +08:00
|
|
|
INITALIZE_BUILTIN_FN("hash", coreHash, 1);
|
2021-02-18 02:27:24 +08:00
|
|
|
INITALIZE_BUILTIN_FN("to_string", coreToString, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("print", corePrint, -1);
|
2021-02-12 01:35:43 +08:00
|
|
|
|
2021-05-16 15:05:54 +08:00
|
|
|
// string functions.
|
|
|
|
INITALIZE_BUILTIN_FN("str_lower", coreStrLower, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("str_upper", coreStrUpper, 1);
|
|
|
|
INITALIZE_BUILTIN_FN("str_strip", coreStrStrip, 1);
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
// Make STD scripts.
|
2021-05-07 17:41:19 +08:00
|
|
|
Script* std; // A temporary pointer to the current std script.
|
|
|
|
Function* fn; // A temporary pointer to the allocated function function.
|
2021-05-15 17:29:44 +08:00
|
|
|
#define STD_NEW_SCRIPT(_name) \
|
|
|
|
do { \
|
|
|
|
/* Create a new Script. */ \
|
|
|
|
String* name = newString(vm, _name); \
|
|
|
|
vmPushTempRef(vm, &name->_super); \
|
|
|
|
std = newScript(vm, name); \
|
2021-05-18 19:21:38 +08:00
|
|
|
std->moudle = name; /* Core libs's path and the module are the same. */ \
|
2021-05-15 17:29:44 +08:00
|
|
|
vmPopTempRef(vm); \
|
|
|
|
/* Add the script to core_libs. */ \
|
|
|
|
vmPushTempRef(vm, &std->_super); \
|
|
|
|
mapSet(vm->core_libs, vm, VAR_OBJ(&name->_super), VAR_OBJ(&std->_super)); \
|
|
|
|
vmPopTempRef(vm); \
|
2021-02-16 02:51:00 +08:00
|
|
|
} while (false)
|
|
|
|
|
|
|
|
#define STD_ADD_FUNCTION(_name, fptr, _arity) \
|
|
|
|
do { \
|
|
|
|
fn = newFunction(vm, _name, (int)strlen(_name), std, true); \
|
|
|
|
fn->native = fptr; \
|
|
|
|
fn->arity = _arity; \
|
|
|
|
} while (false)
|
2021-05-07 17:41:19 +08:00
|
|
|
|
2021-05-11 14:38:23 +08:00
|
|
|
// path
|
|
|
|
STD_NEW_SCRIPT("path");
|
|
|
|
STD_ADD_FUNCTION("abspath", stdPathAbspath, 1);
|
2021-05-15 18:46:55 +08:00
|
|
|
STD_ADD_FUNCTION("curdir", stdPathCurdir, 0);
|
2021-05-07 17:41:19 +08:00
|
|
|
|
2021-05-15 18:46:55 +08:00
|
|
|
// lang
|
|
|
|
STD_NEW_SCRIPT("lang");
|
|
|
|
STD_ADD_FUNCTION("clock", stdLangClock, 0);
|
|
|
|
STD_ADD_FUNCTION("gc", stdLangGC, 0);
|
|
|
|
STD_ADD_FUNCTION("write", stdLangWrite, -1);
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* OPERATORS */
|
|
|
|
/*****************************************************************************/
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
#define UNSUPPORT_OPERAND_TYPES(op) \
|
|
|
|
vm->fiber->error = stringFormat(vm, "Unsupported operand types for " \
|
|
|
|
"operator '" op "' $ and $", varTypeName(v1), varTypeName(v2))
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
Var varAdd(PKVM* vm, Var v1, Var v2) {
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
double d1, d2;
|
|
|
|
if (isNumeric(v1, &d1)) {
|
|
|
|
if (validateNumeric(vm, v2, &d2, "Right operand")) {
|
|
|
|
return VAR_NUM(d1 + d2);
|
|
|
|
}
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
2021-04-26 17:34:30 +08:00
|
|
|
if (IS_OBJ(v1) && IS_OBJ(v2)) {
|
|
|
|
Object *o1 = AS_OBJ(v1), *o2 = AS_OBJ(v2);
|
|
|
|
switch (o1->type) {
|
|
|
|
|
|
|
|
case OBJ_STRING:
|
|
|
|
{
|
|
|
|
if (o2->type == OBJ_STRING) {
|
2021-05-06 13:00:02 +08:00
|
|
|
return VAR_OBJ(stringFormat(vm, "@@", v1, v2));
|
2021-04-26 17:34:30 +08:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case OBJ_LIST:
|
2021-05-08 18:54:07 +08:00
|
|
|
TODO;
|
|
|
|
|
2021-04-26 17:34:30 +08:00
|
|
|
case OBJ_MAP:
|
|
|
|
case OBJ_RANGE:
|
|
|
|
case OBJ_SCRIPT:
|
|
|
|
case OBJ_FUNC:
|
|
|
|
case OBJ_FIBER:
|
|
|
|
case OBJ_USER:
|
2021-05-08 18:54:07 +08:00
|
|
|
break;
|
2021-04-26 17:34:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
UNSUPPORT_OPERAND_TYPES("+");
|
2021-02-12 01:35:43 +08:00
|
|
|
return VAR_NULL;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
Var varSubtract(PKVM* vm, Var v1, Var v2) {
|
2021-02-15 20:49:19 +08:00
|
|
|
double d1, d2;
|
|
|
|
if (isNumeric(v1, &d1)) {
|
|
|
|
if (validateNumeric(vm, v2, &d2, "Right operand")) {
|
|
|
|
return VAR_NUM(d1 - d2);
|
|
|
|
}
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
UNSUPPORT_OPERAND_TYPES("-");
|
2021-02-12 01:35:43 +08:00
|
|
|
return VAR_NULL;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
Var varMultiply(PKVM* vm, Var v1, Var v2) {
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
double d1, d2;
|
|
|
|
if (isNumeric(v1, &d1)) {
|
|
|
|
if (validateNumeric(vm, v2, &d2, "Right operand")) {
|
|
|
|
return VAR_NUM(d1 * d2);
|
|
|
|
}
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
UNSUPPORT_OPERAND_TYPES("*");
|
2021-02-12 01:35:43 +08:00
|
|
|
return VAR_NULL;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
Var varDivide(PKVM* vm, Var v1, Var v2) {
|
2021-02-16 02:51:00 +08:00
|
|
|
double d1, d2;
|
|
|
|
if (isNumeric(v1, &d1)) {
|
|
|
|
if (validateNumeric(vm, v2, &d2, "Right operand")) {
|
|
|
|
return VAR_NUM(d1 / d2);
|
|
|
|
}
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-12 15:57:51 +08:00
|
|
|
UNSUPPORT_OPERAND_TYPES("/");
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Var varModulo(PKVM* vm, Var v1, Var v2) {
|
|
|
|
|
|
|
|
double d1, d2;
|
|
|
|
if (isNumeric(v1, &d1)) {
|
|
|
|
if (validateNumeric(vm, v2, &d2, "Right operand")) {
|
|
|
|
return VAR_NUM(fmod(d1, d2));
|
|
|
|
}
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_OBJ(v1) && AS_OBJ(v1)->type == OBJ_STRING) {
|
|
|
|
String* str = (String*)AS_OBJ(v1);
|
|
|
|
TODO; // "fmt" % v2.
|
|
|
|
}
|
|
|
|
|
|
|
|
UNSUPPORT_OPERAND_TYPES("%");
|
2021-02-12 01:35:43 +08:00
|
|
|
return VAR_NULL;
|
2021-02-12 14:53:52 +08:00
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
bool varGreater(PKVM* vm, Var v1, Var v2) {
|
2021-02-15 20:49:19 +08:00
|
|
|
double d1, d2;
|
|
|
|
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
|
|
|
|
return d1 > d2;
|
|
|
|
}
|
|
|
|
|
|
|
|
TODO;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
bool varLesser(PKVM* vm, Var v1, Var v2) {
|
2021-02-15 20:49:19 +08:00
|
|
|
double d1, d2;
|
|
|
|
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
|
|
|
|
return d1 < d2;
|
|
|
|
}
|
|
|
|
|
|
|
|
TODO;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-02-18 02:27:24 +08:00
|
|
|
// A convinent convenient macro used in varGetAttrib and varSetAttrib.
|
|
|
|
#define IS_ATTRIB(name) \
|
|
|
|
(attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)
|
|
|
|
|
2021-05-06 22:19:30 +08:00
|
|
|
#define ERR_NO_ATTRIB() \
|
|
|
|
vm->fiber->error = stringFormat(vm, "'$' objects has no attribute " \
|
|
|
|
"named '$'", \
|
|
|
|
varTypeName(on), attrib->data);
|
2021-02-18 02:27:24 +08:00
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
2021-02-18 02:27:24 +08:00
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
if (!IS_OBJ(on)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
|
|
|
varTypeName(on));
|
2021-02-16 02:51:00 +08:00
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-07 17:41:19 +08:00
|
|
|
Object* obj = AS_OBJ(on);
|
2021-02-16 02:51:00 +08:00
|
|
|
switch (obj->type) {
|
|
|
|
case OBJ_STRING:
|
2021-02-18 02:27:24 +08:00
|
|
|
{
|
|
|
|
if (IS_ATTRIB("length")) {
|
|
|
|
size_t length = ((String*)obj)->length;
|
|
|
|
return VAR_NUM((double)length);
|
|
|
|
}
|
2021-05-05 13:55:34 +08:00
|
|
|
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return VAR_NULL;
|
2021-02-18 02:27:24 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_LIST:
|
2021-02-18 02:27:24 +08:00
|
|
|
{
|
|
|
|
if (IS_ATTRIB("length")) {
|
|
|
|
size_t length = ((List*)obj)->elements.count;
|
|
|
|
return VAR_NUM((double)length);
|
|
|
|
}
|
2021-05-05 13:55:34 +08:00
|
|
|
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return VAR_NULL;
|
2021-02-18 02:27:24 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_MAP:
|
2021-05-06 13:00:02 +08:00
|
|
|
{
|
|
|
|
Var value = mapGet((Map*)obj, VAR_OBJ(&attrib->_super));
|
|
|
|
if (IS_UNDEF(value)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "Key (\"@\") not exists.", attrib);
|
2021-05-06 13:00:02 +08:00
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_RANGE:
|
|
|
|
TODO;
|
|
|
|
|
|
|
|
case OBJ_SCRIPT: {
|
|
|
|
Script* scr = (Script*)obj;
|
|
|
|
|
|
|
|
// Search in functions.
|
2021-05-05 13:55:34 +08:00
|
|
|
uint32_t index = scriptSearchFunc(scr, attrib->data, attrib->length);
|
2021-02-16 02:51:00 +08:00
|
|
|
if (index != -1) {
|
2021-04-25 23:19:39 +08:00
|
|
|
ASSERT_INDEX(index, scr->functions.count);
|
2021-02-16 02:51:00 +08:00
|
|
|
return VAR_OBJ(scr->functions.data[index]);
|
|
|
|
}
|
|
|
|
|
2021-05-05 13:55:34 +08:00
|
|
|
// Search in globals.
|
|
|
|
index = scriptSearchGlobals(scr, attrib->data, attrib->length);
|
|
|
|
if (index != -1) {
|
|
|
|
ASSERT_INDEX(index, scr->globals.count);
|
|
|
|
return scr->globals.data[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return VAR_NULL;
|
2021-02-16 02:51:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case OBJ_FUNC:
|
2021-02-25 17:03:06 +08:00
|
|
|
case OBJ_FIBER:
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_USER:
|
|
|
|
TODO;
|
|
|
|
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2021-02-25 17:03:06 +08:00
|
|
|
CHECK_MISSING_OBJ_TYPE(7);
|
2021-02-16 02:51:00 +08:00
|
|
|
|
|
|
|
UNREACHABLE();
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
|
2021-02-18 02:27:24 +08:00
|
|
|
|
2021-05-06 22:19:30 +08:00
|
|
|
#define ATTRIB_IMMUTABLE(prop) \
|
|
|
|
do { \
|
|
|
|
if (IS_ATTRIB(prop)) { \
|
|
|
|
vm->fiber->error = stringFormat(vm, "'$' attribute is immutable.", prop); \
|
|
|
|
return; \
|
|
|
|
} \
|
2021-02-18 02:27:24 +08:00
|
|
|
} while (false)
|
|
|
|
|
|
|
|
if (!IS_OBJ(on)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
|
|
|
varTypeName(on));
|
2021-02-18 02:27:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-07 17:41:19 +08:00
|
|
|
Object* obj = AS_OBJ(on);
|
2021-02-18 02:27:24 +08:00
|
|
|
switch (obj->type) {
|
|
|
|
case OBJ_STRING:
|
|
|
|
ATTRIB_IMMUTABLE("length");
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OBJ_LIST:
|
|
|
|
ATTRIB_IMMUTABLE("length");
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OBJ_MAP:
|
|
|
|
TODO;
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OBJ_RANGE:
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OBJ_SCRIPT: {
|
|
|
|
Script* scr = (Script*)obj;
|
|
|
|
|
2021-05-05 13:55:34 +08:00
|
|
|
// Check globals.
|
|
|
|
uint32_t index = scriptSearchGlobals(scr, attrib->data, attrib->length);
|
|
|
|
if (index != -1) {
|
|
|
|
ASSERT_INDEX(index, scr->globals.count);
|
|
|
|
scr->globals.data[index] = value;
|
|
|
|
return;
|
|
|
|
}
|
2021-02-18 02:27:24 +08:00
|
|
|
|
2021-05-05 13:55:34 +08:00
|
|
|
// Check function (Functions are immutable).
|
|
|
|
index = scriptSearchFunc(scr, attrib->data, attrib->length);
|
2021-02-18 02:27:24 +08:00
|
|
|
if (index != -1) {
|
|
|
|
ASSERT_INDEX(index, scr->functions.count);
|
|
|
|
ATTRIB_IMMUTABLE(scr->functions.data[index]->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OBJ_FUNC:
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return;
|
|
|
|
|
2021-02-25 17:03:06 +08:00
|
|
|
case OBJ_FIBER:
|
|
|
|
ERR_NO_ATTRIB();
|
|
|
|
return;
|
|
|
|
|
2021-02-18 02:27:24 +08:00
|
|
|
case OBJ_USER:
|
2021-04-25 23:19:39 +08:00
|
|
|
TODO; //ERR_NO_ATTRIB();
|
2021-02-18 02:27:24 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2021-02-25 17:03:06 +08:00
|
|
|
CHECK_MISSING_OBJ_TYPE(7);
|
2021-02-18 02:27:24 +08:00
|
|
|
UNREACHABLE();
|
2021-02-16 02:51:00 +08:00
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
2021-02-16 02:51:00 +08:00
|
|
|
if (!IS_OBJ(on)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
|
|
|
varTypeName(on));
|
2021-02-16 02:51:00 +08:00
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object* obj = AS_OBJ(on);
|
|
|
|
switch (obj->type) {
|
2021-02-18 02:27:24 +08:00
|
|
|
case OBJ_STRING:
|
|
|
|
{
|
|
|
|
int32_t index;
|
|
|
|
String* str = ((String*)obj);
|
|
|
|
if (!validateIngeger(vm, key, &index, "List index")) {
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
if (!validateIndex(vm, index, str->length, "String")) {
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
2021-05-06 22:19:30 +08:00
|
|
|
String* c = newStringLength(vm, str->data + index, 1);
|
2021-02-18 02:27:24 +08:00
|
|
|
return VAR_OBJ(c);
|
|
|
|
}
|
2021-02-16 02:51:00 +08:00
|
|
|
|
|
|
|
case OBJ_LIST:
|
|
|
|
{
|
|
|
|
int32_t index;
|
|
|
|
VarBuffer* elems = &((List*)obj)->elements;
|
|
|
|
if (!validateIngeger(vm, key, &index, "List index")) {
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
if (!validateIndex(vm, index, (int)elems->count, "List")) {
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
return elems->data[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
case OBJ_MAP:
|
2021-05-06 13:00:02 +08:00
|
|
|
{
|
|
|
|
Var value = mapGet((Map*)obj, key);
|
|
|
|
if (IS_UNDEF(value)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
|
2021-05-16 15:05:54 +08:00
|
|
|
String* key_str = toString(vm, key, true);
|
2021-05-06 13:00:02 +08:00
|
|
|
vmPushTempRef(vm, &key_str->_super);
|
2021-05-16 15:05:54 +08:00
|
|
|
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
|
|
|
vm->fiber->error = stringFormat(vm, "Invalid key '@'.", key_str);
|
|
|
|
} else {
|
|
|
|
vm->fiber->error = stringFormat(vm, "Key '@' not exists", key_str);
|
|
|
|
}
|
2021-05-06 13:00:02 +08:00
|
|
|
vmPopTempRef(vm);
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_RANGE:
|
|
|
|
case OBJ_SCRIPT:
|
|
|
|
case OBJ_FUNC:
|
2021-02-25 17:03:06 +08:00
|
|
|
case OBJ_FIBER:
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_USER:
|
|
|
|
TODO;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2021-02-25 17:03:06 +08:00
|
|
|
CHECK_MISSING_OBJ_TYPE(7);
|
2021-02-16 02:51:00 +08:00
|
|
|
UNREACHABLE();
|
|
|
|
return VAR_NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
2021-02-16 02:51:00 +08:00
|
|
|
if (!IS_OBJ(on)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", varTypeName(on));
|
2021-02-16 02:51:00 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object* obj = AS_OBJ(on);
|
|
|
|
switch (obj->type) {
|
2021-05-06 13:00:02 +08:00
|
|
|
case OBJ_STRING:
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = newString(vm, "String objects are immutable.");
|
2021-05-06 13:00:02 +08:00
|
|
|
return;
|
2021-02-16 02:51:00 +08:00
|
|
|
|
|
|
|
case OBJ_LIST:
|
|
|
|
{
|
|
|
|
int32_t index;
|
|
|
|
VarBuffer* elems = &((List*)obj)->elements;
|
|
|
|
if (!validateIngeger(vm, key, &index, "List index")) return;
|
|
|
|
if (!validateIndex(vm, index, (int)elems->count, "List")) return;
|
|
|
|
elems->data[index] = value;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OBJ_MAP:
|
2021-05-06 13:00:02 +08:00
|
|
|
{
|
|
|
|
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = stringFormat(vm, "$ type is not hashable.", varTypeName(key));
|
2021-05-06 13:00:02 +08:00
|
|
|
} else {
|
|
|
|
mapSet((Map*)obj, vm, key, value);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_RANGE:
|
|
|
|
case OBJ_SCRIPT:
|
|
|
|
case OBJ_FUNC:
|
2021-02-25 17:03:06 +08:00
|
|
|
case OBJ_FIBER:
|
2021-02-16 02:51:00 +08:00
|
|
|
case OBJ_USER:
|
|
|
|
TODO;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2021-02-25 17:03:06 +08:00
|
|
|
CHECK_MISSING_OBJ_TYPE(7);
|
2021-02-16 02:51:00 +08:00
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2021-05-09 18:28:00 +08:00
|
|
|
bool varIterate(PKVM* vm, Var seq, Var* iterator, Var* value) {
|
2021-02-13 01:40:19 +08:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
int32_t _temp;
|
|
|
|
ASSERT(IS_NUM(*iterator) || IS_NULL(*iterator), OOPS);
|
|
|
|
if (IS_NUM(*iterator)) {
|
|
|
|
ASSERT(validateIngeger(vm, *iterator, &_temp, "Assetion."), OOPS);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Primitive types are not iterable.
|
|
|
|
if (!IS_OBJ(seq)) {
|
|
|
|
if (IS_NULL(seq)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = newString(vm, "Null is not iterable.");
|
2021-02-13 01:40:19 +08:00
|
|
|
} else if (IS_BOOL(seq)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = newString(vm, "Boolenan is not iterable.");
|
2021-02-13 01:40:19 +08:00
|
|
|
} else if (IS_NUM(seq)) {
|
2021-05-06 22:19:30 +08:00
|
|
|
vm->fiber->error = newString(vm, "Number is not iterable.");
|
2021-02-13 01:40:19 +08:00
|
|
|
} else {
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
*value = VAR_NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object* obj = AS_OBJ(seq);
|
|
|
|
|
2021-05-04 18:24:26 +08:00
|
|
|
uint32_t iter = 0; //< Nth iteration.
|
2021-02-13 01:40:19 +08:00
|
|
|
if (IS_NUM(*iterator)) {
|
|
|
|
iter = _AS_INTEGER(*iterator);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (obj->type) {
|
|
|
|
case OBJ_STRING: {
|
2021-02-18 02:27:24 +08:00
|
|
|
// TODO: // Need to consider utf8.
|
|
|
|
String* str = ((String*)obj);
|
2021-05-06 13:00:02 +08:00
|
|
|
if (iter >= str->length) {
|
2021-02-18 02:27:24 +08:00
|
|
|
return false; //< Stop iteration.
|
|
|
|
}
|
|
|
|
// TODO: Or I could add char as a type for efficiency.
|
2021-05-06 22:19:30 +08:00
|
|
|
*value = VAR_OBJ(newStringLength(vm, str->data + iter, 1));
|
2021-02-18 02:27:24 +08:00
|
|
|
*iterator = VAR_NUM((double)iter + 1);
|
|
|
|
return true;
|
2021-02-13 01:40:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case OBJ_LIST: {
|
|
|
|
VarBuffer* elems = &((List*)obj)->elements;
|
2021-05-06 13:00:02 +08:00
|
|
|
if (iter >= elems->count) {
|
2021-02-13 01:40:19 +08:00
|
|
|
return false; //< Stop iteration.
|
|
|
|
}
|
|
|
|
*value = elems->data[iter];
|
|
|
|
*iterator = VAR_NUM((double)iter + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-05-06 13:00:02 +08:00
|
|
|
case OBJ_MAP: {
|
|
|
|
Map* map = (Map*)obj;
|
|
|
|
|
|
|
|
// Find the next valid key.
|
|
|
|
for (; iter < map->capacity; iter++) {
|
|
|
|
if (!IS_UNDEF(map->entries[iter].key)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (iter >= map->capacity) {
|
|
|
|
return false; //< Stop iteration.
|
|
|
|
}
|
|
|
|
|
|
|
|
*value = map->entries[iter].key;
|
|
|
|
*iterator = VAR_NUM((double)iter + 1);
|
|
|
|
return true;
|
|
|
|
}
|
2021-02-13 01:40:19 +08:00
|
|
|
|
|
|
|
case OBJ_RANGE: {
|
|
|
|
double from = ((Range*)obj)->from;
|
|
|
|
double to = ((Range*)obj)->to;
|
|
|
|
if (from == to) return false;
|
|
|
|
|
|
|
|
double current;
|
|
|
|
if (from <= to) { //< Straight range.
|
|
|
|
current = from + (double)iter;
|
|
|
|
} else { //< Reversed range.
|
|
|
|
current = from - (double)iter;
|
|
|
|
}
|
|
|
|
if (current == to) return false;
|
|
|
|
*value = VAR_NUM(current);
|
|
|
|
*iterator = VAR_NUM((double)iter + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OBJ_SCRIPT:
|
|
|
|
case OBJ_FUNC:
|
2021-02-25 17:03:06 +08:00
|
|
|
case OBJ_FIBER:
|
2021-02-13 01:40:19 +08:00
|
|
|
case OBJ_USER:
|
|
|
|
TODO;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2021-02-25 17:03:06 +08:00
|
|
|
CHECK_MISSING_OBJ_TYPE(7);
|
2021-02-16 02:51:00 +08:00
|
|
|
UNREACHABLE();
|
2021-02-12 14:53:52 +08:00
|
|
|
return false;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|