Make Range a native object type.
Still need to implement better semantics, but this is an important first step. It's much faster now too, which is good.
This commit is contained in:
+107
-48
@@ -68,31 +68,6 @@ const char* coreLibSource =
|
||||
" result = result + \"]\"\n"
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"}\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"
|
||||
"}\n";
|
||||
|
||||
// Validates that the given argument in [args] is a Num. Returns true if it is.
|
||||
@@ -514,6 +489,26 @@ DEF_NATIVE(num_bitwiseNot)
|
||||
RETURN_NUM(~value);
|
||||
}
|
||||
|
||||
DEF_NATIVE(num_dotDot)
|
||||
{
|
||||
// TODO: Check arg type.
|
||||
double from = AS_NUM(args[0]);
|
||||
double to = AS_NUM(args[1]);
|
||||
|
||||
RETURN_VAL(wrenNewRange(vm, from, to));
|
||||
}
|
||||
|
||||
DEF_NATIVE(num_dotDotDot)
|
||||
{
|
||||
// TODO: Check arg type.
|
||||
double from = AS_NUM(args[0]);
|
||||
|
||||
// TODO: Is this how we want half-open ranges to work?
|
||||
double to = AS_NUM(args[1]) - 1;
|
||||
|
||||
RETURN_VAL(wrenNewRange(vm, from, to));
|
||||
}
|
||||
|
||||
DEF_NATIVE(object_eqeq)
|
||||
{
|
||||
RETURN_BOOL(wrenValuesEqual(args[0], args[1]));
|
||||
@@ -541,6 +536,60 @@ DEF_NATIVE(object_type)
|
||||
RETURN_VAL(OBJ_VAL(wrenGetClass(vm, args[0])));
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_from)
|
||||
{
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
RETURN_NUM(range->from);
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_to)
|
||||
{
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
RETURN_NUM(range->to);
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_min)
|
||||
{
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
RETURN_NUM(fmin(range->from, range->to));
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_max)
|
||||
{
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
RETURN_NUM(fmax(range->from, range->to));
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_iterate)
|
||||
{
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
|
||||
// Start the iteration.
|
||||
if (IS_NULL(args[1])) RETURN_NUM(range->from);
|
||||
|
||||
if (!validateNum(vm, args, 1, "Iterator")) return PRIM_ERROR;
|
||||
|
||||
double iterator = AS_NUM(args[1]);
|
||||
|
||||
if (iterator >= range->to) RETURN_FALSE;
|
||||
|
||||
RETURN_NUM(AS_NUM(args[1]) + 1);
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_iteratorValue)
|
||||
{
|
||||
// Assume the iterator is a number so that is the value of the range.
|
||||
RETURN_VAL(args[1]);
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_toString)
|
||||
{
|
||||
char buffer[51];
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
sprintf(buffer, "%.14g..%.14g", range->from, range->to);
|
||||
RETURN_VAL(wrenNewString(vm, buffer, strlen(buffer)));
|
||||
}
|
||||
|
||||
DEF_NATIVE(string_contains)
|
||||
{
|
||||
if (!validateString(vm, args, 1, "Argument")) return PRIM_ERROR;
|
||||
@@ -710,8 +759,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
NATIVE(vm->fiberClass, "run", fiber_run);
|
||||
NATIVE(vm->fiberClass, "run ", fiber_run1);
|
||||
// TODO: Primitives for switching to a fiber without setting the caller.
|
||||
// (I.e. symmetric coroutines.) Also a getter to tell if a fiber is complete
|
||||
// or not.
|
||||
// (I.e. symmetric coroutines.)
|
||||
|
||||
vm->fnClass = defineClass(vm, "Function");
|
||||
NATIVE(vm->fnClass, "call", fn_call);
|
||||
@@ -735,6 +783,38 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
vm->nullClass = defineClass(vm, "Null");
|
||||
NATIVE(vm->nullClass, "toString", null_toString);
|
||||
|
||||
vm->numClass = defineClass(vm, "Num");
|
||||
NATIVE(vm->numClass, "abs", num_abs);
|
||||
NATIVE(vm->numClass, "ceil", num_ceil);
|
||||
NATIVE(vm->numClass, "cos", num_cos);
|
||||
NATIVE(vm->numClass, "floor", num_floor);
|
||||
NATIVE(vm->numClass, "isNan", num_isNan);
|
||||
NATIVE(vm->numClass, "sin", num_sin);
|
||||
NATIVE(vm->numClass, "sqrt", num_sqrt);
|
||||
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);
|
||||
NATIVE(vm->numClass, ".. ", num_dotDot);
|
||||
NATIVE(vm->numClass, "... ", num_dotDotDot);
|
||||
|
||||
vm->rangeClass = defineClass(vm, "Range");
|
||||
NATIVE(vm->rangeClass, "from", range_from);
|
||||
NATIVE(vm->rangeClass, "to", range_to);
|
||||
NATIVE(vm->rangeClass, "min", range_min);
|
||||
NATIVE(vm->rangeClass, "max", range_max);
|
||||
NATIVE(vm->rangeClass, "iterate ", range_iterate);
|
||||
NATIVE(vm->rangeClass, "iteratorValue ", range_iteratorValue);
|
||||
NATIVE(vm->rangeClass, "toString", range_toString);
|
||||
|
||||
vm->stringClass = defineClass(vm, "String");
|
||||
NATIVE(vm->stringClass, "contains ", string_contains);
|
||||
NATIVE(vm->stringClass, "count", string_count);
|
||||
@@ -760,27 +840,6 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
NATIVE(vm->listClass, "[ ]", list_subscript);
|
||||
NATIVE(vm->listClass, "[ ]=", list_subscriptSetter);
|
||||
|
||||
vm->numClass = AS_CLASS(findGlobal(vm, "Num"));
|
||||
NATIVE(vm->numClass, "abs", num_abs);
|
||||
NATIVE(vm->numClass, "ceil", num_ceil);
|
||||
NATIVE(vm->numClass, "cos", num_cos);
|
||||
NATIVE(vm->numClass, "floor", num_floor);
|
||||
NATIVE(vm->numClass, "isNan", num_isNan);
|
||||
NATIVE(vm->numClass, "sin", num_sin);
|
||||
NATIVE(vm->numClass, "sqrt", num_sqrt);
|
||||
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);
|
||||
|
||||
@@ -301,6 +301,16 @@ Value wrenListRemoveAt(WrenVM* vm, ObjList* list, int index)
|
||||
return removed;
|
||||
}
|
||||
|
||||
Value wrenNewRange(WrenVM* vm, double from, double to)
|
||||
{
|
||||
ObjRange* range = allocate(vm, sizeof(ObjRange) + 16);
|
||||
initObj(vm, &range->obj, OBJ_RANGE);
|
||||
range->from = from;
|
||||
range->to = to;
|
||||
|
||||
return OBJ_VAL(range);
|
||||
}
|
||||
|
||||
Value wrenNewString(WrenVM* vm, const char* text, size_t length)
|
||||
{
|
||||
// Allocate before the string object in case this triggers a GC which would
|
||||
@@ -368,6 +378,7 @@ void wrenFreeObj(WrenVM* vm, Obj* obj)
|
||||
|
||||
case OBJ_CLOSURE:
|
||||
case OBJ_INSTANCE:
|
||||
case OBJ_RANGE:
|
||||
case OBJ_UPVALUE:
|
||||
break;
|
||||
}
|
||||
@@ -389,6 +400,7 @@ static ObjClass* getObjectClass(WrenVM* vm, Obj* obj)
|
||||
case OBJ_FN: return vm->fnClass;
|
||||
case OBJ_INSTANCE: return ((ObjInstance*)obj)->classObj;
|
||||
case OBJ_LIST: return vm->listClass;
|
||||
case OBJ_RANGE: return vm->rangeClass;
|
||||
case OBJ_STRING: return vm->stringClass;
|
||||
case OBJ_UPVALUE:
|
||||
ASSERT(0, "Upvalues should not be used as first-class objects.");
|
||||
@@ -460,6 +472,7 @@ static void printObject(Obj* obj)
|
||||
case OBJ_FN: printf("[fn %p]", obj); break;
|
||||
case OBJ_INSTANCE: printf("[instance %p]", obj); break;
|
||||
case OBJ_LIST: printList((ObjList*)obj); break;
|
||||
case OBJ_RANGE: printf("[fn %p]", obj); break;
|
||||
case OBJ_STRING: printf("%s", ((ObjString*)obj)->value); break;
|
||||
case OBJ_UPVALUE: printf("[upvalue %p]", obj); break;
|
||||
}
|
||||
@@ -530,6 +543,11 @@ bool wrenIsInstance(Value value)
|
||||
return IS_OBJ(value) && AS_OBJ(value)->type == OBJ_INSTANCE;
|
||||
}
|
||||
|
||||
bool wrenIsRange(Value value)
|
||||
{
|
||||
return IS_OBJ(value) && AS_OBJ(value)->type == OBJ_RANGE;
|
||||
}
|
||||
|
||||
bool wrenIsString(Value value)
|
||||
{
|
||||
return IS_OBJ(value) && AS_OBJ(value)->type == OBJ_STRING;
|
||||
|
||||
+23
-1
@@ -54,6 +54,7 @@ typedef enum {
|
||||
OBJ_FN,
|
||||
OBJ_INSTANCE,
|
||||
OBJ_LIST,
|
||||
OBJ_RANGE,
|
||||
OBJ_STRING,
|
||||
OBJ_UPVALUE
|
||||
} ObjType;
|
||||
@@ -66,7 +67,7 @@ typedef enum
|
||||
|
||||
typedef struct sObj
|
||||
{
|
||||
unsigned int type : 3; // ObjType.
|
||||
unsigned int type : 4; // ObjType.
|
||||
unsigned int flags : 1; // ObjFlags.
|
||||
|
||||
// The next object in the linked list of all currently allocated objects.
|
||||
@@ -307,6 +308,17 @@ typedef struct
|
||||
Value* elements;
|
||||
} ObjList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Obj obj;
|
||||
|
||||
// The beginning of the range.
|
||||
double from;
|
||||
|
||||
// The end of the range. May be greater or less than [from].
|
||||
double to;
|
||||
} ObjRange;
|
||||
|
||||
|
||||
// Value -> ObjClass*.
|
||||
#define AS_CLASS(value) ((ObjClass*)AS_OBJ(value))
|
||||
@@ -329,6 +341,9 @@ typedef struct
|
||||
// Value -> double.
|
||||
#define AS_NUM(v) ((v).num)
|
||||
|
||||
// Value -> ObjRange*.
|
||||
#define AS_RANGE(v) ((ObjRange*)AS_OBJ(v))
|
||||
|
||||
// Value -> ObjString*.
|
||||
#define AS_STRING(v) ((ObjString*)AS_OBJ(v))
|
||||
|
||||
@@ -350,6 +365,9 @@ typedef struct
|
||||
// Returns true if [value] is an instance.
|
||||
#define IS_INSTANCE(value) (wrenIsInstance(value))
|
||||
|
||||
// Returns true if [value] is a range object.
|
||||
#define IS_RANGE(value) (wrenIsRange(value))
|
||||
|
||||
// Returns true if [value] is a string object.
|
||||
#define IS_STRING(value) (wrenIsString(value))
|
||||
|
||||
@@ -541,6 +559,9 @@ void wrenListInsert(WrenVM* vm, ObjList* list, Value value, int index);
|
||||
// Removes and returns the item at [index] from [list].
|
||||
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, int index);
|
||||
|
||||
// Creates a new range from [from] to [to].
|
||||
Value wrenNewRange(WrenVM* vm, double from, double to);
|
||||
|
||||
// Creates a new string object and copies [text] into it.
|
||||
Value wrenNewString(WrenVM* vm, const char* text, size_t length);
|
||||
|
||||
@@ -567,6 +588,7 @@ bool wrenIsClosure(Value value);
|
||||
bool wrenIsFiber(Value value);
|
||||
bool wrenIsFn(Value value);
|
||||
bool wrenIsInstance(Value value);
|
||||
bool wrenIsRange(Value value);
|
||||
bool wrenIsString(Value value);
|
||||
|
||||
inline Value wrenObjectToValue(Obj* obj)
|
||||
|
||||
@@ -259,6 +259,13 @@ static void markClosure(WrenVM* vm, ObjClosure* closure)
|
||||
vm->bytesAllocated += sizeof(Upvalue*) * closure->fn->numUpvalues;
|
||||
}
|
||||
|
||||
static void markRange(WrenVM* vm, ObjRange* range)
|
||||
{
|
||||
// Don't recurse if already marked. Avoids getting stuck in a loop on cycles.
|
||||
if (range->obj.flags & FLAG_MARKED) return;
|
||||
range->obj.flags |= FLAG_MARKED;
|
||||
}
|
||||
|
||||
static void markString(WrenVM* vm, ObjString* string)
|
||||
{
|
||||
// Don't recurse if already marked. Avoids getting stuck in a loop on cycles.
|
||||
@@ -291,6 +298,7 @@ void wrenMarkObj(WrenVM* vm, Obj* obj)
|
||||
case OBJ_FN: markFn( vm, (ObjFn*) obj); break;
|
||||
case OBJ_INSTANCE: markInstance(vm, (ObjInstance*)obj); break;
|
||||
case OBJ_LIST: markList( vm, (ObjList*) obj); break;
|
||||
case OBJ_RANGE: markRange( vm, (ObjRange*) obj); break;
|
||||
case OBJ_STRING: markString( vm, (ObjString*) obj); break;
|
||||
case OBJ_UPVALUE: markUpvalue( vm, (Upvalue*) obj); break;
|
||||
}
|
||||
|
||||
@@ -191,6 +191,7 @@ struct WrenVM
|
||||
{
|
||||
SymbolTable methods;
|
||||
|
||||
// TODO: Use an array for some of these.
|
||||
ObjClass* boolClass;
|
||||
ObjClass* classClass;
|
||||
ObjClass* fiberClass;
|
||||
@@ -199,6 +200,7 @@ struct WrenVM
|
||||
ObjClass* nullClass;
|
||||
ObjClass* numClass;
|
||||
ObjClass* objectClass;
|
||||
ObjClass* rangeClass;
|
||||
ObjClass* stringClass;
|
||||
|
||||
SymbolTable globalSymbols;
|
||||
|
||||
Reference in New Issue
Block a user