Skip to content

Commit

Permalink
renamed function and added more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bjartek committed Jan 11, 2022
1 parent e2b38dd commit 04fe5cd
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 20 deletions.
7 changes: 4 additions & 3 deletions docs/language/values-and-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1079,19 +1079,20 @@ are available for both variable-sized and fixed-sized or variable-sized arrays.
let containsKitty = numbers.contains("Kitty")
```

- `cadence•fun indexOf(_ element: T): Int?`
- `cadence•fun firstIndex(of:: T): Int?`
Returns the index of the first matching elmement of type `T`, or nil if no match

```cadence
// Declare an array of integers.
let numbers = [42, 23, 31, 12]
//Check if the array contains 31
let index = numbers.indexOf(31)
let index= numbers.firstIndex(of:31)
// `index` is 2
//Check if the array contains 22
let index = numbers.indexOf(31)
let index= numbers.firstIndex(of:22)
// `index` is nil
```

Expand Down
8 changes: 4 additions & 4 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ func (v *ArrayValue) RemoveLast(interpreter *Interpreter, getLocationRange func(
return v.Remove(interpreter, getLocationRange, v.Count()-1)
}

func (v *ArrayValue) IndexOf(interpreter *Interpreter, getLocationRange func() LocationRange, needleValue Value) OptionalValue {
func (v *ArrayValue) FirstIndex(interpreter *Interpreter, getLocationRange func() LocationRange, needleValue Value) OptionalValue {

needleEquatable, ok := needleValue.(EquatableValue)
if !ok {
Expand Down Expand Up @@ -1512,16 +1512,16 @@ func (v *ArrayValue) GetMember(inter *Interpreter, _ func() LocationRange, name
),
)

case "indexOf":
case "firstIndex":
return NewHostFunctionValue(
func(invocation Invocation) Value {
return v.IndexOf(
return v.FirstIndex(
invocation.Interpreter,
invocation.GetLocationRange,
invocation.Arguments[0],
)
},
sema.ArrayIndexOfFunctionType(
sema.ArrayFirstIndexFunctionType(
v.SemaType(inter).ElementType(false),
),
)
Expand Down
18 changes: 9 additions & 9 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -1511,8 +1511,9 @@ type ArrayType interface {
isArrayType()
}

const arrayTypeIndexOfFunctionDocString = `
Returns the index of the first element matching the given object in the array, nil if no match`
const arrayTypeFirstIndexFunctionDocString = `
Returns the index of the first element matching the given object in the array, nil if no match
`

const arrayTypeContainsFunctionDocString = `
Returns true if the given object is in the array
Expand Down Expand Up @@ -1624,13 +1625,13 @@ func getArrayMembers(arrayType ArrayType) map[string]MemberResolver {
)
},
},
"indexOf": {
"firstIndex": {
Kind: common.DeclarationKindFunction,
Resolve: func(identifier string, targetRange ast.Range, report func(error)) *Member {

elementType := arrayType.ElementType(false)

// It is impossible for an array of resources to have a `indexOf` function:
// It is impossible for an array of resources to have a `firstIndex` function:
// if the resource is passed as an argument, it cannot be inside the array

if elementType.IsResourceType() {
Expand All @@ -1657,8 +1658,8 @@ func getArrayMembers(arrayType ArrayType) map[string]MemberResolver {
return NewPublicFunctionMember(
arrayType,
identifier,
ArrayIndexOfFunctionType(elementType),
arrayTypeIndexOfFunctionDocString,
ArrayFirstIndexFunctionType(elementType),
arrayTypeFirstIndexFunctionDocString,
)
},
},
Expand Down Expand Up @@ -1886,12 +1887,11 @@ func ArrayConcatFunctionType(arrayType Type) *FunctionType {
}
}

func ArrayIndexOfFunctionType(elementType Type) *FunctionType {
func ArrayFirstIndexFunctionType(elementType Type) *FunctionType {
return &FunctionType{
Parameters: []*Parameter{
{
Label: ArgumentLabelNotRequired,
Identifier: "element",
Identifier: "of",
TypeAnnotation: NewTypeAnnotation(elementType),
},
},
Expand Down
54 changes: 53 additions & 1 deletion runtime/tests/checker/arrays_dictionaries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -760,13 +760,65 @@ func TestCheckArrayIndexOf(t *testing.T) {
_, err := ParseAndCheck(t, `
fun test(): Int? {
let x = [1, 2, 3]
return x.indexOf(2)
return x.firstIndex(of:2)
}
`)

require.NoError(t, err)
}

func TestCheckArrayFirstIndexDoesNotExist(t *testing.T) {

t.Parallel()

_, err := ParseAndCheck(t, `
fun test(): Int? {
let x = [1, 2, 3]
return x.firstIndex(of:5)
}
`)

require.NoError(t, err)
}

func TestCheckArrayFirstIndexWrongType(t *testing.T) {

t.Parallel()

_, err := ParseAndCheck(t, `
fun test(): Int? {
let x = [1, 2, 3]
return x.firstIndex(of:"foo")
}
`)
errs := ExpectCheckerErrors(t, err, 1)
assert.IsType(t, &sema.TypeMismatchError{}, errs[0])
}

func TestCheckInvalidResourceFirstIndex(t *testing.T) {

t.Parallel()

_, err := ParseAndCheck(t, `
resource X {}
fun test(): Int? {
let xs <- [<-create X()]
return <-xs.firstIndex(of: "foo")
}
`)

errs := ExpectCheckerErrors(t, err, 7)

assert.IsType(t, &sema.InvalidResourceArrayMemberError{}, errs[0])
assert.IsType(t, &sema.NotEquatableTypeError{}, errs[1])
assert.IsType(t, &sema.TypeMismatchError{}, errs[2])
assert.IsType(t, &sema.MissingMoveOperationError{}, errs[3])
assert.IsType(t, &sema.InvalidMoveOperationError{}, errs[4])
assert.IsType(t, &sema.ResourceLossError{}, errs[5])
assert.IsType(t, &sema.ResourceLossError{}, errs[6])
}

func TestCheckArrayContains(t *testing.T) {

t.Parallel()
Expand Down
27 changes: 25 additions & 2 deletions runtime/tests/interpreter/interpreter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9616,15 +9616,15 @@ func TestInterpretArrayTypeInference(t *testing.T) {
})
}

func TestInterpretArrayIndexOf(t *testing.T) {
func TestInterpretArrayFirstIndex(t *testing.T) {

t.Parallel()

inter := parseCheckAndInterpret(t, `
let xs = [1, 2, 3]
fun test() :Int? {
return xs.indexOf(2)
return xs.firstIndex(of:2)
}
`)

Expand All @@ -9640,3 +9640,26 @@ func TestInterpretArrayIndexOf(t *testing.T) {
value,
)
}

func TestInterpretArrayFirstIndexDoesNotExist(t *testing.T) {

t.Parallel()

inter := parseCheckAndInterpret(t, `
let xs = [1, 2, 3]
fun test() :Int? {
return xs.firstIndex(of:5)
}
`)

value, err := inter.Invoke("test")
require.NoError(t, err)

AssertValuesEqual(
t,
inter,
interpreter.NilValue{},
value,
)
}
2 changes: 1 addition & 1 deletion semantics/fpl.k
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ module FPL-TYPING
syntax Ident ::= "length" [token] | "x" [token] | "y" [token] | "at" [token]
| "concat" [token] | "contains" [token] | "append" [token]
| "insert" [token] | "remove" [token] | "removeFirst" [token]
| "removeLast" [token] | "indexOf" [token]
| "removeLast" [token] | "firstIndex" [token]

/* #type() calculates the result type of an expression, but may
use an extra "type" IntLit when there are integer literals
Expand Down

0 comments on commit 04fe5cd

Please sign in to comment.