Skip to content

Commit

Permalink
v: support in expr with number ranges: if var in 1..4 { (fix #20352
Browse files Browse the repository at this point in the history
…) (#22754)
  • Loading branch information
felipensp authored Nov 5, 2024
1 parent 2dd6587 commit 07eed88
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 5 deletions.
11 changes: 9 additions & 2 deletions vlib/v/checker/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,15 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
}
}
else {
c.error('`${node.op.str()}` can only be used with arrays and maps',
node.pos)
if mut node.right is ast.RangeExpr {
if !left_final_sym.is_number() && left_final_sym.kind != .rune {
c.error('`${left_final_sym.name}` is an invalid type for range expression',
node.pos)
}
} else {
c.error('`${node.op.str()}` can only be used with arrays and maps',
node.pos)
}
}
}
if mut node.left is ast.CallExpr {
Expand Down
3 changes: 3 additions & 0 deletions vlib/v/checker/tests/in_range_expr_err.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vlib/v/checker/tests/in_range_expr_err.vv:1:13: error: `bool` is an invalid type for range expression
1 | assert true in 0..1
| ~~
1 change: 1 addition & 0 deletions vlib/v/checker/tests/in_range_expr_err.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
assert true in 0..1
5 changes: 4 additions & 1 deletion vlib/v/gen/c/assert.v
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
g.write(ctoslit(expr_str))
}
}
ast.IfExpr, ast.MatchExpr {
ast.ParExpr {
g.gen_assert_single_expr(expr.expr, typ)
}
ast.IfExpr, ast.MatchExpr, ast.RangeExpr {
g.write(ctoslit(expr_str))
}
ast.IndexExpr {
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -3735,7 +3735,7 @@ fn (mut g Gen) expr(node_ ast.Expr) {
g.is_amp = false
}
ast.RangeExpr {
// Only used in IndexExpr
// Only used in IndexExpr and InfixExpr
}
ast.SelectExpr {
g.select_expr(node)
Expand Down
30 changes: 29 additions & 1 deletion vlib/v/gen/c/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -645,12 +645,40 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
g.write('(')
g.gen_array_contains(node.right_type, node.right, node.left_type, node.left)
g.write(')')
} else if right.unaliased_sym.kind == .string {
} else if right.unaliased_sym.kind == .string && node.right !is ast.RangeExpr {
g.write2('(', 'string_contains(')
g.expr(node.right)
g.write(', ')
g.expr(node.left)
g.write('))')
} else if node.right is ast.RangeExpr {
// call() in min..max
if node.left is ast.CallExpr {
line := g.go_before_last_stmt().trim_space()
g.empty_line = true
tmp_var := g.new_tmp_var()
g.write('${g.styp(node.left.return_type)} ${tmp_var} = ')
g.expr(node.left)
g.writeln(';')
g.write(line)
g.write('(')
g.write('${tmp_var} >= ')
g.expr(node.right.low)
g.write(' && ')
g.write('${tmp_var} < ')
g.expr(node.right.high)
g.write(')')
} else {
g.write('(')
g.expr(node.left)
g.write(' >= ')
g.expr(node.right.low)
g.write(' && ')
g.expr(node.left)
g.write(' < ')
g.expr(node.right.high)
g.write(')')
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions vlib/v/parser/expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,18 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
}
}
if is_key_in {
if p.tok.kind == .dotdot {
p.check(.dotdot)
pos_high := p.tok.pos()
right = ast.RangeExpr{
low: right
has_low: true
high: p.expr(0)
has_high: true
pos: pos_high
is_gated: false
}
}
p.inside_in_array = false
}
p.expecting_type = prev_expecting_type
Expand Down
48 changes: 48 additions & 0 deletions vlib/v/tests/range_expr_with_int_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module main

const min = 1
const max = 3

fn t() f64 {
return 1.23
}

fn z() ?f64 {
return 1.22
}

fn r() int {
return 5
}

fn test_main() {
y := 4

assert y in 1..20
assert y !in 5..20
assert y in 4..20
assert y !in min..max

assert (t() !in 1..3) == false
assert r() in 5..6
assert r() in 4..6
assert 1.22 in z()?..t()
assert 1.23 !in z()?..t()
assert 1.221 in z()?..t()

a := {
'f': 2
}
assert a['f'] in 1..3
assert a['f'] in 2..3
assert a['f'] !in 0..2
assert a['f'] in min..max
}

fn test_rune() {
assert `a` in `a`..`h`
assert `h` !in `a`..`h`

assert `f` in `a`..`h`
assert `f` !in `g`..`h`
}

0 comments on commit 07eed88

Please sign in to comment.