diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3583c2d6e8..aba8514edf 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -26,7 +26,7 @@ const ( valid_comp_if_platforms = ['amd64', 'aarch64', 'x64', 'x32', 'little_endian', 'big_endian'] valid_comp_if_other = ['js', 'debug', 'prod', 'test', 'glibc', 'prealloc', 'no_bounds_checking'] array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', - 'contains', 'index', 'wait'] + 'contains', 'index', 'wait', 'any', 'all'] ) pub struct Checker { @@ -1318,6 +1318,28 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', arg_expr.pos) } + } else if arg_expr.kind == .variable { + if arg_expr.obj is ast.Var { + expr := arg_expr.obj.expr + if expr is ast.AnonFn { + // copied from above + if expr.decl.params.len > 1 { + c.error('function needs exactly 1 argument', expr.decl.pos) + } else if is_map && (expr.decl.return_type == table.void_type + || expr.decl.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', + expr.decl.pos) + } else if !is_map && (expr.decl.return_type != table.bool_type + || expr.decl.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', + expr.decl.pos) + } + return + } + } + if !is_map && arg_expr.info.typ != table.bool_type { + c.error('type mismatch, should be bool', arg_expr.pos) + } } } else {} @@ -1665,7 +1687,7 @@ fn (mut c Checker) call_array_builtin_method(mut call_expr ast.CallExpr, left_ty } array_info := left_type_sym.info as table.Array elem_typ = array_info.elem_type - if method_name in ['filter', 'map'] { + if method_name in ['filter', 'map', 'any', 'all'] { // position of `it` doesn't matter scope_register_it(mut call_expr.scope, call_expr.pos, elem_typ) } else if method_name == 'sort' { @@ -1728,6 +1750,9 @@ fn (mut c Checker) call_array_builtin_method(mut call_expr ast.CallExpr, left_ty } else if method_name == 'filter' { // check fn c.check_map_and_filter(false, elem_typ, call_expr) + } else if method_name in ['any', 'all'] { + c.check_map_and_filter(false, elem_typ, call_expr) + call_expr.return_type = table.bool_type } else if method_name == 'clone' { // need to return `array_xxx` instead of `array` // in ['clone', 'str'] { diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index 4179be3792..8b1b31878b 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -573,3 +573,131 @@ fn (mut g Gen) gen_array_wait(node ast.CallExpr) { g.expr(node.left) g.write(')') } + +fn (mut g Gen) gen_array_any(node ast.CallExpr) { + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + sym := g.table.get_type_symbol(node.left_type) + info := sym.info as table.Array + // styp := g.typ(node.return_type) + elem_type_str := g.typ(info.elem_type) + g.empty_line = true + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.writeln('int ${tmp}_len = ${tmp}_orig.len;') + g.writeln('bool $tmp = false;') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];') + mut is_embed_map_filter := false + mut expr := node.args[0].expr + match mut expr { + ast.AnonFn { + g.write('\tif (') + g.gen_anon_fn_decl(mut expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + g.write('\tif (') + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym_t := g.table.get_type_symbol(var_info.typ) + if sym_t.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + ast.CallExpr { + if expr.name in ['map', 'filter'] { + is_embed_map_filter = true + g.stmt_path_pos << g.out.len + } + g.write('\tif (') + g.expr(node.args[0].expr) + } + else { + g.write('\tif (') + g.expr(node.args[0].expr) + } + } + g.writeln(') {') + g.writeln('\t\t$tmp = true;\n\t\t\tbreak;\n\t\t}') + g.writeln('}') + if !is_embed_map_filter { + g.stmt_path_pos << g.out.len + } + g.write('\n') + g.write(s) + g.write(tmp) +} + +fn (mut g Gen) gen_array_all(node ast.CallExpr) { + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + sym := g.table.get_type_symbol(node.left_type) + info := sym.info as table.Array + // styp := g.typ(node.return_type) + elem_type_str := g.typ(info.elem_type) + g.empty_line = true + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.writeln('int ${tmp}_len = ${tmp}_orig.len;') + g.writeln('bool $tmp = true;') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];') + mut is_embed_map_filter := false + mut expr := node.args[0].expr + match mut expr { + ast.AnonFn { + g.write('\tif (!(') + g.gen_anon_fn_decl(mut expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + g.write('\tif (!(') + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym_t := g.table.get_type_symbol(var_info.typ) + if sym_t.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + ast.CallExpr { + if expr.name in ['map', 'filter'] { + is_embed_map_filter = true + g.stmt_path_pos << g.out.len + } + g.write('\tif (!(') + g.expr(node.args[0].expr) + } + else { + g.write('\tif (!(') + g.expr(node.args[0].expr) + } + } + g.writeln(')) {') + g.writeln('\t\t$tmp = false;\n\t\t\tbreak;\n\t\t}') + g.writeln('}') + if !is_embed_map_filter { + g.stmt_path_pos << g.out.len + } + g.write('\n') + g.write(s) + g.write(tmp) +} diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index f308bed210..e35fbb779a 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -535,6 +535,14 @@ fn (mut g Gen) method_call(node ast.CallExpr) { g.gen_array_wait(node) return } + 'any' { + g.gen_array_any(node) + return + } + 'all' { + g.gen_array_all(node) + return + } else {} } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 4ee933a460..1f4bbbe414 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2111,7 +2111,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { } else { p.name_error = true } - is_filter := field_name in ['filter', 'map'] + is_filter := field_name in ['filter', 'map', 'any', 'all'] if is_filter || field_name == 'sort' { p.open_scope() } diff --git a/vlib/v/tests/array_methods_test.v b/vlib/v/tests/array_methods_test.v index 19dd2cbfa7..c3ef482e59 100644 --- a/vlib/v/tests/array_methods_test.v +++ b/vlib/v/tests/array_methods_test.v @@ -17,4 +17,14 @@ fn test_array_eval_count() { mut a2 := Counter{} assert a2.new_arr('filter() failed').filter(it < 3) == [1, 2] + + mut a3 := Counter{} + assert a3.new_arr('any() failed').any(it == 2) == true + a3 = Counter{} + assert a3.new_arr('any() failed').any(it < 0) == false + + mut a4 := Counter{} + assert a4.new_arr('all() failed').all(it > 0) == true + a4 = Counter{} + assert a4.new_arr('all() failed').all(it == 2) == false }