feat: add isInclusive field to ObjRange and implement proper exclusive range iteration
Add isInclusive boolean to ObjRange struct to distinguish between inclusive (..) and exclusive (...) ranges. Update wrenNewRange signature to accept isInclusive parameter. Implement correct iteration logic for both ascending and descending ranges, handling empty exclusive ranges and floating-point iteration. Add type validation for range RHS operands. Update min/max/to properties to return raw endpoint values instead of adjusted ones. Add comprehensive test coverage for inclusive/exclusive ranges, negative ranges, empty ranges, floating-point iteration, isInclusive property, toString, and type error cases.
This commit is contained in:
+33
-10
@@ -491,22 +491,22 @@ DEF_NATIVE(num_bitwiseNot)
|
||||
|
||||
DEF_NATIVE(num_dotDot)
|
||||
{
|
||||
// TODO: Check arg type.
|
||||
if (!validateNum(vm, args, 1, "Right hand side of range")) return PRIM_ERROR;
|
||||
|
||||
double from = AS_NUM(args[0]);
|
||||
double to = AS_NUM(args[1]);
|
||||
|
||||
RETURN_VAL(wrenNewRange(vm, from, to));
|
||||
RETURN_VAL(wrenNewRange(vm, from, to, true));
|
||||
}
|
||||
|
||||
DEF_NATIVE(num_dotDotDot)
|
||||
{
|
||||
// TODO: Check arg type.
|
||||
if (!validateNum(vm, args, 1, "Right hand side of range")) return PRIM_ERROR;
|
||||
|
||||
double from = AS_NUM(args[0]);
|
||||
double to = AS_NUM(args[1]);
|
||||
|
||||
// TODO: Is this how we want half-open ranges to work?
|
||||
double to = AS_NUM(args[1]) - 1;
|
||||
|
||||
RETURN_VAL(wrenNewRange(vm, from, to));
|
||||
RETURN_VAL(wrenNewRange(vm, from, to, false));
|
||||
}
|
||||
|
||||
DEF_NATIVE(object_eqeq)
|
||||
@@ -560,10 +560,19 @@ DEF_NATIVE(range_max)
|
||||
RETURN_NUM(fmax(range->from, range->to));
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_isInclusive)
|
||||
{
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
RETURN_BOOL(range->isInclusive);
|
||||
}
|
||||
|
||||
DEF_NATIVE(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);
|
||||
|
||||
@@ -571,9 +580,21 @@ DEF_NATIVE(range_iterate)
|
||||
|
||||
double iterator = AS_NUM(args[1]);
|
||||
|
||||
if (iterator >= range->to) RETURN_FALSE;
|
||||
// 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;
|
||||
}
|
||||
|
||||
RETURN_NUM(AS_NUM(args[1]) + 1);
|
||||
if (!range->isInclusive && iterator == range->to) RETURN_FALSE;
|
||||
|
||||
RETURN_NUM(iterator);
|
||||
}
|
||||
|
||||
DEF_NATIVE(range_iteratorValue)
|
||||
@@ -586,7 +607,8 @@ DEF_NATIVE(range_toString)
|
||||
{
|
||||
char buffer[51];
|
||||
ObjRange* range = AS_RANGE(args[0]);
|
||||
sprintf(buffer, "%.14g..%.14g", range->from, range->to);
|
||||
sprintf(buffer, "%.14g%s%.14g", range->from,
|
||||
range->isInclusive ? ".." : "...", range->to);
|
||||
RETURN_VAL(wrenNewString(vm, buffer, strlen(buffer)));
|
||||
}
|
||||
|
||||
@@ -811,6 +833,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
NATIVE(vm->rangeClass, "to", range_to);
|
||||
NATIVE(vm->rangeClass, "min", range_min);
|
||||
NATIVE(vm->rangeClass, "max", range_max);
|
||||
NATIVE(vm->rangeClass, "isInclusive", range_isInclusive);
|
||||
NATIVE(vm->rangeClass, "iterate ", range_iterate);
|
||||
NATIVE(vm->rangeClass, "iteratorValue ", range_iteratorValue);
|
||||
NATIVE(vm->rangeClass, "toString", range_toString);
|
||||
|
||||
+2
-1
@@ -301,12 +301,13 @@ Value wrenListRemoveAt(WrenVM* vm, ObjList* list, int index)
|
||||
return removed;
|
||||
}
|
||||
|
||||
Value wrenNewRange(WrenVM* vm, double from, double to)
|
||||
Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive)
|
||||
{
|
||||
ObjRange* range = allocate(vm, sizeof(ObjRange) + 16);
|
||||
initObj(vm, &range->obj, OBJ_RANGE);
|
||||
range->from = from;
|
||||
range->to = to;
|
||||
range->isInclusive = isInclusive;
|
||||
|
||||
return OBJ_VAL(range);
|
||||
}
|
||||
|
||||
+4
-1
@@ -317,6 +317,9 @@ typedef struct
|
||||
|
||||
// The end of the range. May be greater or less than [from].
|
||||
double to;
|
||||
|
||||
// True if [to] is included in the range.
|
||||
bool isInclusive;
|
||||
} ObjRange;
|
||||
|
||||
|
||||
@@ -560,7 +563,7 @@ void wrenListInsert(WrenVM* vm, ObjList* list, Value value, int index);
|
||||
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, int index);
|
||||
|
||||
// Creates a new range from [from] to [to].
|
||||
Value wrenNewRange(WrenVM* vm, double from, double to);
|
||||
Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive);
|
||||
|
||||
// Creates a new string object and copies [text] into it.
|
||||
Value wrenNewString(WrenVM* vm, const char* text, size_t length);
|
||||
|
||||
Reference in New Issue
Block a user