Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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