forked from Mirror/wren
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:
25
corelib.wren
25
corelib.wren
@ -27,28 +27,3 @@ class List {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Range {
|
||||
new(min, max) {
|
||||
_min = min
|
||||
_max = max
|
||||
}
|
||||
|
||||
min { return _min }
|
||||
max { return _max }
|
||||
|
||||
iterate(previous) {
|
||||
if (previous == null) return _min
|
||||
if (previous == _max) return false
|
||||
return previous + 1
|
||||
}
|
||||
|
||||
iteratorValue(iterator) {
|
||||
return iterator
|
||||
}
|
||||
}
|
||||
|
||||
class Num {
|
||||
.. other { return new Range(this, other) }
|
||||
... other { return new Range(this, other - 1) }
|
||||
}
|
||||
|
||||
155
src/wren_core.c
155
src/wren_core.c
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
var inclusive = 2..5
|
||||
IO.print(inclusive is Range) // expect: true
|
||||
IO.print(inclusive.min) // expect: 2
|
||||
IO.print(inclusive.max) // expect: 5
|
||||
|
||||
var exclusive = 2...5
|
||||
IO.print(exclusive is Range) // expect: true
|
||||
IO.print(exclusive.min) // expect: 2
|
||||
IO.print(exclusive.max) // expect: 4
|
||||
|
||||
// TODO: Non-number RHS.
|
||||
// TODO: Non-integer RHS.
|
||||
// TODO: Range iteration.
|
||||
// TODO: Empty or negative ranges.
|
||||
31
test/range/from.wren
Normal file
31
test/range/from.wren
Normal file
@ -0,0 +1,31 @@
|
||||
// Ordered range.
|
||||
IO.print((2..5).from) // expect: 2
|
||||
IO.print((3..3).from) // expect: 3
|
||||
IO.print((0..3).from) // expect: 0
|
||||
IO.print((-5..3).from) // expect: -5
|
||||
IO.print((-5..-2).from) // expect: -5
|
||||
|
||||
// Backwards range.
|
||||
IO.print((5..2).from) // expect: 5
|
||||
IO.print((3..0).from) // expect: 3
|
||||
IO.print((3..-5).from) // expect: 3
|
||||
IO.print((-2..-5).from) // expect: -2
|
||||
|
||||
// Exclusive ordered range.
|
||||
IO.print((2...5).from) // expect: 2
|
||||
IO.print((3...3).from) // expect: 3
|
||||
IO.print((0...3).from) // expect: 0
|
||||
IO.print((-5...3).from) // expect: -5
|
||||
IO.print((-5...-2).from) // expect: -5
|
||||
|
||||
// Exclusive backwards range.
|
||||
IO.print((5...2).from) // expect: 5
|
||||
IO.print((3...0).from) // expect: 3
|
||||
IO.print((3...-5).from) // expect: 3
|
||||
IO.print((-2...-5).from) // expect: -2
|
||||
|
||||
// TODO: Test toString.
|
||||
// TODO: Non-number RHS.
|
||||
// TODO: Non-integer RHS.
|
||||
// TODO: Range iteration.
|
||||
// TODO: Empty or negative ranges.
|
||||
8
test/range/iterate.wren
Normal file
8
test/range/iterate.wren
Normal file
@ -0,0 +1,8 @@
|
||||
var range = 1..3
|
||||
IO.print(range.iterate(null)) // expect: 1
|
||||
IO.print(range.iterate(1)) // expect: 2
|
||||
IO.print(range.iterate(2)) // expect: 3
|
||||
IO.print(range.iterate(3)) // expect: false
|
||||
IO.print(range.iterate(4)) // expect: false
|
||||
|
||||
// TODO: Negative and empty ranges.
|
||||
1
test/range/iterate_wrong_type.wren
Normal file
1
test/range/iterate_wrong_type.wren
Normal file
@ -0,0 +1 @@
|
||||
(1..3).iterate("") // expect runtime error: Iterator must be a number.
|
||||
11
test/range/iterator_value.wren
Normal file
11
test/range/iterator_value.wren
Normal file
@ -0,0 +1,11 @@
|
||||
var range = 1..3
|
||||
IO.print(range.iteratorValue(1)) // expect: 1
|
||||
IO.print(range.iteratorValue(2)) // expect: 2
|
||||
IO.print(range.iteratorValue(3)) // expect: 3
|
||||
|
||||
// Doesn't bother to bounds check.
|
||||
IO.print(range.iteratorValue(-2)) // expect: -2
|
||||
IO.print(range.iteratorValue(5)) // expect: 5
|
||||
|
||||
// Or type check.
|
||||
IO.print(range.iteratorValue("s")) // expect: s
|
||||
25
test/range/max.wren
Normal file
25
test/range/max.wren
Normal file
@ -0,0 +1,25 @@
|
||||
// Ordered range.
|
||||
IO.print((2..5).max) // expect: 5
|
||||
IO.print((3..3).max) // expect: 3
|
||||
IO.print((0..3).max) // expect: 3
|
||||
IO.print((-5..3).max) // expect: 3
|
||||
IO.print((-5..-2).max) // expect: -2
|
||||
|
||||
// Backwards range.
|
||||
IO.print((5..2).max) // expect: 5
|
||||
IO.print((3..0).max) // expect: 3
|
||||
IO.print((3..-5).max) // expect: 3
|
||||
IO.print((-2..-5).max) // expect: -2
|
||||
|
||||
// Exclusive ordered range.
|
||||
IO.print((2...5).max) // expect: 4
|
||||
IO.print((3...3).max) // expect: 3
|
||||
IO.print((0...3).max) // expect: 2
|
||||
IO.print((-5...3).max) // expect: 2
|
||||
IO.print((-5...-2).max) // expect: -3
|
||||
|
||||
// Exclusive backwards range.
|
||||
IO.print((5...2).max) // expect: 5
|
||||
IO.print((3...0).max) // expect: 3
|
||||
IO.print((3...-5).max) // expect: 3
|
||||
IO.print((-2...-5).max) // expect: -2
|
||||
26
test/range/min.wren
Normal file
26
test/range/min.wren
Normal file
@ -0,0 +1,26 @@
|
||||
// Ordered range.
|
||||
IO.print((2..5).min) // expect: 2
|
||||
IO.print((3..3).min) // expect: 3
|
||||
IO.print((0..3).min) // expect: 0
|
||||
IO.print((-5..3).min) // expect: -5
|
||||
IO.print((-5..-2).min) // expect: -5
|
||||
|
||||
// Backwards range.
|
||||
IO.print((5..2).min) // expect: 2
|
||||
IO.print((3..0).min) // expect: 0
|
||||
IO.print((3..-5).min) // expect: -5
|
||||
IO.print((-2..-5).min) // expect: -5
|
||||
|
||||
// Exclusive ordered range.
|
||||
IO.print((2...5).min) // expect: 2
|
||||
IO.print((3...3).min) // expect: 2
|
||||
IO.print((0...3).min) // expect: 0
|
||||
IO.print((-5...3).min) // expect: -5
|
||||
IO.print((-5...-2).min) // expect: -5
|
||||
|
||||
// Exclusive backwards range.
|
||||
// TODO: Is this what we want? "..." always means "subtract 1"?
|
||||
IO.print((5...2).min) // expect: 1
|
||||
IO.print((3...0).min) // expect: -1
|
||||
IO.print((3...-5).min) // expect: -6
|
||||
IO.print((-2...-5).min) // expect: -6
|
||||
25
test/range/to.wren
Normal file
25
test/range/to.wren
Normal file
@ -0,0 +1,25 @@
|
||||
// Ordered range.
|
||||
IO.print((2..5).to) // expect: 5
|
||||
IO.print((3..3).to) // expect: 3
|
||||
IO.print((0..3).to) // expect: 3
|
||||
IO.print((-5..3).to) // expect: 3
|
||||
IO.print((-5..-2).to) // expect: -2
|
||||
|
||||
// Backwards range.
|
||||
IO.print((5..2).to) // expect: 2
|
||||
IO.print((3..0).to) // expect: 0
|
||||
IO.print((3..-5).to) // expect: -5
|
||||
IO.print((-2..-5).to) // expect: -5
|
||||
|
||||
// Exclusive ordered range.
|
||||
IO.print((2...5).to) // expect: 4
|
||||
IO.print((3...3).to) // expect: 2
|
||||
IO.print((0...3).to) // expect: 2
|
||||
IO.print((-5...3).to) // expect: 2
|
||||
IO.print((-5...-2).to) // expect: -3
|
||||
|
||||
// Exclusive backwards range.
|
||||
IO.print((5...2).to) // expect: 1
|
||||
IO.print((3...0).to) // expect: -1
|
||||
IO.print((3...-5).to) // expect: -6
|
||||
IO.print((-2...-5).to) // expect: -6
|
||||
6
test/range/type.wren
Normal file
6
test/range/type.wren
Normal file
@ -0,0 +1,6 @@
|
||||
var range = 2..5
|
||||
|
||||
IO.print(range is Range) // expect: true
|
||||
IO.print(range is Object) // expect: true
|
||||
IO.print(range is String) // expect: false
|
||||
IO.print(range.type == Range) // expect: true
|
||||
Reference in New Issue
Block a user