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
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ enum ArrayLiteralOrError {
}

impl Parser<'_> {
#[inline(always)]
pub(crate) fn parse_expression_or_error(&mut self) -> Expression {
self.parse_expression_or_error_impl(true) // allow constructors
}
Expand All @@ -48,6 +49,7 @@ impl Parser<'_> {
self.parse_expression_or_error_impl(false) // allow constructors
}

#[inline(always)]
pub(crate) fn parse_expression_or_error_impl(
&mut self,
allow_constructors: bool,
Expand Down
247 changes: 134 additions & 113 deletions compiler/noirc_frontend/src/parser/parser/infix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,180 +8,201 @@ use crate::{

use super::Parser;

impl<'a> Parser<'a> {
macro_rules! parse_infix {
($self:expr, $next:expr, $operator:expr, $allow_constructors:expr) => {{
let start_location = $self.current_token_location;
let mut lhs = $next($self, $allow_constructors)?;

loop {
let operator_start_location = $self.current_token_location;
let operator = $operator;
let operator = Located::from(operator_start_location, operator);

let Some(rhs) = $next($self, $allow_constructors) else {
$self.push_expected_expression();
break;
};

lhs = $self.new_infix_expression(lhs, operator, rhs, start_location);
}

Some(lhs)
}};
}

impl Parser<'_> {
/// EqualOrNotEqualExpression
/// = OrExpression ( ( '==' | '!=' ) OrExpression )*
#[inline(always)]
pub(super) fn parse_equal_or_not_equal(
&mut self,
allow_constructors: bool,
) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_or, |parser| {
if parser.eat(Token::Equal) {
Some(BinaryOpKind::Equal)
} else if parser.eat(Token::NotEqual) {
Some(BinaryOpKind::NotEqual)
parse_infix!(
self,
Parser::parse_or,
if self.eat(Token::Equal) {
BinaryOpKind::Equal
} else if self.eat(Token::NotEqual) {
BinaryOpKind::NotEqual
} else {
None
}
})
break;
},
allow_constructors
)
}

/// OrExpression
/// = AndExpression ( '|' AndExpression )*
#[inline(always)]
pub(super) fn parse_or(&mut self, allow_constructors: bool) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_and, |parser| {
// Don't parse `x |= ...`, etc.
if parser.next_is(Token::Assign) {
None
} else if parser.eat(Token::Pipe) {
Some(BinaryOpKind::Or)
parse_infix!(
self,
Parser::parse_and,
if self.next_is(Token::Assign) {
break;
} else if self.eat(Token::Pipe) {
BinaryOpKind::Or
} else {
None
}
})
break;
},
allow_constructors
)
}

/// AndExpression
/// = XorExpression ( '&' XorExpression )*
#[inline(always)]
pub(super) fn parse_and(&mut self, allow_constructors: bool) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_xor, |parser| {
parse_infix!(
self,
Parser::parse_xor,
// Don't parse `x |= ...`, etc.
if parser.next_is(Token::Assign) {
None
} else if parser.eat(Token::Ampersand) {
Some(BinaryOpKind::And)
} else if parser.eat(Token::LogicalAnd) {
parser.push_error(ParserErrorReason::LogicalAnd, parser.previous_token_location);
Some(BinaryOpKind::And)
if self.next_is(Token::Assign) {
break;
} else if self.eat(Token::Ampersand) {
BinaryOpKind::And
} else if self.eat(Token::LogicalAnd) {
self.push_error(ParserErrorReason::LogicalAnd, self.previous_token_location);
BinaryOpKind::And
} else {
None
}
})
break;
},
allow_constructors
)
}

/// XorExpression
/// = LessOrGreaterExpression ( '^' LessOrGreaterExpression )*
#[inline(always)]
pub(super) fn parse_xor(&mut self, allow_constructors: bool) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_less_or_greater, |parser| {
parse_infix!(
self,
Parser::parse_less_or_greater,
// Don't parse `x |= ...`, etc.
if parser.next_is(Token::Assign) {
None
} else if parser.eat(Token::Caret) {
Some(BinaryOpKind::Xor)
if self.next_is(Token::Assign) {
break;
} else if self.eat(Token::Caret) {
BinaryOpKind::Xor
} else {
None
}
})
break;
},
allow_constructors
)
}

