From 46c1ba92492e9257aba6418403161072d640cb29 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Mon, 20 Jan 2014 13:20:22 -0800 Subject: [PATCH] Fix #2. Finish implementing Range. Covers error and edge cases and fully tested now. --- src/wren_core.c | 43 ++++++++++++++----- src/wren_value.c | 3 +- src/wren_value.h | 5 ++- .../range/exclusive_range_wrong_rhs_type.wren | 1 + test/range/floating_point.wren | 31 +++++++++++++ test/range/from.wren | 6 --- .../range/inclusive_range_wrong_rhs_type.wren | 1 + test/range/is_inclusive.wren | 5 +++ test/range/iterate.wren | 29 ++++++++++++- test/range/iterate_from_float.wren | 18 ++++++++ test/range/max.wren | 8 ++-- test/range/min.wren | 11 +++-- test/range/to.wren | 18 ++++---- test/range/to_string.wren | 7 +++ 14 files changed, 148 insertions(+), 38 deletions(-) create mode 100644 test/range/exclusive_range_wrong_rhs_type.wren create mode 100644 test/range/floating_point.wren create mode 100644 test/range/inclusive_range_wrong_rhs_type.wren create mode 100644 test/range/is_inclusive.wren create mode 100644 test/range/iterate_from_float.wren create mode 100644 test/range/to_string.wren diff --git a/src/wren_core.c b/src/wren_core.c index 173f8ba3..e263792e 100644 --- a/src/wren_core.c +++ b/src/wren_core.c @@ -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); diff --git a/src/wren_value.c b/src/wren_value.c index 7c7ef545..74f29244 100644 --- a/src/wren_value.c +++ b/src/wren_value.c @@ -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); } diff --git a/src/wren_value.h b/src/wren_value.h index a9341010..4f8193c3 100644 --- a/src/wren_value.h +++ b/src/wren_value.h @@ -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); diff --git a/test/range/exclusive_range_wrong_rhs_type.wren b/test/range/exclusive_range_wrong_rhs_type.wren new file mode 100644 index 00000000..40a14247 --- /dev/null +++ b/test/range/exclusive_range_wrong_rhs_type.wren @@ -0,0 +1 @@ +1..."s" // expect runtime error: Right hand side of range must be a number. diff --git a/test/range/floating_point.wren b/test/range/floating_point.wren new file mode 100644 index 00000000..71df3157 --- /dev/null +++ b/test/range/floating_point.wren @@ -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. diff --git a/test/range/from.wren b/test/range/from.wren index 71df3157..114ea1fb 100644 --- a/test/range/from.wren +++ b/test/range/from.wren @@ -23,9 +23,3 @@ 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. diff --git a/test/range/inclusive_range_wrong_rhs_type.wren b/test/range/inclusive_range_wrong_rhs_type.wren new file mode 100644 index 00000000..ff831f4f --- /dev/null +++ b/test/range/inclusive_range_wrong_rhs_type.wren @@ -0,0 +1 @@ +1.."s" // expect runtime error: Right hand side of range must be a number. diff --git a/test/range/is_inclusive.wren b/test/range/is_inclusive.wren new file mode 100644 index 00000000..8f881aef --- /dev/null +++ b/test/range/is_inclusive.wren @@ -0,0 +1,5 @@ +IO.print((0..0).isInclusive) // expect: true +IO.print((0...0).isInclusive) // expect: false + +IO.print((-1..1).isInclusive) // expect: true +IO.print((-1...1).isInclusive) // expect: false diff --git a/test/range/iterate.wren b/test/range/iterate.wren index ab4dd82a..db4e949d 100644 --- a/test/range/iterate.wren +++ b/test/range/iterate.wren @@ -1,3 +1,4 @@ +// Inclusive. var range = 1..3 IO.print(range.iterate(null)) // expect: 1 IO.print(range.iterate(1)) // expect: 2 @@ -5,4 +6,30 @@ 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. +// Exclusive +range = 1...3 +IO.print(range.iterate(null)) // expect: 1 +IO.print(range.iterate(1)) // expect: 2 +IO.print(range.iterate(2)) // expect: false + +// Negative inclusive range. +range = 3..1 +IO.print(range.iterate(null)) // expect: 3 +IO.print(range.iterate(3)) // expect: 2 +IO.print(range.iterate(2)) // expect: 1 +IO.print(range.iterate(1)) // expect: false + +// Negative exclusive range. +range = 3...1 +IO.print(range.iterate(null)) // expect: 3 +IO.print(range.iterate(3)) // expect: 2 +IO.print(range.iterate(2)) // expect: false + +// Empty inclusive range. +range = 1..1 +IO.print(range.iterate(null)) // expect: 1 +IO.print(range.iterate(1)) // expect: false + +// Empty exclusive range. +range = 1...1 +IO.print(range.iterate(null)) // expect: false diff --git a/test/range/iterate_from_float.wren b/test/range/iterate_from_float.wren new file mode 100644 index 00000000..23e7ca41 --- /dev/null +++ b/test/range/iterate_from_float.wren @@ -0,0 +1,18 @@ +// Starts at "from" and adds 1.0 each iteration. + +for (n in 1.3..4.5) IO.print(n) +// expect: 1.3 +// expect: 2.3 +// expect: 3.3 +// expect: 4.3 + +for (n in 1.3...4.5) IO.print(n) +// expect: 1.3 +// expect: 2.3 +// expect: 3.3 +// expect: 4.3 + +for (n in 1.3...4.3) IO.print(n) +// expect: 1.3 +// expect: 2.3 +// expect: 3.3 diff --git a/test/range/max.wren b/test/range/max.wren index 1cc27493..9c6672d2 100644 --- a/test/range/max.wren +++ b/test/range/max.wren @@ -12,11 +12,11 @@ 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((2...5).max) // expect: 5 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 +IO.print((0...3).max) // expect: 3 +IO.print((-5...3).max) // expect: 3 +IO.print((-5...-2).max) // expect: -2 // Exclusive backwards range. IO.print((5...2).max) // expect: 5 diff --git a/test/range/min.wren b/test/range/min.wren index baaba399..bcc019bf 100644 --- a/test/range/min.wren +++ b/test/range/min.wren @@ -13,14 +13,13 @@ 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((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 // 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 +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 diff --git a/test/range/to.wren b/test/range/to.wren index cec8836a..ad3a5444 100644 --- a/test/range/to.wren +++ b/test/range/to.wren @@ -12,14 +12,14 @@ 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 +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 // 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 +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 diff --git a/test/range/to_string.wren b/test/range/to_string.wren new file mode 100644 index 00000000..795b0d3e --- /dev/null +++ b/test/range/to_string.wren @@ -0,0 +1,7 @@ +IO.print(1..3) // expect: 1..3 +IO.print(12345.6789..12345.6789) // expect: 12345.6789..12345.6789 +IO.print(-100..-300) // expect: -100..-300 + +IO.print(1...3) // expect: 1...3 +IO.print(12345.6789...12345.6789) // expect: 12345.6789...12345.6789 +IO.print(-100...-300) // expect: -100...-300