Skip to content

Commit

Permalink
checker, cgen: implement method map() for fixed arrays (vlang#22644)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyi98 authored Oct 25, 2024
1 parent 4e9f7c2 commit 5ad0186
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 20 deletions.
27 changes: 27 additions & 0 deletions vlib/builtin/fixed_array_map_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
fn test_fixed_array_map() {
a := [1, 2, 3]!

b1 := a.map(it * 2)
println(b1)
assert b1 == [2, 4, 6]!

b11 := a.map(|x| x * 2)
println(b11)
assert b11 == [2, 4, 6]!

b2 := a.map('${it}')
println(b2)
assert b2 == ['1', '2', '3']!

b22 := a.map(|x| '${x}')
println(b22)
assert b22 == ['1', '2', '3']!

b3 := a.map(it + 2)
println(b3)
assert b3 == [3, 4, 5]!

b33 := a.map(|x| x + 2)
println(b33)
assert b33 == [3, 4, 5]!
}
45 changes: 45 additions & 0 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -3498,6 +3498,51 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
c.error('`${left_sym.name}` has no method `wait()` (only thread handles and arrays of them have)',
node.left.pos())
}
} else if method_name == 'map' {
if node.args.len != 1 {
c.error('`.${method_name}` expected 1 argument, but got ${node.args.len}',
node.pos)
return ast.void_type
}
if mut node.args[0].expr is ast.LambdaExpr {
if node.args[0].expr.params.len != 1 {
c.error('lambda expressions used in the builtin array methods require exactly 1 parameter',
node.args[0].expr.pos)
return ast.void_type
}
c.lambda_expr_fix_type_of_param(mut node.args[0].expr, mut node.args[0].expr.params[0],
elem_typ)
le_type := c.expr(mut node.args[0].expr.expr)
c.support_lambda_expr_one_param(elem_typ, le_type, mut node.args[0].expr)
} else {
// position of `it` doesn't matter
scope_register_it(mut node.scope, node.pos, elem_typ)
}

c.check_map_and_filter(true, elem_typ, node)
arg_type := c.check_expr_option_or_result_call(node.args[0].expr, c.expr(mut node.args[0].expr))
arg_sym := c.table.sym(arg_type)
ret_type := match arg_sym.info {
ast.FnType {
if node.args[0].expr is ast.SelectorExpr {
arg_type
} else {
arg_sym.info.func.return_type
}
}
else {
arg_type
}
}
node.return_type = c.table.find_or_register_array_fixed(c.unwrap_generic(ret_type),
array_info.size, array_info.size_expr, false)
if node.return_type.has_flag(.shared_f) {
node.return_type = node.return_type.clear_flag(.shared_f).deref()
}
ret_sym := c.table.sym(ret_type)
if ret_sym.kind == .multi_return {
c.error('returning multiple values is not supported in .map() calls', node.pos)
}
}
return node.return_type
}
Expand Down
56 changes: 36 additions & 20 deletions vlib/v/gen/c/array.v
Original file line number Diff line number Diff line change
Expand Up @@ -463,15 +463,26 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
g.past_tmp_var_done(past)
}

ret_typ := g.styp(node.return_type)
ret_styp := g.styp(node.return_type)
ret_sym := g.table.final_sym(node.return_type)
left_is_array := g.table.final_sym(node.left_type).kind == .array
inp_sym := g.table.final_sym(node.receiver_type)
ret_info := ret_sym.info as ast.Array
mut ret_elem_type := g.styp(ret_info.elem_type)
inp_info := inp_sym.info as ast.Array
inp_elem_type := g.styp(inp_info.elem_type)
if inp_sym.kind != .array {
verror('map() requires an array')

ret_elem_type := if left_is_array {
(ret_sym.info as ast.Array).elem_type
} else {
(ret_sym.info as ast.ArrayFixed).elem_type
}
mut ret_elem_styp := g.styp(ret_elem_type)

inp_elem_type := if left_is_array {
(inp_sym.info as ast.Array).elem_type
} else {
(inp_sym.info as ast.ArrayFixed).elem_type
}
inp_elem_styp := g.styp(inp_elem_type)
if inp_sym.kind !in [.array, .array_fixed] {
verror('map() requires an array or a fixed array')
}

mut expr := node.args[0].expr
Expand All @@ -481,16 +492,18 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
if expr.typ != ast.void_type {
var_sym := g.table.sym(expr.typ)
if var_sym.info is ast.FnType {
ret_elem_type = 'voidptr'
ret_elem_styp = 'voidptr'
closure_var_decl = g.fn_var_signature(var_sym.info.func.return_type, var_sym.info.func.params.map(it.typ),
tmp_map_expr_result_name)
}
}
}
noscan := g.check_noscan(ret_info.elem_type)
has_infix_left_var_name := g.write_prepared_tmp_value(past.tmp_var, node, ret_typ,
noscan := g.check_noscan(ret_elem_type)
has_infix_left_var_name := g.write_prepared_tmp_value(past.tmp_var, node, ret_styp,
'{0}')
g.writeln('${past.tmp_var} = __new_array${noscan}(0, ${past.tmp_var}_len, sizeof(${ret_elem_type}));\n')
if left_is_array {
g.writeln('${past.tmp_var} = __new_array${noscan}(0, ${past.tmp_var}_len, sizeof(${ret_elem_styp}));\n')
}

mut closure_var := ''
if mut expr is ast.AnonFn {
Expand All @@ -504,13 +517,12 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
g.writeln('for (int ${i} = 0; ${i} < ${past.tmp_var}_len; ++${i}) {')
g.indent++
var_name := g.get_array_expr_param_name(mut expr)
g.write_prepared_var(var_name, inp_info.elem_type, inp_elem_type, past.tmp_var, i,
true)
g.write_prepared_var(var_name, inp_elem_type, inp_elem_styp, past.tmp_var, i, left_is_array)
g.set_current_pos_as_last_stmt_pos()
mut is_embed_map_filter := false
match mut expr {
ast.AnonFn {
g.write('${ret_elem_type} ${tmp_map_expr_result_name} = ')
g.write('${ret_elem_styp} ${tmp_map_expr_result_name} = ')
if expr.inherited_vars.len > 0 {
g.write_closure_fn(mut expr, var_name, closure_var)
} else {
Expand All @@ -519,7 +531,7 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
}
}
ast.Ident {
g.write('${ret_elem_type} ${tmp_map_expr_result_name} = ')
g.write('${ret_elem_styp} ${tmp_map_expr_result_name} = ')
if expr.kind == .function {
if expr.obj is ast.Var && expr.obj.is_inherited {
g.write(closure_ctx + '->')
Expand All @@ -545,7 +557,7 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
is_embed_map_filter = true
g.set_current_pos_as_last_stmt_pos()
}
g.write('${ret_elem_type} ${tmp_map_expr_result_name} = ')
g.write('${ret_elem_styp} ${tmp_map_expr_result_name} = ')
g.expr(expr)
}
ast.CastExpr {
Expand All @@ -557,23 +569,27 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
expr.expr_type = g.table.value_type(ctyp)
}
}
g.write('${ret_elem_type} ${tmp_map_expr_result_name} = ')
g.write('${ret_elem_styp} ${tmp_map_expr_result_name} = ')
g.expr(expr)
}
ast.LambdaExpr {
g.write('${ret_elem_type} ${tmp_map_expr_result_name} = ')
g.write('${ret_elem_styp} ${tmp_map_expr_result_name} = ')
g.expr(expr.expr)
}
else {
if closure_var_decl != '' {
g.write('${closure_var_decl} = ')
} else {
g.write('${ret_elem_type} ${tmp_map_expr_result_name} = ')
g.write('${ret_elem_styp} ${tmp_map_expr_result_name} = ')
}
g.expr(expr)
}
}
g.writeln2(';', 'array_push${noscan}((array*)&${past.tmp_var}, &${tmp_map_expr_result_name});')
if left_is_array {
g.writeln2(';', 'array_push${noscan}((array*)&${past.tmp_var}, &${tmp_map_expr_result_name});')
} else {
g.writeln2(';', '${past.tmp_var}[${i}] = ${tmp_map_expr_result_name};')
}
g.indent--
g.writeln('}')
if !is_embed_map_filter {
Expand Down
3 changes: 3 additions & 0 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,9 @@ fn (mut g Gen) gen_fixed_array_method_call(node ast.CallExpr, left_type ast.Type
'all' {
g.gen_array_all(node)
}
'map' {
g.gen_array_map(node)
}
else {
return false
}
Expand Down

0 comments on commit 5ad0186

Please sign in to comment.