diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index be122cf..5f9d372 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -448,12 +448,29 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje case "!=": return nativeBoolToBooleanObject(leftVal != rightVal) case "..": - len := int(rightVal-leftVal) + 1 + // The start and end might not be ascending, so the size + // will be the span + diff := float64(rightVal - leftVal) + len := int(math.Abs(diff)) + 1 + + // Step is generally +1, but if we're going to + // express the range "10..0" it will be -1 to allow + // us to count down via subtraction + var step int64 + step = 1.0 + + if rightVal < leftVal { + step = -1.0 + } + + // Make an array to hold the return value array := make([]object.Object, len) + + // Now make the range of integers, counting via the step. i := 0 for i < len { array[i] = &object.Integer{Value: leftVal} - leftVal++ + leftVal += step i++ } return &object.Array{Elements: array} diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index 9febf82..deb3538 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -721,3 +721,27 @@ if (match( "+", name) ) { puts( "Hello\n" ); } } } + +func TestRangeOperator(t *testing.T) { + tests := []struct { + input string + expected string + }{ + // normal + {"return 0..3;", "[0, 1, 2, 3]"}, + {"return 0..1;", "[0, 1]"}, + {"return 0..0;", "[0]"}, + {"a = -3; b = 3; return a..b;", "[-3, -2, -1, 0, 1, 2, 3]"}, + // reversed + {"return 3..0;", "[3, 2, 1, 0]"}, + {"return 1..0;", "[1, 0]"}, + {"return 0..0;", "[0]"}, + {"return 3..-5;", "[3, 2, 1, 0, -1, -2, -3, -4, -5]"}, + } + for _, tt := range tests { + evaluated := testEval(tt.input) + if tt.expected != evaluated.Inspect() { + t.Fatalf("unexpected output for range operator, got %s for input %s", evaluated.Inspect(), tt.input) + } + } +}