diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 100f8510ca..e05986b890 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -256,6 +256,22 @@ pub fn (a array) last() voidptr { } } +// array.pop returns the last element of the array, and removes it +pub fn (mut a array) pop() voidptr { + // in a sense, this is the opposite of `a << x` + $if !no_bounds_checking? { + if a.len == 0 { + panic('array.pop: array is empty') + } + } + new_len := a.len - 1 + last_elem := unsafe { byteptr(a.data) + (new_len) * a.element_size } + a.len = new_len + // NB: a.cap is not changed here *on purpose*, so that + // further << ops on that array will be more efficient. + return memdup(last_elem, a.element_size) +} + // array.slice returns an array using the same buffer as original array // but starting from the `start` element and ending with the element before // the `end` element of the original array with the length and capacity diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 06f1f78e3a..32b2153d5a 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -946,3 +946,29 @@ fn test_reverse_in_place() { c.reverse_in_place() assert c == [[5, 6], [3, 4], [1, 2]] } + +fn test_array_int_pop() { + mut a := [1,2,3,4,5] + assert a.len == 5 + x := a.last() + y := a.pop() + assert x == y + assert a.len == 4 + z := a.pop() + assert a.len == 3 + assert z == 4 + a.pop() + a.pop() + final := a.pop() + assert final == 1 +} + +fn test_array_string_pop() { + mut a := ['abc', 'def', 'xyz'] + assert a.len == 3 + assert a.pop() == 'xyz' + assert a.pop() == 'def' + assert a.pop() == 'abc' + assert a.len == 0 + assert a.cap == 3 +} diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index ead6d343bc..d4cbbbf18b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -859,10 +859,14 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { call_expr.return_type = left_type call_expr.receiver_type = left_type.to_ptr() return call_expr.return_type - } else if left_type_sym.kind == .array && method_name in ['first', 'last'] { + } else if left_type_sym.kind == .array && method_name in ['first', 'last', 'pop'] { info := left_type_sym.info as table.Array call_expr.return_type = info.elem_type - call_expr.receiver_type = left_type + if method_name == 'pop' { + call_expr.receiver_type = left_type.to_ptr() + } else { + call_expr.receiver_type = left_type + } return call_expr.return_type } else if left_type_sym.kind == .array && method_name in ['insert', 'prepend'] { array_info := left_type_sym.info as table.Array diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index f54eb20cee..1c04cb47dc 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -352,12 +352,12 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } // TODO performance, detect `array` method differently if left_sym.kind == .array && node.name in - ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', 'clone', 'reverse', 'slice'] { + ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice'] { // && rec_sym.name == 'array' { // && rec_sym.name == 'array' && receiver_name.starts_with('array') { // `array_byte_clone` => `array_clone` receiver_type_name = 'array' - if node.name in ['last', 'first'] { + if node.name in ['last', 'first', 'pop'] { return_type_str := g.typ(node.return_type) g.write('*($return_type_str*)') }