mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
builtin: add arr.pop_left() func (#25133)
This commit is contained in:
parent
b85782b7e7
commit
a8187442b3
11 changed files with 110 additions and 9 deletions
|
@ -511,6 +511,50 @@ pub fn (a array) last() voidptr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pop_left returns the first element of the array and removes it by advancing the data pointer.
|
||||||
|
// If the `array` is empty, this will panic.
|
||||||
|
// NOTE: This function:
|
||||||
|
// - Reduces both length and capacity by 1
|
||||||
|
// - Advances the underlying data pointer by one element
|
||||||
|
// - Leaves subsequent elements in-place (no memory copying)
|
||||||
|
// Sliced views will retain access to the original first element position,
|
||||||
|
// which is now detached from the array's active memory range.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// ```v
|
||||||
|
// mut a := [1, 2, 3, 4, 5]
|
||||||
|
// b := unsafe { a[..5] } // full slice view
|
||||||
|
// first := a.pop_left()
|
||||||
|
//
|
||||||
|
// // Array now starts from second element
|
||||||
|
// dump(a) // a: [2, 3, 4, 5]
|
||||||
|
// assert a.len == 4
|
||||||
|
// assert a.cap == 4
|
||||||
|
//
|
||||||
|
// // Slice retains original memory layout
|
||||||
|
// dump(b) // b: [1, 2, 3, 4, 5]
|
||||||
|
// assert b.len == 5
|
||||||
|
//
|
||||||
|
// assert first == 1
|
||||||
|
//
|
||||||
|
// // Modifications affect both array and slice views
|
||||||
|
// a[0] = 99
|
||||||
|
// assert b[1] == 99 // changed in both
|
||||||
|
// ```
|
||||||
|
pub fn (mut a array) pop_left() voidptr {
|
||||||
|
if a.len == 0 {
|
||||||
|
panic('array.pop_left: array is empty')
|
||||||
|
}
|
||||||
|
first_elem := a.data
|
||||||
|
unsafe {
|
||||||
|
a.data = &u8(a.data) + u64(a.element_size)
|
||||||
|
}
|
||||||
|
a.offset += a.element_size
|
||||||
|
a.len--
|
||||||
|
a.cap--
|
||||||
|
return first_elem
|
||||||
|
}
|
||||||
|
|
||||||
// pop returns the last element of the array, and removes it.
|
// pop returns the last element of the array, and removes it.
|
||||||
// If the `array` is empty, this will panic.
|
// If the `array` is empty, this will panic.
|
||||||
// NOTE: this function reduces the length of the given array,
|
// NOTE: this function reduces the length of the given array,
|
||||||
|
|
|
@ -216,6 +216,21 @@ fn (mut a array) prepend_many_noscan(val voidptr, size int) {
|
||||||
unsafe { a.insert_many_noscan(0, val, size) }
|
unsafe { a.insert_many_noscan(0, val, size) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pop_left returns the first element of the array and removes it by advancing the data pointer.
|
||||||
|
fn (mut a array) pop_left_noscan() voidptr {
|
||||||
|
if a.len == 0 {
|
||||||
|
panic('array.pop_left: array is empty')
|
||||||
|
}
|
||||||
|
first_elem := a.data
|
||||||
|
unsafe {
|
||||||
|
a.data = &u8(a.data) + u64(a.element_size)
|
||||||
|
}
|
||||||
|
a.offset += a.element_size
|
||||||
|
a.len--
|
||||||
|
a.cap--
|
||||||
|
return unsafe { memdup_noscan(first_elem, a.element_size) }
|
||||||
|
}
|
||||||
|
|
||||||
// pop returns the last element of the array, and removes it.
|
// pop returns the last element of the array, and removes it.
|
||||||
fn (mut a array) pop_noscan() voidptr {
|
fn (mut a array) pop_noscan() voidptr {
|
||||||
// in a sense, this is the opposite of `a << x`
|
// in a sense, this is the opposite of `a << x`
|
||||||
|
|
|
@ -1197,6 +1197,40 @@ fn test_reverse_in_place() {
|
||||||
assert c == [[5, 6], [3, 4], [1, 2]]
|
assert c == [[5, 6], [3, 4], [1, 2]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_array_int_pop_left() {
|
||||||
|
mut a := [1, 2, 3, 4, 5]
|
||||||
|
b := unsafe { a[..5] } // full slice view
|
||||||
|
assert a.len == 5
|
||||||
|
first := a[0]
|
||||||
|
x := a.pop_left()
|
||||||
|
assert first == x
|
||||||
|
assert a.len == 4
|
||||||
|
assert a.cap == 4
|
||||||
|
y := a.pop_left()
|
||||||
|
assert y == 2
|
||||||
|
a[0] = 100
|
||||||
|
// NOTE: update a[0] also update b[2]
|
||||||
|
assert b == [1, 2, 100, 4, 5]
|
||||||
|
|
||||||
|
mut one_elem := [1]
|
||||||
|
one := one_elem.pop_left()
|
||||||
|
assert one_elem.len == 0
|
||||||
|
assert one_elem.cap == 0
|
||||||
|
assert one == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_array_string_pop_left() {
|
||||||
|
mut a := ['abc', 'def', 'xyz']
|
||||||
|
assert a.len == 3
|
||||||
|
x := a.first()
|
||||||
|
y := a.pop_left()
|
||||||
|
assert x == y
|
||||||
|
assert a.pop_left() == 'def'
|
||||||
|
assert a.pop_left() == 'xyz'
|
||||||
|
assert a.len == 0
|
||||||
|
assert a.cap == 0
|
||||||
|
}
|
||||||
|
|
||||||
fn test_array_int_pop() {
|
fn test_array_int_pop() {
|
||||||
mut a := [1, 2, 3, 4, 5]
|
mut a := [1, 2, 3, 4, 5]
|
||||||
assert a.len == 5
|
assert a.len == 5
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub mut:
|
||||||
arr_insert bool // arr.insert()
|
arr_insert bool // arr.insert()
|
||||||
arr_first bool // arr.first()
|
arr_first bool // arr.first()
|
||||||
arr_last bool // arr.last()
|
arr_last bool // arr.last()
|
||||||
|
arr_pop_left bool // arr.pop_left()
|
||||||
arr_pop bool // arr.pop()
|
arr_pop bool // arr.pop()
|
||||||
arr_delete bool // arr.delete()
|
arr_delete bool // arr.delete()
|
||||||
arr_reverse bool // arr.reverse()
|
arr_reverse bool // arr.reverse()
|
||||||
|
|
|
@ -30,7 +30,7 @@ const generic_fn_postprocess_iterations_cutoff_limit = 1_000_000
|
||||||
// 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.
|
// 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.
|
||||||
pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
|
pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
|
||||||
'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all',
|
'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all',
|
||||||
'first', 'last', 'pop', 'delete', 'insert', 'prepend', 'count']
|
'first', 'last', 'pop_left', 'pop', 'delete', 'insert', 'prepend', 'count']
|
||||||
pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods)
|
pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods)
|
||||||
pub const fixed_array_builtin_methods = ['contains', 'index', 'any', 'all', 'wait', 'map', 'sort',
|
pub const fixed_array_builtin_methods = ['contains', 'index', 'any', 'all', 'wait', 'map', 'sort',
|
||||||
'sorted', 'sort_with_compare', 'sorted_with_compare', 'reverse', 'reverse_in_place', 'count']
|
'sorted', 'sort_with_compare', 'sorted_with_compare', 'reverse', 'reverse_in_place', 'count']
|
||||||
|
|
|
@ -3506,13 +3506,13 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
|
||||||
node.args[i].typ = c.expr(mut arg.expr)
|
node.args[i].typ = c.expr(mut arg.expr)
|
||||||
}
|
}
|
||||||
node.return_type = ast.int_type
|
node.return_type = ast.int_type
|
||||||
} else if method_name in ['first', 'last', 'pop'] {
|
} else if method_name in ['first', 'last', 'pop_left', 'pop'] {
|
||||||
c.markused_array_method(!c.is_builtin_mod, method_name)
|
c.markused_array_method(!c.is_builtin_mod, method_name)
|
||||||
if node_args_len != 0 {
|
if node_args_len != 0 {
|
||||||
c.error('`.${method_name}()` does not have any arguments', arg0.pos)
|
c.error('`.${method_name}()` does not have any arguments', arg0.pos)
|
||||||
}
|
}
|
||||||
node.return_type = array_info.elem_type
|
node.return_type = array_info.elem_type
|
||||||
if method_name == 'pop' {
|
if method_name in ['pop_left', 'pop'] {
|
||||||
c.check_for_mut_receiver(mut node.left)
|
c.check_for_mut_receiver(mut node.left)
|
||||||
node.receiver_type = node.left_type.ref()
|
node.receiver_type = node.left_type.ref()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -201,6 +201,9 @@ fn (mut c Checker) markused_array_method(check bool, method_name string) {
|
||||||
'last' {
|
'last' {
|
||||||
c.table.used_features.arr_last = true
|
c.table.used_features.arr_last = true
|
||||||
}
|
}
|
||||||
|
'pop_left' {
|
||||||
|
c.table.used_features.arr_pop_left = true
|
||||||
|
}
|
||||||
'pop' {
|
'pop' {
|
||||||
c.table.used_features.arr_pop = true
|
c.table.used_features.arr_pop = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1207,15 +1207,15 @@ fn (mut g Gen) gen_array_method_call(node ast.CallExpr, left_type ast.Type, left
|
||||||
g.expr(node.args[0].expr)
|
g.expr(node.args[0].expr)
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
'first', 'last', 'pop' {
|
'first', 'last', 'pop_left', 'pop' {
|
||||||
mut noscan := ''
|
mut noscan := ''
|
||||||
array_info := left_sym.info as ast.Array
|
array_info := left_sym.info as ast.Array
|
||||||
if node.name == 'pop' {
|
if node.name in ['pop_left', 'pop'] {
|
||||||
noscan = g.check_noscan(array_info.elem_type)
|
noscan = g.check_noscan(array_info.elem_type)
|
||||||
}
|
}
|
||||||
return_type_str := g.styp(node.return_type)
|
return_type_str := g.styp(node.return_type)
|
||||||
g.write('(*(${return_type_str}*)array_${node.name}${noscan}(')
|
g.write('(*(${return_type_str}*)array_${node.name}${noscan}(')
|
||||||
if node.name == 'pop' {
|
if node.name in ['pop_left', 'pop'] {
|
||||||
g.gen_arg_from_type(left_type, node.left)
|
g.gen_arg_from_type(left_type, node.left)
|
||||||
} else {
|
} else {
|
||||||
if node.left_type.is_ptr() {
|
if node.left_type.is_ptr() {
|
||||||
|
@ -1475,7 +1475,7 @@ fn (mut g Gen) resolve_receiver_name(node ast.CallExpr, unwrapped_rec_type ast.T
|
||||||
receiver_type_name = 'map'
|
receiver_type_name = 'map'
|
||||||
}
|
}
|
||||||
if final_left_sym.kind == .array && !(left_sym.kind == .alias && left_sym.has_method(node.name))
|
if final_left_sym.kind == .array && !(left_sym.kind == .alias && left_sym.has_method(node.name))
|
||||||
&& node.name in ['clear', 'repeat', 'sort_with_compare', 'sorted_with_compare', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
|
&& node.name in ['clear', 'repeat', 'sort_with_compare', 'sorted_with_compare', 'push_many', 'trim', 'first', 'last', 'pop_left', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
|
||||||
if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
|
if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
|
||||||
// `array_Xyz_clone` => `array_clone`
|
// `array_Xyz_clone` => `array_clone`
|
||||||
receiver_type_name = 'array'
|
receiver_type_name = 'array'
|
||||||
|
|
|
@ -319,7 +319,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last',
|
if node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last',
|
||||||
'pop', 'clone', 'reverse', 'slice', 'pointers'] {
|
'pop_left', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
|
||||||
if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
|
if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
|
||||||
// `array_Xyz_clone` => `array_clone`
|
// `array_Xyz_clone` => `array_clone`
|
||||||
receiver_type_name = 'array'
|
receiver_type_name = 'array'
|
||||||
|
|
|
@ -88,6 +88,10 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
|
||||||
if table.used_features.arr_reverse {
|
if table.used_features.arr_reverse {
|
||||||
core_fns << array_idx_str + '.reverse'
|
core_fns << array_idx_str + '.reverse'
|
||||||
}
|
}
|
||||||
|
if table.used_features.arr_pop_left {
|
||||||
|
core_fns << ref_array_idx_str + '.pop_left'
|
||||||
|
core_fns << ref_array_idx_str + '.pop_left_noscan'
|
||||||
|
}
|
||||||
if table.used_features.arr_pop {
|
if table.used_features.arr_pop {
|
||||||
core_fns << ref_array_idx_str + '.pop'
|
core_fns << ref_array_idx_str + '.pop'
|
||||||
core_fns << ref_array_idx_str + '.pop_noscan'
|
core_fns << ref_array_idx_str + '.pop_noscan'
|
||||||
|
|
|
@ -2,7 +2,7 @@ import v.token
|
||||||
|
|
||||||
fn test_new_keywords_matcher_from_array_trie_works() {
|
fn test_new_keywords_matcher_from_array_trie_works() {
|
||||||
method_names := ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', 'contains',
|
method_names := ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', 'contains',
|
||||||
'index', 'wait', 'any', 'all', 'first', 'last', 'pop']
|
'index', 'wait', 'any', 'all', 'first', 'last', 'pop_left', 'pop']
|
||||||
matcher := token.new_keywords_matcher_from_array_trie(method_names)
|
matcher := token.new_keywords_matcher_from_array_trie(method_names)
|
||||||
for word in [method_names.first(), method_names.last(), 'something', 'another', 'x', 'y', '',
|
for word in [method_names.first(), method_names.last(), 'something', 'another', 'x', 'y', '',
|
||||||
'---', '123', 'abc.def', 'xyz !@#'] {
|
'---', '123', 'abc.def', 'xyz !@#'] {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue