use super::error::Error;
use super::error::SyntaxError;
use super::lex::Keyword;
use super::lex::Lexer;
use super::lex::Token;
use super::lex::TokenId::*;
use crate::alias::AliasSet;
use crate::parser::lex::is_blank;
use crate::syntax::HereDoc;
use crate::syntax::MaybeLiteral;
use std::rc::Rc;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Rec<T> {
AliasSubstituted,
Parsed(T),
}
impl<T> Rec<T> {
pub fn is_alias_substituted(&self) -> bool {
match self {
Rec::AliasSubstituted => true,
Rec::Parsed(_) => false,
}
}
pub fn unwrap(self) -> T {
match self {
Rec::AliasSubstituted => panic!("Rec::AliasSubstituted cannot be unwrapped"),
Rec::Parsed(v) => v,
}
}
pub fn map<U, F>(self, f: F) -> Result<Rec<U>>
where
F: FnOnce(T) -> Result<U>,
{
match self {
Rec::AliasSubstituted => Ok(Rec::AliasSubstituted),
Rec::Parsed(t) => Ok(Rec::Parsed(f(t)?)),
}
}
}
#[derive(Debug)]
pub struct Parser<'a, 'b> {
lexer: &'a mut Lexer<'b>,
aliases: &'a AliasSet,
token: Option<Result<Token>>,
unread_here_docs: Vec<Rc<HereDoc>>,
}
impl<'a, 'b> Parser<'a, 'b> {
pub fn new(lexer: &'a mut Lexer<'b>, aliases: &'a AliasSet) -> Parser<'a, 'b> {
Parser {
lexer,
aliases,
token: None,
unread_here_docs: vec![],
}
}
async fn require_token(&mut self) {
#[allow(clippy::question_mark)] if self.token.is_none() {
self.token = Some(if let Err(e) = self.lexer.skip_blanks_and_comment().await {
Err(e)
} else {
self.lexer.token().await
});
}
}
pub async fn peek_token(&mut self) -> Result<&Token> {
self.require_token().await;
self.token.as_ref().unwrap().as_ref().map_err(|e| e.clone())
}
pub async fn take_token_raw(&mut self) -> Result<Token> {
self.require_token().await;
self.token.take().unwrap()
}
fn substitute_alias(&mut self, token: Token, is_command_name: bool) -> Rec<Token> {
if !self.aliases.is_empty() {
if let Token(_) = token.id {
if let Some(name) = token.word.to_string_if_literal() {
if !token.word.location.code.source.is_alias_for(&name) {
if let Some(alias) = self.aliases.get(&name as &str) {
if is_command_name
|| alias.0.global
|| self.lexer.is_after_blank_ending_alias(token.index)
{
self.lexer.substitute_alias(token.index, &alias.0);
return Rec::AliasSubstituted;
}
}
}
}
}
}
Rec::Parsed(token)
}
pub async fn take_token_manual(&mut self, is_command_name: bool) -> Result<Rec<Token>> {
let token = self.take_token_raw().await?;
Ok(self.substitute_alias(token, is_command_name))
}
pub async fn take_token_auto(&mut self, keywords: &[Keyword]) -> Result<Token> {
loop {
let token = self.take_token_raw().await?;
if let Token(Some(keyword)) = token.id {
if keywords.contains(&keyword) {
return Ok(token);
}
}
if let Rec::Parsed(token) = self.substitute_alias(token, false) {
return Ok(token);
}
}
}
pub async fn has_blank(&mut self) -> Result<bool> {
assert!(self.token.is_none(), "There should be no pending token");
let c = self.lexer.peek_char().await?;
Ok(c.map_or(false, is_blank))
}
pub fn memorize_unread_here_doc(&mut self, here_doc: Rc<HereDoc>) {
self.unread_here_docs.push(here_doc)
}
pub async fn here_doc_contents(&mut self) -> Result<()> {
assert!(
self.token.is_none(),
"No token must be peeked before reading here-doc contents"
);
for here_doc in self.unread_here_docs.drain(..) {
self.lexer.here_doc_content(&here_doc).await?;
}
Ok(())
}
pub fn ensure_no_unread_here_doc(&self) -> Result<()> {
match self.unread_here_docs.first() {
None => Ok(()),
Some(here_doc) => Err(Error {
cause: SyntaxError::MissingHereDocContent.into(),
location: here_doc.delimiter.location.clone(),
}),
}
}
}
#[allow(clippy::bool_assert_comparison)]
#[cfg(test)]
mod tests {
use super::*;
use crate::alias::AliasSet;
use crate::alias::HashEntry;
use crate::source::Location;
use crate::source::Source;
use crate::syntax::Text;
use futures_executor::block_on;
use std::cell::RefCell;
#[test]
fn parser_take_token_manual_successful_substitution() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
"x".to_string(),
false,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(true).await.unwrap();
assert!(
token.is_alias_substituted(),
"{:?} should be AliasSubstituted",
&token
);
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), "x");
});
}
#[test]
fn parser_take_token_manual_not_command_name() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
"x".to_string(),
false,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(false).await.unwrap().unwrap();
assert_eq!(token.to_string(), "X");
});
}
#[test]
fn parser_take_token_manual_not_literal() {
block_on(async {
let mut lexer = Lexer::from_memory(r"\X", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
"x".to_string(),
false,
Location::dummy("?"),
));
aliases.insert(HashEntry::new(
r"\X".to_string(),
"quoted".to_string(),
false,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), r"\X");
});
}
#[test]
fn parser_take_token_manual_operator() {
block_on(async {
let mut lexer = Lexer::from_memory(";", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
";".to_string(),
"x".to_string(),
false,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.id, Operator(super::super::lex::Operator::Semicolon));
assert_eq!(token.word.to_string_if_literal().unwrap(), ";");
})
}
#[test]
fn parser_take_token_manual_no_match() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), "X");
});
}
#[test]
fn parser_take_token_manual_recursive_substitution() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
"Y x".to_string(),
false,
Location::dummy("?"),
));
aliases.insert(HashEntry::new(
"Y".to_string(),
"X y".to_string(),
false,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(true).await.unwrap();
assert!(
token.is_alias_substituted(),
"{:?} should be AliasSubstituted",
&token
);
let token = parser.take_token_manual(true).await.unwrap();
assert!(
token.is_alias_substituted(),
"{:?} should be AliasSubstituted",
&token
);
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), "X");
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), "y");
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), "x");
});
}
#[test]
fn parser_take_token_manual_after_blank_ending_substitution() {
block_on(async {
let mut lexer = Lexer::from_memory("X\tY", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
" X ".to_string(),
false,
Location::dummy("?"),
));
aliases.insert(HashEntry::new(
"Y".to_string(),
"y".to_string(),
false,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(true).await.unwrap();
assert!(
token.is_alias_substituted(),
"{:?} should be AliasSubstituted",
&token
);
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), "X");
let token = parser.take_token_manual(false).await.unwrap();
assert!(
token.is_alias_substituted(),
"{:?} should be AliasSubstituted",
&token
);
let token = parser.take_token_manual(false).await.unwrap().unwrap();
assert_eq!(token.to_string(), "y");
});
}
#[test]
fn parser_take_token_manual_not_after_blank_ending_substitution() {
block_on(async {
let mut lexer = Lexer::from_memory("X\tY", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
" X".to_string(),
false,
Location::dummy("?"),
));
aliases.insert(HashEntry::new(
"Y".to_string(),
"y".to_string(),
false,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(true).await.unwrap();
assert!(
token.is_alias_substituted(),
"{:?} should be AliasSubstituted",
&token
);
let token = parser.take_token_manual(true).await.unwrap().unwrap();
assert_eq!(token.to_string(), "X");
let token = parser.take_token_manual(false).await.unwrap().unwrap();
assert_eq!(token.to_string(), "Y");
});
}
#[test]
fn parser_take_token_manual_global() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
"x".to_string(),
true,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_manual(false).await.unwrap();
assert!(
token.is_alias_substituted(),
"{:?} should be AliasSubstituted",
&token
);
let token = parser.take_token_manual(false).await.unwrap().unwrap();
assert_eq!(token.to_string(), "x");
});
}
#[test]
fn parser_take_token_auto_non_keyword() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
"x".to_string(),
true,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_auto(&[]).await.unwrap();
assert_eq!(token.to_string(), "x");
})
}
#[test]
fn parser_take_token_auto_keyword_matched() {
block_on(async {
let mut lexer = Lexer::from_memory("if", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"if".to_string(),
"x".to_string(),
true,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_auto(&[Keyword::If]).await.unwrap();
assert_eq!(token.to_string(), "if");
})
}
#[test]
fn parser_take_token_auto_keyword_unmatched() {
block_on(async {
let mut lexer = Lexer::from_memory("if", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"if".to_string(),
"x".to_string(),
true,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_auto(&[]).await.unwrap();
assert_eq!(token.to_string(), "x");
})
}
#[test]
fn parser_take_token_auto_alias_substitution_to_keyword_matched() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let mut aliases = AliasSet::new();
aliases.insert(HashEntry::new(
"X".to_string(),
"if".to_string(),
true,
Location::dummy("?"),
));
aliases.insert(HashEntry::new(
"if".to_string(),
"x".to_string(),
true,
Location::dummy("?"),
));
let mut parser = Parser::new(&mut lexer, &aliases);
let token = parser.take_token_auto(&[Keyword::If]).await.unwrap();
assert_eq!(token.to_string(), "if");
})
}
#[test]
fn parser_has_blank_true() {
block_on(async {
let mut lexer = Lexer::from_memory(" ", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.has_blank().await;
assert_eq!(result, Ok(true));
});
}
#[test]
fn parser_has_blank_false() {
block_on(async {
let mut lexer = Lexer::from_memory("(", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.has_blank().await;
assert_eq!(result, Ok(false));
});
}
#[test]
fn parser_has_blank_eof() {
block_on(async {
let mut lexer = Lexer::from_memory("", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.has_blank().await;
assert_eq!(result, Ok(false));
});
}
#[test]
fn parser_has_blank_true_with_line_continuations() {
block_on(async {
let mut lexer = Lexer::from_memory("\\\n\\\n ", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.has_blank().await;
assert_eq!(result, Ok(true));
});
}
#[test]
fn parser_has_blank_false_with_line_continuations() {
block_on(async {
let mut lexer = Lexer::from_memory("\\\n\\\n\\\n(", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let result = parser.has_blank().await;
assert_eq!(result, Ok(false));
});
}
#[test]
#[should_panic(expected = "There should be no pending token")]
fn parser_has_blank_with_pending_token() {
block_on(async {
let mut lexer = Lexer::from_memory("foo", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
parser.peek_token().await.unwrap();
let _ = parser.has_blank().await;
});
}
#[test]
fn parser_reading_no_here_doc_contents() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
parser.here_doc_contents().await.unwrap();
let location = lexer.location().await.unwrap();
assert_eq!(location.code.start_line_number.get(), 1);
assert_eq!(location.range, 0..1);
})
}
#[test]
fn parser_reading_one_here_doc_content() {
let delimiter = "END".parse().unwrap();
block_on(async {
let mut lexer = Lexer::from_memory("END\nX", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let remove_tabs = false;
let here_doc = Rc::new(HereDoc {
delimiter,
remove_tabs,
content: RefCell::new(Text(Vec::new())),
});
parser.memorize_unread_here_doc(Rc::clone(&here_doc));
parser.here_doc_contents().await.unwrap();
assert_eq!(here_doc.delimiter.to_string(), "END");
assert_eq!(here_doc.remove_tabs, remove_tabs);
assert_eq!(here_doc.content.borrow().0, []);
let location = lexer.location().await.unwrap();
assert_eq!(location.code.start_line_number.get(), 1);
assert_eq!(location.range, 4..5);
})
}
#[test]
fn parser_reading_many_here_doc_contents() {
let delimiter1 = "ONE".parse().unwrap();
let delimiter2 = "TWO".parse().unwrap();
let delimiter3 = "THREE".parse().unwrap();
block_on(async {
let mut lexer = Lexer::from_memory("1\nONE\nTWO\n3\nTHREE\nX", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let here_doc1 = Rc::new(HereDoc {
delimiter: delimiter1,
remove_tabs: false,
content: RefCell::new(Text(Vec::new())),
});
parser.memorize_unread_here_doc(Rc::clone(&here_doc1));
let here_doc2 = Rc::new(HereDoc {
delimiter: delimiter2,
remove_tabs: true,
content: RefCell::new(Text(Vec::new())),
});
parser.memorize_unread_here_doc(Rc::clone(&here_doc2));
let here_doc3 = Rc::new(HereDoc {
delimiter: delimiter3,
remove_tabs: false,
content: RefCell::new(Text(Vec::new())),
});
parser.memorize_unread_here_doc(Rc::clone(&here_doc3));
parser.here_doc_contents().await.unwrap();
assert_eq!(here_doc1.delimiter.to_string(), "ONE");
assert_eq!(here_doc1.remove_tabs, false);
assert_eq!(here_doc1.content.borrow().to_string(), "1\n");
assert_eq!(here_doc2.delimiter.to_string(), "TWO");
assert_eq!(here_doc2.remove_tabs, true);
assert_eq!(here_doc2.content.borrow().to_string(), "");
assert_eq!(here_doc3.delimiter.to_string(), "THREE");
assert_eq!(here_doc3.remove_tabs, false);
assert_eq!(here_doc3.content.borrow().to_string(), "3\n");
})
}
#[test]
fn parser_reading_here_doc_contents_twice() {
let delimiter1 = "ONE".parse().unwrap();
let delimiter2 = "TWO".parse().unwrap();
block_on(async {
let mut lexer = Lexer::from_memory("1\nONE\n2\nTWO\n", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
let here_doc1 = Rc::new(HereDoc {
delimiter: delimiter1,
remove_tabs: false,
content: RefCell::new(Text(Vec::new())),
});
parser.memorize_unread_here_doc(Rc::clone(&here_doc1));
parser.here_doc_contents().await.unwrap();
let here_doc2 = Rc::new(HereDoc {
delimiter: delimiter2,
remove_tabs: true,
content: RefCell::new(Text(Vec::new())),
});
parser.memorize_unread_here_doc(Rc::clone(&here_doc2));
parser.here_doc_contents().await.unwrap();
assert_eq!(here_doc1.delimiter.to_string(), "ONE");
assert_eq!(here_doc1.remove_tabs, false);
assert_eq!(here_doc1.content.borrow().to_string(), "1\n");
assert_eq!(here_doc2.delimiter.to_string(), "TWO");
assert_eq!(here_doc2.remove_tabs, true);
assert_eq!(here_doc2.content.borrow().to_string(), "2\n");
})
}
#[test]
#[should_panic(expected = "No token must be peeked before reading here-doc contents")]
fn parser_here_doc_contents_must_be_called_without_pending_token() {
block_on(async {
let mut lexer = Lexer::from_memory("X", Source::Unknown);
let aliases = AliasSet::new();
let mut parser = Parser::new(&mut lexer, &aliases);
parser.peek_token().await.unwrap();
parser.here_doc_contents().await.unwrap();
})
}
}