From af046e1c6e8a34100121cc21c20d8da3fdd6823a Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Fri, 25 Jul 2025 02:10:10 +0800 Subject: [PATCH] eval: add more infix op support; fix early func return (#24965) --- vlib/v/eval/eval.v | 7 ++ vlib/v/eval/expr.c.v | 3 + vlib/v/eval/infix.v | 126 +++++++++++++++++++++++++++ vlib/v/eval/stmt.v | 10 ++- vlib/v/eval/tests/comptime_if_test.v | 21 +++++ vlib/v/eval/tests/if_test.v | 43 +++++++++ 6 files changed, 208 insertions(+), 2 deletions(-) diff --git a/vlib/v/eval/eval.v b/vlib/v/eval/eval.v index 156090a80e..4f47dea584 100644 --- a/vlib/v/eval/eval.v +++ b/vlib/v/eval/eval.v @@ -64,6 +64,7 @@ pub mut: scope_idx int // this is increased when e.open_scope() is called, decreased when e.close_scope() (and all variables with that scope level deleted) returning bool return_values []Object + executed_return_stmt bool // already executed a return stmt in func cur_mod string cur_file string @@ -152,6 +153,7 @@ pub fn (mut e Eval) run_func(func ast.FnDecl, _args ...Object) { scope_idx: e.scope_idx } } + e.executed_return_stmt = false e.stmts(func.stmts) e.returning = false e.close_scope() @@ -317,6 +319,11 @@ pub fn (mut e Eval) comptime_cond(cond ast.Expr) bool { } } } + ast.InfixExpr { + left := e.comptime_cond(cond.left) + right := e.comptime_cond(cond.right) + return e.infix_expr(left, right, cond.op, ast.bool_type) as bool + } else { e.error('unsupported expression') } diff --git a/vlib/v/eval/expr.c.v b/vlib/v/eval/expr.c.v index db2db55342..c8cbc127ed 100644 --- a/vlib/v/eval/expr.c.v +++ b/vlib/v/eval/expr.c.v @@ -498,6 +498,9 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object { } ast.PrefixExpr { match expr.op { + .not { + return !(e.expr(expr.right, ast.bool_type) as bool) + } .amp { x := e.expr(expr.right, expr.right_type) return Ptr{ diff --git a/vlib/v/eval/infix.v b/vlib/v/eval/infix.v index aa0b008621..53b38e4278 100644 --- a/vlib/v/eval/infix.v +++ b/vlib/v/eval/infix.v @@ -6,6 +6,18 @@ import v.ast fn (e &Eval) infix_expr(left Object, right Object, op token.Kind, expecting ast.Type) Object { match op { + .and { + if left is bool && right is bool { + return left && right + } + e.error('invalid operands to &&: ${left.type_name()} and ${right.type_name()}') + } + .logical_or { + if left is bool && right is bool { + return left || right + } + e.error('invalid operands to ||: ${left.type_name()} and ${right.type_name()}') + } .gt { match left { Int { @@ -120,6 +132,120 @@ fn (e &Eval) infix_expr(left Object, right Object, op token.Kind, expecting ast. } } } + .ge { + match left { + Int { + match right { + Int { return left.val >= right.val } + Uint { return left.val >= right.val } + Float { return left.val >= right.val } + i64 { return left.val >= right } + f64 { return left.val >= right } + else { e.error('invalid operands to >=: Int and ${right.type_name()}') } + } + } + Uint { + match right { + Int { return left.val >= right.val } + Uint { return left.val >= right.val } + Float { return left.val >= right.val } + i64 { return left.val >= right } + f64 { return left.val >= right } + else { e.error('invalid operands to >=: Uint and ${right.type_name()}') } + } + } + Float { + match right { + Int { return left.val >= right.val } + Uint { return left.val >= right.val } + Float { return left.val >= right.val } + i64 { return left.val >= right } + f64 { return left.val >= right } + else { e.error('invalid operands to >=: Float and ${right.type_name()}') } + } + } + i64 { + match right { + Int { return left >= right.val } + Uint { return left >= right.val } + Float { return left >= right.val } + i64 { return left >= right } + f64 { return left >= right } + else { e.error('invalid operands to >=: int literal and ${right.type_name()}') } + } + } + f64 { + match right { + Int { return left >= right.val } + Uint { return left >= right.val } + Float { return left >= right.val } + i64 { return left >= right } + f64 { return left >= right } + else { e.error('invalid operands to >=: float literal and ${right.type_name()}') } + } + } + else { + e.error('invalid operands to >=: ${left.type_name()} and ${right.type_name()}') + } + } + } + .le { + match left { + Int { + match right { + Int { return left.val <= right.val } + Uint { return left.val <= right.val } + Float { return left.val <= right.val } + i64 { return left.val <= right } + f64 { return left.val <= right } + else { e.error('invalid operands to <=: Int and ${right.type_name()}') } + } + } + Uint { + match right { + Int { return left.val <= right.val } + Uint { return left.val <= right.val } + Float { return left.val <= right.val } + i64 { return left.val <= right } + f64 { return left.val <= right } + else { e.error('invalid operands to <=: Uint and ${right.type_name()}') } + } + } + Float { + match right { + Int { return left.val <= right.val } + Uint { return left.val <= right.val } + Float { return left.val <= right.val } + i64 { return left.val <= right } + f64 { return left.val <= right } + else { e.error('invalid operands to <=: Float and ${right.type_name()}') } + } + } + i64 { + match right { + Int { return left <= right.val } + Uint { return left <= right.val } + Float { return left <= right.val } + i64 { return left <= right } + f64 { return left <= right } + else { e.error('invalid operands to <=: int literal and ${right.type_name()}') } + } + } + f64 { + match right { + Int { return left <= right.val } + Uint { return left <= right.val } + Float { return left <= right.val } + i64 { return left <= right } + f64 { return left <= right } + else { e.error('invalid operands to <=: float literal and ${right.type_name()}') } + } + } + else { + e.error('invalid operands to <=: ${left.type_name()} and ${right.type_name()}') + } + } + } .eq { match left { Int { diff --git a/vlib/v/eval/stmt.v b/vlib/v/eval/stmt.v index e7513adcf4..8746a69b1a 100644 --- a/vlib/v/eval/stmt.v +++ b/vlib/v/eval/stmt.v @@ -4,10 +4,16 @@ import v.ast import v.token pub fn (mut e Eval) stmts(stmts []ast.Stmt) { + if e.executed_return_stmt { + return + } e.open_scope() for stmt in stmts { - e.stmt(stmt) - if e.returning { + if !e.executed_return_stmt { + e.stmt(stmt) + } + if stmt is ast.Return { + e.executed_return_stmt = true break } } diff --git a/vlib/v/eval/tests/comptime_if_test.v b/vlib/v/eval/tests/comptime_if_test.v index f74ab0bd98..85728c2b20 100644 --- a/vlib/v/eval/tests/comptime_if_test.v +++ b/vlib/v/eval/tests/comptime_if_test.v @@ -47,3 +47,24 @@ fn test_comptime_if_without_func() { dump(ret) assert ret == [] } + +fn test_comptime_if_infix() { + mut e := eval.create() + + ret := e.run('const a = + \$if amd64 || aarch64 || arm64 || rv64 { "64bit" } + \$else \$if i386 || arm32 || rv32 { "32bit" } + \$else \$if s390x { "s390x" } + \$else \$if ppc64le { "ppc64le" } + \$else \$if loongarch64 { "loongarch64" } + \$else { "unknown" } + + const b = 1.5 + + fn display() (string,f64) { println(a) println(b) return a,b } display()')! + + dump(ret) + assert ret[0].string().len != 0 + assert ret[0].string() != 'unknown' + assert ret[1].float_val() == 1.5 +} diff --git a/vlib/v/eval/tests/if_test.v b/vlib/v/eval/tests/if_test.v index 5b56b54e5e..9f38727388 100644 --- a/vlib/v/eval/tests/if_test.v +++ b/vlib/v/eval/tests/if_test.v @@ -29,3 +29,46 @@ fn test_if_return() { dump(ret1) assert ret1[0].int_val() == 666 } + +fn test_if_infix_return_early() { + mut e := eval.create() + + ret := e.run(' + fn display(a int, b int) int { + mut k := false + if !k { + k = !k + } + println(k) + if a == 100 && b == 100 { + return 100 + } else if a == 100 && b != 100 { + return 101 + } else if a == 100 && b > 100 { + return 102 + } else if a == 100 && b < 100 { + return 103 + } else if a == 100 && b >= 100 { + return 104 + } else if a == 100 && b <= 100 { + return 105 + } else if a == 100 || b == 100 { + return 106 + } else if a == 100 || b != 100 { + return 107 + } else if a == 100 || b > 100 { + return 108 + } else if a == 100 || b < 100 { + return 109 + } else if a == 100 || b >= 100 { + return 110 + } else if a == 100 || b <= 100 { + return 111 + } + return 7171 + } + display(200, 101)')! + + dump(ret) + assert ret[0].int_val() == 107 +}