use crate::parser::{
parse_block,
try_parse_expression,
OptionParsingResult,
ParsingError,
};
use crate::parser::token_utils::{
is_symbol,
skip_tokens,
skip_first_token,
skip_first_token_if_is_keyword,
skip_first_token_if_is_symbol,
LuaKeyword,
LuaSymbol,
};
use crate::parser::node_types::NodeTypes;
use lualexer::{Token, TokenType};
pub fn try_parse_for_statement<'a, T: NodeTypes>(
tokens: &'a [Token<'a>]
) -> OptionParsingResult<'a, T::Statement> {
if let Some(after_for_tokens) = skip_first_token_if_is_keyword(tokens, LuaKeyword::For) {
let mut iterator = after_for_tokens.iter();
let first_token = iterator.next()
.filter(|token| token.is_type(TokenType::Identifier))
.ok_or(ParsingError::IdentifierExpectedAfterForKeyword)?;
let second_token = iterator.next();
match second_token {
Some(second_token) if is_symbol(second_token, LuaSymbol::Assign) => {
let next_tokens = skip_tokens(after_for_tokens, 2);
let (start, after_start_tokens) = try_parse_expression::<T>(next_tokens)?
.ok_or(ParsingError::StartExpressionExpectedForNumericFor)?;
let after_comma_tokens = skip_first_token_if_is_symbol(after_start_tokens, LuaSymbol::Comma)
.ok_or(ParsingError::CommaExpectedAfterStartExpressionOfNumericFor)?;
let (end, after_end_tokens) = try_parse_expression::<T>(after_comma_tokens)?
.ok_or(ParsingError::EndExpressionExpectedForNumericFor)?;
let (step, next_tokens) = if let Some(after_comma_tokens) = after_end_tokens.first()
.filter(|token| is_symbol(token, LuaSymbol::Comma))
.map(|_| skip_first_token(after_end_tokens))
{
let (expression, next_tokens) = try_parse_expression::<T>(after_comma_tokens)?
.ok_or(ParsingError::StepExpressionExpectedForNumericFor)?;
(Some(expression), next_tokens)
} else {
(None, after_end_tokens)
};
let after_do_tokens = skip_first_token_if_is_keyword(next_tokens, LuaKeyword::Do)
.ok_or(ParsingError::DoExpectedForNumericFor)?;
let (block, after_block_tokens) = parse_block::<T>(after_do_tokens, LuaKeyword::End)?;
let for_statement = T::NumericForStatement::from((
first_token.get_content().to_owned(),
start,
end,
step,
block,
));
Ok(Some((for_statement.into(), after_block_tokens)))
}
_ => {
let mut identifiers = vec![first_token.get_content().to_owned()];
let mut next_tokens = skip_first_token(after_for_tokens);
while let Some(after_comma_tokens) = skip_first_token_if_is_symbol(next_tokens, LuaSymbol::Comma)
{
let identifier = after_comma_tokens.first()
.filter(|token| token.is_type(TokenType::Identifier))
.map(|token| token.get_content().to_owned())
.ok_or(ParsingError::IdentifierExpectedForGenericFor)?;
identifiers.push(identifier);
next_tokens = skip_first_token(after_comma_tokens);
}
let after_in_tokens = skip_first_token_if_is_keyword(next_tokens, LuaKeyword::In)
.ok_or(ParsingError::InKeywordExpectedForGenericFor)?;
let (first_expression, mut next_tokens) = try_parse_expression::<T>(after_in_tokens)?
.ok_or(ParsingError::ExpressionExpectedForGenericFor)?;
let mut expressions = vec![first_expression];
while let Some(after_comma_tokens) = next_tokens.first()
.filter(|token| is_symbol(token, LuaSymbol::Comma))
.map(|_| skip_first_token(next_tokens))
{
let (expression, after_tokens) = try_parse_expression::<T>(after_comma_tokens)?
.ok_or(ParsingError::ExpressionExpectedForGenericFor)?;
expressions.push(expression);
next_tokens = after_tokens;
}
let after_do_tokens = skip_first_token_if_is_keyword(next_tokens, LuaKeyword::Do)
.ok_or(ParsingError::DoKeywordExpectedForGenericFor)?;
let (block, after_block_tokens) = parse_block::<T>(after_do_tokens, LuaKeyword::End)?;
let for_statement = T::GenericForStatement::from((
identifiers,
expressions,
block,
));
Ok(Some((for_statement.into(), after_block_tokens)))
}
}
} else {
Ok(None)
}
}