use super::core::Parser;
use super::core::Result;
use super::error::Error;
use super::error::SyntaxError;
use super::lex::Keyword::{Elif, Else, Fi, If, Then};
use super::lex::TokenId::Token;
use crate::syntax::CompoundCommand;
use crate::syntax::ElifThen;
impl Parser<'_, '_> {
async fn elif_then_clause(&mut self) -> Result<Option<ElifThen>> {
if self.peek_token().await?.id != Token(Some(Elif)) {
return Ok(None);
}
let elif = self.take_token_raw().await?;
let condition = self.maybe_compound_list_boxed().await?;
let then = self.take_token_raw().await?;
if condition.0.is_empty() {
let cause = SyntaxError::EmptyElifCondition.into();
let location = then.word.location;
return Err(Error { cause, location });
}
if then.id != Token(Some(Then)) {
let elif_location = elif.word.location;
let cause = SyntaxError::ElifMissingThen { elif_location }.into();
let location = then.word.location;
return Err(Error { cause, location });
}
let body = self.maybe_compound_list_boxed().await?;
if body.0.is_empty() {
let cause = SyntaxError::EmptyElifBody.into();
let location = self.take_token_raw().await?.word.location;
return Err(Error { cause, location });
}
Ok(Some(ElifThen { condition, body }))
}
pub async fn if_command(&mut self) -> Result<CompoundCommand> {
let open = self.take_token_raw().await?;
assert_eq!(open.id, Token(Some(If)));
let condition = self.maybe_compound_list_boxed().await?;
let then = self.take_token_raw().await?;
if condition.0.is_empty() {
let cause = SyntaxError::EmptyIfCondition.into();
let location = then.word.location;
return Err(Error { cause, location });
}
if then.id != Token(Some(Then)) {
let if_location = open.word.location;
let cause = SyntaxError::IfMissingThen { if_location }.into();
let location = then.word.location;
return Err(Error { cause, location });
}
let body = self.maybe_compound_list_boxed().await?;
if body.0.is_empty() {
let cause = SyntaxError::EmptyIfBody.into();
let location = self.take_token_raw().await?.word.location;
return Err(Error { cause, location });
}
let mut elifs = Vec::new();
while let Some(elif) = self.elif_then_clause().await? {
elifs.push(elif);
}
let r#else = if self.peek_token().await?.id == Token(Some(Else)) {
self.take_token_raw().await?;
let content = self.maybe_compound_list_boxed().await?;
if content.0.is_empty() {
let cause = SyntaxError::EmptyElse.into();
let location = self.take_token_raw().await?.word.location;
return Err(Error { cause, location });
}
Some(content)
} else {
None
};
let fi = self.take_token_raw().await?;
if fi.id != Token(Some(Fi)) {
let opening_location = open.word.location;
let cause = SyntaxError::UnclosedIf { opening_location }.into();
let location = fi.word.location;
return Err(Error { cause, location });
}
Ok(CompoundCommand::If {
condition,
body,
elifs,
r#else,
})
}
}
#[cfg(test)]
mod tests {
use super::super::error::ErrorCause;
use super::super::lex::Lexer;
use super::super::lex::TokenId::EndOfInput;
use super::*;
use crate::source::Source;
use assert_matches::assert_matches;
use futures_util::FutureExt;
#[test]
fn parser_if_command_minimum() {
let mut lexer = Lexer::from_memory("if a; then b; fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::If { condition, body, elifs, r#else } => {
assert_eq!(condition.to_string(), "a");
assert_eq!(body.to_string(), "b");
assert_eq!(elifs, []);
assert_eq!(r#else, None);
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_if_command_one_elif() {
let mut lexer = Lexer::from_memory(
"if\ntrue\nthen\nfalse\n\nelif x; then y& fi",
Source::Unknown,
);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::If { condition, body, elifs, r#else } => {
assert_eq!(condition.to_string(), "true");
assert_eq!(body.to_string(), "false");
assert_eq!(elifs.len(), 1);
assert_eq!(elifs[0].to_string(), "elif x; then y&");
assert_eq!(r#else, None);
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_if_command_many_elifs() {
let mut lexer = Lexer::from_memory(
"if a; then b; elif c; then d; elif e 1; e 2& then f 1; f 2& elif g; then h; fi",
Source::Unknown,
);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::If { condition, body, elifs, r#else } => {
assert_eq!(condition.to_string(), "a");
assert_eq!(body.to_string(), "b");
assert_eq!(elifs.len(), 3);
assert_eq!(elifs[0].to_string(), "elif c; then d");
assert_eq!(elifs[1].to_string(), "elif e 1; e 2& then f 1; f 2&");
assert_eq!(elifs[2].to_string(), "elif g; then h");
assert_eq!(r#else, None);
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_if_command_else() {
let mut lexer = Lexer::from_memory("if a; then b; else c; d; fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::If { condition, body, elifs, r#else } => {
assert_eq!(condition.to_string(), "a");
assert_eq!(body.to_string(), "b");
assert_eq!(elifs, []);
assert_eq!(r#else.unwrap().to_string(), "c; d");
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_if_command_elif_and_else() {
let mut lexer =
Lexer::from_memory("if 1; then 2; elif 3; then 4; else 5; fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::If { condition, body, elifs, r#else } => {
assert_eq!(condition.to_string(), "1");
assert_eq!(body.to_string(), "2");
assert_eq!(elifs.len(), 1);
assert_eq!(elifs[0].to_string(), "elif 3; then 4");
assert_eq!(r#else.unwrap().to_string(), "5");
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_if_command_without_then_after_if() {
let mut lexer = Lexer::from_memory(" if :; fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_matches!(e.cause, ErrorCause::Syntax(SyntaxError::IfMissingThen { if_location }) => {
assert_eq!(*if_location.code.value.borrow(), " if :; fi");
assert_eq!(if_location.code.start_line_number.get(), 1);
assert_eq!(if_location.code.source, Source::Unknown);
assert_eq!(if_location.range, 1..3);
});
assert_eq!(*e.location.code.value.borrow(), " if :; fi");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 7..9);
}
#[test]
fn parser_if_command_without_then_after_elif() {
let mut lexer = Lexer::from_memory("if a; then b; elif c; fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_matches!(e.cause,
ErrorCause::Syntax(SyntaxError::ElifMissingThen { elif_location }) => {
assert_eq!(*elif_location.code.value.borrow(), "if a; then b; elif c; fi");
assert_eq!(elif_location.code.start_line_number.get(), 1);
assert_eq!(elif_location.code.source, Source::Unknown);
assert_eq!(elif_location.range, 14..18);
});
assert_eq!(*e.location.code.value.borrow(), "if a; then b; elif c; fi");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 22..24);
}
#[test]
fn parser_if_command_without_fi() {
let mut lexer = Lexer::from_memory(" if :; then :; }", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_matches!(e.cause,
ErrorCause::Syntax(SyntaxError::UnclosedIf { opening_location }) => {
assert_eq!(*opening_location.code.value.borrow(), " if :; then :; }");
assert_eq!(opening_location.code.start_line_number.get(), 1);
assert_eq!(opening_location.code.source, Source::Unknown);
assert_eq!(opening_location.range, 2..4);
});
assert_eq!(*e.location.code.value.borrow(), " if :; then :; }");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 16..17);
}
#[test]
fn parser_if_command_empty_condition() {
let mut lexer = Lexer::from_memory(" if then :; fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyIfCondition));
assert_eq!(*e.location.code.value.borrow(), " if then :; fi");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 6..10);
}
#[test]
fn parser_if_command_empty_body() {
let mut lexer = Lexer::from_memory("if :; then fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyIfBody));
assert_eq!(*e.location.code.value.borrow(), "if :; then fi");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 11..13);
}
#[test]
fn parser_if_command_empty_elif_condition() {
let mut lexer = Lexer::from_memory("if :; then :; elif then :; fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyElifCondition));
assert_eq!(
*e.location.code.value.borrow(),
"if :; then :; elif then :; fi"
);
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 19..23);
}
#[test]
fn parser_if_command_empty_elif_body() {
let mut lexer = Lexer::from_memory("if :; then :; elif :; then fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyElifBody));
assert_eq!(
*e.location.code.value.borrow(),
"if :; then :; elif :; then fi"
);
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 27..29);
}
#[test]
fn parser_if_command_empty_else() {
let mut lexer = Lexer::from_memory("if :; then :; else fi", Source::Unknown);
let aliases = Default::default();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::EmptyElse));
assert_eq!(*e.location.code.value.borrow(), "if :; then :; else fi");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 19..21);
}
}