/// LessOrGreaterExpression
/// = ShiftExpression ( ( '<' | '<=' | '>' | '>=' ) ShiftExpression )*
#[inline(always)]
pub(super) fn parse_less_or_greater(&mut self, allow_constructors: bool) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_shift, |parser| {
if parser.eat(Token::Less) {
Some(BinaryOpKind::Less)
} else if parser.eat(Token::LessEqual) {
Some(BinaryOpKind::LessEqual)
} else if parser.next_token.token() != &Token::GreaterEqual
&& parser.eat(Token::Greater)
{
parse_infix!(
self,
Parser::parse_shift,
if self.eat(Token::Less) {
BinaryOpKind::Less
} else if self.eat(Token::LessEqual) {
BinaryOpKind::LessEqual
} else if self.next_token.token() != &Token::GreaterEqual && self.eat(Token::Greater) {
// Make sure to skip the `>>=` case, as `>>=` is lexed as `> >=`.
Some(BinaryOpKind::Greater)
} else if parser.eat(Token::GreaterEqual) {
Some(BinaryOpKind::GreaterEqual)
BinaryOpKind::Greater
} else if self.eat(Token::GreaterEqual) {
BinaryOpKind::GreaterEqual
} else {
None
}
})
break;
},
allow_constructors
)
}

/// ShiftExpression
/// = AddOrSubtractExpression ( ( '<<' | '>' '>' ) AddOrSubtractExpression )*
#[inline(always)]
pub(super) fn parse_shift(&mut self, allow_constructors: bool) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_add_or_subtract, |parser| {
if !parser.next_is(Token::Assign) && parser.eat(Token::ShiftLeft) {
Some(BinaryOpKind::ShiftLeft)
} else if parser.at(Token::Greater) && parser.next_is(Token::Greater) {
parse_infix!(
self,
Parser::parse_add_or_subtract,
if !self.next_is(Token::Assign) && self.eat(Token::ShiftLeft) {
BinaryOpKind::ShiftLeft
} else if self.at(Token::Greater) && self.next_is(Token::Greater) {
// Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier
// to parse nested generic types. For normal expressions however, it means we have to manually
// parse two greater-than tokens as a single right-shift here.
parser.bump();
parser.bump();
Some(BinaryOpKind::ShiftRight)
self.bump();
self.bump();
BinaryOpKind::ShiftRight
} else {
None
}
})
break;
},
allow_constructors
)
}

/// AddOrSubtractExpression
/// = MultiplyOrDivideOrModuloExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloExpression )*
#[inline(always)]
pub(super) fn parse_add_or_subtract(&mut self, allow_constructors: bool) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_multiply_or_divide_or_modulo, |parser| {
if parser.next_is(Token::Assign) {
None
} else if parser.eat(Token::Plus) {
Some(BinaryOpKind::Add)
} else if parser.eat(Token::Minus) {
Some(BinaryOpKind::Subtract)
parse_infix!(
self,
Parser::parse_multiply_or_divide_or_modulo,
if self.next_is(Token::Assign) {
break;
} else if self.eat(Token::Plus) {
BinaryOpKind::Add
} else if self.eat(Token::Minus) {
BinaryOpKind::Subtract
} else {
None
}
})
break;
},
allow_constructors
)
}

/// MultiplyOrDivideOrModuloExpression
/// = Term ( ( '*' | '/' | '%' ) Term )*
#[inline(always)]
pub(super) fn parse_multiply_or_divide_or_modulo(
&mut self,
allow_constructors: bool,
) -> Option<Expression> {
self.parse_infix(allow_constructors, Parser::parse_term, |parser| {
if parser.next_is(Token::Assign) {
None
} else if parser.eat(Token::Star) {
Some(BinaryOpKind::Multiply)
} else if parser.eat(Token::Slash) {
Some(BinaryOpKind::Divide)
} else if parser.eat(Token::Percent) {
Some(BinaryOpKind::Modulo)
} else {
None
}
})
}

fn parse_infix<Next, Op>(
&mut self,
allow_constructors: bool,
mut next: Next,
mut op: Op,
) -> Option<Expression>
where
Next: FnMut(&mut Parser<'a>, bool) -> Option<Expression>,
Op: FnMut(&mut Parser<'a>) -> Option<BinaryOpKind>,
{
let start_location = self.current_token_location;
let mut lhs = next(self, allow_constructors)?;

loop {
let operator_start_location = self.current_token_location;
let Some(operator) = op(self) else {
parse_infix!(
self,
Parser::parse_term,
if self.next_is(Token::Assign) {
break;
};
let operator = Located::from(operator_start_location, operator);

let Some(rhs) = next(self, allow_constructors) else {
self.push_expected_expression();
} else if self.eat(Token::Star) {
BinaryOpKind::Multiply
} else if self.eat(Token::Slash) {
BinaryOpKind::Divide
} else if self.eat(Token::Percent) {
BinaryOpKind::Modulo
} else {
break;
};

lhs = self.new_infix_expression(lhs, operator, rhs, start_location);
}

Some(lhs)
},
allow_constructors
)
}

#[inline(always)]
fn new_infix_expression(
&self,
lhs: Expression,
Expand Down
Loading
Loading