Skip to content

Commit

Permalink
feat: Add Expr::as_block and Expr::has_semicolon (#5784)
Browse files Browse the repository at this point in the history
# Description

## Problem

Part of #5668

## Summary

Both ExpressionKind and StatementKind will be exposed as `Expr` (as
discussed with Jake). So `Expr::as_block` will return the block
statements as a list of expressions. There's also the thing that some of
these statements might be `Semi` (well, all except maybe the last one)
and having to explicitly unwrap that with `as_semi` might be tedious or
bug-prone, so instead that's unwrapped automatically when you call any
of the `as_...` methods, but then there's an `has_semicolon` on `Expr`
to know if it has a semicolon.

## Additional Context

There's just those two methods in this PR because I wanted to validate
this approach. Next I'll add more `as_...` methods but for
StatementKind.

## 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 22, 2024
1 parent 86d8840 commit 19ffa20
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 51 deletions.
146 changes: 102 additions & 44 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ use rustc_hash::FxHashMap as HashMap;
use crate::{
ast::{
ArrayLiteral, ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal,
UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility,
StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility,
},
hir::comptime::{
errors::IResult,
value::{add_token_spans, ExprValue},
InterpreterError, Value,
},
hir::comptime::{errors::IResult, value::add_token_spans, InterpreterError, Value},
hir_def::function::FunctionBody,
macros_api::{ModuleDefId, NodeInterner, Signedness},
node_interner::{DefinitionKind, TraitImplKind},
Expand Down Expand Up @@ -49,6 +53,7 @@ impl<'local, 'context> Interpreter<'local, 'context> {
"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_block" => expr_as_block(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),
Expand All @@ -64,6 +69,7 @@ impl<'local, 'context> Interpreter<'local, 'context> {
"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),
"expr_has_semicolon" => expr_has_semicolon(arguments, 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 @@ -346,7 +352,7 @@ fn quoted_as_expr(
let argument = check_one_argument(arguments, location)?;

let expr = parse(argument, parser::expression(), "an expression").ok();
let value = expr.map(|expr| Value::Expr(expr.kind));
let value = expr.map(|expr| Value::expression(expr.kind));

option(return_type, value)
}
Expand Down Expand Up @@ -773,8 +779,11 @@ fn expr_as_array(
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();
if let ExprValue::Expression(ExpressionKind::Literal(Literal::Array(
ArrayLiteral::Standard(exprs),
))) = expr
{
let exprs = exprs.into_iter().map(|expr| Value::expression(expr.kind)).collect();
let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr)));
Some(Value::Slice(exprs, typ))
} else {
Expand All @@ -790,7 +799,7 @@ fn expr_as_binary_op(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type.clone(), location, |expr| {
if let ExpressionKind::Infix(infix_expr) = expr {
if let ExprValue::Expression(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");
Expand All @@ -807,23 +816,42 @@ fn expr_as_binary_op(
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);
let lhs = Value::expression(infix_expr.lhs.kind);
let rhs = Value::expression(infix_expr.rhs.kind);
Some(Value::Tuple(vec![lhs, unary_op, rhs]))
} else {
None
}
})
}

// fn as_block(self) -> Option<[Expr]>
fn expr_as_block(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExprValue::Expression(ExpressionKind::Block(block_expr)) = expr {
let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr)));
let statements = block_expr.statements.into_iter();
let statements = statements.map(|statement| Value::statement(statement.kind)).collect();

Some(Value::Slice(statements, typ))
} else {
None
}
})
}

