diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 4ed72cc0263..3449d402fbc 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -47,6 +47,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), "expr_as_if" => expr_as_if(arguments, return_type, location), @@ -856,11 +857,11 @@ fn expr_as_unary_op( let unary_op_type = tuple_types.pop().unwrap(); // These values should match the values used in noir_stdlib/src/meta/op.nr - let unary_op_value = match prefix_expr.operator { - UnaryOp::Minus => 0_u128, - UnaryOp::Not => 1_u128, - UnaryOp::MutableReference => 2_u128, - UnaryOp::Dereference { .. } => 3_u128, + let unary_op_value: u128 = match prefix_expr.operator { + UnaryOp::Minus => 0, + UnaryOp::Not => 1, + UnaryOp::MutableReference => 2, + UnaryOp::Dereference { .. } => 3, }; let mut fields = HashMap::default(); @@ -875,6 +876,39 @@ fn expr_as_unary_op( }) } +// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> +fn expr_as_binary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Infix(infix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + + tuple_types.pop().unwrap(); + let binary_op_type = tuple_types.pop().unwrap(); + + // For the op value we use the enum member index, which should match noir_stdlib/src/meta/op.nr + let binary_op_value = infix_expr.operator.contents as u128; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); + + let unary_op = Value::Struct(fields, binary_op_type); + let lhs = Value::Expr(infix_expr.lhs.kind); + let rhs = Value::Expr(infix_expr.rhs.kind); + Some(Value::Tuple(vec![lhs, unary_op, rhs])) + } else { + None + } + }) +} + // fn as_tuple(self) -> Option<[Expr]> fn expr_as_tuple( arguments: Vec<(Value, Location)>, diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index 3230ce42457..2c25e20fe70 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -1,7 +1,11 @@ use crate::option::Option; use crate::meta::op::UnaryOp; +use crate::meta::op::BinaryOp; impl Expr { + #[builtin(expr_as_binary_op)] + fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + #[builtin(expr_as_bool)] fn as_bool(self) -> Option {} diff --git a/noir_stdlib/src/meta/op.nr b/noir_stdlib/src/meta/op.nr index 1fbd4884841..ebd89677c50 100644 --- a/noir_stdlib/src/meta/op.nr +++ b/noir_stdlib/src/meta/op.nr @@ -19,3 +19,74 @@ impl UnaryOp { self.op == 3 } } + +struct BinaryOp { + op: Field +} + +impl BinaryOp { + fn is_add(self) -> bool { + self.op == 0 + } + + fn is_subtract(self) -> bool { + self.op == 1 + } + + fn is_multiply(self) -> bool { + self.op == 2 + } + + fn is_divide(self) -> bool { + self.op == 3 + } + + fn is_equal(self) -> bool { + self.op == 4 + } + + fn is_not_equal(self) -> bool { + self.op == 5 + } + + fn is_less(self) -> bool { + self.op == 6 + } + + fn is_less_equal(self) -> bool { + self.op == 7 + } + + fn is_greater(self) -> bool { + self.op == 8 + } + + fn is_greater_or_equal(self) -> bool { + self.op == 9 + } + + fn is_and(self) -> bool { + self.op == 10 + } + + fn is_or(self) -> bool { + self.op == 11 + } + + fn is_xor(self) -> bool { + self.op == 12 + } + + fn is_shift_right(self) -> bool { + self.op == 13 + } + + fn is_shift_left(self) -> bool { + self.op == 14 + } + + fn is_modulo(self) -> bool { + self.op == 15 + } +} + diff --git a/test_programs/compile_success_empty/comptime_exp/src/main.nr b/test_programs/compile_success_empty/comptime_exp/src/main.nr index eb0d27e2a38..4149beacece 100644 --- a/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ b/test_programs/compile_success_empty/comptime_exp/src/main.nr @@ -1,3 +1,6 @@ +use std::meta::op::UnaryOp; +use std::meta::op::BinaryOp; + fn main() { comptime { @@ -36,20 +39,37 @@ fn main() { assert_eq(expr.as_bool().unwrap(), true); // Check Expr::as_unary_op - let expr = quote { -x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_minus()); + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); - let expr = quote { !x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_not()); + // Check Expr::as_binary_op + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x > y }).is_greater()); + assert(get_binary_op(quote { x >= y }).is_greater_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + } +} - let expr = quote { &mut x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_mutable_reference()); +comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op +} - let expr = quote { *x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_dereference()); - } +comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op }