diff --git a/vlib/builtin/fixed_array_map_test.v b/vlib/builtin/fixed_array_map_test.v new file mode 100644 index 0000000000..116c35ccf3 --- /dev/null +++ b/vlib/builtin/fixed_array_map_test.v @@ -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]! +} diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index c4ab008964..e2291d300d 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -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 } diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index 1fc2f26256..24ca3e3165 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -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 @@ -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 { @@ -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 { @@ -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 + '->') @@ -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 { @@ -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 { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 040c8e46ac..b4fe3902c9 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -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 }