110 lines
4.3 KiB
Rust
110 lines
4.3 KiB
Rust
//! Block-body parsing for Fermented `UnrealScript`.
|
|
//!
|
|
//! Provides shared routines for parsing `{ ... }`-delimited bodies used in
|
|
//! function, loop, state, and similar constructs after the opening `{`
|
|
//! has been consumed.
|
|
|
|
use crate::arena::ArenaVec;
|
|
use crate::ast::{BlockBody, Expression, ExpressionRef, Statement, StatementRef};
|
|
use crate::lexer::{Token, TokenPosition, TokenSpan};
|
|
use crate::parser::{ParseErrorKind, Parser};
|
|
|
|
impl<'src, 'arena> Parser<'src, 'arena> {
|
|
/// Parses a `{ ... }` block after the opening `{` has been consumed.
|
|
///
|
|
/// Consumes tokens until the matching `}` and returns an
|
|
/// [`Expression::Block`] whose span covers the entire block, from
|
|
/// `opening_brace_position` to the closing `}`.
|
|
///
|
|
/// On premature end-of-file, returns a best-effort block.
|
|
#[must_use]
|
|
pub(crate) fn parse_block_tail(
|
|
&mut self,
|
|
opening_brace_position: TokenPosition,
|
|
) -> ExpressionRef<'src, 'arena> {
|
|
let BlockBody { statements, span } =
|
|
self.parse_braced_block_statements_tail(opening_brace_position);
|
|
self.arena.alloc_node(Expression::Block(statements), span)
|
|
}
|
|
|
|
/// Parses a `{ ... }` block after the opening `{` has been consumed.
|
|
///
|
|
/// Consumes tokens until the matching `}` and returns the contained
|
|
/// statements together with a span that covers the entire block, from
|
|
/// `opening_brace_position` to the closing `}`.
|
|
///
|
|
/// On premature end-of-file, returns a best-effort statement list and span.
|
|
#[must_use]
|
|
pub(crate) fn parse_braced_block_statements_tail(
|
|
&mut self,
|
|
opening_brace_position: TokenPosition,
|
|
) -> BlockBody<'src, 'arena> {
|
|
let mut statements = self.arena.vec();
|
|
while let Some((token, token_position)) = self.peek_token_and_position() {
|
|
if token == Token::RightBrace {
|
|
self.advance(); // '}'
|
|
let span = TokenSpan::range(opening_brace_position, token_position);
|
|
return BlockBody { statements, span };
|
|
}
|
|
self.parse_next_block_item_into(&mut statements);
|
|
self.ensure_forward_progress(token_position);
|
|
}
|
|
// Reached EOF without a closing `}`
|
|
self.report_error_here(ParseErrorKind::BlockMissingClosingBrace);
|
|
let span = TokenSpan::range(
|
|
opening_brace_position,
|
|
self.last_consumed_position_or_start(),
|
|
);
|
|
BlockBody { statements, span }
|
|
}
|
|
|
|
/// Parses one statement inside a `{ ... }` block and appends it to
|
|
/// `statements`.
|
|
///
|
|
/// This method never consumes the closing `}` and is only meant to be
|
|
/// called while parsing inside a block. It always appends at least one
|
|
/// statement, even in the presence of syntax errors.
|
|
pub(crate) fn parse_next_block_item_into(
|
|
&mut self,
|
|
statements: &mut ArenaVec<'arena, StatementRef<'src, 'arena>>,
|
|
) {
|
|
let mut next_statement = self.parse_statement().unwrap_or_else(|| {
|
|
let next_expression = self.parse_expression();
|
|
let next_expression_span = *next_expression.span();
|
|
self.arena
|
|
.alloc_node(Statement::Expression(next_expression), next_expression_span)
|
|
});
|
|
if statement_needs_semicolon(&next_statement)
|
|
&& let Some((Token::Semicolon, semicolon_position)) = self.peek_token_and_position()
|
|
{
|
|
next_statement.span_mut().extend_to(semicolon_position);
|
|
self.advance(); // ';'
|
|
}
|
|
statements.push(next_statement);
|
|
}
|
|
}
|
|
|
|
fn statement_needs_semicolon(statement: &Statement) -> bool {
|
|
use Statement::{Empty, Error, Expression, Function, Label, LocalVariableDeclaration};
|
|
match statement {
|
|
Empty | Label(_) | Error | Function(_) => false,
|
|
Expression(expression) => expression_needs_semicolon(expression),
|
|
LocalVariableDeclaration { .. } => true,
|
|
}
|
|
}
|
|
|
|
const fn expression_needs_semicolon(expression: &Expression) -> bool {
|
|
use Expression::{Block, DoUntil, Error, For, ForEach, If, Switch, While};
|
|
matches!(
|
|
expression,
|
|
Block { .. }
|
|
| If { .. }
|
|
| While { .. }
|
|
| DoUntil { .. }
|
|
| ForEach { .. }
|
|
| For { .. }
|
|
| Switch { .. }
|
|
| Error
|
|
)
|
|
}
|