Parser - Labelled loops

This commit is contained in:
Emmanuel BENOîT 2023-01-02 10:44:37 +01:00
parent 2f44a81b6d
commit c36654fb7e

View file

@ -21,6 +21,15 @@ pub enum LoopParsingState {
NamedLoop(String), NamedLoop(String),
} }
impl From<&Option<Token>> for LoopParsingState {
fn from(value: &Option<Token>) -> Self {
match &value {
None => LoopParsingState::UnnamedLoop,
Some(name) => LoopParsingState::NamedLoop(name.lexeme.clone()),
}
}
}
/// The result of one of the parser's functions. /// The result of one of the parser's functions.
type ParserResult<T> = Result<T, ParserError>; type ParserResult<T> = Result<T, ParserError>;
@ -97,6 +106,7 @@ impl Parser {
/// statement := "print" expression ";" /// statement := "print" expression ";"
/// statement := declaration ";" /// statement := declaration ";"
/// statement := block /// statement := block
/// statement := labelled_loop
/// statement := if_statement /// statement := if_statement
/// statement := while_statement /// statement := while_statement
/// statement := for_statement /// statement := for_statement
@ -107,15 +117,14 @@ impl Parser {
self.parse_declaration() self.parse_declaration()
} else if self.expect(&[TokenType::LeftBrace]).is_some() { } else if self.expect(&[TokenType::LeftBrace]).is_some() {
self.parse_block() self.parse_block()
} else if self.expect(&[TokenType::Address]).is_some() {
self.parse_labelled_loop()
} else if self.expect(&[TokenType::If]).is_some() { } else if self.expect(&[TokenType::If]).is_some() {
self.parse_if_statement() self.parse_if_statement()
} else if self.expect(&[TokenType::While]).is_some() { } else if self.expect(&[TokenType::While]).is_some() {
self.loop_state.push(LoopParsingState::UnnamedLoop); self.parse_while_statement(None)
let result = self.parse_while_statement();
self.loop_state.pop();
result
} else if self.expect(&[TokenType::For]).is_some() { } else if self.expect(&[TokenType::For]).is_some() {
self.parse_for_statement(LoopParsingState::UnnamedLoop) self.parse_for_statement(None)
} else if let Some(lcs) = self.expect(&[TokenType::Break, TokenType::Continue]) { } else if let Some(lcs) = self.expect(&[TokenType::Break, TokenType::Continue]) {
self.parse_loop_control_statement(&lcs) self.parse_loop_control_statement(&lcs)
} else if self.expect(&[TokenType::Print]).is_some() { } else if self.expect(&[TokenType::Print]).is_some() {
@ -196,19 +205,56 @@ impl Parser {
}) })
} }
/// Parse the following rule:
/// ```
/// labelled_loop := "@" IDENTIFIER while_statement
/// labelled_loop := "@" IDENTIFIER for_statement
/// ```
fn parse_labelled_loop(&mut self) -> ParserResult<ast::StmtNode> {
let name_token = match self.peek().token_type {
TokenType::Identifier(_) => self.advance().clone(),
_ => {
return Err(ParserError::new(
self.peek(),
"identifier expected after '@'",
))
}
};
if self.expect(&[TokenType::While]).is_some() {
self.parse_while_statement(Some(name_token))
} else if self.expect(&[TokenType::For]).is_some() {
self.parse_for_statement(Some(name_token))
} else {
Err(ParserError::new(
self.peek(),
"'while' or 'for' expected after loop label",
))
}
}
/// Parse the following rule: /// Parse the following rule:
/// ``` /// ```
/// while_statement := "while" "(" expression ")" statement /// while_statement := "while" "(" expression ")" statement
/// ``` /// ```
fn parse_while_statement(&mut self) -> ParserResult<ast::StmtNode> { fn parse_while_statement(&mut self, label: Option<Token>) -> ParserResult<ast::StmtNode> {
self.consume(&TokenType::LeftParen, "expected '(' after 'while'")?; self.consume(&TokenType::LeftParen, "expected '(' after 'while'")?;
let condition = self.parse_expression()?; let condition = self.parse_expression()?;
self.consume( self.consume(
&TokenType::RightParen, &TokenType::RightParen,
"expected ')' after condition in 'while' statement", "expected ')' after condition in 'while' statement",
)?; )?;
let body = Box::new(self.parse_statement()?); let body = Box::new({
Ok(ast::StmtNode::WhileStmt { condition, body }) self.loop_state.push(LoopParsingState::from(&label));
let result = self.parse_statement();
self.loop_state.pop();
result?
});
Ok(ast::StmtNode::WhileStmt {
label,
condition,
body,
})
} }
/// Parse the following rules: /// Parse the following rules:
@ -218,7 +264,7 @@ impl Parser {
/// for_initializer := expression /// for_initializer := expression
/// for_initializer := /// for_initializer :=
/// ``` /// ```
fn parse_for_statement(&mut self, loop_state: LoopParsingState) -> ParserResult<ast::StmtNode> { fn parse_for_statement(&mut self, label: Option<Token>) -> ParserResult<ast::StmtNode> {
self.consume(&TokenType::LeftParen, "expected '(' after 'for'")?; self.consume(&TokenType::LeftParen, "expected '(' after 'for'")?;
let initializer = if self.expect(&[TokenType::Semicolon]).is_some() { let initializer = if self.expect(&[TokenType::Semicolon]).is_some() {
@ -258,7 +304,7 @@ impl Parser {
// Generate a while loop, with an optional initializer which may be // Generate a while loop, with an optional initializer which may be
// inside a specific block if the initializer declares a variable. // inside a specific block if the initializer declares a variable.
let body_stmt = { let body_stmt = {
self.loop_state.push(loop_state); self.loop_state.push(LoopParsingState::from(&label));
let result = self.parse_statement(); let result = self.parse_statement();
self.loop_state.pop(); self.loop_state.pop();
result? result?
@ -276,6 +322,7 @@ impl Parser {
body_stmt body_stmt
}; };
let while_stmt = ast::StmtNode::WhileStmt { let while_stmt = ast::StmtNode::WhileStmt {
label,
condition, condition,
body: Box::new(body_with_incr), body: Box::new(body_with_incr),
}; };