From 74ca7992e692e6c6ac63eb8c6af6ffd2eefed8e3 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 1 Jul 2023 22:56:48 +0530 Subject: [PATCH 1/7] Introduce reverse in Variable/Fixed size Array --- runtime/interpreter/value.go | 24 ++++++++ runtime/sema/type.go | 22 +++++++ .../tests/checker/arrays_dictionaries_test.go | 30 +++++++++ runtime/tests/interpreter/interpreter_test.go | 61 +++++++++++++++++++ 4 files changed, 137 insertions(+) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 9a95830648..f3d5ab6659 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -2192,6 +2192,18 @@ func (v *ArrayValue) FirstIndex(interpreter *Interpreter, locationRange Location return NilOptionalValue } +func (v *ArrayValue) Reverse(interpreter *Interpreter, locationRange LocationRange) VoidValue { + count := v.Count() + + for i := 0; i < count/2; i++ { + tempValue := v.Get(interpreter, locationRange, i) + v.Set(interpreter, locationRange, i, v.Get(interpreter, locationRange, count-i-1)) + v.Set(interpreter, locationRange, count-i-1, tempValue) + } + + return VoidValue{} +} + func (v *ArrayValue) Contains( interpreter *Interpreter, locationRange LocationRange, @@ -2371,6 +2383,18 @@ func (v *ArrayValue) GetMember(interpreter *Interpreter, locationRange LocationR }, ) + case "reverse": + return NewHostFunctionValue( + interpreter, + sema.ArrayReverseFunctionType, + func(invocation Invocation) Value { + return v.Reverse( + invocation.Interpreter, + invocation.LocationRange, + ) + }, + ) + case "contains": return NewHostFunctionValue( interpreter, diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 2e158dfb56..cd5afd6461 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -1714,6 +1714,10 @@ Returns the index of the first element matching the given object in the array, n Available if the array element type is not resource-kinded and equatable. ` +const arrayTypeReverseFunctionDocString = ` +Reverses the elements of the array. +` + const arrayTypeContainsFunctionDocString = ` Returns true if the given object is in the array ` @@ -1865,6 +1869,18 @@ func getArrayMembers(arrayType ArrayType) map[string]MemberResolver { ) }, }, + "reverse": { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { + return NewPublicFunctionMember( + memoryGauge, + arrayType, + identifier, + ArrayReverseFunctionType, + arrayTypeReverseFunctionDocString, + ) + }, + }, } // TODO: maybe still return members but report a helpful error? @@ -2116,6 +2132,12 @@ func ArrayFirstIndexFunctionType(elementType Type) *FunctionType { ), } } + +var ArrayReverseFunctionType *FunctionType = &FunctionType{ + Parameters: []Parameter{}, + ReturnTypeAnnotation: NewTypeAnnotation(VoidType), +} + func ArrayContainsFunctionType(elementType Type) *FunctionType { return &FunctionType{ Parameters: []Parameter{ diff --git a/runtime/tests/checker/arrays_dictionaries_test.go b/runtime/tests/checker/arrays_dictionaries_test.go index 9bcf610093..9774b4dc8b 100644 --- a/runtime/tests/checker/arrays_dictionaries_test.go +++ b/runtime/tests/checker/arrays_dictionaries_test.go @@ -1078,6 +1078,36 @@ func TestCheckInvalidResourceFirstIndex(t *testing.T) { assert.IsType(t, &sema.ResourceLossError{}, errs[2]) } +func TestCheckArrayReverse(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + fun test() { + let x = [1, 2, 3] + x.reverse() + } + `) + + require.NoError(t, err) +} + +func TestCheckInvalidArrayReverse(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + fun test() { + let x = [1, 2, 3] + x.reverse(100) + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.ArgumentCountError{}, errs[0]) +} + func TestCheckArrayContains(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 5e56e230bd..ee83c7cc85 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -10395,6 +10395,67 @@ func TestInterpretArrayFirstIndexDoesNotExist(t *testing.T) { ) } +func TestInterpretArrayReverse(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + let xs = [1, 2, 3, 100, 200] + let ys = [100, 467, 297, 23] + + fun reversexs() { + return xs.reverse() + } + + fun reverseys() { + return ys.reverse() + } + `) + + _, err := inter.Invoke("reversexs") + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + interpreter.NewArrayValue( + inter, + interpreter.EmptyLocationRange, + interpreter.VariableSizedStaticType{ + Type: interpreter.PrimitiveStaticTypeInt, + }, + common.ZeroAddress, + interpreter.NewUnmeteredIntValueFromInt64(200), + interpreter.NewUnmeteredIntValueFromInt64(100), + interpreter.NewUnmeteredIntValueFromInt64(3), + interpreter.NewUnmeteredIntValueFromInt64(2), + interpreter.NewUnmeteredIntValueFromInt64(1), + ), + inter.Globals.Get("xs").GetValue(), + ) + + _, err = inter.Invoke("reverseys") + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + interpreter.NewArrayValue( + inter, + interpreter.EmptyLocationRange, + interpreter.VariableSizedStaticType{ + Type: interpreter.PrimitiveStaticTypeInt, + }, + common.ZeroAddress, + interpreter.NewUnmeteredIntValueFromInt64(23), + interpreter.NewUnmeteredIntValueFromInt64(297), + interpreter.NewUnmeteredIntValueFromInt64(467), + interpreter.NewUnmeteredIntValueFromInt64(100), + ), + inter.Globals.Get("ys").GetValue(), + ) +} + func TestInterpretOptionalReference(t *testing.T) { t.Parallel() From db38bafe07b1fc71ead790c2274dffbc3d214253 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 1 Jul 2023 23:00:27 +0530 Subject: [PATCH 2/7] tab to whitespace in interpreter test case --- runtime/tests/interpreter/interpreter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index ee83c7cc85..51c72eb338 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -10401,7 +10401,7 @@ func TestInterpretArrayReverse(t *testing.T) { inter := parseCheckAndInterpret(t, ` let xs = [1, 2, 3, 100, 200] - let ys = [100, 467, 297, 23] + let ys = [100, 467, 297, 23] fun reversexs() { return xs.reverse() From 78bab43c1f1d402f9d9f315e7d551b33d8ef3497 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Thu, 6 Jul 2023 01:28:38 +0530 Subject: [PATCH 3/7] Improve naming by introducing local variables. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/interpreter/value.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index f3d5ab6659..066e1f76fe 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -2195,10 +2195,14 @@ func (v *ArrayValue) FirstIndex(interpreter *Interpreter, locationRange Location func (v *ArrayValue) Reverse(interpreter *Interpreter, locationRange LocationRange) VoidValue { count := v.Count() - for i := 0; i < count/2; i++ { - tempValue := v.Get(interpreter, locationRange, i) - v.Set(interpreter, locationRange, i, v.Get(interpreter, locationRange, count-i-1)) - v.Set(interpreter, locationRange, count-i-1, tempValue) + for leftIndex := 0; leftIndex < count/2; leftIndex++ { + rightIndex := count - leftIndex - 1 + + leftValue := v.Get(interpreter, locationRange, leftIndex) + rightValue := v.Get(interpreter, locationRange, rightIndex) + + v.Set(interpreter, locationRange, leftIndex, rightValue) + v.Set(interpreter, locationRange, rightIndex, leftValue) } return VoidValue{} From e68bcf4cfc74586d55b70d30c98900bdd2b74464 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Thu, 6 Jul 2023 01:28:55 +0530 Subject: [PATCH 4/7] Remove unnecessary slice allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/sema/type.go | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index cd5afd6461..be3f9ab3c2 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2134,7 +2134,6 @@ func ArrayFirstIndexFunctionType(elementType Type) *FunctionType { } var ArrayReverseFunctionType *FunctionType = &FunctionType{ - Parameters: []Parameter{}, ReturnTypeAnnotation: NewTypeAnnotation(VoidType), } From 64bf9c1c5820c5d47f3a11439464440fc6a2353a Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Thu, 6 Jul 2023 01:29:10 +0530 Subject: [PATCH 5/7] Return Void instead of VoidValue{} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/interpreter/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 066e1f76fe..026d7d8877 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -2205,7 +2205,7 @@ func (v *ArrayValue) Reverse(interpreter *Interpreter, locationRange LocationRan v.Set(interpreter, locationRange, rightIndex, leftValue) } - return VoidValue{} + return Void } func (v *ArrayValue) Contains( From 6cff2d2077bf16184050af7fe2f4440775fc6fb6 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 6 Jul 2023 01:30:44 +0530 Subject: [PATCH 6/7] Make return type of Reverse a Value --- runtime/interpreter/value.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 026d7d8877..e9fc9ed525 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -2192,7 +2192,7 @@ func (v *ArrayValue) FirstIndex(interpreter *Interpreter, locationRange Location return NilOptionalValue } -func (v *ArrayValue) Reverse(interpreter *Interpreter, locationRange LocationRange) VoidValue { +func (v *ArrayValue) Reverse(interpreter *Interpreter, locationRange LocationRange) Value { count := v.Count() for leftIndex := 0; leftIndex < count/2; leftIndex++ { @@ -2200,7 +2200,7 @@ func (v *ArrayValue) Reverse(interpreter *Interpreter, locationRange LocationRan leftValue := v.Get(interpreter, locationRange, leftIndex) rightValue := v.Get(interpreter, locationRange, rightIndex) - + v.Set(interpreter, locationRange, leftIndex, rightValue) v.Set(interpreter, locationRange, rightIndex, leftValue) } From 5056603117f788cdef5548c6c6fb5cc3ef1a1d97 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 6 Jul 2023 01:34:17 +0530 Subject: [PATCH 7/7] declare a constant for 'reverse' --- runtime/interpreter/value.go | 2 +- runtime/sema/type.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index e9fc9ed525..9939450562 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -2387,7 +2387,7 @@ func (v *ArrayValue) GetMember(interpreter *Interpreter, locationRange LocationR }, ) - case "reverse": + case sema.ArrayTypeReverseFunctionName: return NewHostFunctionValue( interpreter, sema.ArrayReverseFunctionType, diff --git a/runtime/sema/type.go b/runtime/sema/type.go index be3f9ab3c2..e8a05b7b13 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -1714,6 +1714,8 @@ Returns the index of the first element matching the given object in the array, n Available if the array element type is not resource-kinded and equatable. ` +const ArrayTypeReverseFunctionName = "reverse" + const arrayTypeReverseFunctionDocString = ` Reverses the elements of the array. ` @@ -1869,7 +1871,7 @@ func getArrayMembers(arrayType ArrayType) map[string]MemberResolver { ) }, }, - "reverse": { + ArrayTypeReverseFunctionName: { Kind: common.DeclarationKindFunction, Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { return NewPublicFunctionMember(