use super::*;
impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
'name: 'ast,
{
pub(in crate::parser) fn parse_do(
&mut self,
location: Location,
) -> Result<ParsedStatement<'ast>> {
let begin = self.current_token_location().begin;
self.advance();
let stats_start = self.current_position();
let mut body = self.parse_block_until(&["end"], "do", location)?;
let end_location = self.current_token_location();
let has_end = self.expect_match_end_with_lookahead("do", location);
let end = if has_end {
end_location.begin
} else {
Position::missing()
};
body.has_end = has_end;
body.base.location.begin = begin;
if has_end {
body.base.location.end = end_location.end;
}
let body = self.arena.alloc_block(body);
Ok(self.parsed_statement_from_node(
body.as_statement(),
self.cst_node(|| CstNode::StatDo(CstStatDo { stats_start, end })),
))
}
pub(in crate::parser) fn parse_for(
&mut self,
location: Location,
) -> Result<ParsedStatement<'ast>> {
self.advance();
let binding = self.parse_binding(false)?;
if self.current.kind() != TokenKind::Equal {
return self.parse_generic_for(location, binding);
}
self.parse_numeric_for(binding, location)
}
pub(in crate::parser) fn expect_match_end_with_lookahead(
&mut self,
opener: &'static str,
opener_location: Location,
) -> bool {
self.expect_match_keyword_with_lookahead(R::End, "end", opener, opener_location)
}
pub(in crate::parser) fn expect_match_keyword_with_lookahead(
&mut self,
expected: R,
expected_name: &'static str,
opener: &'static str,
opener_location: Location,
) -> bool {
if self.current.kind() == TokenKind::Reserved(expected) {
let current_location = self.current_token_location();
if current_location.begin.line != opener_location.begin.line
&& current_location.begin.column != opener_location.begin.column
&& self
.contexts
.end_mismatch_suspect
.is_none_or(|suspect| suspect.line < opener_location.begin.line as usize + 1)
{
self.contexts.end_mismatch_suspect = Some(BlockContext {
opener,
line: opener_location.begin.line as usize + 1,
column: opener_location.begin.column as usize + 1,
});
}
self.advance();
return true;
}
let error = self.matched_block_token_error(expected_name, opener, opener_location);
self.report_parse_error(error);
if self.peek().kind() == TokenKind::Reserved(expected) {
self.advance();
self.advance();
true
} else {
false
}
}
pub(in crate::parser) fn expect_match_token(
&mut self,
expected: Token<'source, 'ast>,
expected_name: &'static str,
opener: &'static str,
opener_location: Location,
search_for_missing: bool,
) -> bool {
let expected_kind = expected.kind();
if self.current.kind() == expected_kind {
self.advance();
return true;
}
let error = self.matched_token_error(expected_name, opener, opener_location);
self.report_parse_error(error);
if search_for_missing {
self.recover_match_on_line(expected)
} else if self.peek().kind() == expected_kind {
self.advance();
self.advance();
true
} else {
false
}
}
pub(in crate::parser) fn parse_numeric_for(
&mut self,
binding: Binding<'ast>,
location: Location,
) -> Result<ParsedStatement<'ast>> {
let equals = self.current_position();
self.advance();
let start = self.parse_expression()?;
let end_comma = if self.expect_and_consume(Token::Comma, "index range") {
self.previous_token_location().begin
} else {
Position::missing()
};
let limit = self.parse_expression()?;
let step = if self.current.kind() == TokenKind::Comma {
let step_comma = self.current_position();
self.advance();
Some((self.parse_expression()?, step_comma))
} else {
None
};
let do_location = self.current_location();
let has_do = self.expect_and_consume_keyword(R::Do, "for loop");
self.push_local_scope();
self.current_function_mut().loop_depth += 1;
let local = self.declare_local(
binding.name,
binding.name_location,
binding.annotation,
binding.is_const,
false,
);
let body = self.parse_block_until_no_scope(&["end"], "do", do_location);
self.current_function_mut().loop_depth -= 1;
self.pop_local_scope();
let mut body = body?;
let end = self.current_token_location();
body.has_end = self.expect_match_end_with_lookahead("do", do_location);
let location = Location::new(location.begin, end.end);
let body = self.arena.alloc_block(body);
let statement = self.arena.alloc_statement_node(StatementNumericFor::new(
location,
false,
local,
start,
limit,
step.map(|(expression, _)| expression),
body,
has_do,
do_location,
));
Ok(self.parsed_statement_from_node(
statement,
self.cst_node(|| {
CstNode::StatFor(CstStatFor {
annotation_colon: binding.colon_position,
equals,
end_comma,
step_comma: step.as_ref().map(|(_, comma)| *comma),
})
}),
))
}
pub(in crate::parser) fn parse_generic_for(
&mut self,
location: Location,
first_binding: Binding<'ast>,
) -> Result<ParsedStatement<'ast>> {
let mut bindings = self.temp_bindings();
bindings.push_back(first_binding);
let mut variable_commas = self.temp_positions();
if self.current.kind() == TokenKind::Comma {
if self.cst.enabled() {
variable_commas.push_back(self.current_position());
}
self.advance();
self.parse_binding_list_into(&mut bindings, false, &mut variable_commas, false)?;
}
let in_location = self.current_location();
let has_in = self.expect_and_consume_keyword(R::In, "for loop");
let mut values = self.temp_expressions();
let mut value_commas = self.temp_positions();
self.parse_expression_list_into(&mut values, &mut value_commas)?;
let do_location = self.current_location();
let has_do = self.expect_and_consume_keyword(R::Do, "for loop");
self.push_local_scope();
self.current_function_mut().loop_depth += 1;
let mut locals = self.temp_locals();
for binding in bindings.iter() {
locals.push_back(self.declare_local(
binding.name,
binding.name_location,
binding.annotation,
binding.is_const,
false,
));
}
let body = self.parse_block_until_no_scope(&["end"], "do", do_location);
self.current_function_mut().loop_depth -= 1;
self.pop_local_scope();
let mut body = body?;
let end = self.current_token_location();
body.has_end = self.expect_match_end_with_lookahead("do", do_location);
let location = Location::new(location.begin, end.end);
let body = self.arena.alloc_block(body);
let statement = self.arena.alloc_statement_node(StatementGenericFor::new(
location,
false,
self.arena.alloc_slice_copy(locals.as_slice()),
self.arena.alloc_slice_copy(values.as_slice()),
body,
has_in,
in_location,
has_do,
do_location,
));
Ok(self.parsed_statement_from_node(statement, {
self.cst_node(|| {
let mut variable_annotation_colons = variable_commas.nested();
for binding in bindings.iter() {
variable_annotation_colons.push_back(binding.colon_position);
}
CstNode::StatForIn(CstStatForIn {
variable_annotation_colons: self
.arena
.alloc_slice_copy(variable_annotation_colons.as_slice()),
variable_commas: self.arena.alloc_slice_copy(variable_commas.as_slice()),
value_commas: self.arena.alloc_slice_copy(value_commas.as_slice()),
})
})
}))
}
pub(in crate::parser) fn parse_if(&mut self, location: Location) -> Result<Statement<'ast>> {
self.advance();
let condition = self.parse_expression()?;
let then_location = self.current_location();
let has_then = self.expect_and_consume_keyword(R::Then, "if statement");
let mut then_body =
self.parse_block_until(&["elseif", "else", "end"], "then", then_location)?;
let mut else_body: Option<Statement<'ast>> = None;
let mut else_location = None;
let end;
if self.current.kind() == TokenKind::Reserved(R::Elseif) {
then_body.has_end = true;
let old_recursion_counter = self.enter_recursion("elseif")?;
let elseif_location = self.current_location();
let elseif = self.parse_if(elseif_location)?;
self.contexts.recursion_counter = old_recursion_counter;
end = elseif.location().end;
else_location = Some(elseif_location);
else_body = Some(elseif);
} else {
let mut match_location = then_location;
let mut match_opener = "then";
if self.current.kind() == TokenKind::Reserved(R::Else) {
then_body.has_end = true;
let current_else_location = self.current_location();
else_location = Some(current_else_location);
match_location = current_else_location;
match_opener = "else";
self.advance();
let mut body = self.parse_block_until(&["end"], "else", current_else_location)?;
body.base.location.begin = current_else_location.end;
end = self.current_token_location().end;
let has_end = self.expect_match_end_with_lookahead(match_opener, match_location);
body.has_end = has_end;
let body = self.arena.alloc_block(body);
else_body = Some(body.as_statement());
} else {
end = self.current_token_location().end;
let has_end = self.expect_match_end_with_lookahead(match_opener, match_location);
then_body.has_end = has_end;
}
}
let then_body = self.arena.alloc_block(then_body);
let location = Location::new(location.begin, end);
Ok(self.arena.alloc_statement_node(StatementIf::new(
location,
false,
condition,
then_body,
else_body,
has_then.then_some(then_location),
else_location,
)))
}
pub(in crate::parser) fn parse_repeat(
&mut self,
location: Location,
) -> Result<ParsedStatement<'ast>> {
self.advance();
let locals_begin = self.locals.stack.len();
let body = match self.parse_loop_block_until_no_scope(&["until"], "repeat", location) {
Ok(body) => body,
Err(error) => {
self.restore_locals(locals_begin);
return Err(error);
}
};
let has_until =
self.expect_match_keyword_with_lookahead(R::Until, "until", "repeat", location);
let until = if has_until {
self.previous_token_location().begin
} else {
Position::missing()
};
let condition = match self.parse_expression() {
Ok(condition) => condition,
Err(error) => {
self.restore_locals(locals_begin);
return Err(error);
}
};
self.restore_locals(locals_begin);
let location = Location::new(location.begin, condition.location.end);
let mut body = body;
body.has_end = has_until;
let body = self.arena.alloc_block(body);
Ok(self.parsed_statement_from_node(
self.arena
.alloc_statement_node(StatementRepeat::new(location, false, body, condition)),
self.cst_node(|| CstNode::StatRepeat(CstStatRepeat { until })),
))
}
pub(in crate::parser) fn parse_while(
&mut self,
location: Location,
) -> Result<ParsedStatement<'ast>> {
self.advance();
let condition = self.parse_expression()?;
let do_location = self.current_location();
let has_do = self.expect_and_consume_keyword(R::Do, "while loop");
let mut body = self.parse_loop_block_until(&["end"], "do", do_location)?;
let end = self.current_token_location();
body.has_end = self.expect_match_end_with_lookahead("do", do_location);
let location = Location::new(location.begin, end.end);
let body = self.arena.alloc_block(body);
Ok(self.parsed_statement_from_node(
self.arena.alloc_statement_node(StatementWhile::new(
location,
false,
condition,
body,
has_do,
do_location,
)),
None,
))
}
}