mod parse_do;
mod parse_for;
mod parse_if;
mod parse_local;
mod parse_repeat;
mod parse_while;
pub use parse_do::*;
pub use parse_for::*;
pub use parse_if::*;
pub use parse_local::*;
pub use parse_repeat::*;
pub use parse_while::*;
use crate::parser::{
parse_expression_list,
try_parse_function_statement,
try_parse_prefix_expression,
OptionParsingResult,
ParsingError,
ParsingResult,
};
use crate::parser::token_utils::{
is_keyword,
is_symbol,
skip_first_token,
LuaKeyword,
LuaSymbol,
};
use crate::parser::node_types::NodeTypes;
use crate::parser::node_types::builders::{
LastStatement,
Variable,
};
use lualexer::Token;
use std::convert::TryInto;
fn try_parse_variable<'a, T: NodeTypes>(
tokens: &'a [Token<'a>]
) -> OptionParsingResult<'a, T::Variable> {
if let Some((prefix, next_tokens)) = try_parse_prefix_expression::<T>(tokens)? {
match T::Variable::try_from_prefix(prefix) {
Ok(variable) => Ok(Some((variable, next_tokens))),
Err(_) => Ok(None),
}
} else {
Ok(None)
}
}
pub fn parse_statement<'a, T: NodeTypes>(tokens: &'a [Token<'a>]) -> ParsingResult<'a, T::Statement> {
if let Some(result) = try_parse_do_statement::<T>(tokens)? {
Ok(result)
} else if let Some(result) = try_parse_for_statement::<T>(tokens)? {
Ok(result)
} else if let Some(result) = try_parse_if_statement::<T>(tokens)? {
Ok(result)
} else if let Some(result) = try_parse_repeat_statement::<T>(tokens)? {
Ok(result)
} else if let Some(result) = try_parse_while_statement::<T>(tokens)? {
Ok(result)
} else if let Some(result) = try_parse_function_statement::<T>(tokens)? {
Ok(result)
} else if let Some(result) = try_parse_local_assign_or_function::<T>(tokens)? {
Ok(result)
} else {
let (prefix, mut next_tokens) = try_parse_prefix_expression::<T>(tokens)?
.ok_or(ParsingError::UnexpectedTokenForStatement)?;
match T::Variable::try_from_prefix(prefix) {
Ok(variable) => {
let mut variables = vec![variable];
while let Some(after_comma_tokens) = next_tokens.first()
.filter(|token| is_symbol(token, LuaSymbol::Comma))
.map(|_| skip_first_token(next_tokens))
{
let (variable, after_tokens) = try_parse_variable::<T>(after_comma_tokens)?
.ok_or(ParsingError::VariableExpectedForAssignStatement)?;
variables.push(variable);
next_tokens = after_tokens;
}
next_tokens = next_tokens.first()
.filter(|token| is_symbol(token, LuaSymbol::Assign))
.map(|_| skip_first_token(next_tokens))
.ok_or(ParsingError::AssignSymbolExpectedForAssignStatement)?;
let (expressions, next_tokens) = parse_expression_list::<T>(next_tokens)?;
if expressions.len() == 0 {
return Err(ParsingError::ExpressionExpectedForAssignStatement)
};
let assign = T::AssignStatement::from((variables, expressions));
Ok((assign.into(), next_tokens))
}
Err(prefix) => if let Some(call) = T::Prefix::try_into(prefix).ok() {
Ok((call.into(), next_tokens))
} else {
Err(ParsingError::UnexpectedTokenForStatement)
}
}
}
}
pub fn try_parse_last_statement<'a, T: NodeTypes>(
tokens: &'a [Token<'a>]
) -> OptionParsingResult<'a, T::LastStatement> {
if let Some(token) = tokens.first() {
if is_keyword(token, LuaKeyword::Break) {
Ok(Some((T::LastStatement::break_statement(), skip_first_token(tokens))))
} else if is_keyword(token, LuaKeyword::Return) {
let (expressions, next_tokens) = parse_expression_list::<T>(skip_first_token(tokens))?;
Ok(Some((T::LastStatement::return_statement(expressions), next_tokens)))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
#[cfg(test)]
mod test {
use super::{parse_statement, try_parse_last_statement};
use crate::nodes::*;
use crate::parser_impl::FastParserNodes;
use lualexer::FastLexer;
macro_rules! test_statements {
($($name:ident($input:literal) => $expect:expr),+) => {
$(
#[test]
fn $name() {
let tokens = FastLexer::new()
.parse($input)
.unwrap();
let (statement, tokens) = parse_statement::<FastParserNodes>(&tokens).unwrap();
let expect: Statement = $expect.into();
assert_eq!(statement, expect);
assert_eq!(tokens, &[]);
}
)+
};
}
test_statements!(
parse_empty_do("do end") => DoStatement::from(Block::default()),
parse_nested_do("do do end end") => DoStatement {
block: Block::from((
vec![DoStatement::from(Block::default()).into()],
None
))
},
parse_empty_if("if true then end") => IfStatement {
branches: vec![
IfBranch::from((Expression::True, Block::default())),
],
else_block: None,
},
parse_empty_if_with_elseif("if true then elseif false then end") => IfStatement {
branches: vec![
IfBranch::from((Expression::True, Block::default())),
IfBranch::from((Expression::False, Block::default())),
],
else_block: None,
},
parse_empty_if_with_else("if true then else end") => IfStatement {
branches: vec![
IfBranch::from((Expression::True, Block::default())),
],
else_block: Some(Block::default()),
},
parse_empty_if_with_elseif_and_else("if true then elseif false then else end") => IfStatement {
branches: vec![
IfBranch::from((Expression::True, Block::default())),
IfBranch::from((Expression::False, Block::default())),
],
else_block: Some(Block::default()),
},
parse_empty_numeric_for("for i=1,2 do end") => NumericForStatement {
identifier: "i".to_owned(),
start: NumberExpression::from("1".to_owned()).into(),
end: NumberExpression::from("2".to_owned()).into(),
step: None,
block: Block::default(),
},
parse_empty_numeric_for_with_step("for i=1,4,2 do end") => NumericForStatement {
identifier: "i".to_owned(),
start: NumberExpression::from("1".to_owned()).into(),
end: NumberExpression::from("4".to_owned()).into(),
step: Some(NumberExpression::from("2".to_owned()).into()),
block: Block::default(),
},
parse_empty_generic_for("for k in t do end") => GenericForStatement {
identifiers: vec!["k".to_owned()],
expressions: vec![Expression::Identifier("t".to_owned())],
block: Block::default(),
},
parse_empty_generic_for_with_two_identifiers_and_values("for k, v in t, next do end") => GenericForStatement {
identifiers: vec!["k".to_owned(), "v".to_owned()],
expressions: vec![
Expression::Identifier("t".to_owned()),
Expression::Identifier("next".to_owned()),
],
block: Block::default(),
},
parse_empty_local_function("local function foo() end") => LocalFunctionStatement {
identifier: "foo".to_owned(),
parameters: Vec::new(),
is_variadic: false,
block: Block::default(),
},
parse_empty_local_function_with_one_param("local function foo(a) end") => LocalFunctionStatement {
identifier: "foo".to_owned(),
parameters: vec!["a".to_owned()],
is_variadic: false,
block: Block::default(),
},
parse_empty_local_function_with_two_params("local function foo(a, b) end") => LocalFunctionStatement {
identifier: "foo".to_owned(),
parameters: vec!["a".to_owned(), "b".to_owned()],
is_variadic: false,
block: Block::default(),
},
parse_empty_variadic_local_function("local function foo(...) end") => LocalFunctionStatement {
identifier: "foo".to_owned(),
parameters: Vec::new(),
is_variadic: true,
block: Block::default(),
},
parse_empty_variadic_local_function_with_one_param("local function foo(a, ...) end") => LocalFunctionStatement {
identifier: "foo".to_owned(),
parameters: vec!["a".to_owned()],
is_variadic: true,
block: Block::default(),
},
parse_single_local_variable("local foo") => LocalAssignStatement {
variables: vec!["foo".to_owned()],
values: Vec::new(),
},
parse_single_local_variable_with_one_value("local foo=true") => LocalAssignStatement {
variables: vec!["foo".to_owned()],
values: vec![Expression::True],
},
parse_single_local_variable_with_two_values("local foo=true, false") => LocalAssignStatement {
variables: vec!["foo".to_owned()],
values: vec![Expression::True, Expression::False],
},
parse_two_local_variable("local foo,bar") => LocalAssignStatement {
variables: vec!["foo".to_owned(), "bar".to_owned()],
values: Vec::new(),
},
parse_two_local_variable_with_two_values("local foo,bar=true, false") => LocalAssignStatement {
variables: vec!["foo".to_owned(), "bar".to_owned()],
values: vec![Expression::True, Expression::False],
},
parse_empty_global_function("function foo() end") => FunctionStatement {
name: "foo".to_owned(),
field_names: Vec::new(),
method: None,
block: Block::default(),
parameters: Vec::new(),
is_variadic: false,
},
parse_empty_global_function_with_field("function foo.bar() end") => FunctionStatement {
name: "foo".to_owned(),
field_names: vec!["bar".to_owned()],
method: None,
block: Block::default(),
parameters: Vec::new(),
is_variadic: false,
},
parse_empty_global_function_with_method("function foo:bar() end") => FunctionStatement {
name: "foo".to_owned(),
field_names: Vec::new(),
method: Some("bar".to_owned()),
block: Block::default(),
parameters: Vec::new(),
is_variadic: false,
},
parse_empty_global_function_with_field_and_method("function foo.bar:baz() end") => FunctionStatement {
name: "foo".to_owned(),
field_names: vec!["bar".to_owned()],
method: Some("baz".to_owned()),
block: Block::default(),
parameters: Vec::new(),
is_variadic: false,
},
parse_empty_global_function_with_one_param("function foo(bar) end") => FunctionStatement {
name: "foo".to_owned(),
field_names: Vec::new(),
method: None,
block: Block::default(),
parameters: vec!["bar".to_owned()],
is_variadic: false,
},
parse_empty_variadic_global_function("function foo(...) end") => FunctionStatement {
name: "foo".to_owned(),
field_names: Vec::new(),
method: None,
block: Block::default(),
parameters: Vec::new(),
is_variadic: true,
},
parse_empty_variadic_global_function_with_one_param("function foo(bar,...) end") => FunctionStatement {
name: "foo".to_owned(),
field_names: Vec::new(),
method: None,
block: Block::default(),
parameters: vec!["bar".to_owned()],
is_variadic: true,
},
parse_empty_repeat("repeat until true") => RepeatStatement::from(
(Expression::True, Block::default())
),
parse_empty_while("while true do end") => WhileStatement::from(
(Expression::True, Block::default())
),
parse_assign("foo=true") => AssignStatement {
variables: vec![Variable::Identifier("foo".to_owned())],
values: vec![Expression::True],
},
parse_assign_field_variable("foo.bar=true") => AssignStatement {
variables: vec![Variable::Field(Box::new(FieldExpression {
prefix: Prefix::Identifier("foo".to_owned()),
field: "bar".to_owned(),
}))],
values: vec![Expression::True],
},
parse_assign_index_variable("foo[false]=true") => AssignStatement {
variables: vec![Variable::Index(Box::new(IndexExpression {
prefix: Prefix::Identifier("foo".to_owned()),
index: Expression::False,
}))],
values: vec![Expression::True],
},
parse_assign_with_two_values("foo=true,false") => AssignStatement {
variables: vec![Variable::Identifier("foo".to_owned())],
values: vec![Expression::True, Expression::False],
},
parse_assign_two_variables("foo, bar=true") => AssignStatement {
variables: vec![
Variable::Identifier("foo".to_owned()),
Variable::Identifier("bar".to_owned()),
],
values: vec![Expression::True],
},
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()),
}
);
macro_rules! test_last_statements {
($($name:ident($input:literal) => $expect:expr),+) => {
$(
#[test]
fn $name() {
let tokens = FastLexer::new()
.parse($input)
.unwrap();
let (statement, tokens) = try_parse_last_statement::<FastParserNodes>(&tokens)
.unwrap()
.unwrap();
assert_eq!(statement, $expect.into());
assert_eq!(tokens, &[]);
}
)+
};
}
test_last_statements!(
parse_break("break") => LastStatement::Break,
parse_return("return") => LastStatement::Return(Vec::new()),
parse_return_one_value("return true") => LastStatement::Return(vec![
Expression::True,
]),
parse_return_two_values("return true, false") => LastStatement::Return(vec![
Expression::True,
Expression::False,
])
);
}