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 branch in node.branches { | ||||||
| 		for expr in branch.exprs { | 		for expr in branch.exprs { | ||||||
| 			mut key := '' | 			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 { | 			match expr { | ||||||
| 				ast.Type { key = c.table.type_to_str(expr.typ) } | 				ast.Type { key = c.table.type_to_str(expr.typ) } | ||||||
| 				ast.EnumVal { key = expr.val } | 				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') } |    44 |         else { println('else') } | ||||||
|    45 |     } |    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() { | fn main() { | ||||||
| 	test_sum_type(St1{}) | 	test_sum_type(St1{}) | ||||||
| 	test_enum(.red) | 	test_enum(.red) | ||||||
| 	test_int(2) | 	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 == _${sym.name}_${branch_sym} ')
 | ||||||
| 						g.write('._interface_idx == ') | 						g.write('._interface_idx == ') | ||||||
| 					} | 					} | ||||||
|  | 					g.expr(expr) | ||||||
| 				} else if type_sym.kind == .string { | 				} else if type_sym.kind == .string { | ||||||
| 					g.write('string_eq(') | 					g.write('string_eq(') | ||||||
| 					//
 | 					//
 | ||||||
| 					g.expr(node.cond) | 					g.expr(node.cond) | ||||||
| 					g.write(', ') | 					g.write(', ') | ||||||
| 					// g.write('string_eq($tmp, ')
 | 					// 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 { | 				} else { | ||||||
| 					g.expr(node.cond) | 					g.expr(node.cond) | ||||||
| 					g.write(' == ') | 					g.write(' == ') | ||||||
| 					// g.write('$tmp == ')
 | 					// g.write('$tmp == ')
 | ||||||
| 				} | 					g.expr(expr) | ||||||
| 				g.expr(expr) |  | ||||||
| 				if type_sym.kind == .string { |  | ||||||
| 					g.write(')') |  | ||||||
| 				} | 				} | ||||||
| 				if i < branch.exprs.len - 1 { | 				if i < branch.exprs.len - 1 { | ||||||
| 					g.write(' || ') | 					g.write(' || ') | ||||||
|  |  | ||||||
|  | @ -187,7 +187,18 @@ fn (mut p Parser) match_expr() ast.MatchExpr { | ||||||
| 				p.inside_match_case = true | 				p.inside_match_case = true | ||||||
| 				expr := p.expr(0) | 				expr := p.expr(0) | ||||||
| 				p.inside_match_case = false | 				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 { | 				if p.tok.kind != .comma { | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -62,6 +62,24 @@ fn test_match_integers() { | ||||||
| 	assert a == -2 | 	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() { | fn test_match_enums() { | ||||||
| 	mut b := Color.red | 	mut b := Color.red | ||||||
| 	match b { | 	match b { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue