use super::*;
impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
'name: 'ast,
{
pub(in crate::parser) fn parse_block_until(
&mut self,
terminators: &'static [&'static str],
opener: &'static str,
location: Location,
) -> Result<BlockNode<'ast>> {
self.parse_block_until_from(terminators, opener, location, location.end)
}
pub(in crate::parser) fn parse_block_until_from(
&mut self,
terminators: &'static [&'static str],
opener: &'static str,
location: Location,
block_begin: Position,
) -> Result<BlockNode<'ast>> {
self.contexts.blocks.push(BlockContext {
opener,
line: location.begin.line as usize + 1,
column: location.begin.column as usize + 1,
});
self.push_local_scope();
let mut statements = self.temp_statements();
let result = self.parse_block_until_inner_into(terminators, &mut statements);
if result.is_ok() {
self.update_end_mismatch_suspect();
}
self.pop_local_scope();
self.contexts.blocks.pop();
result.map(|()| {
let has_end = terminators
.iter()
.any(|terminator| self.current.is_keyword(terminator));
BlockNode::new(
self.arena.alloc_slice_copy(statements.as_slice()),
has_end,
Location::new(block_begin, self.current_token_location().begin),
)
})
}
pub(in crate::parser) fn parse_loop_block_until(
&mut self,
terminators: &'static [&'static str],
opener: &'static str,
location: Location,
) -> Result<BlockNode<'ast>> {
self.current_function_mut().loop_depth += 1;
let result = self.parse_block_until(terminators, opener, location);
self.current_function_mut().loop_depth -= 1;
result
}
pub(in crate::parser) fn parse_block_until_no_scope(
&mut self,
terminators: &'static [&'static str],
opener: &'static str,
location: Location,
) -> Result<BlockNode<'ast>> {
self.contexts.blocks.push(BlockContext {
opener,
line: location.begin.line as usize + 1,
column: location.begin.column as usize + 1,
});
let mut statements = self.temp_statements();
let result = self.parse_block_until_inner_into(terminators, &mut statements);
if result.is_ok() {
self.update_end_mismatch_suspect();
}
self.contexts.blocks.pop();
result.map(|()| {
let has_end = terminators
.iter()
.any(|terminator| self.current.is_keyword(terminator));
BlockNode::new(
self.arena.alloc_slice_copy(statements.as_slice()),
has_end,
Location::new(location.end, self.current_token_location().begin),
)
})
}
pub(in crate::parser) fn parse_loop_block_until_no_scope(
&mut self,
terminators: &'static [&'static str],
opener: &'static str,
location: Location,
) -> Result<BlockNode<'ast>> {
self.current_function_mut().loop_depth += 1;
let result = self.parse_block_until_no_scope(terminators, opener, location);
self.current_function_mut().loop_depth -= 1;
result
}
pub(in crate::parser) fn parse_block_until_inner_into(
&mut self,
terminators: &'static [&'static str],
statements: &mut TempVector<Statement<'ast>>,
) -> Result<()> {
while !self.diagnostics.error_limit_reached {
match self.current {
Token::Eof => return Ok(()),
token
if terminators
.iter()
.any(|terminator| token.is_keyword(terminator)) =>
{
return Ok(());
}
_ => {
let old_recursion_counter = self.enter_recursion("block")?;
let parsed = match self.parse_statement() {
Ok(parsed) => parsed,
Err(error) => {
self.contexts.recursion_counter = old_recursion_counter;
return Err(error);
}
};
self.contexts.recursion_counter = old_recursion_counter;
let statement = parsed.statement;
let terminal = statement.is_terminal();
if self.current.kind() == TokenKind::Semicolon {
statement.set_semicolon(self.current_token_location());
self.advance();
}
statements.push_back(statement);
if terminal {
return Ok(());
}
}
}
}
Ok(())
}
pub(in crate::parser) fn parse_return_values(
&mut self,
) -> Result<(Vec<Expression<'ast>>, Vec<Position>)> {
if matches!(
self.current,
Token::Reserved(R::Elseif | R::Else | R::End | R::Until)
| Token::Semicolon
| Token::Eof
) {
return Ok((Vec::new(), Vec::new()));
}
self.parse_expression_list_with_commas()
}
pub(in crate::parser) fn parse_expression_list_into(
&mut self,
expressions: &mut TempVector<Expression<'ast>>,
comma_positions: &mut TempVector<Position>,
) -> Result<()> {
expressions.push_back(self.parse_expression()?);
while self.current.kind() == TokenKind::Comma {
if self.cst.enabled() {
comma_positions.push_back(self.current_position());
}
self.advance();
if self.current.kind() == TokenKind::RightParen {
self.report_parse_error(
self.located("Expected expression after ',' but got ')' instead"),
);
break;
}
expressions.push_back(self.parse_expression()?);
}
Ok(())
}
pub(in crate::parser) fn parse_expression_list_with_commas(
&mut self,
) -> Result<(Vec<Expression<'ast>>, Vec<Position>)> {
let mut expressions = self.temp_expressions();
let mut comma_positions = self.temp_positions();
self.parse_expression_list_into(&mut expressions, &mut comma_positions)?;
Ok((
expressions.as_slice().to_vec(),
comma_positions.as_slice().to_vec(),
))
}
pub(in crate::parser) fn check_assignable_expression(
&mut self,
expression: Expression<'ast>,
) -> Expression<'ast> {
match expression.kind() {
ExpressionKind::Local { local, .. } => {
if local.is_const {
self.error_lvalue_expression(expression)
} else {
expression
}
}
ExpressionKind::Global(name) => {
if self.locals.classes_within_module.contains(&name) {
self.error_lvalue_expression(expression)
} else {
expression
}
}
ExpressionKind::IndexExpr { .. } | ExpressionKind::IndexName { .. } => expression,
_ => self.error_lvalue_expression(expression),
}
}
pub(in crate::parser) fn error_lvalue_expression(
&mut self,
expression: Expression<'ast>,
) -> Expression<'ast> {
let message = match expression.kind() {
ExpressionKind::Local { local, .. }
if local.is_const && flags::LuauExportValueSyntax.get() =>
{
self.name_message(
b"Variable '",
local.name,
b"' is constant and may not be reassigned",
)
}
ExpressionKind::Global(name)
if flags::DebugLuauUserDefinedClasses.get()
&& self.locals.classes_within_module.contains(&name) =>
{
let class = self
.locals
.classes_within_module
.get(&name)
.copied()
.flatten()
.expect("class map value must be present");
self.name_message(
b"'",
class.name.name,
format!(
"' refers to a class and cannot be used as a variable name (defined on line {})",
class.location().begin.line + 1
)
.as_bytes(),
)
}
_ => ParseMessage::from("Assigned expression must be a variable or a field"),
};
let message_index = self.report_parse_error(ParseError::new(expression.location, message));
self.alloc_expression(ExpressionInit::new(
expression.location,
ExpressionKind::Error {
expressions: self.arena.alloc_slice_copy(&[expression]),
message_index,
},
))
}
}