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))
}