conch-parser 0.1.1

A library for parsing programs written in the shell programming language.
Documentation
extern crate conch_parser;

use conch_parser::ast::builder::*;
use conch_parser::parse::ParseError::*;
use conch_parser::token::Token;

mod parse_support;
use parse_support::*;

#[test]
fn test_if_command_valid_with_else() {
    let guard1 = cmd("guard1");
    let guard2 = cmd("guard2");
    let guard3 = cmd("guard3");

    let body1 = cmd("body1");
    let body2 = cmd("body2");

    let els = cmd("else");

    let correct = IfFragments {
        conditionals: vec!(
            GuardBodyPairGroup {
                guard: CommandGroup {
                    commands: vec!(guard1, guard2),
                    trailing_comments: vec!(Newline(Some("#guard_comment_a".into()))),
                },
                body: CommandGroup {
                    commands: vec!(body1),
                    trailing_comments: vec!(Newline(Some("#body_comment_a".into()))),
                },
            },
            GuardBodyPairGroup {
                guard: CommandGroup {
                    commands: vec!(guard3),
                    trailing_comments: vec!(Newline(Some("#guard_comment_b".into()))),
                },
                body: CommandGroup {
                    commands: vec!(body2),
                    trailing_comments: vec!(Newline(Some("#body_comment_b".into()))),
                },
            },
        ),
        else_branch: Some(CommandGroup {
            commands: vec!(els),
            trailing_comments: vec!(Newline(Some("#else_comment".into()))),
        }),
    };
    let mut p = make_parser("\
        if guard1; guard2;
        #guard_comment_a
        then body1
        #body_comment_a
        elif guard3;
        #guard_comment_b
        then body2;
        #body_comment_b
        else else;
        #else_comment
        fi
    ");
    assert_eq!(correct, p.if_command().unwrap());
}

#[test]
fn test_if_command_valid_without_else() {
    let guard1 = cmd("guard1");
    let guard2 = cmd("guard2");
    let guard3 = cmd("guard3");

    let body1 = cmd("body1");
    let body2 = cmd("body2");

    let correct = IfFragments {
        conditionals: vec!(
            GuardBodyPairGroup {
                guard: CommandGroup {
                    commands: vec!(guard1, guard2),
                    trailing_comments: vec!(Newline(Some("#guard_comment_a".into()))),
                },
                body: CommandGroup {
                    commands: vec!(body1),
                    trailing_comments: vec!(Newline(Some("#body_comment_a".into()))),
                },
            },
            GuardBodyPairGroup {
                guard: CommandGroup {
                    commands: vec!(guard3),
                    trailing_comments: vec!(Newline(Some("#guard_comment_b".into()))),
                },
                body: CommandGroup {
                    commands: vec!(body2),
                    trailing_comments: vec!(Newline(Some("#body_comment_b".into()))),
                },
            },
        ),
        else_branch: None,
    };
    let mut p = make_parser("\
        if guard1; guard2;
        #guard_comment_a
        then body1
        #body_comment_a
        elif guard3;
        #guard_comment_b
        then body2;
        #body_comment_b
        fi
    ");
    assert_eq!(correct, p.if_command().unwrap());
}

#[test]
fn test_if_command_invalid_missing_separator() {
    let mut p = make_parser("if guard; then body1; elif guard2; then body2; else else fi");
    assert_eq!(Err(IncompleteCmd("if", src(0,1,1), "fi", src(59,1,60))), p.if_command());
}

#[test]
fn test_if_command_invalid_missing_keyword() {
    let mut p = make_parser("guard1; then body1; elif guard2; then body2; else else; fi");
    assert_eq!(Err(Unexpected(Token::Name(String::from("guard1")), src(0,1,1))), p.if_command());
    let mut p = make_parser("if guard1; then body1; elif guard2; then body2; else else;");
    assert_eq!(Err(IncompleteCmd("if", src(0,1,1), "fi", src(58,1,59))), p.if_command());
}

#[test]
fn test_if_command_invalid_missing_guard() {
    let mut p = make_parser("if; then body1; elif guard2; then body2; else else; fi");
    assert_eq!(Err(Unexpected(Token::Semi, src(2,1,3))), p.if_command());
}

#[test]
fn test_if_command_invalid_missing_body() {
    let mut p = make_parser("if guard; then; elif guard2; then body2; else else; fi");
    assert_eq!(Err(Unexpected(Token::Semi, src(14, 1, 15))), p.if_command());
    let mut p = make_parser("if guard1; then body1; elif; then body2; else else; fi");
    assert_eq!(Err(Unexpected(Token::Semi, src(27, 1, 28))), p.if_command());
    let mut p = make_parser("if guard1; then body1; elif guard2; then body2; else; fi");
    assert_eq!(Err(Unexpected(Token::Semi, src(52, 1, 53))), p.if_command());
}

#[test]
fn test_if_command_invalid_quoted() {
    let cmds = [
        ("'if' guard1; then body1; elif guard2; then body2; else else; fi", Unexpected(Token::SingleQuote, src(0,1,1))),
        ("if guard1; then body1; elif guard2; then body2; else else; 'fi'", IncompleteCmd("if", src(0,1,1), "fi", src(63,1,64))),
        ("\"if\" guard1; then body1; elif guard2; then body2; else else; fi", Unexpected(Token::DoubleQuote, src(0,1,1))),
        ("if guard1; then body1; elif guard2; then body2; else else; \"fi\"", IncompleteCmd("if", src(0,1,1), "fi", src(63,1,64))),
    ];

    for &(s, ref e) in cmds.into_iter() {
        match make_parser(s).if_command() {
            Ok(result) => panic!("Unexpectedly parsed \"{}\" as\n{:#?}", s, result),
            Err(ref err) => if err != e {
                panic!("Expected the source \"{}\" to return the error `{:?}`, but got `{:?}`",
                       s, e, err);
            },
        }
    }
}

#[test]
fn test_if_command_invalid_concat() {
    let mut p = make_parser_from_tokens(vec!(
        Token::Literal(String::from("i")), Token::Literal(String::from("f")),
        Token::Newline, Token::Literal(String::from("guard1")), Token::Newline,
        Token::Literal(String::from("then")),
        Token::Newline, Token::Literal(String::from("body1")), Token::Newline,
        Token::Literal(String::from("elif")),
        Token::Newline, Token::Literal(String::from("guard2")), Token::Newline,
        Token::Literal(String::from("then")),
        Token::Newline, Token::Literal(String::from("body2")), Token::Newline,
        Token::Literal(String::from("else")),
        Token::Newline, Token::Literal(String::from("else part")), Token::Newline,
        Token::Literal(String::from("fi")),
    ));
    assert_eq!(Err(Unexpected(Token::Literal(String::from("i")), src(0,1,1))), p.if_command());

    // Splitting up `then`, `elif`, and `else` tokens makes them
    // get interpreted as arguments, so an explicit error may not be raised
    // although the command will be malformed

    let mut p = make_parser_from_tokens(vec!(
        Token::Literal(String::from("if")),
        Token::Newline, Token::Literal(String::from("guard1")), Token::Newline,
        Token::Literal(String::from("then")),
        Token::Newline, Token::Literal(String::from("body1")), Token::Newline,
        Token::Literal(String::from("elif")),
        Token::Newline, Token::Literal(String::from("guard2")), Token::Newline,
        Token::Literal(String::from("then")),
        Token::Newline, Token::Literal(String::from("body2")), Token::Newline,
        Token::Literal(String::from("else")),
        Token::Newline, Token::Literal(String::from("else part")), Token::Newline,
        Token::Literal(String::from("f")), Token::Literal(String::from("i")),
    ));
    assert_eq!(Err(IncompleteCmd("if", src(0,1,1), "fi", src(61,11,3))), p.if_command());
}

#[test]
fn test_if_command_should_recognize_literals_and_names() {
    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,
                        ));
                        p.if_command().unwrap();
                    }
                }
            }
        }
    }
}