From b900577dae38017ad72dc0d1f404189f5e79566a Mon Sep 17 00:00:00 2001 From: spaceface777 Date: Thu, 16 Jul 2020 15:41:18 +0200 Subject: [PATCH] compiler: handle ranges as `match` conditions (#5847) --- vlib/v/checker/checker.v | 33 +++++++++++++++++++ .../checker/tests/match_duplicate_branch.out | 7 ++++ .../v/checker/tests/match_duplicate_branch.vv | 9 +++++ vlib/v/gen/cgen.v | 18 +++++++--- vlib/v/parser/if.v | 13 +++++++- vlib/v/tests/match_test.v | 18 ++++++++++ 6 files changed, 93 insertions(+), 5 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index b249cbf094..3fab31963c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 } diff --git a/vlib/v/checker/tests/match_duplicate_branch.out b/vlib/v/checker/tests/match_duplicate_branch.out index d029310e84..e7866f5296 100644 --- a/vlib/v/checker/tests/match_duplicate_branch.out +++ b/vlib/v/checker/tests/match_duplicate_branch.out @@ -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 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/match_duplicate_branch.vv b/vlib/v/checker/tests/match_duplicate_branch.vv index 7ea3265bad..81df309dfc 100644 --- a/vlib/v/checker/tests/match_duplicate_branch.vv +++ b/vlib/v/checker/tests/match_duplicate_branch.vv @@ -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) } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 0157397d74..626a6bc46c 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -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(' || ') diff --git a/vlib/v/parser/if.v b/vlib/v/parser/if.v index fa528f481d..da277e97ae 100644 --- a/vlib/v/parser/if.v +++ b/vlib/v/parser/if.v @@ -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 } diff --git a/vlib/v/tests/match_test.v b/vlib/v/tests/match_test.v index e3a2ed39d4..7cbf220d14 100644 --- a/vlib/v/tests/match_test.v +++ b/vlib/v/tests/match_test.v @@ -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 {