From 6bb086633b5e74ba6d0035546cd610c3ec9e0718 Mon Sep 17 00:00:00 2001 From: Steve Kemp Date: Wed, 22 Nov 2023 16:53:14 +0200 Subject: [PATCH] Fix the range-operator for "backwards" ranges. This pull-request closes #98, by ensuring that the range operator (`..`) can count down as well as up. This allows ranges such as: * 3..10 * 4..0 However note that the first number cannot be negative, as that's then treated as "- [1, 2..]" - i.e. negative array, which makes no sense. This should be resolved, but it is a bigger job. --- evaluator/evaluator.go | 21 +++++++++++++++++++-- evaluator/evaluator_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) 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) + } + } +}