luaparser 0.1.1

Read Lua 5.1 code and produce an abstract syntax tree
Documentation
mod parse_bracket;
mod parse_field;
mod parse_function;
mod parse_method;
mod parse_parenthese;
mod parse_prefix;
mod parse_primary;
mod parse_right;
mod parse_single;
mod parse_table;
mod parse_unary;

pub use parse_bracket::*;
pub use parse_field::*;
pub use parse_function::*;
pub use parse_method::*;
pub use parse_parenthese::*;
pub use parse_prefix::*;
pub use parse_primary::*;
pub use parse_right::*;
pub use parse_single::*;
pub use parse_table::*;
pub use parse_unary::*;

use crate::parser::{
    OptionParsingResult,
    ParsingResult,
};
use crate::parser::token_utils::{
    skip_first_token_if_is_symbol,
    LuaSymbol,
};
use crate::parser::node_types::NodeTypes;

use lualexer::{Token};

pub fn try_parse_expression<'a, T: NodeTypes>(
    tokens: &'a [Token<'a>]
) -> OptionParsingResult<'a, T::Expression> {
    if let Some((left, tokens)) = try_parse_single_expression::<T>(tokens)? {
        let (final_expression, next_tokens) = parse_right_expression::<T>(left, tokens, 0)?;

        Ok(Some((final_expression, next_tokens)))
    } else {
        Ok(None)
    }
}

pub fn parse_expression_list<'a, T: NodeTypes>(
    mut tokens: &'a [Token<'a>]
) -> ParsingResult<'a, Vec<T::Expression>> {
    let mut expressions = Vec::new();

    if let Some((first_expression, mut next_tokens)) = try_parse_expression::<T>(tokens)? {
        expressions.push(first_expression);

        while let Some(next_expression_tokens) = skip_first_token_if_is_symbol(next_tokens, LuaSymbol::Comma) {
            if let Some((expression, next_expression_tokens)) = try_parse_expression::<T>(next_expression_tokens)? {
                expressions.push(expression);
                next_tokens = next_expression_tokens;
            } else {
                break
            }
        }

        tokens = next_tokens;
    }

    Ok((expressions, tokens))
}

#[cfg(test)]
mod test {
    use super::try_parse_expression;

    use crate::nodes::*;
    use crate::parser_impl::FastParserNodes;

    use lualexer::FastLexer;

    macro_rules! test_expressions {
        ($($name:ident($input:literal) => $expect:expr),+) => {
            $(
                #[test]
                fn $name() {
                    let tokens = FastLexer::new()
                        .parse($input)
                        .unwrap();

                    let (expression, tokens) = try_parse_expression::<FastParserNodes>(&tokens)
                        .unwrap()
                        .unwrap();

                    assert_eq!(expression, $expect.into());
                    assert_eq!(tokens, &[]);
                }
            )+
        };
    }

