luaparser 0.1.1

Read Lua 5.1 code and produce an abstract syntax tree
Documentation
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)
    }
}