Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 125 additions & 8 deletions compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::fmt::Display;
use std::sync::atomic::{AtomicU32, Ordering};

use crate::lexer::token::SpannedToken;
use crate::parser::{ParserError, ParserErrorReason};
use crate::token::Token;
use crate::{Expression, ExpressionKind, IndexExpression, MemberAccessExpression, UnresolvedType};
use crate::{
BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression,
MethodCallExpression, UnresolvedType,
};
use acvm::FieldElement;
use iter_extended::vecmap;
use noirc_errors::{Span, Spanned};

Expand Down Expand Up @@ -478,12 +483,123 @@ impl LValue {
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ForRange {
Range(/*start:*/ Expression, /*end:*/ Expression),
Array(Expression),
}

impl ForRange {
/// Create a 'for' expression taking care of desugaring a 'for e in array' loop
/// into the following if needed:
///
/// {
/// let fresh1 = array;
/// for fresh2 in 0 .. std::array::len(fresh1) {
/// let elem = fresh1[fresh2];
/// ...
/// }
/// }
pub(crate) fn into_for(
self,
identifier: Ident,
block: Expression,
for_loop_span: Span,
) -> StatementKind {
/// Counter used to generate unique names when desugaring
/// code in the parser requires the creation of fresh variables.
/// The parser is stateless so this is a static global instead.
static UNIQUE_NAME_COUNTER: AtomicU32 = AtomicU32::new(0);

match self {
ForRange::Range(..) => {
unreachable!()
}
ForRange::Array(array) => {
let array_span = array.span;
let start_range = ExpressionKind::integer(FieldElement::zero());
let start_range = Expression::new(start_range, array_span);

let next_unique_id = UNIQUE_NAME_COUNTER.fetch_add(1, Ordering::Relaxed);
let array_name = format!("$i{next_unique_id}");
let array_span = array.span;
let array_ident = Ident::new(array_name, array_span);

// let fresh1 = array;
let let_array = Statement {
kind: StatementKind::Let(LetStatement {
pattern: Pattern::Identifier(array_ident.clone()),
r#type: UnresolvedType::unspecified(),
expression: array,
}),
span: array_span,
};

// array.len()
let segments = vec![array_ident];
let array_ident =
ExpressionKind::Variable(Path { segments, kind: PathKind::Plain });

let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression {
object: Expression::new(array_ident.clone(), array_span),
method_name: Ident::new("len".to_string(), array_span),
arguments: vec![],
}));
let end_range = Expression::new(end_range, array_span);

let next_unique_id = UNIQUE_NAME_COUNTER.fetch_add(1, Ordering::Relaxed);
let index_name = format!("$i{next_unique_id}");
let fresh_identifier = Ident::new(index_name.clone(), array_span);

// array[i]
let segments = vec![Ident::new(index_name, array_span)];
let index_ident =
ExpressionKind::Variable(Path { segments, kind: PathKind::Plain });

let loop_element = ExpressionKind::Index(Box::new(IndexExpression {
collection: Expression::new(array_ident, array_span),
index: Expression::new(index_ident, array_span),
}));

// let elem = array[i];
let let_elem = Statement {
kind: StatementKind::Let(LetStatement {
pattern: Pattern::Identifier(identifier),
r#type: UnresolvedType::unspecified(),
expression: Expression::new(loop_element, array_span),
}),
span: array_span,
};

let block_span = block.span;
let new_block = BlockExpression(vec![
let_elem,
Statement { kind: StatementKind::Expression(block), span: block_span },
]);
let new_block = Expression::new(ExpressionKind::Block(new_block), block_span);
let for_loop = Statement {
kind: StatementKind::For(ForLoopStatement {
identifier: fresh_identifier,
range: ForRange::Range(start_range, end_range),
block: new_block,
span: for_loop_span,
}),
span: for_loop_span,
};

let block = ExpressionKind::Block(BlockExpression(vec![let_array, for_loop]));
StatementKind::Expression(Expression::new(block, for_loop_span))
}
}
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ForLoopStatement {
pub identifier: Ident,
pub start_range: Expression,
pub end_range: Expression,
pub range: ForRange,
pub block: Expression,
pub span: Span,
}

impl Display for StatementKind {
Expand Down Expand Up @@ -575,10 +691,11 @@ impl Display for Pattern {

impl Display for ForLoopStatement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"for {} in {} .. {} {}",
self.identifier, self.start_range, self.end_range, self.block
)
let range = match &self.range {
ForRange::Range(start, end) => format!("{start}..{end}"),
ForRange::Array(expr) => expr.to_string(),
};

write!(f, "for {} in {range} {}", self.identifier, self.block)
}
}
55 changes: 35 additions & 20 deletions compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ use crate::{
StatementKind,
};
use crate::{
ArrayLiteral, ContractFunctionType, Distinctness, FunctionVisibility, Generics, LValue,
NoirStruct, NoirTypeAlias, Path, PathKind, Pattern, Shared, StructType, Type, TypeAliasType,
TypeBinding, TypeVariable, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint,
UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT,
ArrayLiteral, ContractFunctionType, Distinctness, ForRange, FunctionVisibility, Generics,
LValue, NoirStruct, NoirTypeAlias, Path, PathKind, Pattern, Shared, StructType, Type,
TypeAliasType, TypeBinding, TypeVariable, UnaryOp, UnresolvedGenerics,
UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression,
Visibility, ERROR_IDENT,
};
use fm::FileId;
use iter_extended::vecmap;
Expand Down Expand Up @@ -1007,23 +1008,37 @@ impl<'a> Resolver<'a> {
HirStatement::Assign(stmt)
}
StatementKind::For(for_loop) => {
let start_range = self.resolve_expression(for_loop.start_range);
let end_range = self.resolve_expression(for_loop.end_range);
let (identifier, block) = (for_loop.identifier, for_loop.block);

// TODO: For loop variables are currently mutable by default since we haven't
// yet implemented syntax for them to be optionally mutable.
let (identifier, block) = self.in_new_scope(|this| {
let decl = this.add_variable_decl(
identifier,
false,
true,
DefinitionKind::Local(None),
);
(decl, this.resolve_expression(block))
});
match for_loop.range {
ForRange::Range(start_range, end_range) => {
let start_range = self.resolve_expression(start_range);
let end_range = self.resolve_expression(end_range);
let (identifier, block) = (for_loop.identifier, for_loop.block);

// TODO: For loop variables are currently mutable by default since we haven't
// yet implemented syntax for them to be optionally mutable.
let (identifier, block) = self.in_new_scope(|this| {
let decl = this.add_variable_decl(
identifier,
false,
true,
DefinitionKind::Local(None),
);
(decl, this.resolve_expression(block))
});

HirStatement::For(HirForStatement { start_range, end_range, block, identifier })
HirStatement::For(HirForStatement {
start_range,
end_range,
block,
identifier,
})
}
range @ ForRange::Array(_) => {
let for_stmt =
range.into_for(for_loop.identifier, for_loop.block, for_loop.span);
self.resolve_stmt(for_stmt)
}
}
}
StatementKind::Error => HirStatement::Error,
}
Expand Down
113 changes: 2 additions & 111 deletions compiler/noirc_frontend/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,20 @@ mod labels;
#[allow(clippy::module_inception)]
mod parser;

