2013-11-10 06:01:18 +01:00
|
|
|
#include <math.h>
|
2013-10-28 06:45:40 +01:00
|
|
|
#include <stdio.h>
|
2013-10-27 19:28:15 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2013-11-22 17:55:22 +01:00
|
|
|
#include <time.h>
|
2013-10-27 19:28:15 +01:00
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
#include "wren_core.h"
|
2013-11-28 17:11:50 +01:00
|
|
|
#include "wren_value.h"
|
2013-10-27 19:28:15 +01:00
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
// Binds a native method named [name] (in Wren) implemented using C function
|
|
|
|
|
// [fn] to `ObjClass` [cls].
|
|
|
|
|
#define NATIVE(cls, name, fn) \
|
2013-10-27 19:28:15 +01:00
|
|
|
{ \
|
2013-12-27 18:07:35 +01:00
|
|
|
int symbol = ensureSymbol(vm, &vm->methods, name, strlen(name)); \
|
2013-10-28 06:45:40 +01:00
|
|
|
cls->methods[symbol].type = METHOD_PRIMITIVE; \
|
2013-11-28 17:00:55 +01:00
|
|
|
cls->methods[symbol].primitive = native_##fn; \
|
2013-10-27 19:28:15 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
// Defines a native method whose C function name is [native]. This abstracts
|
|
|
|
|
// the actual type signature of a native function and makes it clear which C
|
|
|
|
|
// functions are intended to be invoked as natives.
|
|
|
|
|
#define DEF_NATIVE(native) \
|
2014-01-01 22:25:24 +01:00
|
|
|
static PrimitiveResult native_##native(WrenVM* vm, ObjFiber* fiber, Value* args)
|
|
|
|
|
|
|
|
|
|
#define RETURN_VAL(value) do { args[0] = value; return PRIM_VALUE; } while (0)
|
2013-11-17 18:02:57 +01:00
|
|
|
|
2014-01-01 22:25:24 +01:00
|
|
|
#define RETURN_BOOL(value) RETURN_VAL(BOOL_VAL(value))
|
|
|
|
|
#define RETURN_FALSE RETURN_VAL(FALSE_VAL)
|
|
|
|
|
#define RETURN_NULL RETURN_VAL(NULL_VAL)
|
|
|
|
|
#define RETURN_NUM(value) RETURN_VAL(NUM_VAL(value))
|
|
|
|
|
#define RETURN_TRUE RETURN_VAL(TRUE_VAL)
|
|
|
|
|
|
|
|
|
|
#define RETURN_ERROR(msg) \
|
|
|
|
|
do { \
|
|
|
|
|
args[0] = wrenNewString(vm, msg, strlen(msg)); \
|
|
|
|
|
return PRIM_ERROR; \
|
|
|
|
|
} while (0);
|
2013-10-28 06:45:40 +01:00
|
|
|
|
2013-12-22 20:50:00 +01:00
|
|
|
// This string literal is generated automatically from corelib.wren using
|
|
|
|
|
// make_corelib. Do not edit here.
|
|
|
|
|
const char* coreLibSource =
|
|
|
|
|
"class IO {\n"
|
|
|
|
|
" static write(obj) {\n"
|
|
|
|
|
" IO.write__native__(obj.toString)\n"
|
|
|
|
|
" return obj\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class List {\n"
|
|
|
|
|
" toString {\n"
|
|
|
|
|
" var result = \"[\"\n"
|
|
|
|
|
" var i = 0\n"
|
|
|
|
|
" // TODO: Use for loop.\n"
|
|
|
|
|
" while (i < this.count) {\n"
|
|
|
|
|
" if (i > 0) result = result + \", \"\n"
|
|
|
|
|
" result = result + this[i].toString\n"
|
|
|
|
|
" i = i + 1\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" result = result + \"]\"\n"
|
|
|
|
|
" return result\n"
|
|
|
|
|
" }\n"
|
2013-12-26 00:01:45 +01:00
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class Range {\n"
|
|
|
|
|
" new(min, max) {\n"
|
|
|
|
|
" _min = min\n"
|
|
|
|
|
" _max = max\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" min { return _min }\n"
|
|
|
|
|
" max { return _max }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" iterate(previous) {\n"
|
|
|
|
|
" if (previous == null) return _min\n"
|
|
|
|
|
" if (previous == _max) return false\n"
|
|
|
|
|
" return previous + 1\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" iteratorValue(iterator) {\n"
|
|
|
|
|
" return iterator\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class Num {\n"
|
|
|
|
|
" .. other { return new Range(this, other) }\n"
|
|
|
|
|
" ... other { return new Range(this, other - 1) }\n"
|
2013-12-22 20:50:00 +01:00
|
|
|
"}\n";
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(bool_not)
|
2013-11-13 16:10:52 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_BOOL(!AS_BOOL(args[0]));
|
2013-11-13 16:10:52 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(bool_toString)
|
2013-11-04 06:38:58 +01:00
|
|
|
{
|
|
|
|
|
if (AS_BOOL(args[0]))
|
|
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(wrenNewString(vm, "true", 4));
|
2013-11-04 06:38:58 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(wrenNewString(vm, "false", 5));
|
2013-11-04 06:38:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-01 22:25:24 +01:00
|
|
|
DEF_NATIVE(fn_call) { return PRIM_CALL; }
|
2013-11-15 03:25:28 +01:00
|
|
|
|
2013-11-27 08:11:11 +01:00
|
|
|
// Validates that [index] is an integer within `[0, count)`. Also allows
|
|
|
|
|
// negative indices which map backwards from the end. Returns the valid positive
|
|
|
|
|
// index value, or -1 if the index wasn't valid (not a number, not an int, out
|
|
|
|
|
// of bounds).
|
|
|
|
|
static int validateIndex(Value index, int count)
|
|
|
|
|
{
|
|
|
|
|
if (!IS_NUM(index)) return -1;
|
|
|
|
|
|
|
|
|
|
double indexNum = AS_NUM(index);
|
|
|
|
|
int intIndex = (int)indexNum;
|
|
|
|
|
// Make sure the index is an integer.
|
|
|
|
|
if (indexNum != intIndex) return -1;
|
|
|
|
|
|
|
|
|
|
// Negative indices count from the end.
|
|
|
|
|
if (indexNum < 0) indexNum = count + indexNum;
|
|
|
|
|
|
|
|
|
|
// Check bounds.
|
|
|
|
|
if (indexNum < 0 || indexNum >= count) return -1;
|
feat: implement closures with upvalue support and statement/expression separation
Add closure objects that wrap functions with captured upvalues, enabling
first-class closures that close over local variables from enclosing scopes.
Introduce new bytecode instructions (CODE_CLOSURE, CODE_LOAD_UPVALUE,
CODE_STORE_UPVALUE, CODE_CLOSE_UPVALUE, CODE_RETURN) and a CompilerUpvalue
tracking system with a MAX_UPVALUES limit of 256. Refactor the grammar to
strictly separate statements from expressions, fixing a bug where block
expressions incorrectly popped locals while temporaries remained on the
stack. Rename internal functions (wrenCallFunction, wrenDebugDumpInstruction,
wrenDebugDumpStack) and add Upvalue allocation, closure creation, and
debug dump support for the new instructions.
2013-12-04 16:43:50 +01:00
|
|
|
|
2013-11-27 08:11:11 +01:00
|
|
|
return indexNum;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(list_add)
|
2013-11-27 03:02:10 +01:00
|
|
|
{
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
2013-12-27 01:58:03 +01:00
|
|
|
wrenListAdd(vm, list, args[1]);
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(args[1]);
|
2013-11-27 03:02:10 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(list_clear)
|
2013-11-27 08:11:11 +01:00
|
|
|
{
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
2013-12-14 23:17:16 +01:00
|
|
|
wrenReallocate(vm, list->elements, 0, 0);
|
2013-12-22 23:31:33 +01:00
|
|
|
list->elements = NULL;
|
2013-11-27 08:11:11 +01:00
|
|
|
list->capacity = 0;
|
|
|
|
|
list->count = 0;
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NULL;
|
2013-11-27 08:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(list_count)
|
2013-11-24 22:38:31 +01:00
|
|
|
{
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(list->count);
|
2013-11-24 22:38:31 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(list_insert)
|
2013-11-25 04:08:46 +01:00
|
|
|
{
|
2013-11-27 08:11:11 +01:00
|
|
|
ObjList* list = AS_LIST(args[0]);
|
2013-11-25 04:08:46 +01:00
|
|
|
|
feat: implement list_removeAt primitive with capacity shrink and test suite
Add DEF_PRIMITIVE(list_removeAt) to src/primitives.c that removes an element at a given index from an ObjList, shifts remaining elements up, shrinks capacity when excess is detected via LIST_GROW_FACTOR, and returns the removed Value. Register the primitive as "removeAt " on vm->listClass. Create test/list/remove_at.wren covering removal at positive indices 0,1,2, negative indices -1,-2,-3, out-of-bounds cases returning null, and verification that the removed value is returned.
2013-11-27 19:20:32 +01:00
|
|
|
// count + 1 here so you can "insert" at the very end.
|
2013-11-27 08:11:11 +01:00
|
|
|
int index = validateIndex(args[2], list->count + 1);
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Instead of returning null here, should signal an error explicitly
|
|
|
|
|
// somehow.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (index == -1) RETURN_NULL;
|
2013-11-25 04:08:46 +01:00
|
|
|
|
2013-12-27 01:58:03 +01:00
|
|
|
wrenListInsert(vm, list, args[1], index);
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(args[1]);
|
2013-11-27 08:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-25 06:04:11 +01:00
|
|
|
DEF_NATIVE(list_iterate)
|
|
|
|
|
{
|
|
|
|
|
// If we're starting the iteration, return the first index.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (IS_NULL(args[1])) RETURN_NUM(0);
|
2013-12-25 06:04:11 +01:00
|
|
|
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
|
double index = AS_NUM(args[1]);
|
|
|
|
|
// TODO: Handle arg not a number or not an integer.
|
|
|
|
|
|
|
|
|
|
// Stop if we're out of elements.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (index >= list->count - 1) RETURN_FALSE;
|
2013-12-25 06:04:11 +01:00
|
|
|
|
|
|
|
|
// Otherwise, move to the next index.
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(index + 1);
|
2013-12-25 06:04:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEF_NATIVE(list_iteratorValue)
|
|
|
|
|
{
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
|
double index = AS_NUM(args[1]);
|
|
|
|
|
// TODO: Handle index out of bounds or not integer.
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(list->elements[(int)index]);
|
2013-12-25 06:04:11 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(list_removeAt)
|
feat: implement list_removeAt primitive with capacity shrink and test suite
Add DEF_PRIMITIVE(list_removeAt) to src/primitives.c that removes an element at a given index from an ObjList, shifts remaining elements up, shrinks capacity when excess is detected via LIST_GROW_FACTOR, and returns the removed Value. Register the primitive as "removeAt " on vm->listClass. Create test/list/remove_at.wren covering removal at positive indices 0,1,2, negative indices -1,-2,-3, out-of-bounds cases returning null, and verification that the removed value is returned.
2013-11-27 19:20:32 +01:00
|
|
|
{
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
|
int index = validateIndex(args[1], list->count);
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Instead of returning null here, should signal an error explicitly
|
|
|
|
|
// somehow.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (index == -1) RETURN_NULL;
|
feat: implement list_removeAt primitive with capacity shrink and test suite
Add DEF_PRIMITIVE(list_removeAt) to src/primitives.c that removes an element at a given index from an ObjList, shifts remaining elements up, shrinks capacity when excess is detected via LIST_GROW_FACTOR, and returns the removed Value. Register the primitive as "removeAt " on vm->listClass. Create test/list/remove_at.wren covering removal at positive indices 0,1,2, negative indices -1,-2,-3, out-of-bounds cases returning null, and verification that the removed value is returned.
2013-11-27 19:20:32 +01:00
|
|
|
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(wrenListRemoveAt(vm, list, index));
|
feat: implement list_removeAt primitive with capacity shrink and test suite
Add DEF_PRIMITIVE(list_removeAt) to src/primitives.c that removes an element at a given index from an ObjList, shifts remaining elements up, shrinks capacity when excess is detected via LIST_GROW_FACTOR, and returns the removed Value. Register the primitive as "removeAt " on vm->listClass. Create test/list/remove_at.wren covering removal at positive indices 0,1,2, negative indices -1,-2,-3, out-of-bounds cases returning null, and verification that the removed value is returned.
2013-11-27 19:20:32 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(list_subscript)
|
2013-11-27 08:11:11 +01:00
|
|
|
{
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
|
|
|
|
|
|
int index = validateIndex(args[1], list->count);
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Instead of returning null here, should signal an error explicitly
|
|
|
|
|
// somehow.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (index == -1) RETURN_NULL;
|
2013-11-25 04:08:46 +01:00
|
|
|
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(list->elements[index]);
|
2013-11-25 04:08:46 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-21 18:37:59 +01:00
|
|
|
DEF_NATIVE(list_subscriptSetter)
|
|
|
|
|
{
|
|
|
|
|
ObjList* list = AS_LIST(args[0]);
|
|
|
|
|
|
|
|
|
|
int index = validateIndex(args[1], list->count);
|
|
|
|
|
// TODO: Instead of returning null here, should signal an error explicitly
|
|
|
|
|
// somehow.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (index == -1) RETURN_NULL;
|
2013-12-21 18:37:59 +01:00
|
|
|
|
|
|
|
|
list->elements[index] = args[2];
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(args[2]);
|
2013-12-21 18:37:59 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-08 03:47:40 +01:00
|
|
|
DEF_NATIVE(null_toString)
|
|
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(wrenNewString(vm, "null", 4));
|
2013-12-08 03:47:40 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_abs)
|
2013-10-27 19:28:15 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(fabs(AS_NUM(args[0])));
|
2013-10-27 19:28:15 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_toString)
|
2013-11-02 07:27:28 +01:00
|
|
|
{
|
2013-12-24 00:38:00 +01:00
|
|
|
// According to Lua implementation, this should be enough for longest number
|
|
|
|
|
// formatted using %.14g.
|
|
|
|
|
char buffer[21];
|
|
|
|
|
sprintf(buffer, "%.14g", AS_NUM(args[0]));
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(wrenNewString(vm, buffer, strlen(buffer)));
|
2013-11-02 07:27:28 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_negate)
|
2013-11-13 16:10:52 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(-AS_NUM(args[0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validates that the second argument in [args] is a Num. Returns true if it is.
|
|
|
|
|
// If not, sets a type error and returns false.
|
|
|
|
|
static bool checkNumType(WrenVM* vm, Value* args)
|
|
|
|
|
{
|
|
|
|
|
if (IS_NUM(args[1])) return true;
|
|
|
|
|
|
|
|
|
|
const char* msg = "TypeError: Right operand must be Num.";
|
|
|
|
|
args[0] = wrenNewString(vm, msg, strlen(msg));
|
|
|
|
|
return false;
|
2013-11-13 16:10:52 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_minus)
|
2013-10-31 15:04:44 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!checkNumType(vm, args)) return PRIM_ERROR;
|
|
|
|
|
RETURN_NUM(AS_NUM(args[0]) - AS_NUM(args[1]));
|
2013-10-31 15:04:44 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_plus)
|
2013-10-31 15:04:44 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!checkNumType(vm, args)) return PRIM_ERROR;
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Handle coercion to string if RHS is a string.
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(AS_NUM(args[0]) + AS_NUM(args[1]));
|
2013-10-31 15:04:44 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_multiply)
|
2013-10-31 15:04:44 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!checkNumType(vm, args)) return PRIM_ERROR;
|
|
|
|
|
RETURN_NUM(AS_NUM(args[0]) * AS_NUM(args[1]));
|
2013-10-31 15:04:44 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_divide)
|
2013-10-31 15:04:44 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!checkNumType(vm, args)) return PRIM_ERROR;
|
|
|
|
|
RETURN_NUM(AS_NUM(args[0]) / AS_NUM(args[1]));
|
2013-10-31 15:04:44 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_mod)
|
2013-11-10 06:01:18 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!checkNumType(vm, args)) return PRIM_ERROR;
|
|
|
|
|
if (!IS_NUM(args[1])) RETURN_NULL;
|
|
|
|
|
RETURN_NUM(fmod(AS_NUM(args[0]), AS_NUM(args[1])));
|
2013-11-10 06:01:18 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_lt)
|
2013-11-04 06:38:58 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
// TODO: Error on wrong argument type.
|
|
|
|
|
if (!IS_NUM(args[1])) RETURN_NULL;
|
|
|
|
|
RETURN_BOOL(AS_NUM(args[0]) < AS_NUM(args[1]));
|
2013-11-04 06:38:58 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_gt)
|
2013-11-04 06:38:58 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
// TODO: Error on wrong argument type.
|
|
|
|
|
if (!IS_NUM(args[1])) RETURN_NULL;
|
|
|
|
|
RETURN_BOOL(AS_NUM(args[0]) > AS_NUM(args[1]));
|
2013-11-04 06:38:58 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_lte)
|
2013-11-04 06:38:58 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
// TODO: Error on wrong argument type.
|
|
|
|
|
if (!IS_NUM(args[1])) RETURN_NULL;
|
|
|
|
|
RETURN_BOOL(AS_NUM(args[0]) <= AS_NUM(args[1]));
|
2013-11-04 06:38:58 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_gte)
|
2013-11-04 06:38:58 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
// TODO: Error on wrong argument type.
|
|
|
|
|
if (!IS_NUM(args[1])) RETURN_NULL;
|
|
|
|
|
RETURN_BOOL(AS_NUM(args[0]) >= AS_NUM(args[1]));
|
2013-11-04 06:38:58 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_eqeq)
|
2013-11-05 18:57:57 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!IS_NUM(args[1])) RETURN_FALSE;
|
|
|
|
|
RETURN_BOOL(AS_NUM(args[0]) == AS_NUM(args[1]));
|
2013-11-05 18:57:57 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(num_bangeq)
|
2013-11-05 18:57:57 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!IS_NUM(args[1])) RETURN_TRUE;
|
|
|
|
|
RETURN_BOOL(AS_NUM(args[0]) != AS_NUM(args[1]));
|
2013-11-05 18:57:57 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-05 07:09:31 +01:00
|
|
|
DEF_NATIVE(num_bitwiseNot)
|
|
|
|
|
{
|
|
|
|
|
// Bitwise operators always work on 32-bit unsigned ints.
|
|
|
|
|
uint32_t value = (uint32_t)AS_NUM(args[0]);
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(~value);
|
2013-12-05 07:09:31 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(object_eqeq)
|
2013-11-26 16:52:37 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_BOOL(wrenValuesEqual(args[0], args[1]));
|
2013-11-26 16:52:37 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(object_bangeq)
|
2013-11-26 16:52:37 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_BOOL(!wrenValuesEqual(args[0], args[1]));
|
2013-11-26 16:52:37 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-19 16:02:27 +01:00
|
|
|
DEF_NATIVE(object_new)
|
|
|
|
|
{
|
|
|
|
|
// This is the default argument-less constructor that all objects inherit.
|
|
|
|
|
// It just returns "this".
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(args[0]);
|
2013-12-19 16:02:27 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-22 20:50:00 +01:00
|
|
|
DEF_NATIVE(object_toString)
|
|
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(wrenNewString(vm, "<object>", 8));
|
2013-12-22 20:50:00 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(object_type)
|
2013-11-26 16:52:37 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(OBJ_VAL(wrenGetClass(vm, args[0])));
|
2013-11-26 16:52:37 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(string_contains)
|
2013-10-27 19:28:15 +01:00
|
|
|
{
|
2013-11-17 02:41:09 +01:00
|
|
|
const char* string = AS_CSTRING(args[0]);
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Check type of arg first!
|
2013-11-17 02:41:09 +01:00
|
|
|
const char* search = AS_CSTRING(args[1]);
|
2013-10-27 19:28:15 +01:00
|
|
|
|
2013-10-28 14:53:51 +01:00
|
|
|
// Corner case, the empty string contains the empty string.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (strlen(string) == 0 && strlen(search) == 0) RETURN_TRUE;
|
2013-10-28 14:53:51 +01:00
|
|
|
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_BOOL(strstr(string, search) != NULL);
|
2013-10-27 19:28:15 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(string_count)
|
2013-10-27 19:28:15 +01:00
|
|
|
{
|
2013-11-17 02:41:09 +01:00
|
|
|
double count = strlen(AS_CSTRING(args[0]));
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(count);
|
2013-10-27 19:28:15 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(string_toString)
|
2013-11-02 07:27:28 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(args[0]);
|
2013-11-02 07:27:28 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(string_plus)
|
2013-10-31 15:04:44 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!IS_STRING(args[1])) RETURN_NULL;
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Handle coercion to string of RHS.
|
2013-10-31 15:04:44 +01:00
|
|
|
|
2013-11-17 02:41:09 +01:00
|
|
|
const char* left = AS_CSTRING(args[0]);
|
|
|
|
|
const char* right = AS_CSTRING(args[1]);
|
2013-10-31 15:04:44 +01:00
|
|
|
|
2013-11-02 07:27:28 +01:00
|
|
|
size_t leftLength = strlen(left);
|
|
|
|
|
size_t rightLength = strlen(right);
|
2013-10-31 15:04:44 +01:00
|
|
|
|
refactor: rename internal value creation functions to wren-prefixed public API names
Move `allocate`, `initObj`, `newClass`, and related static helpers from `vm.c` into `value.c` with consistent `wren` prefix, and update all call sites in `compiler.c`, `primitives.c`, and `vm.c` to use the new names (`wrenNewFunction`, `wrenNewString`, `wrenNewClass`, `wrenNewInstance`, `wrenNewList`). Remove the old declarations from `vm.h` and add comprehensive documentation comments to `value.h` explaining the NaN-tagging representation and type system.
2013-11-28 05:00:03 +01:00
|
|
|
Value value = wrenNewString(vm, NULL, leftLength + rightLength);
|
2013-11-17 02:41:09 +01:00
|
|
|
ObjString* string = AS_STRING(value);
|
2013-11-12 17:32:35 +01:00
|
|
|
strcpy(string->value, left);
|
|
|
|
|
strcpy(string->value + leftLength, right);
|
|
|
|
|
string->value[leftLength + rightLength] = '\0';
|
|
|
|
|
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(value);
|
2013-10-31 15:04:44 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(string_eqeq)
|
2013-11-05 18:57:57 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!IS_STRING(args[1])) RETURN_FALSE;
|
2013-11-17 02:41:09 +01:00
|
|
|
const char* a = AS_CSTRING(args[0]);
|
|
|
|
|
const char* b = AS_CSTRING(args[1]);
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_BOOL(strcmp(a, b) == 0);
|
2013-11-05 18:57:57 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(string_bangeq)
|
2013-11-05 18:57:57 +01:00
|
|
|
{
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!IS_STRING(args[1])) RETURN_TRUE;
|
2013-11-17 02:41:09 +01:00
|
|
|
const char* a = AS_CSTRING(args[0]);
|
|
|
|
|
const char* b = AS_CSTRING(args[1]);
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_BOOL(strcmp(a, b) != 0);
|
2013-11-05 18:57:57 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(string_subscript)
|
2013-11-24 19:45:07 +01:00
|
|
|
{
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Instead of returning null here, all of these failure cases should
|
|
|
|
|
// signal an error explicitly somehow.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (!IS_NUM(args[1])) RETURN_NULL;
|
2013-11-24 19:45:07 +01:00
|
|
|
|
|
|
|
|
double indexNum = AS_NUM(args[1]);
|
|
|
|
|
int index = (int)indexNum;
|
|
|
|
|
// Make sure the index is an integer.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (indexNum != index) RETURN_NULL;
|
2013-11-24 19:45:07 +01:00
|
|
|
|
|
|
|
|
ObjString* string = AS_STRING(args[0]);
|
|
|
|
|
|
|
|
|
|
// Negative indices count from the end.
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Strings should cache their length.
|
2013-11-24 19:45:07 +01:00
|
|
|
int length = (int)strlen(string->value);
|
|
|
|
|
if (index < 0) index = length + index;
|
|
|
|
|
|
|
|
|
|
// Check bounds.
|
2014-01-01 22:25:24 +01:00
|
|
|
if (index < 0 || index >= length) RETURN_NULL;
|
2013-11-24 19:45:07 +01:00
|
|
|
|
|
|
|
|
// The result is a one-character string.
|
2013-12-15 00:28:18 +01:00
|
|
|
// TODO: Handle UTF-8.
|
refactor: rename internal value creation functions to wren-prefixed public API names
Move `allocate`, `initObj`, `newClass`, and related static helpers from `vm.c` into `value.c` with consistent `wren` prefix, and update all call sites in `compiler.c`, `primitives.c`, and `vm.c` to use the new names (`wrenNewFunction`, `wrenNewString`, `wrenNewClass`, `wrenNewInstance`, `wrenNewList`). Remove the old declarations from `vm.h` and add comprehensive documentation comments to `value.h` explaining the NaN-tagging representation and type system.
2013-11-28 05:00:03 +01:00
|
|
|
Value value = wrenNewString(vm, NULL, 2);
|
2013-11-24 19:45:07 +01:00
|
|
|
ObjString* result = AS_STRING(value);
|
|
|
|
|
result->value[0] = AS_CSTRING(args[0])[index];
|
|
|
|
|
result->value[1] = '\0';
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(value);
|
2013-11-24 19:45:07 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-24 00:38:00 +01:00
|
|
|
DEF_NATIVE(io_writeString)
|
2013-10-28 06:45:40 +01:00
|
|
|
{
|
2013-11-27 03:02:10 +01:00
|
|
|
wrenPrintValue(args[1]);
|
2013-10-28 06:45:40 +01:00
|
|
|
printf("\n");
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_VAL(args[1]);
|
2013-10-28 06:45:40 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
DEF_NATIVE(os_clock)
|
2013-11-22 17:55:22 +01:00
|
|
|
{
|
|
|
|
|
double time = (double)clock() / CLOCKS_PER_SEC;
|
2014-01-01 22:25:24 +01:00
|
|
|
RETURN_NUM(time);
|
2013-11-22 17:55:22 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-22 05:44:37 +01:00
|
|
|
static ObjClass* defineClass(WrenVM* vm, const char* name)
|
2013-11-26 16:52:37 +01:00
|
|
|
{
|
2013-12-30 16:30:50 +01:00
|
|
|
// Add the symbol first since it can trigger a GC.
|
2013-12-27 18:07:35 +01:00
|
|
|
int symbol = addSymbol(vm, &vm->globalSymbols, name, strlen(name));
|
2013-12-30 16:30:50 +01:00
|
|
|
|
|
|
|
|
ObjClass* classObj = wrenNewClass(vm, vm->objectClass, 0);
|
2013-11-26 16:52:37 +01:00
|
|
|
vm->globals[symbol] = OBJ_VAL(classObj);
|
|
|
|
|
return classObj;
|
|
|
|
|
}
|
feat: add `is` operator for type checking and refactor primitives registration
Add TOKEN_IS token and CODE_IS bytecode to support runtime type checking via the `is` keyword. Refactor primitive registration by replacing `registerPrimitives` with `loadCore`, which compiles a core library string to define built-in classes (Bool, Class, Function, Num, Null, String, IO) and then attaches primitive methods to them. Update Primitive typedef to remove unused numArgs parameter, and add findGlobal helper to retrieve globals by name. Include test files for class syntax, class instantiation, and the new `is` operator.
2013-11-08 02:07:32 +01:00
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
void wrenInitializeCore(WrenVM* vm)
|
2013-10-27 19:28:15 +01:00
|
|
|
{
|
2013-12-22 05:44:37 +01:00
|
|
|
// Define the root Object class. This has to be done a little specially
|
|
|
|
|
// because it has no superclass and an unusual metaclass (Class).
|
2013-12-27 18:07:35 +01:00
|
|
|
int objectSymbol = addSymbol(vm, &vm->globalSymbols,
|
|
|
|
|
"Object", strlen("Object"));
|
2013-12-30 16:30:50 +01:00
|
|
|
vm->objectClass = wrenNewSingleClass(vm, 0);
|
2013-12-22 05:44:37 +01:00
|
|
|
vm->globals[objectSymbol] = OBJ_VAL(vm->objectClass);
|
|
|
|
|
|
2013-11-28 17:00:55 +01:00
|
|
|
NATIVE(vm->objectClass, "== ", object_eqeq);
|
|
|
|
|
NATIVE(vm->objectClass, "!= ", object_bangeq);
|
2013-12-19 16:02:27 +01:00
|
|
|
NATIVE(vm->objectClass, "new", object_new);
|
2013-12-22 20:50:00 +01:00
|
|
|
NATIVE(vm->objectClass, "toString", object_toString);
|
2013-11-28 17:00:55 +01:00
|
|
|
NATIVE(vm->objectClass, "type", object_type);
|
2013-11-26 16:52:37 +01:00
|
|
|
|
2013-12-22 05:44:37 +01:00
|
|
|
// Now we can define Class, which is a subclass of Object, but Object's
|
|
|
|
|
// metaclass.
|
2013-12-27 18:07:35 +01:00
|
|
|
int classSymbol = addSymbol(vm, &vm->globalSymbols,
|
|
|
|
|
"Class", strlen("Class"));
|
2013-12-30 16:30:50 +01:00
|
|
|
vm->classClass = wrenNewSingleClass(vm, 0);
|
2013-12-22 05:44:37 +01:00
|
|
|
vm->globals[classSymbol] = OBJ_VAL(vm->classClass);
|
|
|
|
|
|
|
|
|
|
// Now that Object and Class are defined, we can wire them up to each other.
|
|
|
|
|
wrenBindSuperclass(vm->classClass, vm->objectClass);
|
|
|
|
|
vm->objectClass->metaclass = vm->classClass;
|
|
|
|
|
vm->classClass->metaclass = vm->classClass;
|
|
|
|
|
|
|
|
|
|
// 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 \\
|
|
|
|
|
// .---------. .--------------. //
|
|
|
|
|
// | Object |==>| Class |==/
|
|
|
|
|
// '---------' '--------------'
|
|
|
|
|
// ^ ^
|
|
|
|
|
// | |
|
|
|
|
|
// .---------. .--------------. \
|
|
|
|
|
// | Base |==>| Base.type | |
|
|
|
|
|
// '---------' '--------------' |
|
|
|
|
|
// ^ ^ | Hypothetical example classes
|
|
|
|
|
// | | |
|
|
|
|
|
// .---------. .--------------. |
|
|
|
|
|
// | Derived |==>| Derived.type | |
|
|
|
|
|
// '---------' '--------------' /
|
|
|
|
|
|
|
|
|
|
// The rest of the classes can not be defined normally.
|
|
|
|
|
vm->boolClass = defineClass(vm, "Bool");
|
2013-11-28 17:00:55 +01:00
|
|
|
NATIVE(vm->boolClass, "toString", bool_toString);
|
|
|
|
|
NATIVE(vm->boolClass, "!", bool_not);
|
2013-11-04 06:38:58 +01:00
|
|
|
|
2013-12-22 23:31:33 +01:00
|
|
|
vm->fiberClass = defineClass(vm, "Fiber");
|
|
|
|
|
|
2013-12-22 05:44:37 +01:00
|
|
|
vm->fnClass = defineClass(vm, "Function");
|
2014-01-01 22:25:24 +01:00
|
|
|
NATIVE(vm->fnClass, "call", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
|
|
|
|
NATIVE(vm->fnClass, "call ", fn_call);
|
2013-11-06 15:52:28 +01:00
|
|
|
|
2013-12-22 05:44:37 +01:00
|
|
|
vm->nullClass = defineClass(vm, "Null");
|
2013-12-08 03:47:40 +01:00
|
|
|
NATIVE(vm->nullClass, "toString", null_toString);
|
feat: add `is` operator for type checking and refactor primitives registration
Add TOKEN_IS token and CODE_IS bytecode to support runtime type checking via the `is` keyword. Refactor primitive registration by replacing `registerPrimitives` with `loadCore`, which compiles a core library string to define built-in classes (Bool, Class, Function, Num, Null, String, IO) and then attaches primitive methods to them. Update Primitive typedef to remove unused numArgs parameter, and add findGlobal helper to retrieve globals by name. Include test files for class syntax, class instantiation, and the new `is` operator.
2013-11-08 02:07:32 +01:00
|
|
|
|
2013-12-22 05:44:37 +01:00
|
|
|
vm->stringClass = defineClass(vm, "String");
|
2013-11-28 17:00:55 +01:00
|
|
|
NATIVE(vm->stringClass, "contains ", string_contains);
|
|
|
|
|
NATIVE(vm->stringClass, "count", string_count);
|
|
|
|
|
NATIVE(vm->stringClass, "toString", string_toString)
|
|
|
|
|
NATIVE(vm->stringClass, "+ ", string_plus);
|
|
|
|
|
NATIVE(vm->stringClass, "== ", string_eqeq);
|
|
|
|
|
NATIVE(vm->stringClass, "!= ", string_bangeq);
|
|
|
|
|
NATIVE(vm->stringClass, "[ ]", string_subscript);
|
2013-10-28 06:45:40 +01:00
|
|
|
|
2013-12-22 05:44:37 +01:00
|
|
|
ObjClass* osClass = defineClass(vm, "OS");
|
2013-11-28 17:00:55 +01:00
|
|
|
NATIVE(osClass->metaclass, "clock", os_clock);
|
2013-10-31 15:04:44 +01:00
|
|
|
|
2014-01-01 22:25:24 +01:00
|
|
|
wrenInterpret(vm, "Wren core library", coreLibSource);
|
2013-12-22 20:50:00 +01:00
|
|
|
|
|
|
|
|
vm->listClass = AS_CLASS(findGlobal(vm, "List"));
|
|
|
|
|
NATIVE(vm->listClass, "add ", list_add);
|
|
|
|
|
NATIVE(vm->listClass, "clear", list_clear);
|
|
|
|
|
NATIVE(vm->listClass, "count", list_count);
|
|
|
|
|
NATIVE(vm->listClass, "insert ", list_insert);
|
2013-12-25 06:04:11 +01:00
|
|
|
NATIVE(vm->listClass, "iterate ", list_iterate);
|
|
|
|
|
NATIVE(vm->listClass, "iteratorValue ", list_iteratorValue);
|
2013-12-22 20:50:00 +01:00
|
|
|
NATIVE(vm->listClass, "removeAt ", list_removeAt);
|
|
|
|
|
NATIVE(vm->listClass, "[ ]", list_subscript);
|
|
|
|
|
NATIVE(vm->listClass, "[ ]=", list_subscriptSetter);
|
|
|
|
|
|
2013-12-26 00:01:45 +01:00
|
|
|
vm->numClass = AS_CLASS(findGlobal(vm, "Num"));
|
|
|
|
|
NATIVE(vm->numClass, "abs", num_abs);
|
|
|
|
|
NATIVE(vm->numClass, "toString", num_toString)
|
|
|
|
|
NATIVE(vm->numClass, "-", num_negate);
|
|
|
|
|
NATIVE(vm->numClass, "- ", num_minus);
|
|
|
|
|
NATIVE(vm->numClass, "+ ", num_plus);
|
|
|
|
|
NATIVE(vm->numClass, "* ", num_multiply);
|
|
|
|
|
NATIVE(vm->numClass, "/ ", num_divide);
|
|
|
|
|
NATIVE(vm->numClass, "% ", num_mod);
|
|
|
|
|
NATIVE(vm->numClass, "< ", num_lt);
|
|
|
|
|
NATIVE(vm->numClass, "> ", num_gt);
|
|
|
|
|
NATIVE(vm->numClass, "<= ", num_lte);
|
|
|
|
|
NATIVE(vm->numClass, ">= ", num_gte);
|
|
|
|
|
NATIVE(vm->numClass, "~", num_bitwiseNot);
|
|
|
|
|
|
|
|
|
|
// These are defined just so that 0 and -0 are equal, which is specified by
|
|
|
|
|
// IEEE 754 even though they have different bit representations.
|
|
|
|
|
NATIVE(vm->numClass, "== ", num_eqeq);
|
|
|
|
|
NATIVE(vm->numClass, "!= ", num_bangeq);
|
|
|
|
|
|
2013-12-22 20:50:00 +01:00
|
|
|
ObjClass* ioClass = AS_CLASS(findGlobal(vm, "IO"));
|
2013-12-24 00:38:00 +01:00
|
|
|
NATIVE(ioClass->metaclass, "write__native__ ", io_writeString);
|
2013-11-06 00:40:21 +01:00
|
|
|
}
|