Skip to content

Commit

Permalink
feat: add Expr::as_array, Expr::as_repeated_element_array and sam…
Browse files Browse the repository at this point in the history
…e for slice (#5750)

# Description

## Problem

Part of #5668

## Summary

## Additional Context

I'm still not sure about the "repeated" name, and also about the
similarity of the methods between array and slice (maybe we can
distinguish array and slice with a boolean? not sure) but changing them
should be easy, so let me know.


## Documentation

Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [x] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Aug 19, 2024
1 parent 6440e80 commit f44e0b3
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 146 deletions.
184 changes: 132 additions & 52 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use rustc_hash::FxHashMap as HashMap;

use crate::{
ast::{
ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, UnaryOp,
UnresolvedType, UnresolvedTypeData, Visibility,
ArrayLiteral, ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal,
UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility,
},
hir::comptime::{errors::IResult, value::add_token_spans, InterpreterError, Value},
hir_def::function::FunctionBody,
Expand Down Expand Up @@ -47,15 +47,23 @@ 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_array" => expr_as_array(arguments, return_type, 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),
"expr_as_index" => expr_as_index(arguments, return_type, location),
"expr_as_integer" => expr_as_integer(arguments, return_type, location),
"expr_as_member_access" => expr_as_member_access(arguments, return_type, location),
"expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location),
"expr_as_repeated_element_array" => {
expr_as_repeated_element_array(arguments, return_type, location)
}
"expr_as_repeated_element_slice" => {
expr_as_repeated_element_slice(arguments, return_type, location)
}
"expr_as_slice" => expr_as_slice(arguments, return_type, location),
"expr_as_tuple" => expr_as_tuple(arguments, return_type, location),
"expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location),
"is_unconstrained" => Ok(Value::Bool(true)),
"function_def_name" => function_def_name(interner, arguments, location),
"function_def_parameters" => function_def_parameters(interner, arguments, location),
Expand Down Expand Up @@ -758,6 +766,56 @@ fn zeroed(return_type: Type) -> IResult<Value> {
}
}

// fn as_array(self) -> Option<[Expr]>
fn expr_as_array(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr {
let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect();
let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr)));
Some(Value::Slice(exprs, typ))
} else {
None
}
})
}

// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)>
fn expr_as_binary_op(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
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_bool(self) -> Option<bool>
fn expr_as_bool(
arguments: Vec<(Value, Location)>,
Expand Down Expand Up @@ -872,70 +930,55 @@ fn expr_as_member_access(
})
}

// fn as_unary_op(self) -> Option<(UnaryOp, Expr)>
fn expr_as_unary_op(
// fn as_repeated_element_array(self) -> Option<(Expr, Expr)>
fn expr_as_repeated_element_array(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type.clone(), location, |expr| {
if let ExpressionKind::Prefix(prefix_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(), 2);

tuple_types.pop().unwrap();
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: u128 = match prefix_expr.operator {
UnaryOp::Minus => 0,
UnaryOp::Not => 1,
UnaryOp::MutableReference => 2,
UnaryOp::Dereference { .. } => 3,
};

let mut fields = HashMap::default();
fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into()));

let unary_op = Value::Struct(fields, unary_op_type);
let rhs = Value::Expr(prefix_expr.rhs.kind);
Some(Value::Tuple(vec![unary_op, rhs]))
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated {
repeated_element,
length,
})) = expr
{
Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)]))
} else {
None
}
})
}

// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)>
fn expr_as_binary_op(
// fn as_repeated_element_slice(self) -> Option<(Expr, Expr)>
fn expr_as_repeated_element_slice(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
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()));
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Repeated {
repeated_element,
length,
})) = expr
{
Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)]))
} else {
None
}
})
}

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]))
// fn as_slice(self) -> Option<[Expr]>
fn expr_as_slice(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr {
let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect();
let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr)));
Some(Value::Slice(exprs, typ))
} else {
None
}
Expand All @@ -959,6 +1002,43 @@ fn expr_as_tuple(
})
}

// fn as_unary_op(self) -> Option<(UnaryOp, Expr)>
fn expr_as_unary_op(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type.clone(), location, |expr| {
if let ExpressionKind::Prefix(prefix_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(), 2);

tuple_types.pop().unwrap();
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: u128 = match prefix_expr.operator {
UnaryOp::Minus => 0,
UnaryOp::Not => 1,
UnaryOp::MutableReference => 2,
UnaryOp::Dereference { .. } => 3,
};

let mut fields = HashMap::default();
fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into()));

let unary_op = Value::Struct(fields, unary_op_type);
let rhs = Value::Expr(prefix_expr.rhs.kind);
Some(Value::Tuple(vec![unary_op, rhs]))
} else {
None
}
})
}

// Helper function for implementing the `expr_as_...` functions.
fn expr_as<F>(
arguments: Vec<(Value, Location)>,
Expand Down
Loading

0 comments on commit f44e0b3

Please sign in to comment.