From 07eed88df7b07bf52ce62a319292e30273dcf824 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Tue, 5 Nov 2024 07:47:17 -0300 Subject: [PATCH] v: support `in` expr with number ranges: `if var in 1..4 {` (fix #20352) (#22754) --- vlib/v/checker/infix.v | 11 ++++- vlib/v/checker/tests/in_range_expr_err.out | 3 ++ vlib/v/checker/tests/in_range_expr_err.vv | 1 + vlib/v/gen/c/assert.v | 5 ++- vlib/v/gen/c/cgen.v | 2 +- vlib/v/gen/c/infix.v | 30 +++++++++++++- vlib/v/parser/expr.v | 12 ++++++ vlib/v/tests/range_expr_with_int_test.v | 48 ++++++++++++++++++++++ 8 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 vlib/v/checker/tests/in_range_expr_err.out create mode 100644 vlib/v/checker/tests/in_range_expr_err.vv create mode 100644 vlib/v/tests/range_expr_with_int_test.v diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index 6a7a8f5c4..fbeb0f29e 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -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 { diff --git a/vlib/v/checker/tests/in_range_expr_err.out b/vlib/v/checker/tests/in_range_expr_err.out new file mode 100644 index 000000000..f7603c18f --- /dev/null +++ b/vlib/v/checker/tests/in_range_expr_err.out @@ -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 + | ~~ diff --git a/vlib/v/checker/tests/in_range_expr_err.vv b/vlib/v/checker/tests/in_range_expr_err.vv new file mode 100644 index 000000000..d1fd9e250 --- /dev/null +++ b/vlib/v/checker/tests/in_range_expr_err.vv @@ -0,0 +1 @@ +assert true in 0..1 diff --git a/vlib/v/gen/c/assert.v b/vlib/v/gen/c/assert.v index f31160f46..3d19cd171 100644 --- a/vlib/v/gen/c/assert.v +++ b/vlib/v/gen/c/assert.v @@ -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 { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 57cbc15d9..2ca559639 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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) diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index 5160d7584..e88ff78be 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -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(')') + } } } diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 38d7f63dd..49388ffe5 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -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 diff --git a/vlib/v/tests/range_expr_with_int_test.v b/vlib/v/tests/range_expr_with_int_test.v new file mode 100644 index 000000000..5493a970e --- /dev/null +++ b/vlib/v/tests/range_expr_with_int_test.v @@ -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` +} -- 2.39.5