1394 lines
37 KiB
C
1394 lines
37 KiB
C
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "wren_common.h"
|
|
#include "wren_core.h"
|
|
#include "wren_primitive.h"
|
|
#include "wren_value.h"
|
|
|
|
#include "wren_core.wren.inc"
|
|
|
|
DEF_PRIMITIVE(bool_not)
|
|
{
|
|
RETURN_BOOL(!AS_BOOL(args[0]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(bool_toString)
|
|
{
|
|
if (AS_BOOL(args[0]))
|
|
{
|
|
RETURN_VAL(CONST_STRING(vm, "true"));
|
|
}
|
|
else
|
|
{
|
|
RETURN_VAL(CONST_STRING(vm, "false"));
|
|
}
|
|
}
|
|
|
|
DEF_PRIMITIVE(class_name)
|
|
{
|
|
RETURN_OBJ(AS_CLASS(args[0])->name);
|
|
}
|
|
|
|
DEF_PRIMITIVE(class_supertype)
|
|
{
|
|
ObjClass* classObj = AS_CLASS(args[0]);
|
|
|
|
// Object has no superclass.
|
|
if (classObj->superclass == NULL) RETURN_NULL;
|
|
|
|
RETURN_OBJ(classObj->superclass);
|
|
}
|
|
|
|
DEF_PRIMITIVE(class_toString)
|
|
{
|
|
RETURN_OBJ(AS_CLASS(args[0])->name);
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_new)
|
|
{
|
|
if (!validateFn(vm, args[1], "Argument")) return false;
|
|
|
|
ObjClosure* closure = AS_CLOSURE(args[1]);
|
|
if (closure->fn->arity > 1)
|
|
{
|
|
RETURN_ERROR("Function cannot take more than one parameter.");
|
|
}
|
|
|
|
RETURN_OBJ(wrenNewFiber(vm, closure));
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_abort)
|
|
{
|
|
vm->fiber->error = args[1];
|
|
|
|
// If the error is explicitly null, it's not really an abort.
|
|
return IS_NULL(args[1]);
|
|
}
|
|
|
|
// Transfer execution to [fiber] coming from the current fiber whose stack has
|
|
// [args].
|
|
//
|
|
// [isCall] is true if [fiber] is being called and not transferred.
|
|
//
|
|
// [hasValue] is true if a value in [args] is being passed to the new fiber.
|
|
// Otherwise, `null` is implicitly being passed.
|
|
static bool runFiber(WrenVM* vm, ObjFiber* fiber, Value* args, bool isCall,
|
|
bool hasValue, const char* verb)
|
|
{
|
|
|
|
if (wrenHasError(fiber))
|
|
{
|
|
RETURN_ERROR_FMT("Cannot $ an aborted fiber.", verb);
|
|
}
|
|
|
|
if (isCall)
|
|
{
|
|
// You can't call a called fiber, but you can transfer directly to it,
|
|
// which is why this check is gated on `isCall`. This way, after resuming a
|
|
// suspended fiber, it will run and then return to the fiber that called it
|
|
// and so on.
|
|
if (fiber->caller != NULL) RETURN_ERROR("Fiber has already been called.");
|
|
|
|
if (fiber->state == FIBER_ROOT) RETURN_ERROR("Cannot call root fiber.");
|
|
|
|
// Remember who ran it.
|
|
fiber->caller = vm->fiber;
|
|
}
|
|
|
|
if (fiber->numFrames == 0)
|
|
{
|
|
RETURN_ERROR_FMT("Cannot $ a finished fiber.", verb);
|
|
}
|
|
|
|
// When the calling fiber resumes, we'll store the result of the call in its
|
|
// stack. If the call has two arguments (the fiber and the value), we only
|
|
// need one slot for the result, so discard the other slot now.
|
|
if (hasValue) vm->fiber->stackTop--;
|
|
|
|
if (fiber->numFrames == 1 &&
|
|
fiber->frames[0].ip == fiber->frames[0].closure->fn->code.data)
|
|
{
|
|
// The fiber is being started for the first time. If its function takes a
|
|
// parameter, bind an argument to it.
|
|
if (fiber->frames[0].closure->fn->arity == 1)
|
|
{
|
|
fiber->stackTop[0] = hasValue ? args[1] : NULL_VAL;
|
|
fiber->stackTop++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The fiber is being resumed, make yield() or transfer() return the result.
|
|
fiber->stackTop[-1] = hasValue ? args[1] : NULL_VAL;
|
|
}
|
|
|
|
vm->fiber = fiber;
|
|
return false;
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_call)
|
|
{
|
|
return runFiber(vm, AS_FIBER(args[0]), args, true, false, "call");
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_call1)
|
|
{
|
|
return runFiber(vm, AS_FIBER(args[0]), args, true, true, "call");
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_current)
|
|
{
|
|
RETURN_OBJ(vm->fiber);
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_error)
|
|
{
|
|
RETURN_VAL(AS_FIBER(args[0])->error);
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_isDone)
|
|
{
|
|
ObjFiber* runFiber = AS_FIBER(args[0]);
|
|
RETURN_BOOL(runFiber->numFrames == 0 || wrenHasError(runFiber));
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_suspend)
|
|
{
|
|
// Switching to a null fiber tells the interpreter to stop and exit.
|
|
vm->fiber = NULL;
|
|
vm->apiStack = NULL;
|
|
return false;
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_transfer)
|
|
{
|
|
return runFiber(vm, AS_FIBER(args[0]), args, false, false, "transfer to");
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_transfer1)
|
|
{
|
|
return runFiber(vm, AS_FIBER(args[0]), args, false, true, "transfer to");
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_transferError)
|
|
{
|
|
runFiber(vm, AS_FIBER(args[0]), args, false, true, "transfer to");
|
|
vm->fiber->error = args[1];
|
|
return false;
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_try)
|
|
{
|
|
runFiber(vm, AS_FIBER(args[0]), args, true, false, "try");
|
|
|
|
// If we're switching to a valid fiber to try, remember that we're trying it.
|
|
if (!wrenHasError(vm->fiber)) vm->fiber->state = FIBER_TRY;
|
|
return false;
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_yield)
|
|
{
|
|
ObjFiber* current = vm->fiber;
|
|
vm->fiber = current->caller;
|
|
|
|
// Unhook this fiber from the one that called it.
|
|
current->caller = NULL;
|
|
current->state = FIBER_OTHER;
|
|
|
|
if (vm->fiber != NULL)
|
|
{
|
|
// Make the caller's run method return null.
|
|
vm->fiber->stackTop[-1] = NULL_VAL;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DEF_PRIMITIVE(fiber_yield1)
|
|
{
|
|
ObjFiber* current = vm->fiber;
|
|
vm->fiber = current->caller;
|
|
|
|
// Unhook this fiber from the one that called it.
|
|
current->caller = NULL;
|
|
current->state = FIBER_OTHER;
|
|
|
|
if (vm->fiber != NULL)
|
|
{
|
|
// Make the caller's run method return the argument passed to yield.
|
|
vm->fiber->stackTop[-1] = args[1];
|
|
|
|
// When the yielding fiber resumes, we'll store the result of the yield
|
|
// call in its stack. Since Fiber.yield(value) has two arguments (the Fiber
|
|
// class and the value) and we only need one slot for the result, discard
|
|
// the other slot now.
|
|
current->stackTop--;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DEF_PRIMITIVE(fn_new)
|
|
{
|
|
if (!validateFn(vm, args[1], "Argument")) return false;
|
|
|
|
// The block argument is already a function, so just return it.
|
|
RETURN_VAL(args[1]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(fn_arity)
|
|
{
|
|
RETURN_NUM(AS_CLOSURE(args[0])->fn->arity);
|
|
}
|
|
|
|
static void call_fn(WrenVM* vm, Value* args, int numArgs)
|
|
{
|
|
// We only care about missing arguments, not extras.
|
|
if (AS_CLOSURE(args[0])->fn->arity > numArgs)
|
|
{
|
|
vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
|
|
return;
|
|
}
|
|
|
|
// +1 to include the function itself.
|
|
wrenCallFunction(vm, vm->fiber, AS_CLOSURE(args[0]), numArgs + 1);
|
|
}
|
|
|
|
#define DEF_FN_CALL(numArgs) \
|
|
DEF_PRIMITIVE(fn_call##numArgs) \
|
|
{ \
|
|
call_fn(vm, args, numArgs); \
|
|
return false; \
|
|
} \
|
|
|
|
DEF_FN_CALL(0)
|
|
DEF_FN_CALL(1)
|
|
DEF_FN_CALL(2)
|
|
DEF_FN_CALL(3)
|
|
DEF_FN_CALL(4)
|
|
DEF_FN_CALL(5)
|
|
DEF_FN_CALL(6)
|
|
DEF_FN_CALL(7)
|
|
DEF_FN_CALL(8)
|
|
DEF_FN_CALL(9)
|
|
DEF_FN_CALL(10)
|
|
DEF_FN_CALL(11)
|
|
DEF_FN_CALL(12)
|
|
DEF_FN_CALL(13)
|
|
DEF_FN_CALL(14)
|
|
DEF_FN_CALL(15)
|
|
DEF_FN_CALL(16)
|
|
|
|
DEF_PRIMITIVE(fn_toString)
|
|
{
|
|
RETURN_VAL(CONST_STRING(vm, "<fn>"));
|
|
}
|
|
|
|
// Creates a new list of size args[1], with all elements initialized to args[2].
|
|
DEF_PRIMITIVE(list_filled)
|
|
{
|
|
if (!validateInt(vm, args[1], "Size")) return false;
|
|
if (AS_NUM(args[1]) < 0) RETURN_ERROR("Size cannot be negative.");
|
|
|
|
uint32_t size = (uint32_t)AS_NUM(args[1]);
|
|
ObjList* list = wrenNewList(vm, size);
|
|
|
|
for (uint32_t i = 0; i < size; i++)
|
|
{
|
|
list->elements.data[i] = args[2];
|
|
}
|
|
|
|
RETURN_OBJ(list);
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_new)
|
|
{
|
|
RETURN_OBJ(wrenNewList(vm, 0));
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_add)
|
|
{
|
|
wrenValueBufferWrite(vm, &AS_LIST(args[0])->elements, args[1]);
|
|
RETURN_VAL(args[1]);
|
|
}
|
|
|
|
// Adds an element to the list and then returns the list itself. This is called
|
|
// by the compiler when compiling list literals instead of using add() to
|
|
// minimize stack churn.
|
|
DEF_PRIMITIVE(list_addCore)
|
|
{
|
|
wrenValueBufferWrite(vm, &AS_LIST(args[0])->elements, args[1]);
|
|
|
|
// Return the list.
|
|
RETURN_VAL(args[0]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_clear)
|
|
{
|
|
wrenValueBufferClear(vm, &AS_LIST(args[0])->elements);
|
|
RETURN_NULL;
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_count)
|
|
{
|
|
RETURN_NUM(AS_LIST(args[0])->elements.count);
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_insert)
|
|
{
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
// count + 1 here so you can "insert" at the very end.
|
|
uint32_t index = validateIndex(vm, args[1], list->elements.count + 1,
|
|
"Index");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
wrenListInsert(vm, list, args[2], index);
|
|
RETURN_VAL(args[2]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_iterate)
|
|
{
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
// If we're starting the iteration, return the first index.
|
|
if (IS_NULL(args[1]))
|
|
{
|
|
if (list->elements.count == 0) RETURN_FALSE;
|
|
RETURN_NUM(0);
|
|
}
|
|
|
|
if (!validateInt(vm, args[1], "Iterator")) return false;
|
|
|
|
// Stop if we're out of bounds.
|
|
double index = AS_NUM(args[1]);
|
|
if (index < 0 || index >= list->elements.count - 1) RETURN_FALSE;
|
|
|
|
// Otherwise, move to the next index.
|
|
RETURN_NUM(index + 1);
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_iteratorValue)
|
|
{
|
|
ObjList* list = AS_LIST(args[0]);
|
|
uint32_t index = validateIndex(vm, args[1], list->elements.count, "Iterator");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
RETURN_VAL(list->elements.data[index]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_removeAt)
|
|
{
|
|
ObjList* list = AS_LIST(args[0]);
|
|
uint32_t index = validateIndex(vm, args[1], list->elements.count, "Index");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
RETURN_VAL(wrenListRemoveAt(vm, list, index));
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_subscript)
|
|
{
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
if (IS_NUM(args[1]))
|
|
{
|
|
uint32_t index = validateIndex(vm, args[1], list->elements.count,
|
|
"Subscript");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
RETURN_VAL(list->elements.data[index]);
|
|
}
|
|
|
|
if (!IS_RANGE(args[1]))
|
|
{
|
|
RETURN_ERROR("Subscript must be a number or a range.");
|
|
}
|
|
|
|
int step;
|
|
uint32_t count = list->elements.count;
|
|
uint32_t start = calculateRange(vm, AS_RANGE(args[1]), &count, &step);
|
|
if (start == UINT32_MAX) return false;
|
|
|
|
ObjList* result = wrenNewList(vm, count);
|
|
for (uint32_t i = 0; i < count; i++)
|
|
{
|
|
result->elements.data[i] = list->elements.data[start + i * step];
|
|
}
|
|
|
|
RETURN_OBJ(result);
|
|
}
|
|
|
|
DEF_PRIMITIVE(list_subscriptSetter)
|
|
{
|
|
ObjList* list = AS_LIST(args[0]);
|
|
uint32_t index = validateIndex(vm, args[1], list->elements.count,
|
|
"Subscript");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
list->elements.data[index] = args[2];
|
|
RETURN_VAL(args[2]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_new)
|
|
{
|
|
RETURN_OBJ(wrenNewMap(vm));
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_subscript)
|
|
{
|
|
if (!validateKey(vm, args[1])) return false;
|
|
|
|
ObjMap* map = AS_MAP(args[0]);
|
|
Value value = wrenMapGet(map, args[1]);
|
|
if (IS_UNDEFINED(value)) RETURN_NULL;
|
|
|
|
RETURN_VAL(value);
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_subscriptSetter)
|
|
{
|
|
if (!validateKey(vm, args[1])) return false;
|
|
|
|
wrenMapSet(vm, AS_MAP(args[0]), args[1], args[2]);
|
|
RETURN_VAL(args[2]);
|
|
}
|
|
|
|
// Adds an entry to the map and then returns the map itself. This is called by
|
|
// the compiler when compiling map literals instead of using [_]=(_) to
|
|
// minimize stack churn.
|
|
DEF_PRIMITIVE(map_addCore)
|
|
{
|
|
if (!validateKey(vm, args[1])) return false;
|
|
|
|
wrenMapSet(vm, AS_MAP(args[0]), args[1], args[2]);
|
|
|
|
// Return the map itself.
|
|
RETURN_VAL(args[0]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_clear)
|
|
{
|
|
wrenMapClear(vm, AS_MAP(args[0]));
|
|
RETURN_NULL;
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_containsKey)
|
|
{
|
|
if (!validateKey(vm, args[1])) return false;
|
|
|
|
RETURN_BOOL(!IS_UNDEFINED(wrenMapGet(AS_MAP(args[0]), args[1])));
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_count)
|
|
{
|
|
RETURN_NUM(AS_MAP(args[0])->count);
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_iterate)
|
|
{
|
|
ObjMap* map = AS_MAP(args[0]);
|
|
|
|
if (map->count == 0) RETURN_FALSE;
|
|
|
|
// If we're starting the iteration, start at the first used entry.
|
|
uint32_t index = 0;
|
|
|
|
// Otherwise, start one past the last entry we stopped at.
|
|
if (!IS_NULL(args[1]))
|
|
{
|
|
if (!validateInt(vm, args[1], "Iterator")) return false;
|
|
|
|
if (AS_NUM(args[1]) < 0) RETURN_FALSE;
|
|
index = (uint32_t)AS_NUM(args[1]);
|
|
|
|
if (index >= map->capacity) RETURN_FALSE;
|
|
|
|
// Advance the iterator.
|
|
index++;
|
|
}
|
|
|
|
// Find a used entry, if any.
|
|
for (; index < map->capacity; index++)
|
|
{
|
|
if (!IS_UNDEFINED(map->entries[index].key)) RETURN_NUM(index);
|
|
}
|
|
|
|
// If we get here, walked all of the entries.
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_remove)
|
|
{
|
|
if (!validateKey(vm, args[1])) return false;
|
|
|
|
RETURN_VAL(wrenMapRemoveKey(vm, AS_MAP(args[0]), args[1]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_keyIteratorValue)
|
|
{
|
|
ObjMap* map = AS_MAP(args[0]);
|
|
uint32_t index = validateIndex(vm, args[1], map->capacity, "Iterator");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
MapEntry* entry = &map->entries[index];
|
|
if (IS_UNDEFINED(entry->key))
|
|
{
|
|
RETURN_ERROR("Invalid map iterator.");
|
|
}
|
|
|
|
RETURN_VAL(entry->key);
|
|
}
|
|
|
|
DEF_PRIMITIVE(map_valueIteratorValue)
|
|
{
|
|
ObjMap* map = AS_MAP(args[0]);
|
|
uint32_t index = validateIndex(vm, args[1], map->capacity, "Iterator");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
MapEntry* entry = &map->entries[index];
|
|
if (IS_UNDEFINED(entry->key))
|
|
{
|
|
RETURN_ERROR("Invalid map iterator.");
|
|
}
|
|
|
|
RETURN_VAL(entry->value);
|
|
}
|
|
|
|
DEF_PRIMITIVE(null_not)
|
|
{
|
|
RETURN_VAL(TRUE_VAL);
|
|
}
|
|
|
|
DEF_PRIMITIVE(null_toString)
|
|
{
|
|
RETURN_VAL(CONST_STRING(vm, "null"));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_fromString)
|
|
{
|
|
if (!validateString(vm, args[1], "Argument")) return false;
|
|
|
|
ObjString* string = AS_STRING(args[1]);
|
|
|
|
// Corner case: Can't parse an empty string.
|
|
if (string->length == 0) RETURN_NULL;
|
|
|
|
errno = 0;
|
|
char* end;
|
|
double number = strtod(string->value, &end);
|
|
|
|
// Skip past any trailing whitespace.
|
|
while (*end != '\0' && isspace((unsigned char)*end)) end++;
|
|
|
|
if (errno == ERANGE) RETURN_ERROR("Number literal is too large.");
|
|
|
|
// We must have consumed the entire string. Otherwise, it contains non-number
|
|
// characters and we can't parse it.
|
|
if (end < string->value + string->length) RETURN_NULL;
|
|
|
|
RETURN_NUM(number);
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_pi)
|
|
{
|
|
RETURN_NUM(3.14159265358979323846);
|
|
}
|
|
|
|
// Defines a primitive on Num that calls infix [op] and returns [type].
|
|
#define DEF_NUM_INFIX(name, op, type) \
|
|
DEF_PRIMITIVE(num_##name) \
|
|
{ \
|
|
if (!validateNum(vm, args[1], "Right operand")) return false; \
|
|
RETURN_##type(AS_NUM(args[0]) op AS_NUM(args[1])); \
|
|
}
|
|
|
|
DEF_NUM_INFIX(minus, -, NUM)
|
|
DEF_NUM_INFIX(plus, +, NUM)
|
|
DEF_NUM_INFIX(multiply, *, NUM)
|
|
DEF_NUM_INFIX(divide, /, NUM)
|
|
DEF_NUM_INFIX(lt, <, BOOL)
|
|
DEF_NUM_INFIX(gt, >, BOOL)
|
|
DEF_NUM_INFIX(lte, <=, BOOL)
|
|
DEF_NUM_INFIX(gte, >=, BOOL)
|
|
|
|
// Defines a primitive on Num that call infix bitwise [op].
|
|
#define DEF_NUM_BITWISE(name, op) \
|
|
DEF_PRIMITIVE(num_bitwise##name) \
|
|
{ \
|
|
if (!validateNum(vm, args[1], "Right operand")) return false; \
|
|
uint32_t left = (uint32_t)AS_NUM(args[0]); \
|
|
uint32_t right = (uint32_t)AS_NUM(args[1]); \
|
|
RETURN_NUM(left op right); \
|
|
}
|
|
|
|
DEF_NUM_BITWISE(And, &)
|
|
DEF_NUM_BITWISE(Or, |)
|
|
DEF_NUM_BITWISE(Xor, ^)
|
|
DEF_NUM_BITWISE(LeftShift, <<)
|
|
DEF_NUM_BITWISE(RightShift, >>)
|
|
|
|
// Defines a primitive method on Num that returns the result of [fn].
|
|
#define DEF_NUM_FN(name, fn) \
|
|
DEF_PRIMITIVE(num_##name) \
|
|
{ \
|
|
RETURN_NUM(fn(AS_NUM(args[0]))); \
|
|
}
|
|
|
|
DEF_NUM_FN(abs, fabs)
|
|
DEF_NUM_FN(acos, acos)
|
|
DEF_NUM_FN(asin, asin)
|
|
DEF_NUM_FN(atan, atan)
|
|
DEF_NUM_FN(ceil, ceil)
|
|
DEF_NUM_FN(cos, cos)
|
|
DEF_NUM_FN(floor, floor)
|
|
DEF_NUM_FN(negate, -)
|
|
DEF_NUM_FN(round, round)
|
|
DEF_NUM_FN(sin, sin)
|
|
DEF_NUM_FN(sqrt, sqrt)
|
|
DEF_NUM_FN(tan, tan)
|
|
DEF_NUM_FN(log, log)
|
|
|
|
DEF_PRIMITIVE(num_mod)
|
|
{
|
|
if (!validateNum(vm, args[1], "Right operand")) return false;
|
|
RETURN_NUM(fmod(AS_NUM(args[0]), AS_NUM(args[1])));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_eqeq)
|
|
{
|
|
if (!IS_NUM(args[1])) RETURN_FALSE;
|
|
RETURN_BOOL(AS_NUM(args[0]) == AS_NUM(args[1]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_bangeq)
|
|
{
|
|
if (!IS_NUM(args[1])) RETURN_TRUE;
|
|
RETURN_BOOL(AS_NUM(args[0]) != AS_NUM(args[1]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_bitwiseNot)
|
|
{
|
|
// Bitwise operators always work on 32-bit unsigned ints.
|
|
RETURN_NUM(~(uint32_t)AS_NUM(args[0]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_dotDot)
|
|
{
|
|
if (!validateNum(vm, args[1], "Right hand side of range")) return false;
|
|
|
|
double from = AS_NUM(args[0]);
|
|
double to = AS_NUM(args[1]);
|
|
RETURN_VAL(wrenNewRange(vm, from, to, true));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_dotDotDot)
|
|
{
|
|
if (!validateNum(vm, args[1], "Right hand side of range")) return false;
|
|
|
|
double from = AS_NUM(args[0]);
|
|
double to = AS_NUM(args[1]);
|
|
RETURN_VAL(wrenNewRange(vm, from, to, false));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_atan2)
|
|
{
|
|
RETURN_NUM(atan2(AS_NUM(args[0]), AS_NUM(args[1])));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_pow)
|
|
{
|
|
RETURN_NUM(pow(AS_NUM(args[0]), AS_NUM(args[1])));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_fraction)
|
|
{
|
|
double dummy;
|
|
RETURN_NUM(modf(AS_NUM(args[0]) , &dummy));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_isInfinity)
|
|
{
|
|
RETURN_BOOL(isinf(AS_NUM(args[0])));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_isInteger)
|
|
{
|
|
double value = AS_NUM(args[0]);
|
|
if (isnan(value) || isinf(value)) RETURN_FALSE;
|
|
RETURN_BOOL(trunc(value) == value);
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_isNan)
|
|
{
|
|
RETURN_BOOL(isnan(AS_NUM(args[0])));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_sign)
|
|
{
|
|
double value = AS_NUM(args[0]);
|
|
if (value > 0)
|
|
{
|
|
RETURN_NUM(1);
|
|
}
|
|
else if (value < 0)
|
|
{
|
|
RETURN_NUM(-1);
|
|
}
|
|
else
|
|
{
|
|
RETURN_NUM(0);
|
|
}
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_largest)
|
|
{
|
|
RETURN_NUM(DBL_MAX);
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_smallest)
|
|
{
|
|
RETURN_NUM(DBL_MIN);
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_toString)
|
|
{
|
|
RETURN_VAL(wrenNumToString(vm, AS_NUM(args[0])));
|
|
}
|
|
|
|
DEF_PRIMITIVE(num_truncate)
|
|
{
|
|
double integer;
|
|
modf(AS_NUM(args[0]) , &integer);
|
|
RETURN_NUM(integer);
|
|
}
|
|
|
|
DEF_PRIMITIVE(object_same)
|
|
{
|
|
RETURN_BOOL(wrenValuesEqual(args[1], args[2]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(object_not)
|
|
{
|
|
RETURN_VAL(FALSE_VAL);
|
|
}
|
|
|
|
DEF_PRIMITIVE(object_eqeq)
|
|
{
|
|
RETURN_BOOL(wrenValuesEqual(args[0], args[1]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(object_bangeq)
|
|
{
|
|
RETURN_BOOL(!wrenValuesEqual(args[0], args[1]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(object_is)
|
|
{
|
|
if (!IS_CLASS(args[1]))
|
|
{
|
|
RETURN_ERROR("Right operand must be a class.");
|
|
}
|
|
|
|
ObjClass *classObj = wrenGetClass(vm, args[0]);
|
|
ObjClass *baseClassObj = AS_CLASS(args[1]);
|
|
|
|
// Walk the superclass chain looking for the class.
|
|
do
|
|
{
|
|
if (baseClassObj == classObj) RETURN_BOOL(true);
|
|
|
|
classObj = classObj->superclass;
|
|
}
|
|
while (classObj != NULL);
|
|
|
|
RETURN_BOOL(false);
|
|
}
|
|
|
|
DEF_PRIMITIVE(object_toString)
|
|
{
|
|
Obj* obj = AS_OBJ(args[0]);
|
|
Value name = OBJ_VAL(obj->classObj->name);
|
|
RETURN_VAL(wrenStringFormat(vm, "instance of @", name));
|
|
}
|
|
|
|
DEF_PRIMITIVE(object_type)
|
|
{
|
|
RETURN_OBJ(wrenGetClass(vm, args[0]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_from)
|
|
{
|
|
RETURN_NUM(AS_RANGE(args[0])->from);
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_to)
|
|
{
|
|
RETURN_NUM(AS_RANGE(args[0])->to);
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_min)
|
|
{
|
|
ObjRange* range = AS_RANGE(args[0]);
|
|
RETURN_NUM(fmin(range->from, range->to));
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_max)
|
|
{
|
|
ObjRange* range = AS_RANGE(args[0]);
|
|
RETURN_NUM(fmax(range->from, range->to));
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_isInclusive)
|
|
{
|
|
RETURN_BOOL(AS_RANGE(args[0])->isInclusive);
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_iterate)
|
|
{
|
|
ObjRange* range = AS_RANGE(args[0]);
|
|
|
|
// Special case: empty range.
|
|
if (range->from == range->to && !range->isInclusive) RETURN_FALSE;
|
|
|
|
// Start the iteration.
|
|
if (IS_NULL(args[1])) RETURN_NUM(range->from);
|
|
|
|
if (!validateNum(vm, args[1], "Iterator")) return false;
|
|
|
|
double iterator = AS_NUM(args[1]);
|
|
|
|
// Iterate towards [to] from [from].
|
|
if (range->from < range->to)
|
|
{
|
|
iterator++;
|
|
if (iterator > range->to) RETURN_FALSE;
|
|
}
|
|
else
|
|
{
|
|
iterator--;
|
|
if (iterator < range->to) RETURN_FALSE;
|
|
}
|
|
|
|
if (!range->isInclusive && iterator == range->to) RETURN_FALSE;
|
|
|
|
RETURN_NUM(iterator);
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_iteratorValue)
|
|
{
|
|
// Assume the iterator is a number so that is the value of the range.
|
|
RETURN_VAL(args[1]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(range_toString)
|
|
{
|
|
ObjRange* range = AS_RANGE(args[0]);
|
|
|
|
Value from = wrenNumToString(vm, range->from);
|
|
wrenPushRoot(vm, AS_OBJ(from));
|
|
|
|
Value to = wrenNumToString(vm, range->to);
|
|
wrenPushRoot(vm, AS_OBJ(to));
|
|
|
|
Value result = wrenStringFormat(vm, "@$@", from,
|
|
range->isInclusive ? ".." : "...", to);
|
|
|
|
wrenPopRoot(vm);
|
|
wrenPopRoot(vm);
|
|
RETURN_VAL(result);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_fromCodePoint)
|
|
{
|
|
if (!validateInt(vm, args[1], "Code point")) return false;
|
|
|
|
int codePoint = (int)AS_NUM(args[1]);
|
|
if (codePoint < 0)
|
|
{
|
|
RETURN_ERROR("Code point cannot be negative.");
|
|
}
|
|
else if (codePoint > 0x10ffff)
|
|
{
|
|
RETURN_ERROR("Code point cannot be greater than 0x10ffff.");
|
|
}
|
|
|
|
RETURN_VAL(wrenStringFromCodePoint(vm, codePoint));
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_fromByte)
|
|
{
|
|
if (!validateInt(vm, args[1], "Byte")) return false;
|
|
int byte = (int) AS_NUM(args[1]);
|
|
if (byte < 0)
|
|
{
|
|
RETURN_ERROR("Byte cannot be negative.");
|
|
}
|
|
else if (byte > 0xff)
|
|
{
|
|
RETURN_ERROR("Byte cannot be greater than 0xff.");
|
|
}
|
|
RETURN_VAL(wrenStringFromByte(vm, (uint8_t) byte));
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_byteAt)
|
|
{
|
|
ObjString* string = AS_STRING(args[0]);
|
|
|
|
uint32_t index = validateIndex(vm, args[1], string->length, "Index");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
RETURN_NUM((uint8_t)string->value[index]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_byteCount)
|
|
{
|
|
RETURN_NUM(AS_STRING(args[0])->length);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_codePointAt)
|
|
{
|
|
ObjString* string = AS_STRING(args[0]);
|
|
|
|
uint32_t index = validateIndex(vm, args[1], string->length, "Index");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
// If we are in the middle of a UTF-8 sequence, indicate that.
|
|
const uint8_t* bytes = (uint8_t*)string->value;
|
|
if ((bytes[index] & 0xc0) == 0x80) RETURN_NUM(-1);
|
|
|
|
// Decode the UTF-8 sequence.
|
|
RETURN_NUM(wrenUtf8Decode((uint8_t*)string->value + index,
|
|
string->length - index));
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_contains)
|
|
{
|
|
if (!validateString(vm, args[1], "Argument")) return false;
|
|
|
|
ObjString* string = AS_STRING(args[0]);
|
|
ObjString* search = AS_STRING(args[1]);
|
|
|
|
RETURN_BOOL(wrenStringFind(string, search, 0) != UINT32_MAX);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_endsWith)
|
|
{
|
|
if (!validateString(vm, args[1], "Argument")) return false;
|
|
|
|
ObjString* string = AS_STRING(args[0]);
|
|
ObjString* search = AS_STRING(args[1]);
|
|
|
|
// Edge case: If the search string is longer then return false right away.
|
|
if (search->length > string->length) RETURN_FALSE;
|
|
|
|
RETURN_BOOL(memcmp(string->value + string->length - search->length,
|
|
search->value, search->length) == 0);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_indexOf1)
|
|
{
|
|
if (!validateString(vm, args[1], "Argument")) return false;
|
|
|
|
ObjString* string = AS_STRING(args[0]);
|
|
ObjString* search = AS_STRING(args[1]);
|
|
|
|
uint32_t index = wrenStringFind(string, search, 0);
|
|
RETURN_NUM(index == UINT32_MAX ? -1 : (int)index);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_indexOf2)
|
|
{
|
|
if (!validateString(vm, args[1], "Argument")) return false;
|
|
|
|
ObjString* string = AS_STRING(args[0]);
|
|
ObjString* search = AS_STRING(args[1]);
|
|
uint32_t start = validateIndex(vm, args[2], string->length, "Start");
|
|
if (start == UINT32_MAX) return false;
|
|
|
|
uint32_t index = wrenStringFind(string, search, start);
|
|
RETURN_NUM(index == UINT32_MAX ? -1 : (int)index);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_iterate)
|
|
{
|
|
ObjString* string = AS_STRING(args[0]);
|
|
|
|
// If we're starting the iteration, return the first index.
|
|
if (IS_NULL(args[1]))
|
|
{
|
|
if (string->length == 0) RETURN_FALSE;
|
|
RETURN_NUM(0);
|
|
}
|
|
|
|
if (!validateInt(vm, args[1], "Iterator")) return false;
|
|
|
|
if (AS_NUM(args[1]) < 0) RETURN_FALSE;
|
|
uint32_t index = (uint32_t)AS_NUM(args[1]);
|
|
|
|
// Advance to the beginning of the next UTF-8 sequence.
|
|
do
|
|
{
|
|
index++;
|
|
if (index >= string->length) RETURN_FALSE;
|
|
} while ((string->value[index] & 0xc0) == 0x80);
|
|
|
|
RETURN_NUM(index);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_iterateByte)
|
|
{
|
|
ObjString* string = AS_STRING(args[0]);
|
|
|
|
// If we're starting the iteration, return the first index.
|
|
if (IS_NULL(args[1]))
|
|
{
|
|
if (string->length == 0) RETURN_FALSE;
|
|
RETURN_NUM(0);
|
|
}
|
|
|
|
if (!validateInt(vm, args[1], "Iterator")) return false;
|
|
|
|
if (AS_NUM(args[1]) < 0) RETURN_FALSE;
|
|
uint32_t index = (uint32_t)AS_NUM(args[1]);
|
|
|
|
// Advance to the next byte.
|
|
index++;
|
|
if (index >= string->length) RETURN_FALSE;
|
|
|
|
RETURN_NUM(index);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_iteratorValue)
|
|
{
|
|
ObjString* string = AS_STRING(args[0]);
|
|
uint32_t index = validateIndex(vm, args[1], string->length, "Iterator");
|
|
if (index == UINT32_MAX) return false;
|
|
|
|
RETURN_VAL(wrenStringCodePointAt(vm, string, index));
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_startsWith)
|
|
{
|
|
if (!validateString(vm, args[1], "Argument")) return false;
|
|
|
|
ObjString* string = AS_STRING(args[0]);
|
|
ObjString* search = AS_STRING(args[1]);
|
|
|
|
// Edge case: If the search string is longer then return false right away.
|
|
if (search->length > string->length) RETURN_FALSE;
|
|
|
|
RETURN_BOOL(memcmp(string->value, search->value, search->length) == 0);
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_plus)
|
|
{
|
|
if (!validateString(vm, args[1], "Right operand")) return false;
|
|
RETURN_VAL(wrenStringFormat(vm, "@@", args[0], args[1]));
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_subscript)
|
|
{
|
|
ObjString* string = AS_STRING(args[0]);
|
|
|
|
if (IS_NUM(args[1]))
|
|
{
|
|
int index = validateIndex(vm, args[1], string->length, "Subscript");
|
|
if (index == -1) return false;
|
|
|
|
RETURN_VAL(wrenStringCodePointAt(vm, string, index));
|
|
}
|
|
|
|
if (!IS_RANGE(args[1]))
|
|
{
|
|
RETURN_ERROR("Subscript must be a number or a range.");
|
|
}
|
|
|
|
int step;
|
|
uint32_t count = string->length;
|
|
int start = calculateRange(vm, AS_RANGE(args[1]), &count, &step);
|
|
if (start == -1) return false;
|
|
|
|
RETURN_VAL(wrenNewStringFromRange(vm, string, start, count, step));
|
|
}
|
|
|
|
DEF_PRIMITIVE(string_toString)
|
|
{
|
|
RETURN_VAL(args[0]);
|
|
}
|
|
|
|
DEF_PRIMITIVE(system_clock)
|
|
{
|
|
RETURN_NUM((double)clock() / CLOCKS_PER_SEC);
|
|
}
|
|
|
|
DEF_PRIMITIVE(system_gc)
|
|
{
|
|
wrenCollectGarbage(vm);
|
|
RETURN_NULL;
|
|
}
|
|
|
|
DEF_PRIMITIVE(system_writeString)
|
|
{
|
|
if (vm->config.writeFn != NULL)
|
|
{
|
|
vm->config.writeFn(vm, AS_CSTRING(args[1]));
|
|
}
|
|
|
|
RETURN_VAL(args[1]);
|
|
}
|
|
|
|
// Creates either the Object or Class class in the core module with [name].
|
|
static ObjClass* defineClass(WrenVM* vm, ObjModule* module, const char* name)
|
|
{
|
|
ObjString* nameString = AS_STRING(wrenNewString(vm, name));
|
|
wrenPushRoot(vm, (Obj*)nameString);
|
|
|
|
ObjClass* classObj = wrenNewSingleClass(vm, 0, nameString);
|
|
|
|
wrenDefineVariable(vm, module, name, nameString->length, OBJ_VAL(classObj));
|
|
|
|
wrenPopRoot(vm);
|
|
return classObj;
|
|
}
|
|
|
|
void wrenInitializeCore(WrenVM* vm)
|
|
{
|
|
ObjModule* coreModule = wrenNewModule(vm, NULL);
|
|
wrenPushRoot(vm, (Obj*)coreModule);
|
|
|
|
// The core module's key is null in the module map.
|
|
wrenMapSet(vm, vm->modules, NULL_VAL, OBJ_VAL(coreModule));
|
|
wrenPopRoot(vm); // coreModule.
|
|
|
|
// Define the root Object class. This has to be done a little specially
|
|
// because it has no superclass.
|
|
vm->objectClass = defineClass(vm, coreModule, "Object");
|
|
PRIMITIVE(vm->objectClass, "!", object_not);
|
|
PRIMITIVE(vm->objectClass, "==(_)", object_eqeq);
|
|
PRIMITIVE(vm->objectClass, "!=(_)", object_bangeq);
|
|
PRIMITIVE(vm->objectClass, "is(_)", object_is);
|
|
PRIMITIVE(vm->objectClass, "toString", object_toString);
|
|
PRIMITIVE(vm->objectClass, "type", object_type);
|
|
|
|
// Now we can define Class, which is a subclass of Object.
|
|
vm->classClass = defineClass(vm, coreModule, "Class");
|
|
wrenBindSuperclass(vm, vm->classClass, vm->objectClass);
|
|
PRIMITIVE(vm->classClass, "name", class_name);
|
|
PRIMITIVE(vm->classClass, "supertype", class_supertype);
|
|
PRIMITIVE(vm->classClass, "toString", class_toString);
|
|
|
|
// Finally, we can define Object's metaclass which is a subclass of Class.
|
|
ObjClass* objectMetaclass = defineClass(vm, coreModule, "Object metaclass");
|
|
|
|
// Wire up the metaclass relationships now that all three classes are built.
|
|
vm->objectClass->obj.classObj = objectMetaclass;
|
|
objectMetaclass->obj.classObj = vm->classClass;
|
|
vm->classClass->obj.classObj = vm->classClass;
|
|
|
|
// Do this after wiring up the metaclasses so objectMetaclass doesn't get
|
|
// collected.
|
|
wrenBindSuperclass(vm, objectMetaclass, vm->classClass);
|
|
|
|
PRIMITIVE(objectMetaclass, "same(_,_)", object_same);
|
|
|
|
// The core class diagram ends up looking like this, where single lines point
|
|
// to a class's superclass, and double lines point to its metaclass:
|
|
//
|
|
// .------------------------------------. .====.
|
|
// | .---------------. | # #
|
|
// v | v | v #
|
|
// .---------. .-------------------. .-------. #
|
|
// | Object |==>| Object metaclass |==>| Class |=="
|
|
// '---------' '-------------------' '-------'
|
|
// ^ ^ ^ ^ ^
|
|
// | .--------------' # | #
|
|
// | | # | #
|
|
// .---------. .-------------------. # | # -.
|
|
// | Base |==>| Base metaclass |======" | # |
|
|
// '---------' '-------------------' | # |
|
|
// ^ | # |
|
|
// | .------------------' # | Example classes
|
|
// | | # |
|
|
// .---------. .-------------------. # |
|
|
// | Derived |==>| Derived metaclass |==========" |
|
|
// '---------' '-------------------' -'
|
|
|
|
// The rest of the classes can now be defined normally.
|
|
wrenInterpret(vm, NULL, coreModuleSource);
|
|
|
|
vm->boolClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Bool"));
|
|
PRIMITIVE(vm->boolClass, "toString", bool_toString);
|
|
PRIMITIVE(vm->boolClass, "!", bool_not);
|
|
|
|
vm->fiberClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Fiber"));
|
|
PRIMITIVE(vm->fiberClass->obj.classObj, "new(_)", fiber_new);
|
|
PRIMITIVE(vm->fiberClass->obj.classObj, "abort(_)", fiber_abort);
|
|
PRIMITIVE(vm->fiberClass->obj.classObj, "current", fiber_current);
|
|
PRIMITIVE(vm->fiberClass->obj.classObj, "suspend()", fiber_suspend);
|
|
PRIMITIVE(vm->fiberClass->obj.classObj, "yield()", fiber_yield);
|
|
PRIMITIVE(vm->fiberClass->obj.classObj, "yield(_)", fiber_yield1);
|
|
PRIMITIVE(vm->fiberClass, "call()", fiber_call);
|
|
PRIMITIVE(vm->fiberClass, "call(_)", fiber_call1);
|
|
PRIMITIVE(vm->fiberClass, "error", fiber_error);
|
|
PRIMITIVE(vm->fiberClass, "isDone", fiber_isDone);
|
|
PRIMITIVE(vm->fiberClass, "transfer()", fiber_transfer);
|
|
PRIMITIVE(vm->fiberClass, "transfer(_)", fiber_transfer1);
|
|
PRIMITIVE(vm->fiberClass, "transferError(_)", fiber_transferError);
|
|
PRIMITIVE(vm->fiberClass, "try()", fiber_try);
|
|
|
|
vm->fnClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Fn"));
|
|
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
|
|
|
|
PRIMITIVE(vm->fnClass, "arity", fn_arity);
|
|
PRIMITIVE(vm->fnClass, "call()", fn_call0);
|
|
PRIMITIVE(vm->fnClass, "call(_)", fn_call1);
|
|
PRIMITIVE(vm->fnClass, "call(_,_)", fn_call2);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_)", fn_call3);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_)", fn_call4);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_)", fn_call5);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_)", fn_call6);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_)", fn_call7);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_)", fn_call8);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_)", fn_call9);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_)", fn_call10);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_)", fn_call11);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_)", fn_call12);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call13);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call14);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call15);
|
|
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call16);
|
|
PRIMITIVE(vm->fnClass, "toString", fn_toString);
|
|
|
|
vm->nullClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Null"));
|
|
PRIMITIVE(vm->nullClass, "!", null_not);
|
|
PRIMITIVE(vm->nullClass, "toString", null_toString);
|
|
|
|
vm->numClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Num"));
|
|
PRIMITIVE(vm->numClass->obj.classObj, "fromString(_)", num_fromString);
|
|
PRIMITIVE(vm->numClass->obj.classObj, "pi", num_pi);
|
|
PRIMITIVE(vm->numClass->obj.classObj, "largest", num_largest);
|
|
PRIMITIVE(vm->numClass->obj.classObj, "smallest", num_smallest);
|
|
PRIMITIVE(vm->numClass, "-(_)", num_minus);
|
|
PRIMITIVE(vm->numClass, "+(_)", num_plus);
|
|
PRIMITIVE(vm->numClass, "*(_)", num_multiply);
|
|
PRIMITIVE(vm->numClass, "/(_)", num_divide);
|
|
PRIMITIVE(vm->numClass, "<(_)", num_lt);
|
|
PRIMITIVE(vm->numClass, ">(_)", num_gt);
|
|
PRIMITIVE(vm->numClass, "<=(_)", num_lte);
|
|
PRIMITIVE(vm->numClass, ">=(_)", num_gte);
|
|
PRIMITIVE(vm->numClass, "&(_)", num_bitwiseAnd);
|
|
PRIMITIVE(vm->numClass, "|(_)", num_bitwiseOr);
|
|
PRIMITIVE(vm->numClass, "^(_)", num_bitwiseXor);
|
|
PRIMITIVE(vm->numClass, "<<(_)", num_bitwiseLeftShift);
|
|
PRIMITIVE(vm->numClass, ">>(_)", num_bitwiseRightShift);
|
|
PRIMITIVE(vm->numClass, "abs", num_abs);
|
|
PRIMITIVE(vm->numClass, "acos", num_acos);
|
|
PRIMITIVE(vm->numClass, "asin", num_asin);
|
|
PRIMITIVE(vm->numClass, "atan", num_atan);
|
|
PRIMITIVE(vm->numClass, "ceil", num_ceil);
|
|
PRIMITIVE(vm->numClass, "cos", num_cos);
|
|
PRIMITIVE(vm->numClass, "floor", num_floor);
|
|
PRIMITIVE(vm->numClass, "-", num_negate);
|
|
PRIMITIVE(vm->numClass, "round", num_round);
|
|
PRIMITIVE(vm->numClass, "sin", num_sin);
|
|
PRIMITIVE(vm->numClass, "sqrt", num_sqrt);
|
|
PRIMITIVE(vm->numClass, "tan", num_tan);
|
|
PRIMITIVE(vm->numClass, "log", num_log);
|
|
PRIMITIVE(vm->numClass, "%(_)", num_mod);
|
|
PRIMITIVE(vm->numClass, "~", num_bitwiseNot);
|
|
PRIMITIVE(vm->numClass, "..(_)", num_dotDot);
|
|
PRIMITIVE(vm->numClass, "...(_)", num_dotDotDot);
|
|
PRIMITIVE(vm->numClass, "atan(_)", num_atan2);
|
|
PRIMITIVE(vm->numClass, "pow(_)", num_pow);
|
|
PRIMITIVE(vm->numClass, "fraction", num_fraction);
|
|
PRIMITIVE(vm->numClass, "isInfinity", num_isInfinity);
|
|
PRIMITIVE(vm->numClass, "isInteger", num_isInteger);
|
|
PRIMITIVE(vm->numClass, "isNan", num_isNan);
|
|
PRIMITIVE(vm->numClass, "sign", num_sign);
|
|
PRIMITIVE(vm->numClass, "toString", num_toString);
|
|
PRIMITIVE(vm->numClass, "truncate", num_truncate);
|
|
|
|
// These are defined just so that 0 and -0 are equal, which is specified by
|
|
// IEEE 754 even though they have different bit representations.
|
|
PRIMITIVE(vm->numClass, "==(_)", num_eqeq);
|
|
PRIMITIVE(vm->numClass, "!=(_)", num_bangeq);
|
|
|
|
vm->stringClass = AS_CLASS(wrenFindVariable(vm, coreModule, "String"));
|
|
PRIMITIVE(vm->stringClass->obj.classObj, "fromCodePoint(_)", string_fromCodePoint);
|
|
PRIMITIVE(vm->stringClass->obj.classObj, "fromByte(_)", string_fromByte);
|
|
PRIMITIVE(vm->stringClass, "+(_)", string_plus);
|
|
PRIMITIVE(vm->stringClass, "[_]", string_subscript);
|
|
PRIMITIVE(vm->stringClass, "byteAt_(_)", string_byteAt);
|
|
PRIMITIVE(vm->stringClass, "byteCount_", string_byteCount);
|
|
PRIMITIVE(vm->stringClass, "codePointAt_(_)", string_codePointAt);
|
|
PRIMITIVE(vm->stringClass, "contains(_)", string_contains);
|
|
PRIMITIVE(vm->stringClass, "endsWith(_)", string_endsWith);
|
|
PRIMITIVE(vm->stringClass, "indexOf(_)", string_indexOf1);
|
|
PRIMITIVE(vm->stringClass, "indexOf(_,_)", string_indexOf2);
|
|
PRIMITIVE(vm->stringClass, "iterate(_)", string_iterate);
|
|
PRIMITIVE(vm->stringClass, "iterateByte_(_)", string_iterateByte);
|
|
PRIMITIVE(vm->stringClass, "iteratorValue(_)", string_iteratorValue);
|
|
PRIMITIVE(vm->stringClass, "startsWith(_)", string_startsWith);
|
|
PRIMITIVE(vm->stringClass, "toString", string_toString);
|
|
|
|
vm->listClass = AS_CLASS(wrenFindVariable(vm, coreModule, "List"));
|
|
PRIMITIVE(vm->listClass->obj.classObj, "filled(_,_)", list_filled);
|
|
PRIMITIVE(vm->listClass->obj.classObj, "new()", list_new);
|
|
PRIMITIVE(vm->listClass, "[_]", list_subscript);
|
|
PRIMITIVE(vm->listClass, "[_]=(_)", list_subscriptSetter);
|
|
PRIMITIVE(vm->listClass, "add(_)", list_add);
|
|
PRIMITIVE(vm->listClass, "addCore_(_)", list_addCore);
|
|
PRIMITIVE(vm->listClass, "clear()", list_clear);
|
|
PRIMITIVE(vm->listClass, "count", list_count);
|
|
PRIMITIVE(vm->listClass, "insert(_,_)", list_insert);
|
|
PRIMITIVE(vm->listClass, "iterate(_)", list_iterate);
|
|
PRIMITIVE(vm->listClass, "iteratorValue(_)", list_iteratorValue);
|
|
PRIMITIVE(vm->listClass, "removeAt(_)", list_removeAt);
|
|
|
|
vm->mapClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Map"));
|
|
PRIMITIVE(vm->mapClass->obj.classObj, "new()", map_new);
|
|
PRIMITIVE(vm->mapClass, "[_]", map_subscript);
|
|
PRIMITIVE(vm->mapClass, "[_]=(_)", map_subscriptSetter);
|
|
PRIMITIVE(vm->mapClass, "addCore_(_,_)", map_addCore);
|
|
PRIMITIVE(vm->mapClass, "clear()", map_clear);
|
|
PRIMITIVE(vm->mapClass, "containsKey(_)", map_containsKey);
|
|
PRIMITIVE(vm->mapClass, "count", map_count);
|
|
PRIMITIVE(vm->mapClass, "remove(_)", map_remove);
|
|
PRIMITIVE(vm->mapClass, "iterate(_)", map_iterate);
|
|
PRIMITIVE(vm->mapClass, "keyIteratorValue_(_)", map_keyIteratorValue);
|
|
PRIMITIVE(vm->mapClass, "valueIteratorValue_(_)", map_valueIteratorValue);
|
|
|
|
vm->rangeClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Range"));
|
|
PRIMITIVE(vm->rangeClass, "from", range_from);
|
|
PRIMITIVE(vm->rangeClass, "to", range_to);
|
|
PRIMITIVE(vm->rangeClass, "min", range_min);
|
|
PRIMITIVE(vm->rangeClass, "max", range_max);
|
|
PRIMITIVE(vm->rangeClass, "isInclusive", range_isInclusive);
|
|
PRIMITIVE(vm->rangeClass, "iterate(_)", range_iterate);
|
|
PRIMITIVE(vm->rangeClass, "iteratorValue(_)", range_iteratorValue);
|
|
PRIMITIVE(vm->rangeClass, "toString", range_toString);
|
|
|
|
ObjClass* systemClass = AS_CLASS(wrenFindVariable(vm, coreModule, "System"));
|
|
PRIMITIVE(systemClass->obj.classObj, "clock", system_clock);
|
|
PRIMITIVE(systemClass->obj.classObj, "gc()", system_gc);
|
|
PRIMITIVE(systemClass->obj.classObj, "writeString_(_)", system_writeString);
|
|
|
|
// While bootstrapping the core types and running the core module, a number
|
|
// of string objects have been created, many of which were instantiated
|
|
// before stringClass was stored in the VM. Some of them *must* be created
|
|
// first -- the ObjClass for string itself has a reference to the ObjString
|
|
// for its name.
|
|
//
|
|
// These all currently have a NULL classObj pointer, so go back and assign
|
|
// them now that the string class is known.
|
|
for (Obj* obj = vm->first; obj != NULL; obj = obj->next)
|
|
{
|
|
if (obj->type == OBJ_STRING) obj->classObj = vm->stringClass;
|
|
}
|
|
}
|