use std::sync::atomic::{AtomicU32, Ordering};

use crate::token::{Keyword, Token};
use crate::{ast::ImportStatement, Expression, NoirStruct};
use crate::{
BlockExpression, ExpressionKind, ForLoopStatement, Ident, IndexExpression, LetStatement,
MethodCallExpression, NoirFunction, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind,
Pattern, Recoverable, Statement, StatementKind, TypeImpl, UnresolvedType, UseTree,
Ident, LetStatement, NoirFunction, NoirTrait, NoirTraitImpl, NoirTypeAlias, Recoverable,
StatementKind, TypeImpl, UseTree,
};

use acvm::FieldElement;
use chumsky::prelude::*;
use chumsky::primitive::Container;
pub use errors::ParserError;
pub use errors::ParserErrorReason;
use noirc_errors::Span;
pub use parser::parse_program;

/// Counter used to generate unique names when desugaring
/// code in the parser requires the creation of fresh variables.
/// The parser is stateless so this is a static global instead.
static UNIQUE_NAME_COUNTER: AtomicU32 = AtomicU32::new(0);

#[derive(Debug, Clone)]
pub(crate) enum TopLevelStatement {
Function(NoirFunction),
Expand Down Expand Up @@ -478,106 +469,6 @@ impl Precedence {
}
}

enum ForRange {
Range(/*start:*/ Expression, /*end:*/ Expression),
Array(Expression),
}

impl ForRange {
/// Create a 'for' expression taking care of desugaring a 'for e in array' loop
/// into the following if needed:
///
/// {
/// let fresh1 = array;
/// for fresh2 in 0 .. std::array::len(fresh1) {
/// let elem = fresh1[fresh2];
/// ...
/// }
/// }
fn into_for(self, identifier: Ident, block: Expression, for_loop_span: Span) -> StatementKind {
match self {
ForRange::Range(start_range, end_range) => {
StatementKind::For(ForLoopStatement { identifier, start_range, end_range, block })
}
ForRange::Array(array) => {
let array_span = array.span;
let start_range = ExpressionKind::integer(FieldElement::zero());
let start_range = Expression::new(start_range, array_span);

let next_unique_id = UNIQUE_NAME_COUNTER.fetch_add(1, Ordering::Relaxed);
let array_name = format!("$i{next_unique_id}");
let array_span = array.span;
let array_ident = Ident::new(array_name, array_span);

// let fresh1 = array;
let let_array = Statement {
kind: StatementKind::Let(LetStatement {
pattern: Pattern::Identifier(array_ident.clone()),
r#type: UnresolvedType::unspecified(),
expression: array,
}),
span: array_span,
};

// array.len()
let segments = vec![array_ident];
let array_ident =
ExpressionKind::Variable(Path { segments, kind: PathKind::Plain });

let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression {
object: Expression::new(array_ident.clone(), array_span),
method_name: Ident::new("len".to_string(), array_span),
arguments: vec![],
}));
let end_range = Expression::new(end_range, array_span);

let next_unique_id = UNIQUE_NAME_COUNTER.fetch_add(1, Ordering::Relaxed);
let index_name = format!("$i{next_unique_id}");
let fresh_identifier = Ident::new(index_name.clone(), array_span);

// array[i]
let segments = vec![Ident::new(index_name, array_span)];
let index_ident =
ExpressionKind::Variable(Path { segments, kind: PathKind::Plain });

let loop_element = ExpressionKind::Index(Box::new(IndexExpression {
collection: Expression::new(array_ident, array_span),
index: Expression::new(index_ident, array_span),
}));

// let elem = array[i];
let let_elem = Statement {
kind: StatementKind::Let(LetStatement {
pattern: Pattern::Identifier(identifier),
r#type: UnresolvedType::unspecified(),
expression: Expression::new(loop_element, array_span),
}),
span: array_span,
};

let block_span = block.span;
let new_block = BlockExpression(vec![
let_elem,
Statement { kind: StatementKind::Expression(block), span: block_span },
]);
let new_block = Expression::new(ExpressionKind::Block(new_block), block_span);
let for_loop = Statement {
kind: StatementKind::For(ForLoopStatement {
identifier: fresh_identifier,
start_range,
end_range,
block: new_block,
}),
span: for_loop_span,
};

let block = ExpressionKind::Block(BlockExpression(vec![let_array, for_loop]));
StatementKind::Expression(Expression::new(block, for_loop_span))
}
}
}
}

impl std::fmt::Display for TopLevelStatement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down
Loading