compiler: handle ranges as `match` conditions (#5847)

pull/5849/head
spaceface777 2020-07-16 15:41:18 +02:00 committed by GitHub
parent 612fe1b8fb
commit b900577dae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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