Skip to content

Commit

Permalink
Introduce reversed in Array type
Browse files Browse the repository at this point in the history
  • Loading branch information
darkdrag00nv2 committed Jul 11, 2023
1 parent 9f9c5fd commit efb1b4a
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 0 deletions.
45 changes: 45 additions & 0 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -2411,6 +2411,20 @@ func (v *ArrayValue) GetMember(interpreter *Interpreter, locationRange LocationR
)
},
)

case sema.ArrayTypeReversedFunctionName:
return NewHostFunctionValue(
interpreter,
sema.ArrayReversedFunctionType(
v.SemaType(interpreter).ElementType(false),
),
func(invocation Invocation) Value {
return v.Reversed(
invocation.Interpreter,
invocation.LocationRange,
)
},
)
}

return nil
Expand Down Expand Up @@ -2870,6 +2884,37 @@ func (v *ArrayValue) Slice(
)
}

func (v *ArrayValue) Reversed(
interpreter *Interpreter,
locationRange LocationRange,
) Value {
count := v.Count()
idx := count - 1

return NewArrayValueWithIterator(
interpreter,
NewVariableSizedStaticType(interpreter, v.Type.ElementType()),
common.ZeroAddress,
uint64(count),
func() Value {
if idx < 0 {
return nil
}

value := v.Get(interpreter, locationRange, idx)
idx--

return value.Transfer(
interpreter,
locationRange,
atree.Address{},
false,
nil,
)
},
)
}

// NumberValue
type NumberValue interface {
ComparableValue
Expand Down
40 changes: 40 additions & 0 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -1788,6 +1788,12 @@ It does not modify the original array.
If either of the parameters are out of the bounds of the array, or the indices are invalid (` + "`from > upTo`" + `), then the function will fail.
`

const ArrayTypeReversedFunctionName = "reversed"

const arrayTypeReversedFunctionDocString = `
Returns a new array with contents in the reversed order.
`

func getArrayMembers(arrayType ArrayType) map[string]MemberResolver {

members := map[string]MemberResolver{
Expand Down Expand Up @@ -1881,6 +1887,31 @@ func getArrayMembers(arrayType ArrayType) map[string]MemberResolver {
)
},
},
ArrayTypeReversedFunctionName: {
Kind: common.DeclarationKindFunction,
Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member {
elementType := arrayType.ElementType(false)

// It is impossible for a resource to be present in two arrays.
if elementType.IsResourceType() {
report(
&InvalidResourceArrayMemberError{
Name: identifier,
DeclarationKind: common.DeclarationKindFunction,
Range: targetRange,
},
)
}

return NewPublicFunctionMember(
memoryGauge,
arrayType,
identifier,
ArrayReversedFunctionType(elementType),
arrayTypeReversedFunctionDocString,
)
},
},
}

// TODO: maybe still return members but report a helpful error?
Expand Down Expand Up @@ -2193,6 +2224,15 @@ func ArraySliceFunctionType(elementType Type) *FunctionType {
}
}

func ArrayReversedFunctionType(elementType Type) *FunctionType {
return &FunctionType{
Parameters: []Parameter{},
ReturnTypeAnnotation: NewTypeAnnotation(&VariableSizedType{
Type: elementType,
}),
}
}

// VariableSizedType is a variable sized array type
type VariableSizedType struct {
Type Type
Expand Down
49 changes: 49 additions & 0 deletions runtime/tests/checker/arrays_dictionaries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,55 @@ func TestCheckInvalidResourceFirstIndex(t *testing.T) {
assert.IsType(t, &sema.ResourceLossError{}, errs[2])
}

func TestCheckArrayReversed(t *testing.T) {

t.Parallel()

_, err := ParseAndCheck(t, `
fun test() {
let x = [1, 2, 3]
let y = x.reversed()
}
`)

require.NoError(t, err)
}

func TestCheckArrayReversedInvalidArgs(t *testing.T) {

t.Parallel()

_, err := ParseAndCheck(t, `
fun test() {
let x = [1, 2, 3]
let y = x.reversed(100)
}
`)

errs := RequireCheckerErrors(t, err, 1)

assert.IsType(t, &sema.ArgumentCountError{}, errs[0])
}

func TestCheckResourceArrayReversedInvalid(t *testing.T) {

t.Parallel()

_, err := ParseAndCheck(t, `
resource X {}
fun test(): @[X] {
let xs <- [<-create X()]
return <-xs.reversed()
}
`)

errs := RequireCheckerErrors(t, err, 2)

assert.IsType(t, &sema.InvalidResourceArrayMemberError{}, errs[0])
assert.IsType(t, &sema.ResourceLossError{}, errs[1])
}

func TestCheckArrayContains(t *testing.T) {

t.Parallel()
Expand Down
175 changes: 175 additions & 0 deletions runtime/tests/interpreter/interpreter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10395,6 +10395,181 @@ func TestInterpretArrayFirstIndexDoesNotExist(t *testing.T) {
)
}

func TestInterpretArrayReversed(t *testing.T) {
t.Parallel()

inter := parseCheckAndInterpret(t, `
let xs = [1, 2, 3, 100, 200]
let ys = [100, 467, 297, 23]
let emptyVals: [Int] = []
fun reversedxs(): [Int] {
return xs.reversed()
}
fun originalxs(): [Int] {
return xs
}
fun reversedys(): [Int] {
return ys.reversed()
}
fun originalys(): [Int] {
return ys
}
fun reverseempty(): [Int] {
return emptyVals.reversed()
}
fun originalempty(): [Int] {
return emptyVals
}
pub struct TestStruct {
pub var test: Int
init(_ t: Int) {
self.test = t
}
}
let sa = [TestStruct(1), TestStruct(2), TestStruct(3)]
fun reversedsa(): [Int] {
let sa_rev = sa.reversed()
let res: [Int] = [];
for s in sa_rev {
res.append(s.test)
}
return res
}
fun originalsa(): [Int] {
let res: [Int] = [];
for s in sa {
res.append(s.test)
}
return res
}
`)

runValidCase := func(t *testing.T, reverseFuncName, originalFuncName string, reversedArray, originalArray *interpreter.ArrayValue) {
val, err := inter.Invoke(reverseFuncName)
require.NoError(t, err)

AssertValuesEqual(
t,
inter,
reversedArray,
val,
)

origVal, err := inter.Invoke(originalFuncName)
require.NoError(t, err)

// Original array remains unchanged
AssertValuesEqual(
t,
inter,
originalArray,
origVal,
)
}

runValidCase(t, "reverseempty", "originalempty",
interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt,
},
common.ZeroAddress,
), interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt,
},
common.ZeroAddress,
))

runValidCase(t, "reversedxs", "originalxs",
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),
), interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt,
},
common.ZeroAddress,
interpreter.NewUnmeteredIntValueFromInt64(1),
interpreter.NewUnmeteredIntValueFromInt64(2),
interpreter.NewUnmeteredIntValueFromInt64(3),
interpreter.NewUnmeteredIntValueFromInt64(100),
interpreter.NewUnmeteredIntValueFromInt64(200),
))

runValidCase(t, "reversedys", "originalys",
interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt,
},
common.ZeroAddress,
interpreter.NewUnmeteredIntValueFromInt64(23),
interpreter.NewUnmeteredIntValueFromInt64(297),
interpreter.NewUnmeteredIntValueFromInt64(467),
interpreter.NewUnmeteredIntValueFromInt64(100),
), interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt,
},
common.ZeroAddress,
interpreter.NewUnmeteredIntValueFromInt64(100),
interpreter.NewUnmeteredIntValueFromInt64(467),
interpreter.NewUnmeteredIntValueFromInt64(297),
interpreter.NewUnmeteredIntValueFromInt64(23),
))

runValidCase(t, "reversedsa", "originalsa",
interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt,
},
common.ZeroAddress,
interpreter.NewUnmeteredIntValueFromInt64(3),
interpreter.NewUnmeteredIntValueFromInt64(2),
interpreter.NewUnmeteredIntValueFromInt64(1),
), interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt,
},
common.ZeroAddress,
interpreter.NewUnmeteredIntValueFromInt64(1),
interpreter.NewUnmeteredIntValueFromInt64(2),
interpreter.NewUnmeteredIntValueFromInt64(3),
))
}

func TestInterpretOptionalReference(t *testing.T) {

t.Parallel()
Expand Down

0 comments on commit efb1b4a

Please sign in to comment.