Break out parsing for for loop components

master
Nick Krichevsky 2024-05-26 10:55:59 -04:00
parent ccd5de6720
commit dab467b560
1 changed files with 67 additions and 32 deletions

View File

@ -19,6 +19,12 @@ enum ParsedStatement {
ImplicitStmt { stmt: Stmt, line_number: usize },
}
struct ForLoopPreamble {
initializer: Option<Stmt>,
condition: Option<Expr>,
increment: Option<Expr>,
}
pub fn parse_program<I: Iterator<Item = Token>, F: FnMut(ScriptError)>(
iter: I,
mut on_error: F,
@ -269,63 +275,94 @@ fn parse_for_statement<I: Iterator<Item = Token>>(
iter: &mut Peekable<I>,
for_token: &Token,
) -> Result<ParsedStatement, ParseError> {
// TODO: this function is kinda messy
let preamble = parse_for_loop_preamble(iter, for_token)?;
let body = ensure_explicit_statement(parse_statement(iter)?)?;
let loop_stmt = desugar_for_loop(preamble, body, for_token);
Ok(ParsedStatement::Stmt { stmt: loop_stmt })
}
fn parse_for_loop_preamble<I: Iterator<Item = Token>>(
iter: &mut Peekable<I>,
for_token: &Token,
) -> Result<ForLoopPreamble, ParseError> {
match_token_kind!(iter, TokenKind::LeftParen).map_err(|_| ParseError {
message: "Expected '(' after 'for'".to_string(),
line: for_token.line().into(),
})?;
let initializer = if match_token_kind!(iter, TokenKind::SemiColon).is_ok() {
None
} else if iter
.peek()
.is_some_and(|token| token.kind() == &TokenKind::Var)
{
Some(ensure_explicit_statement(parse_declaration(iter)?)?)
} else {
Some(ensure_explicit_statement(parse_expression_statement(
iter,
)?)?)
};
let initializer = optionally_parse_with_suffix(iter, TokenKind::SemiColon, |iter| {
let statement = if match_next_token(iter, &TokenKind::Var).is_some() {
parse_var_declaration(iter)?
} else {
parse_expression_statement(iter)?
};
let condition = if match_token_kind!(iter, TokenKind::SemiColon).is_ok() {
None
} else {
ensure_explicit_statement(statement)
})?;
let condition = optionally_parse_with_suffix(iter, TokenKind::SemiColon, |iter| {
let expr = parse_expression(iter)?;
match_token_kind!(iter, TokenKind::SemiColon).map_err(|_| ParseError {
message: "Expected ';' after for loop condition".to_string(),
line: for_token.line().into(),
})?;
Some(expr)
};
let increment = if match_token_kind!(iter, TokenKind::RightParen).is_ok() {
None
} else {
Ok(expr)
})?;
let increment = optionally_parse_with_suffix(iter, TokenKind::RightParen, |iter| {
let expr = parse_expression(iter)?;
match_token_kind!(iter, TokenKind::RightParen).map_err(|_| ParseError {
message: "Expected ')' after for conditions".to_string(),
line: for_token.line().into(),
})?;
Some(expr)
};
let body = ensure_explicit_statement(parse_statement(iter)?)?;
let while_condition = condition.unwrap_or(Expr::Literal {
Ok(expr)
})?;
Ok(ForLoopPreamble {
initializer,
condition,
increment,
})
}
fn optionally_parse_with_suffix<
I: Iterator<Item = Token>,
R,
F: FnOnce(&mut Peekable<I>) -> Result<R, ParseError>,
>(
iter: &mut Peekable<I>,
suffix_kind: TokenKind,
parse: F,
) -> Result<Option<R>, ParseError> {
if match_token_kind_eq(iter, &[suffix_kind]).is_some() {
Ok(None)
} else {
let res = parse(iter)?;
Ok(Some(res))
}
}
fn desugar_for_loop(preamble: ForLoopPreamble, body: Stmt, for_token: &Token) -> Stmt {
let while_condition = preamble.condition.unwrap_or(Expr::Literal {
value: LiteralValue::True,
// Kinda wrong, but not completely off either
token: for_token.clone(),
});
let body_statements = [
Some(body),
increment.map(|expression| Stmt::Expression { expression }),
preamble
.increment
.map(|expression| Stmt::Expression { expression }),
]
.into_iter()
.flatten()
.collect::<Vec<_>>();
let wrapping_body_statements = [
initializer,
preamble.initializer,
Some(Stmt::While {
condition: while_condition,
body: Box::new(Stmt::Block {
@ -337,11 +374,9 @@ fn parse_for_statement<I: Iterator<Item = Token>>(
.flatten()
.collect::<Vec<_>>();
Ok(ParsedStatement::Stmt {
stmt: Stmt::Block {
statements: wrapping_body_statements,
},
})
Stmt::Block {
statements: wrapping_body_statements,
}
}
fn parse_statement_containing_expression<