luaparser 0.1.1

Read Lua 5.1 code and produce an abstract syntax tree
Documentation
use crate::parser::{
    parse_block,
    OptionParsingResult,
    ParsingError,
    ParsingResult,
};
use crate::parser::token_utils::{
    is_symbol,
    skip_first_token,
    skip_tokens,
    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_function_statement<'a, T: NodeTypes>(
    tokens: &'a [Token<'a>]
) -> OptionParsingResult<'a, T::Statement> {
    if let Some(after_function_tokens) = skip_first_token_if_is_keyword(tokens, LuaKeyword::Function) {
        let function_name = after_function_tokens.first()
            .filter(|token| token.is_type(TokenType::Identifier))
            .map(Token::get_content)
            .ok_or(ParsingError::IdentifierExpectedForFunction)?;

        let mut field_names = Vec::new();
        let mut next_tokens = skip_first_token(after_function_tokens);

        while let Some(after_dot_tokens) = skip_first_token_if_is_symbol(next_tokens, LuaSymbol::Dot) {
            let field = after_dot_tokens.first()
                .filter(|token| token.is_type(TokenType::Identifier))
                .map(|token| token.get_content().to_owned())
                .ok_or(ParsingError::IdentifierExpectedForFunction)?;

            field_names.push(field);
            next_tokens = skip_first_token(after_dot_tokens);
        }

        let (method, next_tokens) = skip_first_token_if_is_symbol(next_tokens, LuaSymbol::Colon)
            .and_then(|tokens| tokens.first())
            .filter(|token| token.is_type(TokenType::Identifier))
            .map(|token| (Some(token.get_content().to_owned()), skip_tokens(next_tokens, 2)))
            .unwrap_or_else(|| (None, next_tokens));

        let (values, next_tokens) = parse_function_body::<T>(next_tokens)?;
        let (parameters, is_variadic, block) = values;

        let statement = T::FunctionStatement::from((
            function_name.to_owned(),
            field_names,
            method,
            block,
            parameters,
            is_variadic,
        ));

        Ok(Some((statement.into(), next_tokens)))

    } else {
        Ok(None)
    }
}

pub fn parse_function_body<'a, T: NodeTypes>(
    mut tokens: &'a [Token<'a>],
) -> ParsingResult<'a, (Vec<String>, bool, T::Block)> {
    tokens = skip_first_token_if_is_symbol(tokens, LuaSymbol::OpenParenthese)
        .ok_or(ParsingError::OpeningParentheseExpectedForFunction)?;

    let mut parameters = vec![];
    let mut is_variadic = false;

    if let Some(identifier) = tokens.first()
        .filter(|token| token.get_type() == &TokenType::Identifier)
        .map(Token::get_content)
    {
        parameters.push(identifier.to_owned());
        tokens = skip_first_token(tokens);

        while let Some(_comma) = tokens.first()
            .filter(|token| is_symbol(token, LuaSymbol::Comma))
        {
            tokens = skip_first_token(tokens);

            if let Some(token) = tokens.first() {
                match (token.get_type(), token.get_content()) {
                    (TokenType::Identifier, identifier) => {
                        parameters.push(identifier.to_owned());
                        tokens = skip_first_token(tokens);
                    }
                    (TokenType::Symbol, "...") => {
                        is_variadic = true;
                        tokens = skip_first_token(tokens);
                        break
                    }
                    _ => return Err(ParsingError::InvalidFunctionParameter)
                }
            }
        }
    } else if let Some(_) = tokens.first()
        .filter(|token| is_symbol(token, LuaSymbol::VariableArguments))
    {
        is_variadic = true;
        tokens = skip_first_token(tokens);
    }

    tokens = tokens.first()
        .filter(|token| is_symbol(token, LuaSymbol::CloseParenthese))
        .map(|_| skip_first_token(tokens))
        .ok_or(ParsingError::ClosingParentheseExpectedForFunction)?;

    let (block, next_tokens) = parse_block::<T>(tokens, LuaKeyword::End)?;

    Ok(((parameters, is_variadic, block), next_tokens))
}