    test_expressions!(
        parse_true("true") => Expression::True,
        parse_false("false") => Expression::False,
        parse_nil("nil") => Expression::Nil,
        parse_variable_arguments("...") => Expression::VariableArguments,
        parse_string("'hello'") => StringExpression { value: "'hello'".to_string() },
        parse_number("123") => NumberExpression { value: "123".to_string() },
        parse_empty_function("function() end") => FunctionExpression {
            block: Block::from((Vec::new(), None)),
            parameters: Vec::new(),
            is_variadic: false,
        },
        parse_single_parameter_function("function(a) end") => FunctionExpression {
            block: Block::from((Vec::new(), None)),
            parameters: vec!["a".to_owned()],
            is_variadic: false,
        },
        parse_multiple_parameters_function("function(a, b,c) end") => FunctionExpression {
            block: Block::from((Vec::new(), None)),
            parameters: vec!["a".to_owned(), "b".to_owned(), "c".to_owned()],
            is_variadic: false,
        },
        parse_empty_variadic_function("function(...) end") => FunctionExpression {
            block: Block::from((Vec::new(), None)),
            parameters: Vec::new(),
            is_variadic: true,
        },
        parse_single_parameter_variadic_function("function(a, ...) end") => FunctionExpression {
            block: Block::from((Vec::new(), None)),
            parameters: vec!["a".to_owned()],
            is_variadic: true,
        },
        parse_multiple_parameters_variadic_function("function(a, b,c,...) end") => FunctionExpression {
            block: Block::from((Vec::new(), None)),
            parameters: vec!["a".to_owned(), "b".to_owned(), "c".to_owned()],
            is_variadic: true,
        },
        parse_binary_true_and_false("true and false") => BinaryExpression {
            left: Expression::True,
            right: Expression::False,
            operator: BinaryOperator::And,
        },
        parse_binary_true_or_false("true or false") => BinaryExpression {
            left: Expression::True,
            right: Expression::False,
            operator: BinaryOperator::Or,
        },
        parse_with_precedence_binary_or_and("true or true and false") => BinaryExpression {
            left: Expression::True,
            operator: BinaryOperator::Or,
            right: BinaryExpression {
                left: Expression::True,
                operator: BinaryOperator::And,
                right: Expression::False,
            }.into(),
        },
        parse_with_precedence_binary_and_or("false and true or true") => BinaryExpression {
            left: BinaryExpression {
                left: Expression::False,
                operator: BinaryOperator::And,
                right: Expression::True,
            }.into(),
            operator: BinaryOperator::Or,
            right: Expression::True,
        },
        parse_with_precedence_concat_plus("'1' .. 4 + 6") => BinaryExpression {
            left: StringExpression { value: "'1'".to_owned() }.into(),
            operator: BinaryOperator::Concat,
            right: BinaryExpression {
                left: NumberExpression::from("4".to_owned()).into(),
                operator: BinaryOperator::Plus,
                right: NumberExpression::from("6".to_owned()).into(),
            }.into(),
        },
        parse_with_precedence_exponent_plus("1 ^ 4 + 6") => BinaryExpression {
            left: BinaryExpression {
                left: NumberExpression::from("1".to_owned()).into(),
                operator: BinaryOperator::Caret,
                right: NumberExpression::from("4".to_owned()).into(),
            }.into(),
            operator: BinaryOperator::Plus,
            right: NumberExpression::from("6".to_owned()).into(),
        },
        parse_exponent_right_associativity("2^3^2") => BinaryExpression {
            left: NumberExpression::from("2".to_owned()).into(),
            operator: BinaryOperator::Caret,
            right: BinaryExpression {
                left: NumberExpression::from("3".to_owned()).into(),
                operator: BinaryOperator::Caret,
                right: NumberExpression::from("2".to_owned()).into(),
            }.into(),
        },
        parse_unary_not_true("not true") => UnaryExpression {
            operator: UnaryOperator::Not,
            expression: Expression::True,
        },
        parse_unary_length("#foo") => UnaryExpression {
            operator: UnaryOperator::Length,
            expression: Expression::Identifier("foo".to_owned()),
        },
        parse_exponent_precedence_over_unary_minus("- 2 ^ 4") => UnaryExpression {
            operator: UnaryOperator::Minus,
            expression: BinaryExpression {
                left: NumberExpression::from("2".to_owned()).into(),
                operator: BinaryOperator::Caret,
                right: NumberExpression::from("4".to_owned()).into(),
            }.into(),
        },
        parse_unary_precedence_over_addition("- 2+3") => BinaryExpression {
            left: UnaryExpression {
                operator: UnaryOperator::Minus,
                expression: NumberExpression::from("2".to_owned()).into(),
            }.into(),
            operator: BinaryOperator::Plus,
            right: NumberExpression::from("3".to_owned()).into(),
        },
        parse_empty_table("{}") => TableExpression::from(Vec::new()),
        parse_table_with_one_value("{true}") => TableExpression::from(vec![
            TableEntry::Value(Expression::True),
        ]),
        parse_table_with_one_value_ending_with_comma("{true,}") => TableExpression::from(vec![
            TableEntry::Value(Expression::True),
        ]),
        parse_table_with_one_value_ending_with_semicolon("{true;}") => TableExpression::from(vec![
            TableEntry::Value(Expression::True),
        ]),
        parse_table_with_two_values_separated_with_comma("{true, false}") => TableExpression::from(vec![
            TableEntry::Value(Expression::True),
            TableEntry::Value(Expression::False),
        ]),
        parse_table_with_two_values_separated_with_semicolon("{true; false}") => TableExpression::from(vec![
            TableEntry::Value(Expression::True),
            TableEntry::Value(Expression::False),
        ]),
        parse_table_with_field("{key = true}") => TableExpression::from(vec![
            TableEntry::Field("key".to_owned(), Expression::True),
        ]),
        parse_table_with_index("{[true] = false}") => TableExpression::from(vec![
            TableEntry::Index(Expression::True, Expression::False),
        ]),
        parse_identifier("id") => Expression::Identifier("id".to_owned()),
        parse_field_index("foo.bar") => FieldExpression {
            prefix: Prefix::Identifier("foo".to_owned()),
            field: "bar".to_owned(),
        },
        parse_multiple_field_index("foo.bar.baz") => FieldExpression {
            prefix: Prefix::Field(Box::new(FieldExpression {
                prefix: Prefix::Identifier("foo".to_owned()),
                field: "bar".to_owned(),
            })),
            field: "baz".to_owned(),
        },
        parse_bracket_index("foo[1]") => IndexExpression {
            prefix: Prefix::Identifier("foo".to_owned()),
            index: NumberExpression { value: "1".to_owned() }.into(),
        },
        parse_function_call("foo()") => FunctionCall {
            prefix: Box::new(Prefix::Identifier("foo".to_owned())),
            arguments: Arguments::Tuple(Vec::new()),
            method: None,
        },
        parse_function_call_with_index("foo.bar()") => FunctionCall {
            prefix: Box::new(Prefix::Field(Box::new(FieldExpression {
                prefix: Prefix::Identifier("foo".to_owned()),
                field: "bar".to_owned(),
            }))),
            arguments: Arguments::Tuple(Vec::new()),
            method: None,
        },
        parse_function_call_with_string_argument("foo''") => FunctionCall {
            prefix: Box::new(Prefix::Identifier("foo".to_owned())),
            arguments: Arguments::String(StringExpression::from("''".to_owned())),
            method: None,
        },
        parse_function_call_with_table_argument("foo{}") => FunctionCall {
            prefix: Box::new(Prefix::Identifier("foo".to_owned())),
            arguments: Arguments::Table(TableExpression::from(Vec::new())),
            method: None,
        },
        parse_method_call("foo:bar()") => FunctionCall {
            prefix: Box::new(Prefix::Identifier("foo".to_owned())),
            arguments: Arguments::Tuple(Vec::new()),
            method: Some("bar".to_owned()),
        },
        parse_method_call_with_index("foo.bar:baz()") => FunctionCall {
            prefix: Box::new(Prefix::Field(Box::new(FieldExpression {
                prefix: Prefix::Identifier("foo".to_owned()),
                field: "bar".to_owned(),
            }))),
            arguments: Arguments::Tuple(Vec::new()),
            method: Some("baz".to_owned()),
        },
        parse_function_call_with_single_argument("foo(true)") => FunctionCall {
            prefix: Box::new(Prefix::Identifier("foo".to_owned())),
            arguments: Arguments::Tuple(vec![Expression::True]),
            method: None,
        },
        parse_function_call_with_two_arguments("foo(true, false)") => FunctionCall {
            prefix: Box::new(Prefix::Identifier("foo".to_owned())),
            arguments: Arguments::Tuple(vec![
                Expression::True,
                Expression::False,
            ]),
            method: None,
        },
        parse_method_call_with_two_arguments("foo:bar(true, false)") => FunctionCall {
            prefix: Box::new(Prefix::Identifier("foo".to_owned())),
            arguments: Arguments::Tuple(vec![
                Expression::True,
                Expression::False,
            ]),
            method: Some("bar".to_owned()),
        },
        parse_parenthese_expression("(true)") => Expression::Parenthese(Box::new(Expression::True))
    );
}