all: implement array .any and .all (#9347)
							parent
							
								
									624c1f3bcf
								
							
						
					
					
						commit
						7222ee476b
					
				|  | @ -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'] {
 | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
|  | @ -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 {} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -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() | ||||
| 	} | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue