extern crate conch_parser;
use std::rc::Rc;
use conch_parser::ast::*;
use conch_parser::ast::Command::*;
use conch_parser::ast::CompoundCommandKind::*;
use conch_parser::ast::PipeableCommand::*;
use conch_parser::token::Token;
mod parse_support;
use parse_support::*;
#[test]
fn test_complete_command_job() {
let mut p = make_parser("foo && bar & baz");
let cmd1 = p.complete_command().unwrap().expect("failed to parse first command");
let cmd2 = p.complete_command().unwrap().expect("failed to parse second command");
let correct1 = TopLevelCommand(Job(CommandList {
first: ListableCommand::Single(Simple(cmd_simple("foo"))),
rest: vec!(
AndOr::And(ListableCommand::Single(Simple(cmd_simple("bar"))))
),
}));
let correct2 = cmd("baz");
assert_eq!(correct1, cmd1);
assert_eq!(correct2, cmd2);
}
#[test]
fn test_complete_command_non_eager_parse() {
let mut p = make_parser("foo && bar; baz\n\nqux");
let cmd1 = p.complete_command().unwrap().expect("failed to parse first command");
let cmd2 = p.complete_command().unwrap().expect("failed to parse second command");
let cmd3 = p.complete_command().unwrap().expect("failed to parse third command");
let correct1 = TopLevelCommand(List(CommandList {
first: ListableCommand::Single(Simple(cmd_simple("foo"))),
rest: vec!(
AndOr::And(ListableCommand::Single(Simple(cmd_simple("bar"))))
),
}));
let correct2 = cmd("baz");
let correct3 = cmd("qux");
assert_eq!(correct1, cmd1);
assert_eq!(correct2, cmd2);
assert_eq!(correct3, cmd3);
}
#[test]
fn test_complete_command_valid_no_input() {
let mut p = make_parser("");
p.complete_command().expect("no input caused an error");
}
#[test]
fn test_command_delegates_valid_commands_brace() {
let correct = Compound(Box::new(CompoundCommand {
kind: Brace(vec!(cmd("foo"))),
io: vec!(),
}));
assert_eq!(correct, make_parser("{ foo; }").command().unwrap());
}
#[test]
fn test_command_delegates_valid_commands_subshell() {
let commands = [
"(foo)",
"( foo)",
];
let correct = Compound(Box::new(CompoundCommand {
kind: Subshell(vec!(cmd("foo"))),
io: vec!(),
}));
for cmd in &commands {
match make_parser(cmd).command() {
Ok(ref result) if result == &correct => {},
Ok(result) => panic!("Parsed \"{}\" as an unexpected command type:\n{:#?}", cmd, result),
Err(err) => panic!("Failed to parse \"{}\": {}", cmd, err),
}
}
}
#[test]
fn test_command_delegates_valid_commands_while() {
let correct = Compound(Box::new(CompoundCommand {
kind: While(GuardBodyPair {
guard: vec!(cmd("guard")),
body: vec!(cmd("foo")),
}),
io: vec!(),
}));
assert_eq!(correct, make_parser("while guard; do foo; done").command().unwrap());
}
#[test]
fn test_command_delegates_valid_commands_until() {
let correct = Compound(Box::new(CompoundCommand {
kind: Until(GuardBodyPair {
guard: vec!(cmd("guard")),
body: vec!(cmd("foo")),
}),
io: vec!(),
}));
assert_eq!(correct, make_parser("until guard; do foo; done").command().unwrap());
}
#[test]
fn test_command_delegates_valid_commands_for() {
let correct = Compound(Box::new(CompoundCommand {
kind: For {
var: String::from("var"),
words: Some(vec!()),
body: vec!(cmd("foo")),
},
io: vec!(),
}));
assert_eq!(correct, make_parser("for var in; do foo; done").command().unwrap());
}
#[test]
fn test_command_delegates_valid_commands_if() {
let correct = Compound(Box::new(CompoundCommand {
kind: If {
conditionals: vec!(GuardBodyPair {
guard: vec!(cmd("guard")),
body: vec!(cmd("body")),
}),
else_branch: None,
},
io: vec!(),
}));
assert_eq!(correct, make_parser("if guard; then body; fi").command().unwrap());
}
#[test]
fn test_command_delegates_valid_commands_case() {
let correct = Compound(Box::new(CompoundCommand {
kind: Case {
word: word("foo"),
arms: vec!(),
},
io: vec!(),
}));
assert_eq!(correct, make_parser("case foo in esac").command().unwrap());
}
#[test]
fn test_command_delegates_valid_simple_commands() {
let correct = Simple(cmd_args_simple("echo", &["foo", "bar"]));
assert_eq!(correct, make_parser("echo foo bar").command().unwrap());
}
#[test]
fn test_command_delegates_valid_commands_function() {
let commands = [
"function foo() { echo body; }",
"function foo () { echo body; }",
"function foo ( ) { echo body; }",
"function foo( ) { echo body; }",
"function foo { echo body; }",
"foo() { echo body; }",
"foo () { echo body; }",
"foo ( ) { echo body; }",
"foo( ) { echo body; }",
];
let correct = FunctionDef(String::from("foo"), Rc::new(CompoundCommand {
kind: Brace(vec!(cmd_args("echo", &["body"]))),
io: vec!(),
}));
for cmd in &commands {
match make_parser(cmd).command() {
Ok(ref result) if result == &correct => {},
Ok(result) => panic!("Parsed \"{}\" as an unexpected command type:\n{:#?}", cmd, result),
Err(err) => panic!("Failed to parse \"{}\": {}", cmd, err),
}
}
}
#[test]
fn test_command_parses_quoted_compound_commands_as_simple_commands() {
let cases = [
"{foo; }", "'{' foo; }",
"'(' foo; )",
"'while' guard do foo; done",
"'until' guard do foo; done",
"'if' guard; then body; fi",
"'for' var in; do echo $var; done",
"'function' name { echo body; }",
"name'()' { echo body; }",
"123fn() { echo body; }",
"'case' foo in esac",
"\"{\" foo; }",
"\"(\" foo; )",
"\"while\" guard do foo; done",
"\"until\" guard do foo; done",
"\"if\" guard; then body; fi",
"\"for\" var in; do echo $var; done",
"\"function\" name { echo body; }",
"name\"()\" { echo body; }",
"\"case\" foo in esac",
];
for cmd in &cases {
match make_parser(cmd).command() {
Ok(Simple(_)) => {},
Ok(result) =>
panic!("Parse::command unexpectedly parsed \"{}\" as a non-simple command:\n{:#?}", cmd, result),
Err(err) => panic!("Parse::command failed to parse \"{}\": {}", cmd, err),
}
}
}
#[test]
fn test_command_should_delegate_literals_and_names_loop_while() {
for kw in vec!(
Token::Literal(String::from("while")),
Token::Name(String::from("while"))
) {
let mut p = make_parser_from_tokens(vec!(
kw,
Token::Newline,
Token::Literal(String::from("guard")),
Token::Newline,
Token::Literal(String::from("do")),
Token::Newline,
Token::Literal(String::from("foo")),
Token::Newline,
Token::Literal(String::from("done")),
));
let cmd = p.command().unwrap();
if let Compound(ref compound_cmd) = cmd {
if let While(..) = compound_cmd.kind {
continue;
}
}
panic!("Parsed an unexpected command:\n{:#?}", cmd)
}
}
#[test]
fn test_command_should_delegate_literals_and_names_loop_until() {
for kw in vec!(
Token::Literal(String::from("until")),
Token::Name(String::from("until"))
) {
let mut p = make_parser_from_tokens(vec!(
kw,
Token::Newline,
Token::Literal(String::from("guard")),
Token::Newline,
Token::Literal(String::from("do")),
Token::Newline,
Token::Literal(String::from("foo")),
Token::Newline,
Token::Literal(String::from("done")),
));
let cmd = p.command().unwrap();
if let Compound(ref compound_cmd) = cmd {
if let Until(..) = compound_cmd.kind {
continue;
}
}
panic!("Parsed an unexpected command:\n{:#?}", cmd)
}
}
#[test]
fn test_command_should_delegate_literals_and_names_if() {
for if_tok in vec!(Token::Literal(String::from("if")), Token::Name(String::from("if"))) {
for then_tok in vec!(Token::Literal(String::from("then")), Token::Name(String::from("then"))) {
for elif_tok in vec!(Token::Literal(String::from("elif")), Token::Name(String::from("elif"))) {
for else_tok in vec!(Token::Literal(String::from("else")), Token::Name(String::from("else"))) {
for fi_tok in vec!(Token::Literal(String::from("fi")), Token::Name(String::from("fi"))) {
let mut p = make_parser_from_tokens(vec!(
if_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("guard1")),
Token::Newline,
then_tok.clone(),
Token::Newline,
Token::Literal(String::from("body1")),
elif_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("guard2")),
Token::Newline,
then_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("body2")),
else_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("else part")),
Token::Newline,
fi_tok,
));
let cmd = p.command().unwrap();
if let Compound(ref compound_cmd) = cmd {
if let If { .. } = compound_cmd.kind {
continue;
}
}
panic!("Parsed an unexpected command:\n{:#?}", cmd)
}
}
}
}
}
}
#[test]
fn test_command_should_delegate_literals_and_names_for() {
for for_tok in vec!(Token::Literal(String::from("for")), Token::Name(String::from("for"))) {
for in_tok in vec!(Token::Literal(String::from("in")), Token::Name(String::from("in"))) {
let mut p = make_parser_from_tokens(vec!(
for_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Name(String::from("var")),
Token::Whitespace(String::from(" ")),
in_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("one")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("two")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("three")),
Token::Whitespace(String::from(" ")),
Token::Newline,
Token::Literal(String::from("do")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("echo")),
Token::Whitespace(String::from(" ")),
Token::Dollar,
Token::Name(String::from("var")),
Token::Newline,
Token::Literal(String::from("done")),
));
let cmd = p.command().unwrap();
if let Compound(ref compound_cmd) = cmd {
if let For { .. } = compound_cmd.kind {
continue;
}
}
panic!("Parsed an unexpected command:\n{:#?}", cmd)
}
}
}
#[test]
fn test_command_should_delegate_literals_and_names_case() {
let case_str = String::from("case");
let in_str = String::from("in");
let esac_str = String::from("esac");
for case_tok in vec!(Token::Literal(case_str.clone()), Token::Name(case_str.clone())) {
for in_tok in vec!(Token::Literal(in_str.clone()), Token::Name(in_str.clone())) {
for esac_tok in vec!(Token::Literal(esac_str.clone()), Token::Name(esac_str.clone())) {
let mut p = make_parser_from_tokens(vec!(
case_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("foo")),
Token::Literal(String::from("bar")),
Token::Whitespace(String::from(" ")),
in_tok.clone(),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("foo")),
Token::ParenClose,
Token::Newline,
Token::Literal(String::from("echo")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("foo")),
Token::Newline,
Token::Newline,
Token::DSemi,
esac_tok
));
let cmd = p.command().unwrap();
if let Compound(ref compound_cmd) = cmd {
if let Case { .. } = compound_cmd.kind {
continue;
}
}
panic!("Parsed an unexpected command:\n{:#?}", cmd)
}
}
}
}
#[test]
fn test_command_should_delegate_literals_and_names_for_function_declaration() {
for fn_tok in vec!(Token::Literal(String::from("function")), Token::Name(String::from("function"))) {
let mut p = make_parser_from_tokens(vec!(
fn_tok,
Token::Whitespace(String::from(" ")),
Token::Name(String::from("fn_name")),
Token::Whitespace(String::from(" ")),
Token::ParenOpen, Token::ParenClose,
Token::Whitespace(String::from(" ")),
Token::CurlyOpen,
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("echo")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("fn body")),
Token::Semi,
Token::CurlyClose,
));
match p.command() {
Ok(FunctionDef(..)) => {},
Ok(result) => panic!("Parsed an unexpected command type:\n{:#?}", result),
Err(err) => panic!("Failed to parse command: {}", err),
}
}
}
#[test]
fn test_command_do_not_delegate_functions_only_if_fn_name_is_a_literal_token() {
let mut p = make_parser_from_tokens(vec!(
Token::Literal(String::from("fn_name")),
Token::Whitespace(String::from(" ")),
Token::ParenOpen, Token::ParenClose,
Token::Whitespace(String::from(" ")),
Token::CurlyOpen,
Token::Literal(String::from("echo")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("fn body")),
Token::Semi,
Token::CurlyClose,
));
match p.command() {
Ok(Simple(..)) => {},
Ok(result) => panic!("Parsed an unexpected command type:\n{:#?}", result),
Err(err) => panic!("Failed to parse command: {}", err),
}
}
#[test]
fn test_command_delegate_functions_only_if_fn_name_is_a_name_token() {
let mut p = make_parser_from_tokens(vec!(
Token::Name(String::from("fn_name")),
Token::Whitespace(String::from(" ")),
Token::ParenOpen, Token::ParenClose,
Token::Whitespace(String::from(" ")),
Token::CurlyOpen,
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("echo")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("fn body")),
Token::Semi,
Token::CurlyClose,
));
match p.command() {
Ok(FunctionDef(..)) => {},
Ok(result) => panic!("Parsed an unexpected command type:\n{:#?}", result),
Err(err) => panic!("Failed to parse command: {}", err),
}
}