Skip to content

Commit ef05fb1

Browse files
authored
builtin: add array.last_index() (#26252)
1 parent 604417e commit ef05fb1

15 files changed

Lines changed: 149 additions & 37 deletions

File tree

‎vlib/builtin/array.v‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,9 @@ pub fn (a array) contains(value voidptr) bool
996996
// index returns the first index at which a given element can be found in the array or `-1` if the value is not found.
997997
pub fn (a array) index(value voidptr) int
998998

999+
// last_index returns the last index at which a given element can be found in the array or `-1` if the value is not found.
1000+
pub fn (a array) last_index(value voidptr) int
1001+
9991002
@[direct_array_access; unsafe]
10001003
pub fn (mut a []string) free() {
10011004
$if prealloc {

‎vlib/builtin/array_test.v‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,29 @@ fn test_find_index() {
556556
assert d.index(`u`) == -1
557557
}
558558

559+
fn test_find_last_index() {
560+
// string
561+
a := ['v', 'is', 'great', 'is', 'k', 'm']
562+
assert a.last_index('v') == 0
563+
assert a.last_index('is') == 3
564+
assert a.last_index('gre') == -1
565+
// int
566+
b := [1, 2, 3, 4, 0, 1, 2, 3, 0, 1, 2, 3]
567+
assert b.last_index(1) == 9
568+
assert b.last_index(4) == 3
569+
assert b.last_index(5) == -1
570+
// byte
571+
c := [0x22, 0x33, 0x55, 0x22, 0x44, 0x55]
572+
assert c.last_index(0x22) == 3
573+
assert c.last_index(0x55) == 5
574+
assert c.last_index(0x99) == -1
575+
// char
576+
d := [`a`, `b`, `c`, `e`, `a`, `b`, `c`, `k`]
577+
assert d.last_index(`b`) == 5
578+
assert d.last_index(`c`) == 6
579+
assert d.last_index(`u`) == -1
580+
}
581+
559582
fn test_multi() {
560583
a := [[1, 2, 3], [4, 5, 6]]
561584
assert a.len == 2

‎vlib/builtin/js/array.js.v‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ pub fn (a array) index(v string) int {
182182
return -1
183183
}
184184

185+
pub fn (a array) last_index(v string) int {
186+
for i := a.len - 1; i >= 0; i-- {
187+
#if (a.arr.get(i).toString() == v.toString())
188+
189+
{
190+
return i
191+
}
192+
}
193+
return -1
194+
}
195+
185196
pub fn (a array) slice(start int, end int) array {
186197
mut result := a
187198
#let slice = a.arr.arr.slice(start,end)

‎vlib/builtin/js/array_test.js.v‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,29 @@ fn test_find_index() {
542542
assert d.index(`u`) == -1
543543
}
544544

545+
fn test_find_last_index() {
546+
// string
547+
a := ['v', 'is', 'great', 'is', 'k', 'm']
548+
assert a.last_index('v') == 0
549+
assert a.last_index('is') == 3
550+
assert a.last_index('gre') == -1
551+
// int
552+
b := [1, 2, 3, 4, 0, 1, 2, 3, 0, 1, 2, 3]
553+
assert b.last_index(1) == 9
554+
assert b.last_index(4) == 3
555+
assert b.last_index(5) == -1
556+
// byte
557+
c := [0x22, 0x33, 0x55, 0x22, 0x44, 0x55]
558+
assert c.last_index(0x22) == 3
559+
assert c.last_index(0x55) == 5
560+
assert c.last_index(0x99) == -1
561+
// char
562+
d := [`a`, `b`, `c`, `e`, `a`, `b`, `c`, `k`]
563+
assert d.last_index(`b`) == 5
564+
assert d.last_index(`c`) == 6
565+
assert d.last_index(`u`) == -1
566+
}
567+
545568
fn test_multi() {
546569
a := [[1, 2, 3], [4, 5, 6]]
547570
assert a.len == 2

‎vlib/v/ast/ast.v‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,7 @@ pub enum CallKind {
840840
trim
841841
contains
842842
index
843+
last_index
843844
first
844845
last
845846
pop_left

‎vlib/v/checker/checker.v‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ const generic_fn_postprocess_iterations_cutoff_limit = 1_000_000
2929
// are properly checked.
3030
// Note that methods that do not return anything, or that return known types, are not listed here, since they are just ordinary non generic methods.
3131
pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
32-
'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all',
33-
'first', 'last', 'pop_left', 'pop', 'delete', 'insert', 'prepend', 'count']
32+
'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'last_index', 'wait',
33+
'any', 'all', 'first', 'last', 'pop_left', 'pop', 'delete', 'insert', 'prepend', 'count']
3434
pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods)
35-
pub const fixed_array_builtin_methods = ['contains', 'index', 'any', 'all', 'wait', 'map', 'sort',
36-
'sorted', 'sort_with_compare', 'sorted_with_compare', 'reverse', 'reverse_in_place', 'count',
37-
'filter']
35+
pub const fixed_array_builtin_methods = ['contains', 'index', 'last_index', 'any', 'all', 'wait',
36+
'map', 'sort', 'sorted', 'sort_with_compare', 'sorted_with_compare', 'reverse',
37+
'reverse_in_place', 'count', 'filter']
3838
pub const fixed_array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(fixed_array_builtin_methods)
3939
// TODO: remove `byte` from this list when it is no longer supported
4040
pub const reserved_type_names = ['bool', 'char', 'i8', 'i16', 'i32', 'int', 'i64', 'u8', 'u16',

‎vlib/v/checker/fn.v‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3638,13 +3638,14 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
36383638
node.args[i].typ = c.expr(mut arg.expr)
36393639
}
36403640
node.return_type = ast.bool_type
3641-
} else if node.kind == .index {
3641+
} else if node.kind in [.index, .last_index] {
36423642
if node_args_len != 1 {
3643-
c.error('`.index()` expected 1 argument, but got ${node_args_len}', node.pos)
3644-
} else if !left_sym.has_method('index') {
3643+
c.error('`.${method_name}()` expected 1 argument, but got ${node_args_len}',
3644+
node.pos)
3645+
} else if !left_sym.has_method(method_name) {
36453646
arg_typ := c.unwrap_generic(c.expr(mut arg0.expr))
36463647
c.check_expected_call_arg(arg_typ, c.unwrap_generic(elem_typ), node.language,
3647-
arg0) or { c.error('${err.msg()} in argument 1 to `.index()`', arg0.pos) }
3648+
arg0) or { c.error('${err.msg()} in argument 1 to `.${method_name}()`', arg0.pos) }
36483649
}
36493650
for i, mut arg in node.args {
36503651
node.args[i].typ = c.expr(mut arg.expr)
@@ -3710,14 +3711,16 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
37103711
node_args_len := node.args.len
37113712
mut arg0 := if node_args_len > 0 { node.args[0] } else { ast.CallArg{} }
37123713
elem_typ := array_info.elem_type
3713-
if node.kind == .index {
3714+
if node.kind in [.index, .last_index] {
37143715
if node_args_len != 1 {
3715-
c.error('`.index()` expected 1 argument, but got ${node_args_len}', node.pos)
3716+
c.error('`.${method_name}()` expected 1 argument, but got ${node_args_len}',
3717+
node.pos)
37163718
return ast.int_type
3717-
} else if !left_sym.has_method('index') {
3719+
} else if (node.kind == .index && !left_sym.has_method('index'))
3720+
|| (node.kind == .last_index && !left_sym.has_method('last_index')) {
37183721
arg_typ := c.expr(mut arg0.expr)
37193722
c.check_expected_call_arg(arg_typ, elem_typ, node.language, arg0) or {
3720-
c.error('${err.msg()} in argument 1 to `.index()`', arg0.pos)
3723+
c.error('${err.msg()} in argument 1 to `.${method_name}()`', arg0.pos)
37213724
return ast.int_type
37223725
}
37233726
}

‎vlib/v/gen/c/array.v‎

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,22 +1308,33 @@ fn (mut g Gen) gen_array_contains(left_type ast.Type, left ast.Expr, right_type
13081308
g.write(')')
13091309
}
13101310

1311-
fn (mut g Gen) get_array_index_method(typ ast.Type) string {
1311+
fn (mut g Gen) get_array_index_method(typ ast.Type, is_last_index bool) string {
13121312
t := g.unwrap_generic(typ).set_nr_muls(0)
1313-
g.array_index_types << t
1314-
return g.styp(t) + '_index'
1313+
return if is_last_index {
1314+
g.array_last_index_types << t
1315+
g.styp(t) + '_last_index'
1316+
} else {
1317+
g.array_index_types << t
1318+
g.styp(t) + '_index'
1319+
}
13151320
}
13161321

1317-
fn (mut g Gen) gen_array_index_methods() {
1322+
fn (mut g Gen) gen_array_index_methods(is_last_index bool) {
13181323
mut done := []ast.Type{}
1319-
for t in g.array_index_types {
1320-
if t in done || g.table.sym(t).has_method('index') {
1324+
indxe_types := if is_last_index { g.array_last_index_types } else { g.array_index_types }
1325+
for t in indxe_types {
1326+
if t in done || (is_last_index && g.table.sym(t).has_method('last_index'))
1327+
|| (!is_last_index && g.table.sym(t).has_method('index')) {
13211328
continue
13221329
}
13231330
done << t
13241331
final_left_sym := g.table.final_sym(t)
13251332
mut left_type_str := g.styp(t)
1326-
fn_name := '${left_type_str}_index'
1333+
fn_name := if is_last_index {
1334+
'${left_type_str}_last_index'
1335+
} else {
1336+
'${left_type_str}_index'
1337+
}
13271338
mut fn_builder := strings.new_builder(512)
13281339

13291340
if final_left_sym.kind == .array {
@@ -1336,8 +1347,14 @@ fn (mut g Gen) gen_array_index_methods() {
13361347
}
13371348
g.type_definitions.writeln('${g.static_non_parallel}${ast.int_type_name} ${fn_name}(${left_type_str} a, ${elem_type_str} v);')
13381349
fn_builder.writeln('${g.static_non_parallel}${ast.int_type_name} ${fn_name}(${left_type_str} a, ${elem_type_str} v) {')
1339-
fn_builder.writeln('\t${elem_type_str}* pelem = a.data;')
1340-
fn_builder.writeln('\tfor (${ast.int_type_name} i = 0; i < a.len; ++i, ++pelem) {')
1350+
if is_last_index {
1351+
fn_builder.writeln('\tif (a.len == 0) return -1;')
1352+
fn_builder.writeln('\t${elem_type_str}* pelem = (${elem_type_str}*)((byte*)a.data + (a.len-1)*a.element_size);')
1353+
fn_builder.writeln('\tfor (${ast.int_type_name} i = a.len-1; i >= 0; --i, --pelem) {')
1354+
} else {
1355+
fn_builder.writeln('\t${elem_type_str}* pelem = a.data;')
1356+
fn_builder.writeln('\tfor (${ast.int_type_name} i = 0; i < a.len; ++i, ++pelem) {')
1357+
}
13411358
if elem_sym.kind == .string {
13421359
fn_builder.writeln('\t\tif (builtin__fast_string_eq(*pelem, v)) {')
13431360
} else if elem_sym.kind in [.array, .array_fixed] && !info.elem_type.is_ptr() {
@@ -1425,8 +1442,9 @@ fn (mut g Gen) gen_array_index_methods() {
14251442
}
14261443

14271444
// `nums.index(2)`
1428-
fn (mut g Gen) gen_array_index(node ast.CallExpr) {
1429-
fn_name := g.get_array_index_method(node.left_type)
1445+
// `nums.last_index(2)`
1446+
fn (mut g Gen) gen_array_index(node ast.CallExpr, is_last_index bool) {
1447+
fn_name := g.get_array_index_method(node.left_type, is_last_index)
14301448
left_sym := g.table.final_sym(node.left_type)
14311449
g.write('${fn_name}(')
14321450
if node.left_type.is_ptr() {

‎vlib/v/gen/c/cgen.v‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ mut:
196196
array_sort_fn shared []string
197197
array_contains_types []ast.Type
198198
array_index_types []ast.Type
199+
array_last_index_types []ast.Type
199200
auto_fn_definitions []string // auto generated functions definition list
200201
sumtype_casting_fns []SumtypeCastingFn
201202
anon_fn_definitions []string // anon generated functions definition list
@@ -480,6 +481,7 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO
480481
global_g.needed_equality_fns << g.needed_equality_fns // duplicates are resolved later in gen_equality_fns
481482
global_g.array_contains_types << g.array_contains_types
482483
global_g.array_index_types << g.array_index_types
484+
global_g.array_last_index_types << g.array_last_index_types
483485
global_g.pcs << g.pcs
484486
global_g.json_types << g.json_types
485487
global_g.hotcode_fn_names << g.hotcode_fn_names
@@ -522,7 +524,8 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO
522524
global_g.write_chan_pop_option_fns()
523525
global_g.write_chan_push_option_fns()
524526
global_g.gen_array_contains_methods()
525-
global_g.gen_array_index_methods()
527+
global_g.gen_array_index_methods(false) // .index()
528+
global_g.gen_array_index_methods(true) // .last_index()
526529
global_g.gen_equality_fns()
527530
global_g.gen_free_methods()
528531
global_g.register_iface_return_types()

‎vlib/v/gen/c/fn.v‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,7 +1203,10 @@ fn (mut g Gen) gen_array_method_call(node ast.CallExpr, left_type ast.Type, left
12031203
g.gen_array_contains(left_type, node.left, node.args[0].typ, node.args[0].expr)
12041204
}
12051205
.index {
1206-
g.gen_array_index(node)
1206+
g.gen_array_index(node, false)
1207+
}
1208+
.last_index {
1209+
g.gen_array_index(node, true)
12071210
}
12081211
.wait {
12091212
g.gen_array_wait(node)
@@ -1308,7 +1311,10 @@ fn (mut g Gen) gen_fixed_array_method_call(node ast.CallExpr, left_type ast.Type
13081311
g.gen_array_filter(node)
13091312
}
13101313
.index {
1311-
g.gen_array_index(node)
1314+
g.gen_array_index(node, false)
1315+
}
1316+
.last_index {
1317+
g.gen_array_index(node, true)
13121318
}
13131319
.contains {
13141320
g.gen_array_contains(left_type, node.left, node.args[0].typ, node.args[0].expr)

0 commit comments

Comments
 (0)