extern crate conch_parser;
use conch_parser::ast::*;
use conch_parser::ast::CompoundCommandKind::*;
use conch_parser::ast::PipeableCommand::*;
use conch_parser::parse::ParseError::*;
use conch_parser::token::Token;
use std::rc::Rc;
mod parse_support;
use parse_support::*;
#[test]
fn test_function_declaration_valid() {
let correct = FunctionDef(
String::from("foo"),
Rc::new(CompoundCommand {
kind: Brace(vec!(cmd_args("echo", &["body"]))),
io: vec!(),
})
);
assert_eq!(correct, make_parser("function foo() { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo () { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo ( ) { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo( ) { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo() { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo () { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo ( ) { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo( ) { echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo() \n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo () \n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo ( )\n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo( ) \n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo \n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo() \n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo () \n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo ( ) \n{ echo body; }").function_declaration().unwrap());
assert_eq!(correct, make_parser("foo( ) \n{ echo body; }").function_declaration().unwrap());
}
#[test]
fn test_function_declaration_valid_body_need_not_be_a_compound_command() {
let src = vec!(
("function foo() echo body;", src(20, 1, 21)),
("function foo () echo body;", src(20, 1, 21)),
("function foo ( ) echo body;", src(20, 1, 21)),
("function foo( ) echo body;", src(20, 1, 21)),
("function foo echo body;", src(20, 1, 21)),
("foo() echo body;", src(20, 1, 21)),
("foo () echo body;", src(20, 1, 21)),
("foo ( ) echo body;", src(20, 1, 21)),
("foo( ) echo body;", src(20, 1, 21)),
("function foo() \necho body;", src(20, 2, 1)),
("function foo () \necho body;", src(20, 2, 1)),
("function foo ( )\necho body;", src(20, 2, 1)),
("function foo( ) \necho body;", src(20, 2, 1)),
("function foo \necho body;", src(20, 2, 1)),
("foo() \necho body;", src(20, 2, 1)),
("foo () \necho body;", src(20, 2, 1)),
("foo ( ) \necho body;", src(20, 2, 1)),
("foo( ) \necho body;", src(20, 2, 1)),
);
for (s, p) in src {
let correct = Unexpected(Token::Name(String::from("echo")), p);
match make_parser(s).function_declaration() {
Ok(w) => panic!("Unexpectedly parsed the source \"{}\" as\n{:?}", s, w),
Err(ref err) => if err != &correct {
panic!("Expected the source \"{}\" to return the error `{:?}`, but got `{:?}`",
s, correct, err);
},
}
}
}
#[test]
fn test_function_declaration_parens_can_be_subshell_if_function_keyword_present() {
let correct = FunctionDef(
String::from("foo"),
Rc::new(CompoundCommand {
kind: Subshell(vec!(cmd_args("echo", &["subshell"]))),
io: vec!(),
})
);
assert_eq!(correct, make_parser("function foo (echo subshell)").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo() (echo subshell)").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo () (echo subshell)").function_declaration().unwrap());
assert_eq!(correct, make_parser("function foo\n(echo subshell)").function_declaration().unwrap());
}
#[test]
fn test_function_declaration_invalid_newline_in_declaration() {
let mut p = make_parser("function\nname() { echo body; }");
assert_eq!(Err(Unexpected(Token::Newline, src(8,1,9))), p.function_declaration());
let mut p = make_parser("function name\n() { echo body; }");
assert_eq!(Err(Unexpected(Token::ParenClose, src(15,2,2))), p.function_declaration());
}
#[test]
fn test_function_declaration_invalid_missing_space_after_fn_keyword_and_no_parens() {
let mut p = make_parser("functionname { echo body; }");
assert_eq!(Err(Unexpected(Token::CurlyOpen, src(13,1,14))), p.function_declaration());
}
#[test]
fn test_function_declaration_invalid_missing_fn_keyword_and_parens() {
let mut p = make_parser("name { echo body; }");
assert_eq!(Err(Unexpected(Token::CurlyOpen, src(5,1,6))), p.function_declaration());
}
#[test]
fn test_function_declaration_invalid_missing_space_after_name_no_parens() {
let mut p = make_parser("function name{ echo body; }");
assert_eq!(Err(Unexpected(Token::CurlyOpen, src(13,1,14))), p.function_declaration());
let mut p = make_parser("function name( echo body; )");
assert_eq!(Err(Unexpected(Token::Name(String::from("echo")), src(15,1,16))), p.function_declaration());
}
#[test]
fn test_function_declaration_invalid_missing_name() {
let mut p = make_parser("function { echo body; }");
assert_eq!(Err(Unexpected(Token::CurlyOpen, src(9,1,10))), p.function_declaration());
let mut p = make_parser("function () { echo body; }");
assert_eq!(Err(Unexpected(Token::ParenOpen, src(9,1,10))), p.function_declaration());
let mut p = make_parser("() { echo body; }");
assert_eq!(Err(Unexpected(Token::ParenOpen, src(0,1,1))), p.function_declaration());
}
#[test]
fn test_function_declaration_invalid_missing_body() {
let mut p = make_parser("function name");
assert_eq!(Err(UnexpectedEOF), p.function_declaration());
let mut p = make_parser("function name()");
assert_eq!(Err(UnexpectedEOF), p.function_declaration());
let mut p = make_parser("name()");
assert_eq!(Err(UnexpectedEOF), p.function_declaration());
}
#[test]
fn test_function_declaration_invalid_quoted() {
let cmds = [
("'function' name { echo body; }", Unexpected(Token::SingleQuote, src(0,1,1))),
("function 'name'() { echo body; }", Unexpected(Token::SingleQuote, src(9,1,10))),
("name'()' { echo body; }", Unexpected(Token::SingleQuote, src(4,1,5))),
("\"function\" name { echo body; }", Unexpected(Token::DoubleQuote, src(0,1,1))),
("function \"name\"() { echo body; }", Unexpected(Token::DoubleQuote, src(9,1,10))),
("name\"()\" { echo body; }", Unexpected(Token::DoubleQuote, src(4,1,5))),
];
for &(c, ref e) in cmds.into_iter() {
match make_parser(c).function_declaration() {
Ok(result) => panic!("Unexpectedly parsed \"{}\" as\n{:#?}", c, result),
Err(ref err) => if err != e {
panic!("Expected the source \"{}\" to return the error `{:?}`, but got `{:?}`",
c, e, err);
},
}
}
}
#[test]
fn test_function_declaration_invalid_fn_must_be_name() {
let mut p = make_parser("function 123fn { echo body; }");
assert_eq!(Err(BadIdent(String::from("123fn"), src(9, 1, 10))), p.function_declaration());
let mut p = make_parser("function 123fn() { echo body; }");
assert_eq!(Err(BadIdent(String::from("123fn"), src(9, 1, 10))), p.function_declaration());
let mut p = make_parser("123fn() { echo body; }");
assert_eq!(Err(BadIdent(String::from("123fn"), src(0, 1, 1))), p.function_declaration());
}
#[test]
fn test_function_declaration_invalid_fn_name_must_be_name_token() {
let mut p = make_parser_from_tokens(vec!(
Token::Literal(String::from("function")),
Token::Whitespace(String::from(" ")),
Token::Literal(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,
));
assert_eq!(Err(BadIdent(String::from("fn_name"), src(9, 1, 10))), p.function_declaration());
let mut p = make_parser_from_tokens(vec!(
Token::Literal(String::from("function")),
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,
));
p.function_declaration().unwrap();
}
#[test]
fn test_function_declaration_invalid_concat() {
let mut p = make_parser_from_tokens(vec!(
Token::Literal(String::from("func")), Token::Literal(String::from("tion")),
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::Literal(String::from("echo")),
Token::Whitespace(String::from(" ")),
Token::Literal(String::from("fn body")),
Token::Semi,
Token::CurlyClose,
));
assert_eq!(Err(BadIdent(String::from("func"), src(0, 1, 1))), p.function_declaration());
}
#[test]
fn test_function_declaration_should_recognize_literals_and_names_for_fn_keyword() {
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,
));
p.function_declaration().unwrap();
}
}