Parser - Labelled loops
This commit is contained in:
parent
2f44a81b6d
commit
c36654fb7e
1 changed files with 57 additions and 10 deletions
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue