compiler: handle ranges as `match` conditions (#5847)
							parent
							
								
									612fe1b8fb
								
							
						
					
					
						commit
						b900577dae
					
				|  | @ -2585,6 +2585,39 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol | |||
| 	for branch in node.branches { | ||||
| 		for expr in branch.exprs { | ||||
| 			mut key := '' | ||||
| 			if expr is ast.RangeExpr { | ||||
| 				mut low := 0 | ||||
| 				mut high := 0 | ||||
| 				c.expected_type = node.expected_type | ||||
| 				if expr.low is ast.IntegerLiteral as low_expr { | ||||
| 					if expr.high is ast.IntegerLiteral as high_expr { | ||||
| 						low = low_expr.val.int() | ||||
| 						high = high_expr.val.int() | ||||
| 					} else { | ||||
| 						c.error('mismatched range types', low_expr.pos) | ||||
| 					} | ||||
| 				} else if expr.low is ast.CharLiteral as low_expr { | ||||
| 					if expr.high is ast.CharLiteral as high_expr { | ||||
| 						low = low_expr.val[0] | ||||
| 						high = high_expr.val[0] | ||||
| 					} else { | ||||
| 						c.error('mismatched range types', low_expr.pos) | ||||
| 					} | ||||
| 				} else { | ||||
| 					typ := c.table.type_to_str(c.expr(expr.low)) | ||||
| 					c.error('cannot use type `$typ` in match range', branch.pos) | ||||
| 				} | ||||
| 
 | ||||
| 				for i in low..high { | ||||
| 					key = i.str() | ||||
| 					val := if key in branch_exprs { branch_exprs[key] } else { 0 } | ||||
| 					if val == 1 { | ||||
| 						c.error('match case `$key` is handled more than once', branch.pos) | ||||
| 					} | ||||
| 					branch_exprs[key] = val + 1 | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			match expr { | ||||
| 				ast.Type { key = c.table.type_to_str(expr.typ) } | ||||
| 				ast.EnumVal { key = expr.val } | ||||
|  |  | |||
|  | @ -33,3 +33,10 @@ vlib/v/checker/tests/match_duplicate_branch.v:43:3: error: match case `2` is han | |||
|       |         ~~~ | ||||
|    44 |         else { println('else') } | ||||
|    45 |     } | ||||
| vlib/v/checker/tests/match_duplicate_branch.v:51:3: error: match case `3` is handled more than once | ||||
|    49 |     match i { | ||||
|    50 |         1..5 { println('1 to 4') } | ||||
|    51 |         3 { println('3') } | ||||
|       |         ~~~ | ||||
|    52 |         else { println('else') } | ||||
|    53 |     } | ||||
|  | @ -45,8 +45,17 @@ fn test_int(i int) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn test_range(i int) { | ||||
| 	match i { | ||||
| 		1..5 { println('1 to 4') } | ||||
| 		3 { println('3') } | ||||
| 		else { println('else') } | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	test_sum_type(St1{}) | ||||
| 	test_enum(.red) | ||||
| 	test_int(2) | ||||
| 	test_range(4) | ||||
| } | ||||
|  |  | |||
|  | @ -2226,20 +2226,30 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { | |||
| 						// g.write('._interface_idx == _${sym.name}_${branch_sym} ')
 | ||||
| 						g.write('._interface_idx == ') | ||||
| 					} | ||||
| 					g.expr(expr) | ||||
| 				} else if type_sym.kind == .string { | ||||
| 					g.write('string_eq(') | ||||
| 					//
 | ||||
| 					g.expr(node.cond) | ||||
| 					g.write(', ') | ||||
| 					// g.write('string_eq($tmp, ')
 | ||||
| 					g.expr(expr) | ||||
| 					g.write(')') | ||||
| 				} else if expr is ast.RangeExpr { | ||||
| 					g.write('(') | ||||
| 					g.expr(node.cond) | ||||
| 					g.write(' >= ') | ||||
| 					g.expr(expr.low) | ||||
| 					g.write(' && ') | ||||
| 					g.expr(node.cond) | ||||
| 					g.write(' < ') | ||||
| 					g.expr(expr.high) | ||||
| 					g.write(')') | ||||
| 				} else { | ||||
| 					g.expr(node.cond) | ||||
| 					g.write(' == ') | ||||
| 					// g.write('$tmp == ')
 | ||||
| 				} | ||||
| 				g.expr(expr) | ||||
| 				if type_sym.kind == .string { | ||||
| 					g.write(')') | ||||
| 					g.expr(expr) | ||||
| 				} | ||||
| 				if i < branch.exprs.len - 1 { | ||||
| 					g.write(' || ') | ||||
|  |  | |||
|  | @ -187,7 +187,18 @@ fn (mut p Parser) match_expr() ast.MatchExpr { | |||
| 				p.inside_match_case = true | ||||
| 				expr := p.expr(0) | ||||
| 				p.inside_match_case = false | ||||
| 				exprs << expr | ||||
| 				if p.tok.kind == .dotdot { | ||||
| 					p.next() | ||||
| 					expr2 := p.expr(0) | ||||
| 					exprs << ast.RangeExpr{ | ||||
| 						low: expr | ||||
| 						high: expr2 | ||||
| 						has_low: true | ||||
| 						has_high: true | ||||
| 					} | ||||
| 				} else { | ||||
| 					exprs << expr | ||||
| 				} | ||||
| 				if p.tok.kind != .comma { | ||||
| 					break | ||||
| 				} | ||||
|  |  | |||
|  | @ -62,6 +62,24 @@ fn test_match_integers() { | |||
| 	assert a == -2 | ||||
| } | ||||
| 
 | ||||
| fn test_match_multiple() { | ||||
| 	assert match 5 { | ||||
| 		1, 2, 3 { '1-3' } | ||||
| 		4, 5 { '4-5' } | ||||
| 		6..9, 9 { '6-9' } | ||||
| 		else { 'other' } | ||||
| 	} == '4-5' | ||||
| } | ||||
| 
 | ||||
| fn test_match_range() { | ||||
| 	assert match `f` { | ||||
| 		`0`..`9` { 'digit' } | ||||
| 		`A`..`Z` { 'uppercase' } | ||||
| 		`a`..`z` { 'lowercase' } | ||||
| 		else { 'other' } | ||||
| 	} == 'lowercase' | ||||
| } | ||||
| 
 | ||||
| fn test_match_enums() { | ||||
| 	mut b := Color.red | ||||
| 	match b { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue