all: implement array .any and .all (#9347)

pull/9365/head
Miccah 2021-03-18 09:24:16 -05:00 committed by GitHub
parent 624c1f3bcf
commit 7222ee476b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 174 additions and 3 deletions

View File

@ -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'] {

View File

@ -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)
}

View File

@ -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 {}
}
}

View File

@ -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()
}

View File

@ -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
}