use super::core::Parser;
use super::core::Result;
use super::error::Error;
use super::error::SyntaxError;
use super::lex::Keyword::{Until, While};
use super::lex::TokenId::Token;
use crate::syntax::CompoundCommand;
impl Parser<'_, '_> {
pub async fn while_loop(&mut self) -> Result<CompoundCommand> {
let open = self.take_token_raw().await?;
assert_eq!(open.id, Token(Some(While)));
let condition = self.maybe_compound_list_boxed().await?;
if condition.0.is_empty() {
let cause = SyntaxError::EmptyWhileCondition.into();
let location = self.take_token_raw().await?.word.location;
return Err(Error { cause, location });
}
let body = match self.do_clause().await? {
Some(body) => body,
None => {
let opening_location = open.word.location;
let cause = SyntaxError::UnclosedWhileClause { opening_location }.into();
let location = self.take_token_raw().await?.word.location;
return Err(Error { cause, location });
}
};
Ok(CompoundCommand::While { condition, body })
}
pub async fn until_loop(&mut self) -> Result<CompoundCommand> {
let open = self.take_token_raw().await?;
assert_eq!(open.id, Token(Some(Until)));
let condition = self.maybe_compound_list_boxed().await?;
if condition.0.is_empty() {
let cause = SyntaxError::EmptyUntilCondition.into();
let location = self.take_token_raw().await?.word.location;
return Err(Error { cause, location });
}
let body = match self.do_clause().await? {
Some(body) => body,
None => {
let opening_location = open.word.location;
let cause = SyntaxError::UnclosedUntilClause { opening_location }.into();
let location = self.take_token_raw().await?.word.location;
return Err(Error { cause, location });
}
};
Ok(CompoundCommand::Until { condition, body })
}
}
#[cfg(test)]
mod tests {
use super::super::error::ErrorCause;
use super::super::lex::Lexer;
use super::super::lex::TokenId::EndOfInput;
use super::*;
use crate::alias::{AliasSet, EmptyGlossary, HashEntry};
use crate::source::Location;
use crate::source::Source;
use assert_matches::assert_matches;
use futures_util::FutureExt;
#[test]
fn parser_while_loop_short() {
let mut lexer = Lexer::from_memory("while true; do :; done", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::While { condition, body } => {
assert_eq!(condition.to_string(), "true");
assert_eq!(body.to_string(), ":");
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_while_loop_long() {
let mut lexer = Lexer::from_memory("while false; true& do foo; bar& done", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::While { condition, body } => {
assert_eq!(condition.to_string(), "false; true&");
assert_eq!(body.to_string(), "foo; bar&");
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_while_loop_unclosed() {
let mut lexer = Lexer::from_memory("while :", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_matches!(e.cause,
ErrorCause::Syntax(SyntaxError::UnclosedWhileClause { opening_location }) => {
assert_eq!(*opening_location.code.value.borrow(), "while :");
assert_eq!(opening_location.code.start_line_number.get(), 1);
assert_eq!(*opening_location.code.source, Source::Unknown);
assert_eq!(opening_location.range, 0..5);
});
assert_eq!(*e.location.code.value.borrow(), "while :");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(*e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 7..7);
}
#[test]
fn parser_while_loop_empty_posix() {
let mut lexer = Lexer::from_memory(" while do :; done", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_eq!(
e.cause,
ErrorCause::Syntax(SyntaxError::EmptyWhileCondition)
);
assert_eq!(*e.location.code.value.borrow(), " while do :; done");
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_while_loop_aliasing() {
let mut lexer = Lexer::from_memory(" while :; DO :; done", Source::Unknown);
#[allow(clippy::mutable_key_type)]
let mut aliases = AliasSet::new();
let origin = Location::dummy("");
aliases.insert(HashEntry::new(
"DO".to_string(),
"do".to_string(),
false,
origin.clone(),
));
aliases.insert(HashEntry::new(
"while".to_string(),
";;".to_string(),
false,
origin,
));
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_eq!(compound_command.to_string(), "while :; do :; done");
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_until_loop_short() {
let mut lexer = Lexer::from_memory("until true; do :; done", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::Until { condition, body } => {
assert_eq!(condition.to_string(), "true");
assert_eq!(body.to_string(), ":");
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_until_loop_long() {
let mut lexer = Lexer::from_memory("until false; true& do foo; bar& done", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_matches!(compound_command, CompoundCommand::Until { condition, body } => {
assert_eq!(condition.to_string(), "false; true&");
assert_eq!(body.to_string(), "foo; bar&");
});
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
#[test]
fn parser_until_loop_unclosed() {
let mut lexer = Lexer::from_memory("until :", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_matches!(e.cause,
ErrorCause::Syntax(SyntaxError::UnclosedUntilClause { opening_location }) => {
assert_eq!(*opening_location.code.value.borrow(), "until :");
assert_eq!(opening_location.code.start_line_number.get(), 1);
assert_eq!(*opening_location.code.source, Source::Unknown);
assert_eq!(opening_location.range, 0..5);
});
assert_eq!(*e.location.code.value.borrow(), "until :");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(*e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 7..7);
}
#[test]
fn parser_until_loop_empty_posix() {
let mut lexer = Lexer::from_memory(" until do :; done", Source::Unknown);
let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
let result = parser.compound_command().now_or_never().unwrap();
let e = result.unwrap_err();
assert_eq!(
e.cause,
ErrorCause::Syntax(SyntaxError::EmptyUntilCondition)
);
assert_eq!(*e.location.code.value.borrow(), " until do :; done");
assert_eq!(e.location.code.start_line_number.get(), 1);
assert_eq!(*e.location.code.source, Source::Unknown);
assert_eq!(e.location.range, 8..10);
}
#[test]
fn parser_until_loop_aliasing() {
let mut lexer = Lexer::from_memory(" until :; DO :; done", Source::Unknown);
#[allow(clippy::mutable_key_type)]
let mut aliases = AliasSet::new();
let origin = Location::dummy("");
aliases.insert(HashEntry::new(
"DO".to_string(),
"do".to_string(),
false,
origin.clone(),
));
aliases.insert(HashEntry::new(
"until".to_string(),
";;".to_string(),
false,
origin,
));
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.compound_command().now_or_never().unwrap();
let compound_command = result.unwrap().unwrap();
assert_eq!(compound_command.to_string(), "until :; do :; done");
let next = parser.peek_token().now_or_never().unwrap().unwrap();
assert_eq!(next.id, EndOfInput);
}
}