extern crate conch_parser; use conch_parser::ast::*; use conch_parser::ast::ComplexWord::*; use conch_parser::ast::SimpleWord::*; use conch_parser::parse::ParseError::*; use conch_parser::token::Token; mod parse_support; use parse_support::*; #[test] fn test_word_single_quote_valid() { let correct = single_quoted("abc&&||\n\n#comment\nabc"); assert_eq!(Some(correct), make_parser("'abc&&||\n\n#comment\nabc'").word().unwrap()); } #[test] fn test_word_single_quote_valid_slash_remains_literal() { let correct = single_quoted("\\\n"); assert_eq!(Some(correct), make_parser("'\\\n'").word().unwrap()); } #[test] fn test_word_single_quote_valid_does_not_quote_single_quotes() { let correct = single_quoted("hello \\"); assert_eq!(Some(correct), make_parser("'hello \\'").word().unwrap()); } #[test] fn test_word_single_quote_invalid_missing_close_quote() { assert_eq!(Err(Unmatched(Token::SingleQuote, src(0, 1, 1))), make_parser("'hello").word()); } #[test] fn test_word_double_quote_valid() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!(Literal(String::from("abc&&||\n\n#comment\nabc")))))); assert_eq!(Some(correct), make_parser("\"abc&&||\n\n#comment\nabc\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_recognizes_parameters() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("test asdf")), Param(Parameter::Var(String::from("foo"))), Literal(String::from(" $")), )))); assert_eq!(Some(correct), make_parser("\"test asdf$foo $\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_recognizes_backticks() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("test asdf ")), Subst(Box::new(ParameterSubstitution::Command(vec!(cmd("foo"))))), )))); assert_eq!(Some(correct), make_parser("\"test asdf `foo`\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_slash_escapes_dollar() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("test")), Escaped(String::from("$")), Literal(String::from("foo ")), Param(Parameter::At), )))); assert_eq!(Some(correct), make_parser("\"test\\$foo $@\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_slash_escapes_backtick() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("test")), Escaped(String::from("`")), Literal(String::from(" ")), Param(Parameter::Star), )))); assert_eq!(Some(correct), make_parser("\"test\\` $*\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_slash_escapes_double_quote() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("test")), Escaped(String::from("\"")), Literal(String::from(" ")), Param(Parameter::Pound), )))); assert_eq!(Some(correct), make_parser("\"test\\\" $#\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_slash_escapes_newline() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("test")), Escaped(String::from("\n")), Literal(String::from(" ")), Param(Parameter::Question), Literal(String::from("\n")), )))); assert_eq!(Some(correct), make_parser("\"test\\\n $?\n\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_slash_escapes_slash() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("test")), Escaped(String::from("\\")), Literal(String::from(" ")), Param(Parameter::Positional(0)), )))); assert_eq!(Some(correct), make_parser("\"test\\\\ $0\"").word().unwrap()); } #[test] fn test_word_double_quote_valid_slash_remains_literal_in_general_case() { let correct = TopLevelWord(Single(Word::DoubleQuoted(vec!( Literal(String::from("t\\est ")), Param(Parameter::Dollar), )))); assert_eq!(Some(correct), make_parser("\"t\\est $$\"").word().unwrap()); } #[test] fn test_word_double_quote_slash_invalid_missing_close_quote() { assert_eq!(Err(Unmatched(Token::DoubleQuote, src(0, 1, 1))), make_parser("\"hello").word()); assert_eq!(Err(Unmatched(Token::DoubleQuote, src(0, 1, 1))), make_parser("\"hello\\\"").word()); } #[test] fn test_word_delegate_parameters() { let params = [ "$@", "$*", "$#", "$?", "$-", "$$", "$!", "$3", "${@}", "${*}", "${#}", "${?}", "${-}", "${$}", "${!}", "${foo}", "${3}", "${1000}", ]; for p in params.into_iter() { match make_parser(p).word() { Ok(Some(TopLevelWord(Single(Word::Simple(w))))) => if let Param(_) = w {} else { panic!("Unexpectedly parsed \"{}\" as a non-parameter word:\n{:#?}", p, w); }, Ok(Some(w)) => panic!("Unexpectedly parsed \"{}\" as a non-parameter word:\n{:#?}", p, w), Ok(None) => panic!("Did not parse \"{}\" as a parameter", p), Err(e) => panic!("Did not parse \"{}\" as a parameter: {}", p, e), } } } #[test] fn test_word_literal_dollar_if_not_param() { let correct = word("$%asdf"); assert_eq!(correct, make_parser("$%asdf").word().unwrap().unwrap()); } #[test] fn test_word_does_not_capture_comments() { assert_eq!(Ok(None), make_parser("#comment\n").word()); assert_eq!(Ok(None), make_parser(" #comment\n").word()); let mut p = make_parser("word #comment\n"); p.word().unwrap().unwrap(); assert_eq!(Ok(None), p.word()); } #[test] fn test_word_pound_in_middle_is_not_comment() { let correct = word("abc#def"); assert_eq!(Ok(Some(correct)), make_parser("abc#def\n").word()); } #[test] fn test_word_tokens_which_become_literal_words() { let words = [ "{", "}", "!", "name", "1notname", ]; for w in words.into_iter() { match make_parser(w).word() { Ok(Some(res)) => { let correct = word(*w); if correct != res { panic!("Unexpectedly parsed \"{}\": expected:\n{:#?}\ngot:\n{:#?}", w, correct, res); } }, Ok(None) => panic!("Did not parse \"{}\" as a word", w), Err(e) => panic!("Did not parse \"{}\" as a word: {}", w, e), } } } #[test] fn test_word_concatenation_works() { let correct = TopLevelWord(Concat(vec!( lit("foo=bar"), Word::DoubleQuoted(vec!(Literal(String::from("double")))), Word::SingleQuoted(String::from("single")), ))); assert_eq!(Ok(Some(correct)), make_parser("foo=bar\"double\"'single'").word()); } #[test] fn test_word_special_words_recognized_as_such() { assert_eq!(Ok(Some(TopLevelWord(Single(Word::Simple(Star))))), make_parser("*").word()); assert_eq!(Ok(Some(TopLevelWord(Single(Word::Simple(Question))))), make_parser("?").word()); assert_eq!(Ok(Some(TopLevelWord(Single(Word::Simple(Tilde))))), make_parser("~").word()); assert_eq!(Ok(Some(TopLevelWord(Single(Word::Simple(SquareOpen))))), make_parser("[").word()); assert_eq!(Ok(Some(TopLevelWord(Single(Word::Simple(SquareClose))))), make_parser("]").word()); assert_eq!(Ok(Some(TopLevelWord(Single(Word::Simple(Colon))))), make_parser(":").word()); } #[test] fn test_word_backslash_makes_things_literal() { let lit = [ "a", "&", ";", "(", "*", "?", "$", ]; for l in lit.into_iter() { let src = format!("\\{}", l); match make_parser(&src).word() { Ok(Some(res)) => { let correct = word_escaped(l); if correct != res { panic!("Unexpectedly parsed \"{}\": expected:\n{:#?}\ngot:\n{:#?}", src, correct, res); } }, Ok(None) => panic!("Did not parse \"{}\" as a word", src), Err(e) => panic!("Did not parse \"{}\" as a word: {}", src, e), } } } #[test] fn test_word_escaped_newline_becomes_whitespace() { let mut p = make_parser("foo\\\nbar"); assert_eq!(Ok(Some(word("foo"))), p.word()); assert_eq!(Ok(Some(word("bar"))), p.word()); }