// fn as_bool(self) -> Option<bool>
fn expr_as_bool(
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Literal(Literal::Bool(bool)) = expr {
if let ExprValue::Expression(ExpressionKind::Literal(Literal::Bool(bool))) = expr {
Some(Value::Bool(bool))
} else {
None
Expand All @@ -838,10 +866,10 @@ fn expr_as_function_call(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Call(call_expression) = expr {
let function = Value::Expr(call_expression.func.kind);
if let ExprValue::Expression(ExpressionKind::Call(call_expression)) = expr {
let function = Value::expression(call_expression.func.kind);
let arguments = call_expression.arguments.into_iter();
let arguments = arguments.map(|argument| Value::Expr(argument.kind)).collect();
let arguments = arguments.map(|argument| Value::expression(argument.kind)).collect();
let arguments =
Value::Slice(arguments, Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))));
Some(Value::Tuple(vec![function, arguments]))
Expand All @@ -858,7 +886,7 @@ fn expr_as_if(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type.clone(), location, |expr| {
if let ExpressionKind::If(if_expr) = expr {
if let ExprValue::Expression(ExpressionKind::If(if_expr)) = expr {
// Get the type of `Option<Expr>`
let option_type = extract_option_generic_type(return_type.clone());
let Type::Tuple(option_types) = option_type else {
Expand All @@ -867,12 +895,14 @@ fn expr_as_if(
assert_eq!(option_types.len(), 3);
let alternative_option_type = option_types[2].clone();

let alternative =
option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind)));
let alternative = option(
alternative_option_type,
if_expr.alternative.map(|e| Value::expression(e.kind)),
);

Some(Value::Tuple(vec![
Value::Expr(if_expr.condition.kind),
Value::Expr(if_expr.consequence.kind),
Value::expression(if_expr.condition.kind),
Value::expression(if_expr.consequence.kind),
alternative.ok()?,
]))
} else {
Expand All @@ -888,10 +918,10 @@ fn expr_as_index(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Index(index_expr) = expr {
if let ExprValue::Expression(ExpressionKind::Index(index_expr)) = expr {
Some(Value::Tuple(vec![
Value::Expr(index_expr.collection.kind),
Value::Expr(index_expr.index.kind),
Value::expression(index_expr.collection.kind),
Value::expression(index_expr.index.kind),
]))
} else {
None
Expand All @@ -906,7 +936,8 @@ fn expr_as_integer(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type.clone(), location, |expr| {
if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr {
if let ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, sign))) = expr
{
Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)]))
} else {
None
Expand All @@ -921,9 +952,12 @@ fn expr_as_member_access(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::MemberAccess(member_access) = expr {
if let ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) = expr {
let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]);
Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)]))
Some(Value::Tuple(vec![
Value::expression(member_access.lhs.kind),
Value::Quoted(tokens),
]))
} else {
None
}
Expand All @@ -937,12 +971,14 @@ fn expr_as_repeated_element_array(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated {
repeated_element,
length,
})) = expr
if let ExprValue::Expression(ExpressionKind::Literal(Literal::Array(
ArrayLiteral::Repeated { repeated_element, length },
))) = expr
{
Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)]))
Some(Value::Tuple(vec![
Value::expression(repeated_element.kind),
Value::expression(length.kind),
]))
} else {
None
}
Expand All @@ -956,12 +992,14 @@ fn expr_as_repeated_element_slice(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Repeated {
repeated_element,
length,
})) = expr
if let ExprValue::Expression(ExpressionKind::Literal(Literal::Slice(
ArrayLiteral::Repeated { repeated_element, length },
))) = expr
{
Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)]))
Some(Value::Tuple(vec![
Value::expression(repeated_element.kind),
Value::expression(length.kind),
]))
} else {
None
}
Expand All @@ -975,8 +1013,11 @@ fn expr_as_slice(
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();
if let ExprValue::Expression(ExpressionKind::Literal(Literal::Slice(
ArrayLiteral::Standard(exprs),
))) = expr
{
let exprs = exprs.into_iter().map(|expr| Value::expression(expr.kind)).collect();
let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr)));
Some(Value::Slice(exprs, typ))
} else {
Expand All @@ -992,8 +1033,9 @@ fn expr_as_tuple(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type, location, |expr| {
if let ExpressionKind::Tuple(expressions) = expr {
let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect();
if let ExprValue::Expression(ExpressionKind::Tuple(expressions)) = expr {
let expressions =
expressions.into_iter().map(|expr| Value::expression(expr.kind)).collect();
let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr)));
Some(Value::Slice(expressions, typ))
} else {
Expand All @@ -1009,7 +1051,7 @@ fn expr_as_unary_op(
location: Location,
) -> IResult<Value> {
expr_as(arguments, return_type.clone(), location, |expr| {
if let ExpressionKind::Prefix(prefix_expr) = expr {
if let ExprValue::Expression(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");
Expand All @@ -1031,14 +1073,21 @@ fn expr_as_unary_op(
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);
let rhs = Value::expression(prefix_expr.rhs.kind);
Some(Value::Tuple(vec![unary_op, rhs]))
} else {
None
}
})
}

// fn as_has_semicolon(self) -> bool
fn expr_has_semicolon(arguments: Vec<(Value, Location)>, location: Location) -> IResult<Value> {
let self_argument = check_one_argument(arguments, location)?;
let expr_value = get_expr(self_argument)?;
Ok(Value::Bool(matches!(expr_value, ExprValue::Statement(StatementKind::Semi(..)))))
}

// Helper function for implementing the `expr_as_...` functions.
fn expr_as<F>(
arguments: Vec<(Value, Location)>,
Expand All @@ -1047,15 +1096,24 @@ fn expr_as<F>(
f: F,
) -> IResult<Value>
where
F: FnOnce(ExpressionKind) -> Option<Value>,
F: FnOnce(ExprValue) -> Option<Value>,
{
let self_argument = check_one_argument(arguments, location)?;
let mut expression_kind = get_expr(self_argument)?;
while let ExpressionKind::Parenthesized(expression) = expression_kind {
expression_kind = expression.kind;
let mut expr_value = get_expr(self_argument)?;
loop {
match expr_value {
ExprValue::Expression(ExpressionKind::Parenthesized(expression)) => {
expr_value = ExprValue::Expression(expression.kind);
}
ExprValue::Statement(StatementKind::Expression(expression))
| ExprValue::Statement(StatementKind::Semi(expression)) => {
expr_value = ExprValue::Expression(expression.kind);
}
_ => break,
}
}

let option_value = f(expression_kind);
let option_value = f(expr_value);
option(return_type, option_value)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ use acvm::FieldElement;
use noirc_errors::Location;

use crate::{
ast::{ExpressionKind, IntegerBitSize, Signedness},
ast::{IntegerBitSize, Signedness},
hir::{
comptime::{errors::IResult, value::add_token_spans, Interpreter, InterpreterError, Value},
comptime::{
errors::IResult,
value::{add_token_spans, ExprValue},
Interpreter, InterpreterError, Value,
},
def_map::ModuleId,
},
hir_def::{
Expand Down Expand Up @@ -137,7 +141,7 @@ pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult<u32> {
}
}

pub(crate) fn get_expr((value, location): (Value, Location)) -> IResult<ExpressionKind> {
pub(crate) fn get_expr((value, location): (Value, Location)) -> IResult<ExprValue> {
match value {
Value::Expr(expr) => Ok(expr),
value => type_mismatch(value, Type::Quoted(QuotedType::Expr), location),
Expand Down
Loading

0 comments on commit 19ffa20

Please sign in to comment.