<commit_before>use std::iter::Peekable;
use std::result;

use super::ast::Expression;
use super::scanner::{Scanner, Token, TokenError};

pub type Result<T> = result::Result<T, ParseError>;

#[derive(Debug,PartialEq)]
pub enum ParseError {
    ScanError(TokenError),
    Unexpected(Token),
    UnexpectedEOF,
}

pub struct Parser<'a> {
    scanner: Peekable<Scanner<'a>>,
}

impl<'a> Parser<'a> {
    pub fn new(input: &'a str) -> Self {
        Parser{scanner: Scanner::new(input).peekable()}
    }

    // Assuming we've read an open paren, parse the inner expression and the
    // closing paren.
    fn parse_bracketed_expr(&mut self) -> Result<Expression> {
        let inner = match self.next() {
            Some(Ok(expr)) => expr,
            Some(Err(e)) => return Err(e),
            None => return Err(ParseError::UnexpectedEOF),
        };

        match self.scanner.next() {
            Some(Ok(Token::CloseParen)) => Ok(inner),
            Some(Ok(t)) => Err(ParseError::Unexpected(t)),
            Some(Err(e)) => Err(ParseError::ScanError(e)),
            None => Err(ParseError::UnexpectedEOF),
        }
    }

    // Assuming we've read an open curly, parse the inner block and the closing
    // curly.
    fn parse_block(&mut self) -> Result<Expression> {
        let mut body = vec![];

        loop {
            match self.scanner.peek().cloned() {
                None => return Err(ParseError::UnexpectedEOF),
                Some(Err(e)) => return Err(ParseError::ScanError(e)),
                Some(Ok(Token::CloseCurly)) => {
                    self.scanner.next();
                    return Ok(Expression::Block(body));
                },
                _ => match self.next() {
                    Some(Ok(expr)) => body.push(expr),
                    Some(Err(e)) => return Err(e),
                    None => return Err(ParseError::UnexpectedEOF),
                },
            }
        }
    }

    // Assuming we've parsed an identifier, parse the rest of the expression.
    fn parse_identifier(&mut self, name: String) -> Result<Expression> {
        match self.scanner.peek() {
            Some(&Ok(Token::OpenParen)) => self.scanner.next(),
            _ => return Ok(Expression::Variable(name)),
        };

        match self.parse_expr_list(&Token::CloseParen) {
            Ok(args) => Ok(Expression::FunctionCall {
                name: name,
                args: args,
            }),
            Err(e) => Err(e),
        }
    }

    // parse_expr_list parses a comma-separated list of expressions until the
    // specified token is found.
    fn parse_expr_list(&mut self, until: &Token) -> Result<Vec<Expression>> {
        let mut expressions = Vec::new();

        let mut done = false;
        if let Some(&Ok(ref t)) = self.scanner.peek() {
            done = t == until;
        }

        if done {
            self.scanner.next();
            return Ok(expressions);
        }

        loop {
            match self.next() {
                Some(Ok(expr)) => expressions.push(expr),
                Some(Err(e)) => return Err(e),
                None => return Err(ParseError::UnexpectedEOF),
            }

            match self.scanner.next() {
                Some(Ok(Token::Comma)) => continue,
                Some(Ok(ref t)) if t == until => return Ok(expressions),
                Some(Ok(t)) => return Err(ParseError::Unexpected(t)),
                Some(Err(e)) => return Err(ParseError::ScanError(e)),
                None => return Err(ParseError::UnexpectedEOF),
            }
        }
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = Result<Expression>;

    fn next(&mut self) -> Option<Self::Item> {
        let token = match self.scanner.next() {
            None => return None,
            Some(Err(e)) => return Some(Err(ParseError::ScanError(e))),
            Some(Ok(t)) => t,
        };

        let expr_res = match token {
            Token::Nil => Ok(Expression::NilLiteral),
            Token::Boolean(b) => Ok(Expression::BooleanLiteral(b)),
            Token::Number(n) => Ok(Expression::NumberLiteral(n)),
            Token::String(s) => Ok(Expression::StrLiteral(s)),
            Token::OpenParen => self.parse_bracketed_expr(),
            Token::OpenCurly => self.parse_block(),
            Token::Identifier(s) => self.parse_identifier(s),
            t => Err(ParseError::Unexpected(t)),
        };

        let lhs = match expr_res {
            Ok(e) => e,
            Err(e) => return Some(Err(e)),
        };

        // Copy the next token because we might be part of a larger expression.
        let next = match self.scanner.peek().cloned() {
            Some(Ok(t)) => t,
            _ => return Some(Ok(lhs)),
        };

        // Binary expression.
        if let Some(op) = next.to_binary_op() {
            self.scanner.next();
            let rhs = match self.next() {
                Some(Ok(e)) => e,
                Some(Err(e)) => return Some(Err(e)),
                None => return Some(Err(ParseError::UnexpectedEOF)),
            };

            return Some(Ok(Expression::BinaryExpr{
                left: Box::new(lhs),
                op: op,
                right: Box::new(rhs),
            }));
        }

        // Assignment.
        if next == Token::Eq {
            if let Expression::Variable(v) = lhs {
                self.scanner.next();
                let rhs = match self.next() {
                    Some(Ok(e)) => e,
                    Some(Err(e)) => return Some(Err(e)),
                    None => return Some(Err(ParseError::UnexpectedEOF)),
                };

                return Some(Ok(Expression::Assignment{
                    left: v,
                    right: Box::new(rhs),
                }));
            }
        }

        Some(Ok(lhs))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::ast::*;

    #[test]
    fn test_literals() {
        let mut parser = Parser::new(r#"nil true false 1 "foo""#);
        assert_eq!(parser.next(), Some(Ok(Expression::NilLiteral)));
        assert_eq!(parser.next(), Some(Ok(Expression::BooleanLiteral(true))));
        assert_eq!(parser.next(), Some(Ok(Expression::BooleanLiteral(false))));
        assert_eq!(parser.next(), Some(Ok(Expression::NumberLiteral(1.0))));
        assert_eq!(parser.next(), Some(Ok(Expression::StrLiteral("foo".to_owned()))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_parentheses() {
        let mut parser = Parser::new(r#"(nil)(((true)))"#);
        assert_eq!(parser.next(), Some(Ok(Expression::NilLiteral)));
        assert_eq!(parser.next(), Some(Ok(Expression::BooleanLiteral(true))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_identifiers_and_functions() {
        let foo_var = Expression::Variable("foo".to_owned());

        let mut parser = Parser::new(r#"foo foo() foo(foo) foo(foo, foo)"#);
        assert_eq!(parser.next(), Some(Ok(foo_var.clone())));
        assert_eq!(parser.next(), Some(Ok(Expression::FunctionCall{
            name: "foo".to_owned(),
            args: vec![],
        })));
        assert_eq!(parser.next(), Some(Ok(Expression::FunctionCall{
            name: "foo".to_owned(),
            args: vec![foo_var.clone()],
        })));
        assert_eq!(parser.next(), Some(Ok(Expression::FunctionCall{
            name: "foo".to_owned(),
            args: vec![foo_var.clone(), foo_var.clone()],
        })));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_binary_expr() {
        let mut parser = Parser::new(r#"1 + 2 - 3 * 4 / 5"#);

        assert_eq!(parser.next(), Some(Ok(Expression::BinaryExpr{
            left: Box::new(Expression::NumberLiteral(1.0)),
            op: BinaryOp::Add,
            right: Box::new(Expression::BinaryExpr{
                left: Box::new(Expression::NumberLiteral(2.0)),
                op: BinaryOp::Sub,
                right: Box::new(Expression::BinaryExpr{
                    left: Box::new(Expression::NumberLiteral(3.0)),
                    op: BinaryOp::Mul,
                    right: Box::new(Expression::BinaryExpr{
                        left: Box::new(Expression::NumberLiteral(4.0)),
                        op: BinaryOp::Div,
                        right: Box::new(Expression::NumberLiteral(5.0)),
                    }),
                }),
            }),
        })));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_binary_ops() {
        let cases = vec![
            ("+", BinaryOp::Add),
            ("-", BinaryOp::Sub),
            ("*", BinaryOp::Mul),
            ("/", BinaryOp::Div),
            ("==", BinaryOp::Eq),
            ("<", BinaryOp::Lt),
            ("<=", BinaryOp::LtEq),
            (">", BinaryOp::Gt),
            (">=", BinaryOp::GtEq),
        ];

        for (s, op) in cases {
            let expr_str = format!("1 {} 2", s);

            let mut parser = Parser::new(&expr_str);
            assert_eq!(parser.next(), Some(Ok(Expression::BinaryExpr{
                left: Box::new(Expression::NumberLiteral(1.0)),
                op: op,
                right: Box::new(Expression::NumberLiteral(2.0)),
            })));
            assert_eq!(parser.next(), None);
        }
    }

    #[test]
    fn test_blocks() {
        let mut parser = Parser::new("{1{}2}");

        assert_eq!(parser.next(), Some(Ok(Expression::Block(vec![
            Expression::NumberLiteral(1.0),
            Expression::Block(vec![]),
            Expression::NumberLiteral(2.0),
        ]))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_assignments() {
        let mut parser = Parser::new("x = y = z");

        assert_eq!(parser.next(), Some(Ok(Expression::Assignment{
            left: "x".to_owned(),
            right: Box::new(Expression::Assignment{
                left: "y".to_owned(),
                right: Box::new(Expression::Variable("z".to_owned())),
            }),
        })));
        assert_eq!(parser.next(), None);
    }
}
<commit_msg>Use consistent names for tests<commit_after>use std::iter::Peekable;
use std::result;

use super::ast::Expression;
use super::scanner::{Scanner, Token, TokenError};

pub type Result<T> = result::Result<T, ParseError>;

#[derive(Debug,PartialEq)]
pub enum ParseError {
    ScanError(TokenError),
    Unexpected(Token),
    UnexpectedEOF,
}

pub struct Parser<'a> {
    scanner: Peekable<Scanner<'a>>,
}

impl<'a> Parser<'a> {
    pub fn new(input: &'a str) -> Self {
        Parser{scanner: Scanner::new(input).peekable()}
    }

    // Assuming we've read an open paren, parse the inner expression and the
    // closing paren.
    fn parse_bracketed_expr(&mut self) -> Result<Expression> {
        let inner = match self.next() {
            Some(Ok(expr)) => expr,
            Some(Err(e)) => return Err(e),
            None => return Err(ParseError::UnexpectedEOF),
        };

        match self.scanner.next() {
            Some(Ok(Token::CloseParen)) => Ok(inner),
            Some(Ok(t)) => Err(ParseError::Unexpected(t)),
            Some(Err(e)) => Err(ParseError::ScanError(e)),
            None => Err(ParseError::UnexpectedEOF),
        }
    }

    // Assuming we've read an open curly, parse the inner block and the closing
    // curly.
    fn parse_block(&mut self) -> Result<Expression> {
        let mut body = vec![];

        loop {
            match self.scanner.peek().cloned() {
                None => return Err(ParseError::UnexpectedEOF),
                Some(Err(e)) => return Err(ParseError::ScanError(e)),
                Some(Ok(Token::CloseCurly)) => {
                    self.scanner.next();
                    return Ok(Expression::Block(body));
                },
                _ => match self.next() {
                    Some(Ok(expr)) => body.push(expr),
                    Some(Err(e)) => return Err(e),
                    None => return Err(ParseError::UnexpectedEOF),
                },
            }
        }
    }

    // Assuming we've parsed an identifier, parse the rest of the expression.
    fn parse_identifier(&mut self, name: String) -> Result<Expression> {
        match self.scanner.peek() {
            Some(&Ok(Token::OpenParen)) => self.scanner.next(),
            _ => return Ok(Expression::Variable(name)),
        };

        match self.parse_expr_list(&Token::CloseParen) {
            Ok(args) => Ok(Expression::FunctionCall {
                name: name,
                args: args,
            }),
            Err(e) => Err(e),
        }
    }

    // parse_expr_list parses a comma-separated list of expressions until the
    // specified token is found.
    fn parse_expr_list(&mut self, until: &Token) -> Result<Vec<Expression>> {
        let mut expressions = Vec::new();

        let mut done = false;
        if let Some(&Ok(ref t)) = self.scanner.peek() {
            done = t == until;
        }

        if done {
            self.scanner.next();
            return Ok(expressions);
        }

        loop {
            match self.next() {
                Some(Ok(expr)) => expressions.push(expr),
                Some(Err(e)) => return Err(e),
                None => return Err(ParseError::UnexpectedEOF),
            }

            match self.scanner.next() {
                Some(Ok(Token::Comma)) => continue,
                Some(Ok(ref t)) if t == until => return Ok(expressions),
                Some(Ok(t)) => return Err(ParseError::Unexpected(t)),
                Some(Err(e)) => return Err(ParseError::ScanError(e)),
                None => return Err(ParseError::UnexpectedEOF),
            }
        }
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = Result<Expression>;

    fn next(&mut self) -> Option<Self::Item> {
        let token = match self.scanner.next() {
            None => return None,
            Some(Err(e)) => return Some(Err(ParseError::ScanError(e))),
            Some(Ok(t)) => t,
        };

        let expr_res = match token {
            Token::Nil => Ok(Expression::NilLiteral),
            Token::Boolean(b) => Ok(Expression::BooleanLiteral(b)),
            Token::Number(n) => Ok(Expression::NumberLiteral(n)),
            Token::String(s) => Ok(Expression::StrLiteral(s)),
            Token::OpenParen => self.parse_bracketed_expr(),
            Token::OpenCurly => self.parse_block(),
            Token::Identifier(s) => self.parse_identifier(s),
            t => Err(ParseError::Unexpected(t)),
        };

        let lhs = match expr_res {
            Ok(e) => e,
            Err(e) => return Some(Err(e)),
        };

        // Copy the next token because we might be part of a larger expression.
        let next = match self.scanner.peek().cloned() {
            Some(Ok(t)) => t,
            _ => return Some(Ok(lhs)),
        };

        // Binary expression.
        if let Some(op) = next.to_binary_op() {
            self.scanner.next();
            let rhs = match self.next() {
                Some(Ok(e)) => e,
                Some(Err(e)) => return Some(Err(e)),
                None => return Some(Err(ParseError::UnexpectedEOF)),
            };

            return Some(Ok(Expression::BinaryExpr{
                left: Box::new(lhs),
                op: op,
                right: Box::new(rhs),
            }));
        }

        // Assignment.
        if next == Token::Eq {
            if let Expression::Variable(v) = lhs {
                self.scanner.next();
                let rhs = match self.next() {
                    Some(Ok(e)) => e,
                    Some(Err(e)) => return Some(Err(e)),
                    None => return Some(Err(ParseError::UnexpectedEOF)),
                };

                return Some(Ok(Expression::Assignment{
                    left: v,
                    right: Box::new(rhs),
                }));
            }
        }

        Some(Ok(lhs))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::ast::*;

    #[test]
    fn test_literal() {
        let mut parser = Parser::new(r#"nil true false 1 "foo""#);
        assert_eq!(parser.next(), Some(Ok(Expression::NilLiteral)));
        assert_eq!(parser.next(), Some(Ok(Expression::BooleanLiteral(true))));
        assert_eq!(parser.next(), Some(Ok(Expression::BooleanLiteral(false))));
        assert_eq!(parser.next(), Some(Ok(Expression::NumberLiteral(1.0))));
        assert_eq!(parser.next(), Some(Ok(Expression::StrLiteral("foo".to_owned()))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_parenthesis() {
        let mut parser = Parser::new(r#"(nil)(((true)))"#);
        assert_eq!(parser.next(), Some(Ok(Expression::NilLiteral)));
        assert_eq!(parser.next(), Some(Ok(Expression::BooleanLiteral(true))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_identifier_and_function_call() {
        let foo_var = Expression::Variable("foo".to_owned());

        let mut parser = Parser::new(r#"foo foo() foo(foo) foo(foo, foo)"#);
        assert_eq!(parser.next(), Some(Ok(foo_var.clone())));
        assert_eq!(parser.next(), Some(Ok(Expression::FunctionCall{
            name: "foo".to_owned(),
            args: vec![],
        })));
        assert_eq!(parser.next(), Some(Ok(Expression::FunctionCall{
            name: "foo".to_owned(),
            args: vec![foo_var.clone()],
        })));
        assert_eq!(parser.next(), Some(Ok(Expression::FunctionCall{
            name: "foo".to_owned(),
            args: vec![foo_var.clone(), foo_var.clone()],
        })));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_binary_expr() {
        let mut parser = Parser::new(r#"1 + 2 - 3 * 4 / 5"#);

        assert_eq!(parser.next(), Some(Ok(Expression::BinaryExpr{
            left: Box::new(Expression::NumberLiteral(1.0)),
            op: BinaryOp::Add,
            right: Box::new(Expression::BinaryExpr{
                left: Box::new(Expression::NumberLiteral(2.0)),
                op: BinaryOp::Sub,
                right: Box::new(Expression::BinaryExpr{
                    left: Box::new(Expression::NumberLiteral(3.0)),
                    op: BinaryOp::Mul,
                    right: Box::new(Expression::BinaryExpr{
                        left: Box::new(Expression::NumberLiteral(4.0)),
                        op: BinaryOp::Div,
                        right: Box::new(Expression::NumberLiteral(5.0)),
                    }),
                }),
            }),
        })));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_binary_op() {
        let cases = vec![
            ("+", BinaryOp::Add),
            ("-", BinaryOp::Sub),
            ("*", BinaryOp::Mul),
            ("/", BinaryOp::Div),
            ("==", BinaryOp::Eq),
            ("<", BinaryOp::Lt),
            ("<=", BinaryOp::LtEq),
            (">", BinaryOp::Gt),
            (">=", BinaryOp::GtEq),
        ];

        for (s, op) in cases {
            let expr_str = format!("1 {} 2", s);

            let mut parser = Parser::new(&expr_str);
            assert_eq!(parser.next(), Some(Ok(Expression::BinaryExpr{
                left: Box::new(Expression::NumberLiteral(1.0)),
                op: op,
                right: Box::new(Expression::NumberLiteral(2.0)),
            })));
            assert_eq!(parser.next(), None);
        }
    }

    #[test]
    fn test_block() {
        let mut parser = Parser::new("{1{}2}");

        assert_eq!(parser.next(), Some(Ok(Expression::Block(vec![
            Expression::NumberLiteral(1.0),
            Expression::Block(vec![]),
            Expression::NumberLiteral(2.0),
        ]))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn test_assignment() {
        let mut parser = Parser::new("x = y = z");

        assert_eq!(parser.next(), Some(Ok(Expression::Assignment{
            left: "x".to_owned(),
            right: Box::new(Expression::Assignment{
                left: "y".to_owned(),
                right: Box::new(Expression::Variable("z".to_owned())),
            }),
        })));
        assert_eq!(parser.next(), None);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::str;

use cargo_process;
use support::paths;
use support::{execs, project, mkdir_recursive, ProjectBuilder, ERROR};
use hamcrest::{assert_that};

fn setup() {
}

/// Add an empty file with executable flags (and platform-dependent suffix).
/// TODO: move this to `ProjectBuilder` if other cases using this emerge.
fn fake_executable(proj: ProjectBuilder, dir: &Path, name: &str) -> ProjectBuilder {
    let path = proj.root().join(dir).join(&format!("{}{}", name,
                                                   env::consts::EXE_SUFFIX));
    mkdir_recursive(path.parent().unwrap()).unwrap();
    File::create(&path).unwrap();
    make_executable(&path);
    return proj;

    #[cfg(unix)]
    fn make_executable(p: &Path) {
        use std::os::unix::prelude::*;

        let mut perms = fs::metadata(p).unwrap().permissions();
        let mode = perms.mode();
        perms.set_mode(mode | 0o111);
        fs::set_permissions(p, perms).unwrap();
    }
    #[cfg(windows)]
    fn make_executable(_: &Path) {}
}

fn path() -> Vec<PathBuf> {
    env::split_paths(&env::var_os("PATH").unwrap_or(OsString::new())).collect()
}

test!(list_command_looks_at_path {
    let proj = project("list-non-overlapping");
    let proj = fake_executable(proj, &Path::new("path-test"), "cargo-1");
    let mut pr = cargo_process();

    let mut path = path();
    path.push(proj.root().join("path-test"));
    let path = env::join_paths(path.iter()).unwrap();
    let output = pr.arg("-v").arg("--list")
                   .env("PATH", &path);
    let output = output.exec_with_output().unwrap();
    let output = str::from_utf8(&output.stdout).unwrap();
    assert!(output.contains("\n    1\n"), "missing 1: {}", output);
});

test!(find_closest_biuld_to_build {
    let mut pr = cargo_process();
    pr.arg("biuld");

    assert_that(pr,
                execs().with_status(101)
                       .with_stderr(&format!("{error} no such subcommand

<tab>Did you mean `build`?

",
error = ERROR)));
});

// if a subcommand is more than 3 edit distance away, we don't make a suggestion
test!(find_closest_dont_correct_nonsense {
    let mut pr = cargo_process();
    pr.arg("there-is-no-way-that-there-is-a-command-close-to-this")
      .cwd(&paths::root());

    assert_that(pr,
                execs().with_status(101)
                       .with_stderr(&format!("{error} no such subcommand
",
error = ERROR)));
});

test!(override_cargo_home {
    let root = paths::root();
    let my_home = root.join("my_home");
    fs::create_dir(&my_home).unwrap();
    File::create(&my_home.join("config")).unwrap().write_all(br#"
        [cargo-new]
        name = "foo"
        email = "bar"
        git = false
    "#).unwrap();

    assert_that(cargo_process()
                    .arg("new").arg("foo")
                    .env("USER", "foo")
                    .env("CARGO_HOME", &my_home),
                execs().with_status(0));

    let toml = paths::root().join("foo/Cargo.toml");
    let mut contents = String::new();
    File::open(&toml).unwrap().read_to_string(&mut contents).unwrap();
    assert!(contents.contains(r#"authors = ["foo <bar>"]"#));
});

test!(cargo_help {
    assert_that(cargo_process(),
                execs().with_status(0));
    assert_that(cargo_process().arg("help"),
                execs().with_status(0));
    assert_that(cargo_process().arg("-h"),
                execs().with_status(0));
    assert_that(cargo_process().arg("help").arg("build"),
                execs().with_status(0));
    assert_that(cargo_process().arg("build").arg("-h"),
                execs().with_status(0));
    assert_that(cargo_process().arg("help").arg("-h"),
                execs().with_status(0));
    assert_that(cargo_process().arg("help").arg("help"),
                execs().with_status(0));
});
<commit_msg>Add test list_command_resolves_symlinks<commit_after>use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::str;

use cargo_process;
use support::paths;
use support::{cargo_dir, execs, project, mkdir_recursive, ProjectBuilder, ERROR};
use hamcrest::{assert_that};

fn setup() {
}

enum FakeKind<'a> {
    Executable,
    Symlink{target:&'a Path},
}

/// Add an empty file with executable flags (and platform-dependent suffix).
/// TODO: move this to `ProjectBuilder` if other cases using this emerge.
fn fake_file(proj: ProjectBuilder, dir: &Path, name: &str, kind: FakeKind) -> ProjectBuilder {
    let path = proj.root().join(dir).join(&format!("{}{}", name,
                                                   env::consts::EXE_SUFFIX));
    mkdir_recursive(path.parent().unwrap()).unwrap();
    match kind {
        FakeKind::Executable => {
            File::create(&path).unwrap();
            make_executable(&path);
        },
        FakeKind::Symlink{target} => {
            make_symlink(&path,target);
        }
    }
    return proj;

    #[cfg(unix)]
    fn make_executable(p: &Path) {
        use std::os::unix::prelude::*;

        let mut perms = fs::metadata(p).unwrap().permissions();
        let mode = perms.mode();
        perms.set_mode(mode | 0o111);
        fs::set_permissions(p, perms).unwrap();
    }
    #[cfg(windows)]
    fn make_executable(_: &Path) {}
    #[cfg(unix)]
    fn make_symlink(p: &Path, t: &Path) {
        ::std::os::unix::fs::symlink(t,p).expect("Failed to create symlink");
    }
    #[cfg(windows)]
    fn make_symlink(_: &Path, _: &Path) {
        panic!("Not supported")
    }
}

fn path() -> Vec<PathBuf> {
    env::split_paths(&env::var_os("PATH").unwrap_or(OsString::new())).collect()
}

test!(list_command_looks_at_path {
    let proj = project("list-non-overlapping");
    let proj = fake_file(proj, &Path::new("path-test"), "cargo-1", FakeKind::Executable);
    let mut pr = cargo_process();

    let mut path = path();
    path.push(proj.root().join("path-test"));
    let path = env::join_paths(path.iter()).unwrap();
    let output = pr.arg("-v").arg("--list")
                   .env("PATH", &path);
    let output = output.exec_with_output().unwrap();
    let output = str::from_utf8(&output.stdout).unwrap();
    assert!(output.contains("\n    1\n"), "missing 1: {}", output);
});

// windows and symlinks don't currently agree that well
#[cfg(unix)]
test!(list_command_resolves_symlinks {
    let proj = project("list-non-overlapping");
    let proj = fake_file(proj, &Path::new("path-test"), "cargo-2", FakeKind::Symlink{target:&cargo_dir().join("cargo")});
    let mut pr = cargo_process();

    let mut path = path();
    path.push(proj.root().join("path-test"));
    let path = env::join_paths(path.iter()).unwrap();
    let output = pr.arg("-v").arg("--list")
                   .env("PATH", &path);
    let output = output.exec_with_output().unwrap();
    let output = str::from_utf8(&output.stdout).unwrap();
    assert!(output.contains("\n    2\n"), "missing 2: {}", output);
});

test!(find_closest_biuld_to_build {
    let mut pr = cargo_process();
    pr.arg("biuld");

    assert_that(pr,
                execs().with_status(101)
                       .with_stderr(&format!("{error} no such subcommand

<tab>Did you mean `build`?

",
error = ERROR)));
});

// if a subcommand is more than 3 edit distance away, we don't make a suggestion
test!(find_closest_dont_correct_nonsense {
    let mut pr = cargo_process();
    pr.arg("there-is-no-way-that-there-is-a-command-close-to-this")
      .cwd(&paths::root());

    assert_that(pr,
                execs().with_status(101)
                       .with_stderr(&format!("{error} no such subcommand
",
error = ERROR)));
});

test!(override_cargo_home {
    let root = paths::root();
    let my_home = root.join("my_home");
    fs::create_dir(&my_home).unwrap();
    File::create(&my_home.join("config")).unwrap().write_all(br#"
        [cargo-new]
        name = "foo"
        email = "bar"
        git = false
    "#).unwrap();

    assert_that(cargo_process()
                    .arg("new").arg("foo")
                    .env("USER", "foo")
                    .env("CARGO_HOME", &my_home),
                execs().with_status(0));

    let toml = paths::root().join("foo/Cargo.toml");
    let mut contents = String::new();
    File::open(&toml).unwrap().read_to_string(&mut contents).unwrap();
    assert!(contents.contains(r#"authors = ["foo <bar>"]"#));
});

test!(cargo_help {
    assert_that(cargo_process(),
                execs().with_status(0));
    assert_that(cargo_process().arg("help"),
                execs().with_status(0));
    assert_that(cargo_process().arg("-h"),
                execs().with_status(0));
    assert_that(cargo_process().arg("help").arg("build"),
                execs().with_status(0));
    assert_that(cargo_process().arg("build").arg("-h"),
                execs().with_status(0));
    assert_that(cargo_process().arg("help").arg("-h"),
                execs().with_status(0));
    assert_that(cargo_process().arg("help").arg("help"),
                execs().with_status(0));
});
<|endoftext|>


<commit_before>use codemap::Spanned;
use lexer::Token;
use std::iter::Peekable;
use std::slice::Iter;
use self::ParseErrorKind::*;

type ParseIter<'a> = Peekable<Iter<'a, Spanned<Token>>>;
type Parse = Result<Node, ParseErrorKind>;

#[derive(Debug, Clone, PartialEq)]
pub enum Node {
  Block(Vec<Node>),
  If {
    cond: Box<Node>,
  },
  Stmt(Box<Node>),
  Break,
  Continue,
  Expr,
  Pass,
  Index {
    lhs: Box<Node>,
    rhs: Box<Node>,
  },

  Method {
    owner: Box<Node>,
    method: Box<Node>,
    args: Vec<Node>,
  },

  Lambda {
    params: Vec<String>,
    expr: Box<Node>,
  },

  Func {
    func: Box<Node>,
    args: Vec<Node>,
  },

  BinExpr {
    lhs: Box<Node>,
    op: Token,
    rhs: Box<Node>,
  },

  UnExpr {
    val: Box<Node>,
    op: Token,
  },

  // Literals
  Null,
  Bool(bool),
  Float(f64),
  Int(i64),
  Str(String),
  Name(String),
  Table,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Op {
  Right(u32),
  Left(u32),
  None,
}

#[derive(Debug, Clone, PartialEq)]
pub enum ParseErrorKind {
  UnexpectedToken(Token),
  UnexpectedEOF,
  UnknownBinaryOperator,
  UnknownUnaryOperator,
}

// Return true if the next token in `it` is `kind`
fn peek_token(it: &mut ParseIter, kind: Token) -> bool {
  if let Some(&tok) = it.peek() {
    tok.node == kind
  } else {
    false
  }
}

// Return true if the next token in `it` is `kind` *and* consume the token
fn use_token(it: &mut ParseIter, kind: Token) -> bool {
  if let Some(&tok) = it.peek() {
    if tok.node == kind {
      it.next();
    }
    tok.node == kind
  } else {
    false
  }
}

// Panic if the next token in `it` is *not* `kind`
fn require_token(it: &mut ParseIter, kind: Token) -> Result<(), ParseErrorKind> {
  if let Some(&tok) = it.peek() {
    if tok.node == kind {
      it.next();
      return Ok(());
    }

    return Err(UnexpectedToken(tok.node.clone()));
  }

  return Err(UnexpectedEOF);
}

fn op_precedence(op: &Token) -> Op {
  match *op {
    Token::Add | Token::Sub => Op::Left(10),
    Token::Div | Token::Mul => Op::Left(20),
    Token::Car => Op::Right(30),
    _ => Op::None,
  }
}

fn parse_il_expr(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Or => {
        it.next();
        let params = parse_fn_params(it)?;
        require_token(it, Token::Or)?;
        let expr = parse_il_expr(it)?;
        Ok(Node::Lambda {
          params: params,
          expr: Box::new(expr),
        })
      }
      _ => parse_bin_expr(it),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_bin_expr(it: &mut ParseIter) -> Parse {
  let mut expr = parse_un_expr(it)?;

  // prevents this from breaking the LHS until we know we made it
  // otherwise, things like (2 + 3) * 4 get restructured into 2 + (3 * 4)
  let mut break_left = false;

  while let Some(&tok) = it.peek() {
    let prec = op_precedence(&tok.node);

    if let Op::None = prec {
      break;
    }

    it.next();

    let rhs = parse_un_expr(it)?;

    expr = match (break_left, expr.clone()) {
      (
        true,
        Node::BinExpr {
          lhs: cur_lhs,
          op: cur_op,
          rhs: cur_rhs,
        },
      ) => {
        let cur_prec = op_precedence(&cur_op);
        match (cur_prec, prec) {
          // these should never happen
          (_, Op::None) => break,
          (Op::None, _) => break,

          // left-to-right
          // there has to be a better way to handle this, no?
          (Op::Left(n), Op::Left(m)) if n >= m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },
          (Op::Right(n), Op::Right(m)) if n > m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },
          (Op::Right(n), Op::Left(m)) if n >= m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },
          (Op::Left(n), Op::Right(m)) if n >= m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },

          // right-to-left
          _ => Node::BinExpr {
            lhs: cur_lhs,
            op: cur_op,
            rhs: Box::new(Node::BinExpr {
              lhs: cur_rhs,
              op: tok.node.clone(),
              rhs: Box::new(rhs),
            }),
          },
        }
      }
      _ => Node::BinExpr {
        lhs: Box::new(expr),
        op: tok.node.clone(),
        rhs: Box::new(rhs),
      },
    };

    break_left = true;
  }

  Ok(expr)
}

fn parse_un_expr(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Sub | Token::Not | Token::Neg => {
        it.next();
        let val = parse_un_expr(it)?;
        Ok(Node::UnExpr {
          op: tok.node.clone(),
          val: Box::new(val),
        })
      }
      _ => parse_simple(it),
    };
  }

  Err(UnexpectedEOF)
}

/* unused, here for reference
fn parse_index(it: &mut ParseIter) -> Parse {
  if let Some(&c) = it.peek() {
    return match c.node {
      Token::Sql => {
        it.next();
        let idx = parse_bin_expr(it)?;
        require_token(it, Token::Sqr)?;
        Ok(idx)
      }
      Token::Dot => {
        it.next();
        parse_name_as_str(it)
      }
      _ => {
        Err(UnexpectedToken(c.node.clone()))
      }
    };
  }

  Err(UnexpectedEOF)
}
*/

fn parse_fn_params(it: &mut ParseIter) -> Result<Vec<String>, ParseErrorKind> {
  let mut params: Vec<String> = Vec::new();
  while let Some(&tok) = it.peek() {
    match tok.node {
      Token::Name(ref x) => {
        it.next();
        params.push(x.to_string());
        if !use_token(it, Token::Com) {
          break;
        }
      }
      _ => break,
    }
  }

  Ok(params)
}

fn parse_fn_args(it: &mut ParseIter) -> Result<Vec<Node>, ParseErrorKind> {
  require_token(it, Token::Pal)?;
  // FIXME
  require_token(it, Token::Par)?;
  Ok(Vec::new())
}

fn parse_simple(it: &mut ParseIter) -> Parse {
  let mut atom = parse_atom(it)?;
  while let Some(&tok) = it.peek() {
    match tok.node {
      Token::Col => {
        it.next();
        let method = parse_name_as_str(it)?;
        let args = parse_fn_args(it)?;
        atom = Node::Method {
          owner: Box::new(atom),
          method: Box::new(method),
          args: args,
        };
      }

      Token::Pal => {
        let args = parse_fn_args(it)?;
        atom = Node::Func {
          func: Box::new(atom),
          args: args,
        };
      }

      Token::Sql => {
        it.next();
        let idx = parse_bin_expr(it)?;
        require_token(it, Token::Sqr)?;
        atom = Node::Index {
          lhs: Box::new(atom),
          rhs: Box::new(idx),
        };
      }

      Token::Dot => {
        it.next();
        let idx = parse_name_as_str(it)?;
        atom = Node::Index {
          lhs: Box::new(atom),
          rhs: Box::new(idx),
        };
      }

      _ => break,
    }
  }

  Ok(atom)
}

fn parse_atom(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Pal => {
        it.next();
        let out = parse_bin_expr(it)?;
        require_token(it, Token::Par)?;
        Ok(out)
      }
      _ => parse_quark(it),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_name_as_str(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Name(ref x) => {
        it.next();
        Ok(Node::Str(x.clone()))
      }
      ref x => Err(UnexpectedToken(x.clone())),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_name(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Name(ref x) => {
        it.next();
        Ok(Node::Name(x.clone()))
      }
      ref x => Err(UnexpectedToken(x.clone())),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_quark(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Null => {
        it.next();
        Ok(Node::Null)
      }
      Token::Bool(x) => {
        it.next();
        Ok(Node::Bool(x))
      }
      Token::Float(x) => {
        it.next();
        Ok(Node::Float(x))
      }
      Token::Int(x) => {
        it.next();
        Ok(Node::Int(x))
      }
      Token::Str(ref x) => {
        it.next();
        Ok(Node::Str(x.clone()))
      }
      Token::Name(ref x) => {
        it.next();
        Ok(Node::Name(x.clone()))
      }
      Token::Table => {
        it.next();
        Ok(Node::Table)
      }
      ref x => Err(UnexpectedToken(x.clone())),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_stmt(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Break => {
        it.next();
        Ok(Node::Break)
      }

      Token::Continue => {
        it.next();
        Ok(Node::Continue)
      }

      Token::Pass => {
        it.next();
        Ok(Node::Pass)
      }

      _ => parse_il_expr(it).map(|expr| Node::Stmt(Box::new(expr))),
    };
  }

  Err(UnexpectedEOF)
}

pub fn parse(tokens: Vec<Spanned<Token>>) -> Parse {
  let mut it: ParseIter = tokens.iter().peekable();
  let mut nodes: Vec<Node> = vec![];

  while !peek_token(&mut it, Token::EOF) {
    nodes.push(parse_stmt(&mut it)?);
    require_token(&mut it, Token::End)?;
  }

  Ok(Node::Block(nodes))
}

#[cfg(test)]
#[path = "./tests/parser.rs"]
mod tests;
<commit_msg>parser: Add parse_ml_expr()<commit_after>use codemap::Spanned;
use lexer::Token;
use std::iter::Peekable;
use std::slice::Iter;
use self::ParseErrorKind::*;

type ParseIter<'a> = Peekable<Iter<'a, Spanned<Token>>>;
type Parse = Result<Node, ParseErrorKind>;

#[derive(Debug, Clone, PartialEq)]
pub enum Node {
  Block(Vec<Node>),
  Catch(Vec<Node>),
  If {
    cond: Box<Node>,
  },
  Stmt(Box<Node>),
  Break,
  Continue,
  Expr,
  Pass,
  Index {
    lhs: Box<Node>,
    rhs: Box<Node>,
  },

  Method {
    owner: Box<Node>,
    method: Box<Node>,
    args: Vec<Node>,
  },

  Func {
    params: Vec<String>,
    body: Vec<Node>,
  },

  Lambda {
    params: Vec<String>,
    expr: Box<Node>,
  },

  Call {
    func: Box<Node>,
    args: Vec<Node>,
  },

  BinExpr {
    lhs: Box<Node>,
    op: Token,
    rhs: Box<Node>,
  },

  UnExpr {
    val: Box<Node>,
    op: Token,
  },

  // Literals
  Null,
  Bool(bool),
  Float(f64),
  Int(i64),
  Str(String),
  Name(String),
  Table,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Op {
  Right(u32),
  Left(u32),
  None,
}

#[derive(Debug, Clone, PartialEq)]
pub enum ParseErrorKind {
  UnexpectedToken(Token),
  UnexpectedEOF,
  UnknownBinaryOperator,
  UnknownUnaryOperator,
}

// Return true if the next token in `it` is `kind`
fn peek_token(it: &mut ParseIter, kind: Token) -> bool {
  if let Some(&tok) = it.peek() {
    tok.node == kind
  } else {
    false
  }
}

// Return true if the next token in `it` is `kind` *and* consume the token
fn use_token(it: &mut ParseIter, kind: Token) -> bool {
  if let Some(&tok) = it.peek() {
    if tok.node == kind {
      it.next();
    }
    tok.node == kind
  } else {
    false
  }
}

// Panic if the next token in `it` is *not* `kind`
fn require_token(it: &mut ParseIter, kind: Token) -> Result<(), ParseErrorKind> {
  if let Some(&tok) = it.peek() {
    if tok.node == kind {
      it.next();
      return Ok(());
    }

    return Err(UnexpectedToken(tok.node.clone()));
  }

  return Err(UnexpectedEOF);
}

fn op_precedence(op: &Token) -> Op {
  match *op {
    Token::Add | Token::Sub => Op::Left(10),
    Token::Div | Token::Mul => Op::Left(20),
    Token::Car => Op::Right(30),
    _ => Op::None,
  }
}

fn parse_ml_expr(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Func => {
        it.next();
        require_token(it, Token::Pal)?;
        let params = parse_fn_params(it)?;
        require_token(it, Token::Par)?;
        let body  = parse_block(it)?;
        Ok(Node::Func {
            params: params,
            body: body,
        })
      }
      Token::Catch => {
        it.next();
        let block = parse_block(it)?;
        Ok(Node::Catch(block))
      }
      _ => parse_il_expr(it),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_il_expr(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Or => {
        it.next();
        let params = parse_fn_params(it)?;
        require_token(it, Token::Or)?;
        let expr = parse_il_expr(it)?;
        Ok(Node::Lambda {
          params: params,
          expr: Box::new(expr),
        })
      }
      _ => parse_bin_expr(it),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_bin_expr(it: &mut ParseIter) -> Parse {
  let mut expr = parse_un_expr(it)?;

  // prevents this from breaking the LHS until we know we made it
  // otherwise, things like (2 + 3) * 4 get restructured into 2 + (3 * 4)
  let mut break_left = false;

  while let Some(&tok) = it.peek() {
    let prec = op_precedence(&tok.node);

    if let Op::None = prec {
      break;
    }

    it.next();

    let rhs = parse_un_expr(it)?;

    expr = match (break_left, expr.clone()) {
      (
        true,
        Node::BinExpr {
          lhs: cur_lhs,
          op: cur_op,
          rhs: cur_rhs,
        },
      ) => {
        let cur_prec = op_precedence(&cur_op);
        match (cur_prec, prec) {
          // these should never happen
          (_, Op::None) => break,
          (Op::None, _) => break,

          // left-to-right
          // there has to be a better way to handle this, no?
          (Op::Left(n), Op::Left(m)) if n >= m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },
          (Op::Right(n), Op::Right(m)) if n > m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },
          (Op::Right(n), Op::Left(m)) if n >= m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },
          (Op::Left(n), Op::Right(m)) if n >= m => Node::BinExpr {
            lhs: Box::new(expr),
            op: tok.node.clone(),
            rhs: Box::new(rhs),
          },

          // right-to-left
          _ => Node::BinExpr {
            lhs: cur_lhs,
            op: cur_op,
            rhs: Box::new(Node::BinExpr {
              lhs: cur_rhs,
              op: tok.node.clone(),
              rhs: Box::new(rhs),
            }),
          },
        }
      }
      _ => Node::BinExpr {
        lhs: Box::new(expr),
        op: tok.node.clone(),
        rhs: Box::new(rhs),
      },
    };

    break_left = true;
  }

  Ok(expr)
}

fn parse_un_expr(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Sub | Token::Not | Token::Neg => {
        it.next();
        let val = parse_un_expr(it)?;
        Ok(Node::UnExpr {
          op: tok.node.clone(),
          val: Box::new(val),
        })
      }
      _ => parse_simple(it),
    };
  }

  Err(UnexpectedEOF)
}

/* unused, here for reference
fn parse_index(it: &mut ParseIter) -> Parse {
  if let Some(&c) = it.peek() {
    return match c.node {
      Token::Sql => {
        it.next();
        let idx = parse_bin_expr(it)?;
        require_token(it, Token::Sqr)?;
        Ok(idx)
      }
      Token::Dot => {
        it.next();
        parse_name_as_str(it)
      }
      _ => {
        Err(UnexpectedToken(c.node.clone()))
      }
    };
  }

  Err(UnexpectedEOF)
}
*/

fn parse_fn_params(it: &mut ParseIter) -> Result<Vec<String>, ParseErrorKind> {
  let mut params: Vec<String> = Vec::new();
  while let Some(&tok) = it.peek() {
    match tok.node {
      Token::Name(ref x) => {
        it.next();
        params.push(x.to_string());
        if !use_token(it, Token::Com) {
          break;
        }
      }
      _ => break,
    }
  }

  Ok(params)
}

fn parse_fn_args(it: &mut ParseIter) -> Result<Vec<Node>, ParseErrorKind> {
  require_token(it, Token::Pal)?;
  // FIXME
  require_token(it, Token::Par)?;
  Ok(Vec::new())
}

fn parse_simple(it: &mut ParseIter) -> Parse {
  let mut atom = parse_atom(it)?;
  while let Some(&tok) = it.peek() {
    match tok.node {
      Token::Col => {
        it.next();
        let method = parse_name_as_str(it)?;
        let args = parse_fn_args(it)?;
        atom = Node::Method {
          owner: Box::new(atom),
          method: Box::new(method),
          args: args,
        };
      }

      Token::Pal => {
        let args = parse_fn_args(it)?;
        atom = Node::Call {
          func: Box::new(atom),
          args: args,
        };
      }

      Token::Sql => {
        it.next();
        let idx = parse_bin_expr(it)?;
        require_token(it, Token::Sqr)?;
        atom = Node::Index {
          lhs: Box::new(atom),
          rhs: Box::new(idx),
        };
      }

      Token::Dot => {
        it.next();
        let idx = parse_name_as_str(it)?;
        atom = Node::Index {
          lhs: Box::new(atom),
          rhs: Box::new(idx),
        };
      }

      _ => break,
    }
  }

  Ok(atom)
}

fn parse_atom(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Pal => {
        it.next();
        let out = parse_bin_expr(it)?;
        require_token(it, Token::Par)?;
        Ok(out)
      }
      _ => parse_quark(it),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_name_as_str(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Name(ref x) => {
        it.next();
        Ok(Node::Str(x.clone()))
      }
      ref x => Err(UnexpectedToken(x.clone())),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_name(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Name(ref x) => {
        it.next();
        Ok(Node::Name(x.clone()))
      }
      ref x => Err(UnexpectedToken(x.clone())),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_quark(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Null => {
        it.next();
        Ok(Node::Null)
      }
      Token::Bool(x) => {
        it.next();
        Ok(Node::Bool(x))
      }
      Token::Float(x) => {
        it.next();
        Ok(Node::Float(x))
      }
      Token::Int(x) => {
        it.next();
        Ok(Node::Int(x))
      }
      Token::Str(ref x) => {
        it.next();
        Ok(Node::Str(x.clone()))
      }
      Token::Name(ref x) => {
        it.next();
        Ok(Node::Name(x.clone()))
      }
      Token::Table => {
        it.next();
        Ok(Node::Table)
      }
      ref x => Err(UnexpectedToken(x.clone())),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_stmt(it: &mut ParseIter) -> Parse {
  if let Some(&tok) = it.peek() {
    return match tok.node {
      Token::Break => {
        it.next();
        Ok(Node::Break)
      }

      Token::Continue => {
        it.next();
        Ok(Node::Continue)
      }

      Token::Pass => {
        it.next();
        Ok(Node::Pass)
      }

      _ => parse_ml_expr(it).map(|expr| Node::Stmt(Box::new(expr))),
    };
  }

  Err(UnexpectedEOF)
}

fn parse_block(it: &mut ParseIter) -> Result<Vec<Node>, ParseErrorKind> {
  let mut nodes: Vec<Node> = vec![];

  require_token(it, Token::Enter)?;

  while !peek_token(it, Token::Exit) {
    let stmt = parse_stmt(it)?;
    nodes.push(stmt);
    require_token(it, Token::End)?;
  }

  require_token(it, Token::Exit)?;

  Ok(nodes)
}

pub fn parse(tokens: Vec<Spanned<Token>>) -> Parse {
  let mut it: ParseIter = tokens.iter().peekable();
  let mut nodes: Vec<Node> = vec![];

  while !peek_token(&mut it, Token::EOF) {
    let stmt = parse_stmt(&mut it)?;
    nodes.push(stmt);
    require_token(&mut it, Token::End)?;
  }

  Ok(Node::Block(nodes))
}

#[cfg(test)]
#[path = "./tests/parser.rs"]
mod tests;
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use {Word, Result};
use private::layout::{StructReader, StructBuilder, StructSize, PointerBuilder, PointerReader};

pub trait FromStructReader<'a> {
    fn new(reader : StructReader<'a>) -> Self;
}

pub trait HasStructSize {
    fn struct_size(unused_self : Option<Self>) -> StructSize;
}

pub trait FromStructBuilder<'a> {
    fn new(structBuilder : StructBuilder<'a>) -> Self;
}

pub trait FromPointerReader<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>) -> Result<Self>;
}

pub trait FromPointerReaderRefDefault<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>, default_value : *const Word) -> Self;
}

pub trait FromPointerBuilder<'a> {
    fn init_pointer(PointerBuilder<'a>, u32) -> Self;
    fn get_from_pointer(builder : PointerBuilder<'a>) -> Result<Self>;
}

pub trait FromPointerBuilderRefDefault<'a> {
    fn get_from_pointer(builder : PointerBuilder<'a>, default_value : *const Word) -> Self;
}

pub trait SetPointerBuilder<To> {
    fn set_pointer_builder<'a>(PointerBuilder<'a>, Self) -> Result<()>;
}

pub trait HasTypeId {
    fn type_id(unused_self : Option<Self>) -> u64;
}

pub trait CastableTo<T> {
    fn cast(self) -> T;
}

pub trait ToU16 {
    fn to_u16(self) -> u16;
}

pub trait FromU16 {
    fn from_u16(value : u16) -> ::std::result::Result<Self, ::NotInSchema>;
}

pub trait IndexMove<I, T> {
    fn index_move(&self, index : I) -> T;
}

pub struct ListIter<T, U> {
    marker : ::std::marker::PhantomData<U>,
    list : T,
    index : u32,
    size : u32,
}

impl <T, U> ListIter<T, U> {
    pub fn new(list : T, size : u32) -> ListIter<T, U> {
        ListIter { list : list, index : 0, size : size, marker : ::std::marker::PhantomData }
    }
}

impl <U, T : IndexMove<u32, U>> ::std::iter::Iterator for ListIter<T, U> {
    type Item = U;
    fn next(&mut self) -> ::std::option::Option<U> {
        if self.index < self.size {
            let result = self.list.index_move(self.index);
            self.index += 1;
            return Some(result);
        } else {
            return None;
        }
    }
}
<commit_msg>Remove unused parameter in trait HasTypeId<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use {Word, Result};
use private::layout::{StructReader, StructBuilder, StructSize, PointerBuilder, PointerReader};

pub trait FromStructReader<'a> {
    fn new(reader : StructReader<'a>) -> Self;
}

pub trait HasStructSize {
    fn struct_size(unused_self : Option<Self>) -> StructSize;
}

pub trait FromStructBuilder<'a> {
    fn new(structBuilder : StructBuilder<'a>) -> Self;
}

pub trait FromPointerReader<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>) -> Result<Self>;
}

pub trait FromPointerReaderRefDefault<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>, default_value : *const Word) -> Self;
}

pub trait FromPointerBuilder<'a> {
    fn init_pointer(PointerBuilder<'a>, u32) -> Self;
    fn get_from_pointer(builder : PointerBuilder<'a>) -> Result<Self>;
}

pub trait FromPointerBuilderRefDefault<'a> {
    fn get_from_pointer(builder : PointerBuilder<'a>, default_value : *const Word) -> Self;
}

pub trait SetPointerBuilder<To> {
    fn set_pointer_builder<'a>(PointerBuilder<'a>, Self) -> Result<()>;
}

pub trait HasTypeId {
    fn type_id() -> u64;
}

pub trait CastableTo<T> {
    fn cast(self) -> T;
}

pub trait ToU16 {
    fn to_u16(self) -> u16;
}

pub trait FromU16 {
    fn from_u16(value : u16) -> ::std::result::Result<Self, ::NotInSchema>;
}

pub trait IndexMove<I, T> {
    fn index_move(&self, index : I) -> T;
}

pub struct ListIter<T, U> {
    marker : ::std::marker::PhantomData<U>,
    list : T,
    index : u32,
    size : u32,
}

impl <T, U> ListIter<T, U> {
    pub fn new(list : T, size : u32) -> ListIter<T, U> {
        ListIter { list : list, index : 0, size : size, marker : ::std::marker::PhantomData }
    }
}

impl <U, T : IndexMove<u32, U>> ::std::iter::Iterator for ListIter<T, U> {
    type Item = U;
    fn next(&mut self) -> ::std::option::Option<U> {
        if self.index < self.size {
            let result = self.list.index_move(self.index);
            self.index += 1;
            return Some(result);
        } else {
            return None;
        }
    }
}
<|endoftext|>


<commit_before>// see 'rustc -W help'
#![warn(
    missing_docs,
    unused,
    unused_results,
    nonstandard_style,
    rust_2018_compatibility,
    rust_2018_idioms
)]

//! Test all of the tests in chats/, which are .txt files formatted with IRC
//! input beginning with <, expected IRC output beginning with >, and expected
//! github output beginning with !.

use futures::prelude::*;
use futures::task::Poll;
use irc::client::prelude::{Client as IrcClient, Config as IrcConfig};
use lazy_static::lazy_static;
use log::{debug, info};
use std::cell::{Cell, RefCell};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpListener;
use tokio::time::{Duration, Instant};
use wgmeeting_github_ircbot::*;

const MOCK_SERVER_HOST: &str = "127.0.0.1";
const MOCK_SERVER_PORT: u16 = 43210;

#[tokio::test]
async fn test_chats() -> Result<(), failure::Error> {
    env_logger::init();

    let chats_dir = Path::new(file!()).parent().unwrap().join("chats");
    info!("Going through {:?}", chats_dir);
    let mut fail_count = 0;
    for direntry in chats_dir.read_dir()? {
        if let Ok(direntry) = direntry {
            if !test_one_chat(direntry.path().as_path()).await? {
                fail_count += 1;
            }
        }
    }
    assert!(
        fail_count == 0,
        "{} chat test failure(s), see above",
        fail_count
    );

    Ok(())
}

async fn test_one_chat(path: &Path) -> Result<bool, failure::Error> {
    info!("Testing {:?}", path);

    // We're given the path to a file (the chat file) that represents a dialog between the bot
    // and other users on the IRC server, and also contains the comments the bot makes on github
    // issues.

    // All of the lines in the chat file, as a vec (lines) of vecs (bytes).
    let chat_file_lines = {
        let mut file_bytes: Vec<u8> = Vec::new();
        let _size = File::open(path)?.read_to_end(&mut file_bytes)?;
        file_bytes
    }
    .split(|byte| *byte == '\n' as u8)
    .map(|arr| arr.to_vec())
    .collect::<Vec<Vec<u8>>>();

    let is_finished = Cell::new(false);

    let server = mock_irc_server(&chat_file_lines, &is_finished);
    let bot = run_irc_bot(&is_finished);

    let (actual_lines, bot_result) = future::join(server, bot).await;
    let _ = bot_result?;
    let actual_lines = actual_lines?;

    let actual_str = str::from_utf8(actual_lines.as_slice())?;
    let expected_lines = chat_lines_to_expected_lines(&chat_file_lines);
    let expected_str = str::from_utf8(expected_lines.as_slice())?;
    let test_pass = actual_str == expected_str;
    println!("\n{:?} {}", path, if test_pass { "PASS" } else { "FAIL" });
    if !test_pass {
        for d in diff::lines(expected_str, actual_str) {
            match d {
                diff::Result::Left(actual) => println!("-{}", actual),
                diff::Result::Both(actual, _) => println!(" {}", actual),
                diff::Result::Right(expected) => println!("+{}", expected),
            }
        }
    }

    Ok(test_pass)
}

/// Run the fake IRC server for the chat test, driving the dialog based on the chat file.
/// Record the entire conversation and return that recording for comparison with the expected
/// result.
async fn mock_irc_server(
    chat_file_lines: &Vec<Vec<u8>>,
    is_finished: &Cell<bool>,
) -> Result<Vec<u8>, failure::Error> {
    let actual_lines = RefCell::new(Vec::<u8>::new());

    struct WaitLinesData {
        expect_lines: i32,
        wait_deadline: Instant,
    }

    impl WaitLinesData {
        pub fn should_wait(&self) -> bool {
            let time_remains = self.wait_deadline > Instant::now();
            let result = self.expect_lines > 0 && time_remains;
            debug!(
                "should_wait: expect_lines={}, time_remains={} ==> {}",
                self.expect_lines, time_remains, result
            );
            if !time_remains {
                info!("wait for {} lines timed out", self.expect_lines);
            }
            result
        }
    }

    const WAIT_DURATION: Duration = Duration::from_millis(100u64);
    const SERVER_SHUTDOWN_DURATION: Duration = Duration::from_millis(10u64);

    let wait_lines_data = RefCell::new(WaitLinesData {
        expect_lines: 3, // length of identify sequence
        wait_deadline: Instant::now() + WAIT_DURATION,
    });

    let irc_server_addr = format!("{}:{}", MOCK_SERVER_HOST, MOCK_SERVER_PORT);
    let mut irc_server_listener = TcpListener::bind(&irc_server_addr).await?;
    let (mut tcp_stream, _socket_addr) = irc_server_listener.accept().await?;
    tcp_stream.set_nodelay(true)?;
    debug!(
        "IRC server got incoming connection: nodelay={} recv_buffer_size={} send_buffer_size={}",
        tcp_stream.nodelay()?,
        tcp_stream.recv_buffer_size()?,
        tcp_stream.send_buffer_size()?
    );
    let (reader, mut writer) = tcp_stream.split();

    let reader_future = async {
        let mut lines = BufReader::new(reader).lines();
        while let Some(line) = lines.next_line().await? {
            if line.starts_with("PING ") {
                continue;
            }
            debug!("IRC server read line: {}", line);

            {
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines - 1;
            }

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.append(&mut ">".bytes().collect());
                actual_lines.extend_from_slice(
                    line.chars()
                        .flat_map(|c| c.escape_default())
                        .collect::<String>()
                        .as_bytes(),
                );
                actual_lines.append(&mut "\r\n".bytes().collect());
            }
        }

        Ok::<(), failure::Error>(())
    };

    let writer_future = async {
        for line in chat_file_lines.iter() {
            let first_char = line.first().map(|b| *b as char);
            if first_char == Some('>') || first_char == Some('!') {
                // This is a line we should expect to recieve from the bot.  Note this in
                // |wait_lines_data|, which |reader_future| will use to adjust its timing.
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines + 1;
                wait_lines_data.wait_deadline = Instant::now() + WAIT_DURATION;
            }

            if first_char != Some('<') {
                continue;
            }

            while wait_lines_data.borrow().should_wait() {
                tokio::time::delay_for(Duration::from_millis(1)).await;
            }

            // note that line still begins with '<'
            // FIXME: Clean up this total hack for \u{1} !
            // (The other direction uses escape_default().)
            let mut line_str = str::from_utf8(&line[1..])?.replace("\\u{1}", "\u{1}");
            debug!("IRC server writing line: {}", line_str);
            line_str.push_str("\r\n");

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.extend_from_slice(&line);
                actual_lines.append(&mut "\r\n".bytes().collect());
            }

            let _ = writer.write_all(line_str.as_bytes()).await?;
        }

        tokio::time::delay_for(SERVER_SHUTDOWN_DURATION).await;

        debug!("SHUTTING DOWN THE SERVER");
        is_finished.set(true);
        // This seems (to my surprise) to be good enough to make the reader terminate as well.
        writer.shutdown().await?;

        Ok::<(), failure::Error>(())
    };

    let (reader_result, writer_result) = future::join(reader_future, writer_future).await;
    reader_result?;
    writer_result?;

    Ok(actual_lines.into_inner())
}

/// Run the IRC bot side of the chat test (i.e., the code we're testing).
async fn run_irc_bot(is_finished: &Cell<bool>) -> Result<(), failure::Error> {
    let irc_config = IrcConfig {
        use_mock_connection: false,
        owners: vec![format!("dbaron")],
        nickname: Some(format!("test-github-bot")),
        alt_nicks: vec![format!("test-github-bot-"), format!("test-github-bot--")],
        username: Some(format!("dbaron-gh-bot")),
        realname: Some(format!("Bot to add meeting minutes to github issues.")),
        server: Some(format!("{}", MOCK_SERVER_HOST)),
        port: Some(MOCK_SERVER_PORT),
        use_tls: Some(false),
        encoding: Some(format!("UTF-8")),
        channels: vec![format!("#meetingbottest"), format!("#testchannel2")],
        user_info: Some(format!("Bot to add meeting minutes to github issues.")),

        // In testing mode, we send the github comments as IRC messages, so we
        // need to be able to handle more substantial bursts of messages
        // without delay.
        burst_window_length: Some(0),
        max_messages_in_burst: Some(50),
        ..Default::default()
    };
    lazy_static! {
        static ref BOT_CONFIG: BotConfig = BotConfig {
            source: "https://github.com/dbaron/wgmeeting-github-ircbot".to_string(),
            channels: vec![
                (format!("#meetingbottest"), ChannelConfig {
                    group: format!("Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                        "dbaron/nonexistentrepo".to_string(),
                        "upsuper/*".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
                (format!("#testchannel2"), ChannelConfig {
                    group: format!("Second Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
            ].into_iter().collect(),
            // Use of a 0 value disables timeouts, which is needed to avoid intermittent
            // failures (using really-0 timeouts) or having the event loop wait until the
            // timeout completes (positive timeouts).
            activity_timeout_minutes: 0,
            owners: vec![format!("dbaron")],
            ..Default::default()
        };
    }

    let mut irc_state = IRCState::new(GithubType::MockGithubConnection);

    let irc_client: &'static mut _ = Box::leak(Box::new(IrcClient::from_config(irc_config).await?));

    irc_client.identify()?;

    let finished_cb = {
        future::poll_fn(move |_cx| {
            if is_finished.get() {
                debug!("in take_until callback for messages stream: terminating");
                Poll::Ready(())
            } else {
                debug!("in take_until callback for messages stream: continuing");
                Poll::Pending
            }
        })
    };

    let mut irc_stream = irc_client.stream()?.take_until(finished_cb);
    while let Some(message) = irc_stream.next().await.transpose()? {
        // This is where we call into the IRC bot's logic, i.e., where we call the code that's
        // being tested here.
        process_irc_message(irc_client, &mut irc_state, &BOT_CONFIG, message);
    }

    Ok(())
}

/// Convert the lines in the chat file to the dialog that the test should expect to have been
/// recorded by the IRC server.
fn chat_lines_to_expected_lines(chat_file_lines: &Vec<Vec<u8>>) -> Vec<u8> {
    let mut expected_lines = ">CAP END\r\n>NICK test-github-bot\r\n>USER dbaron-gh-bot 0 * :Bot to add meeting minutes to github issues.\r\n".bytes().collect::<Vec<u8>>();

    for line in chat_file_lines.iter() {
        match line.first().map(|b| *b as char) {
            // just skip blank lines (and the empty string after the last
            // line, given the use of split)
            None => (),
            Some('<') => {
                let line = str::from_utf8(line).unwrap();
                expected_lines.extend_from_slice(line.as_bytes());
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('>') => {
                expected_lines.extend(
                    str::from_utf8(line)
                        .unwrap()
                        .replace("[[CODE_DESCRIPTION]]", &*code_description())
                        .bytes(),
                );
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('!') => {
                // for now, we send the github comments over IRC when
                // testing, but we don't encode that into the chat
                // format
                expected_lines.append(&mut ">PRIVMSG github-comments ".bytes().collect());
                // Match use of ":" in the stringify function in irc-proto's
                // src/command.rs.
                if line.len() == 1 || line.contains(&0x20u8 /* space */) {
                    expected_lines.append(&mut ":".bytes().collect());
                }
                expected_lines.extend_from_slice(&line[1..]);
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            _ => {
                panic!(
                    //"Unexpected line in test file {:?}:\n{}",
                    // path,
                    "Unexpected line in test file:\n{}",
                    str::from_utf8(line).unwrap_or("[non-UTF-8 line]")
                );
            }
        }
    }

    expected_lines
}
<commit_msg>Improve unexpected line error message so I stop getting confused when I hit it with a swap file.<commit_after>// see 'rustc -W help'
#![warn(
    missing_docs,
    unused,
    unused_results,
    nonstandard_style,
    rust_2018_compatibility,
    rust_2018_idioms
)]

//! Test all of the tests in chats/, which are .txt files formatted with IRC
//! input beginning with <, expected IRC output beginning with >, and expected
//! github output beginning with !.

use futures::prelude::*;
use futures::task::Poll;
use irc::client::prelude::{Client as IrcClient, Config as IrcConfig};
use lazy_static::lazy_static;
use log::{debug, info};
use std::cell::{Cell, RefCell};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpListener;
use tokio::time::{Duration, Instant};
use wgmeeting_github_ircbot::*;

const MOCK_SERVER_HOST: &str = "127.0.0.1";
const MOCK_SERVER_PORT: u16 = 43210;

#[tokio::test]
async fn test_chats() -> Result<(), failure::Error> {
    env_logger::init();

    let chats_dir = Path::new(file!()).parent().unwrap().join("chats");
    info!("Going through {:?}", chats_dir);
    let mut fail_count = 0;
    for direntry in chats_dir.read_dir()? {
        if let Ok(direntry) = direntry {
            if !test_one_chat(direntry.path().as_path()).await? {
                fail_count += 1;
            }
        }
    }
    assert!(
        fail_count == 0,
        "{} chat test failure(s), see above",
        fail_count
    );

    Ok(())
}

async fn test_one_chat(path: &Path) -> Result<bool, failure::Error> {
    info!("Testing {:?}", path);

    // We're given the path to a file (the chat file) that represents a dialog between the bot
    // and other users on the IRC server, and also contains the comments the bot makes on github
    // issues.

    // All of the lines in the chat file, as a vec (lines) of vecs (bytes).
    let chat_file_lines = {
        let mut file_bytes: Vec<u8> = Vec::new();
        let _size = File::open(path)?.read_to_end(&mut file_bytes)?;
        file_bytes
    }
    .split(|byte| *byte == '\n' as u8)
    .map(|arr| arr.to_vec())
    .collect::<Vec<Vec<u8>>>();

    let is_finished = Cell::new(false);

    let server = mock_irc_server(&chat_file_lines, &is_finished);
    let bot = run_irc_bot(&is_finished);

    let (actual_lines, bot_result) = future::join(server, bot).await;
    let _ = bot_result?;
    let actual_lines = actual_lines?;

    let actual_str = str::from_utf8(actual_lines.as_slice())?;
    let expected_lines = chat_lines_to_expected_lines(path, &chat_file_lines);
    let expected_str = str::from_utf8(expected_lines.as_slice())?;
    let test_pass = actual_str == expected_str;
    println!("\n{:?} {}", path, if test_pass { "PASS" } else { "FAIL" });
    if !test_pass {
        for d in diff::lines(expected_str, actual_str) {
            match d {
                diff::Result::Left(actual) => println!("-{}", actual),
                diff::Result::Both(actual, _) => println!(" {}", actual),
                diff::Result::Right(expected) => println!("+{}", expected),
            }
        }
    }

    Ok(test_pass)
}

/// Run the fake IRC server for the chat test, driving the dialog based on the chat file.
/// Record the entire conversation and return that recording for comparison with the expected
/// result.
async fn mock_irc_server(
    chat_file_lines: &Vec<Vec<u8>>,
    is_finished: &Cell<bool>,
) -> Result<Vec<u8>, failure::Error> {
    let actual_lines = RefCell::new(Vec::<u8>::new());

    struct WaitLinesData {
        expect_lines: i32,
        wait_deadline: Instant,
    }

    impl WaitLinesData {
        pub fn should_wait(&self) -> bool {
            let time_remains = self.wait_deadline > Instant::now();
            let result = self.expect_lines > 0 && time_remains;
            debug!(
                "should_wait: expect_lines={}, time_remains={} ==> {}",
                self.expect_lines, time_remains, result
            );
            if !time_remains {
                info!("wait for {} lines timed out", self.expect_lines);
            }
            result
        }
    }

    const WAIT_DURATION: Duration = Duration::from_millis(100u64);
    const SERVER_SHUTDOWN_DURATION: Duration = Duration::from_millis(10u64);

    let wait_lines_data = RefCell::new(WaitLinesData {
        expect_lines: 3, // length of identify sequence
        wait_deadline: Instant::now() + WAIT_DURATION,
    });

    let irc_server_addr = format!("{}:{}", MOCK_SERVER_HOST, MOCK_SERVER_PORT);
    let mut irc_server_listener = TcpListener::bind(&irc_server_addr).await?;
    let (mut tcp_stream, _socket_addr) = irc_server_listener.accept().await?;
    tcp_stream.set_nodelay(true)?;
    debug!(
        "IRC server got incoming connection: nodelay={} recv_buffer_size={} send_buffer_size={}",
        tcp_stream.nodelay()?,
        tcp_stream.recv_buffer_size()?,
        tcp_stream.send_buffer_size()?
    );
    let (reader, mut writer) = tcp_stream.split();

    let reader_future = async {
        let mut lines = BufReader::new(reader).lines();
        while let Some(line) = lines.next_line().await? {
            if line.starts_with("PING ") {
                continue;
            }
            debug!("IRC server read line: {}", line);

            {
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines - 1;
            }

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.append(&mut ">".bytes().collect());
                actual_lines.extend_from_slice(
                    line.chars()
                        .flat_map(|c| c.escape_default())
                        .collect::<String>()
                        .as_bytes(),
                );
                actual_lines.append(&mut "\r\n".bytes().collect());
            }
        }

        Ok::<(), failure::Error>(())
    };

    let writer_future = async {
        for line in chat_file_lines.iter() {
            let first_char = line.first().map(|b| *b as char);
            if first_char == Some('>') || first_char == Some('!') {
                // This is a line we should expect to recieve from the bot.  Note this in
                // |wait_lines_data|, which |reader_future| will use to adjust its timing.
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines + 1;
                wait_lines_data.wait_deadline = Instant::now() + WAIT_DURATION;
            }

            if first_char != Some('<') {
                continue;
            }

            while wait_lines_data.borrow().should_wait() {
                tokio::time::delay_for(Duration::from_millis(1)).await;
            }

            // note that line still begins with '<'
            // FIXME: Clean up this total hack for \u{1} !
            // (The other direction uses escape_default().)
            let mut line_str = str::from_utf8(&line[1..])?.replace("\\u{1}", "\u{1}");
            debug!("IRC server writing line: {}", line_str);
            line_str.push_str("\r\n");

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.extend_from_slice(&line);
                actual_lines.append(&mut "\r\n".bytes().collect());
            }

            let _ = writer.write_all(line_str.as_bytes()).await?;
        }

        tokio::time::delay_for(SERVER_SHUTDOWN_DURATION).await;

        debug!("SHUTTING DOWN THE SERVER");
        is_finished.set(true);
        // This seems (to my surprise) to be good enough to make the reader terminate as well.
        writer.shutdown().await?;

        Ok::<(), failure::Error>(())
    };

    let (reader_result, writer_result) = future::join(reader_future, writer_future).await;
    reader_result?;
    writer_result?;

    Ok(actual_lines.into_inner())
}

/// Run the IRC bot side of the chat test (i.e., the code we're testing).
async fn run_irc_bot(is_finished: &Cell<bool>) -> Result<(), failure::Error> {
    let irc_config = IrcConfig {
        use_mock_connection: false,
        owners: vec![format!("dbaron")],
        nickname: Some(format!("test-github-bot")),
        alt_nicks: vec![format!("test-github-bot-"), format!("test-github-bot--")],
        username: Some(format!("dbaron-gh-bot")),
        realname: Some(format!("Bot to add meeting minutes to github issues.")),
        server: Some(format!("{}", MOCK_SERVER_HOST)),
        port: Some(MOCK_SERVER_PORT),
        use_tls: Some(false),
        encoding: Some(format!("UTF-8")),
        channels: vec![format!("#meetingbottest"), format!("#testchannel2")],
        user_info: Some(format!("Bot to add meeting minutes to github issues.")),

        // In testing mode, we send the github comments as IRC messages, so we
        // need to be able to handle more substantial bursts of messages
        // without delay.
        burst_window_length: Some(0),
        max_messages_in_burst: Some(50),
        ..Default::default()
    };
    lazy_static! {
        static ref BOT_CONFIG: BotConfig = BotConfig {
            source: "https://github.com/dbaron/wgmeeting-github-ircbot".to_string(),
            channels: vec![
                (format!("#meetingbottest"), ChannelConfig {
                    group: format!("Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                        "dbaron/nonexistentrepo".to_string(),
                        "upsuper/*".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
                (format!("#testchannel2"), ChannelConfig {
                    group: format!("Second Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
            ].into_iter().collect(),
            // Use of a 0 value disables timeouts, which is needed to avoid intermittent
            // failures (using really-0 timeouts) or having the event loop wait until the
            // timeout completes (positive timeouts).
            activity_timeout_minutes: 0,
            owners: vec![format!("dbaron")],
            ..Default::default()
        };
    }

    let mut irc_state = IRCState::new(GithubType::MockGithubConnection);

    let irc_client: &'static mut _ = Box::leak(Box::new(IrcClient::from_config(irc_config).await?));

    irc_client.identify()?;

    let finished_cb = {
        future::poll_fn(move |_cx| {
            if is_finished.get() {
                debug!("in take_until callback for messages stream: terminating");
                Poll::Ready(())
            } else {
                debug!("in take_until callback for messages stream: continuing");
                Poll::Pending
            }
        })
    };

    let mut irc_stream = irc_client.stream()?.take_until(finished_cb);
    while let Some(message) = irc_stream.next().await.transpose()? {
        // This is where we call into the IRC bot's logic, i.e., where we call the code that's
        // being tested here.
        process_irc_message(irc_client, &mut irc_state, &BOT_CONFIG, message);
    }

    Ok(())
}

/// Convert the lines in the chat file to the dialog that the test should expect to have been
/// recorded by the IRC server.
fn chat_lines_to_expected_lines(path: &Path, chat_file_lines: &Vec<Vec<u8>>) -> Vec<u8> {
    let mut expected_lines = ">CAP END\r\n>NICK test-github-bot\r\n>USER dbaron-gh-bot 0 * :Bot to add meeting minutes to github issues.\r\n".bytes().collect::<Vec<u8>>();

    for line in chat_file_lines.iter() {
        match line.first().map(|b| *b as char) {
            // just skip blank lines (and the empty string after the last
            // line, given the use of split)
            None => (),
            Some('<') => {
                let line = str::from_utf8(line).unwrap();
                expected_lines.extend_from_slice(line.as_bytes());
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('>') => {
                expected_lines.extend(
                    str::from_utf8(line)
                        .unwrap()
                        .replace("[[CODE_DESCRIPTION]]", &*code_description())
                        .bytes(),
                );
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('!') => {
                // for now, we send the github comments over IRC when
                // testing, but we don't encode that into the chat
                // format
                expected_lines.append(&mut ">PRIVMSG github-comments ".bytes().collect());
                // Match use of ":" in the stringify function in irc-proto's
                // src/command.rs.
                if line.len() == 1 || line.contains(&0x20u8 /* space */) {
                    expected_lines.append(&mut ":".bytes().collect());
                }
                expected_lines.extend_from_slice(&line[1..]);
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            _ => {
                panic!(
                    "Unexpected line in test file {:?}:\n{}",
                    path,
                    str::from_utf8(line).unwrap_or("[non-UTF-8 line]")
                );
            }
        }
    }

    expected_lines
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use lexer::{Symbol, Token, Tokens};
use error::ParserError;

type Operators = HashMap<Symbol, Operator>;

#[derive(PartialEq, Debug)]
enum Associativity {
    LeftToRight,
    RightToLeft,
}

#[derive(PartialEq, Debug)]
struct Operator(usize, Associativity);

impl Operator {
    fn new(precedence: usize, associativity: Associativity) -> Operator {
        Operator(precedence, associativity)
    }
}

pub struct Parser {
    operators: Operators,
}

impl Parser {
    fn new() -> Parser {
        Parser { operators: Operators::new() }
    }

    fn lower_precedence(&self, new_token: &Token, top_token: &Token) -> bool {
        let &Operator(new_token_prec, ref new_token_assoc) = match *new_token {
            Token::Operator(ref new_token_name) => self.operators.get(&new_token_name).unwrap(),
            _ => unreachable!(),
        };

        let &Operator(top_token_prec, _) = match *top_token {
            Token::Operator(ref top_token_name) => self.operators.get(&top_token_name).unwrap(),
            _ => unreachable!(),
        };

        (*new_token_assoc == Associativity::LeftToRight && new_token_prec > top_token_prec) ||
        (*new_token_assoc == Associativity::RightToLeft && new_token_prec >= top_token_prec)
    }

    pub fn parse(&mut self, tokens: Tokens) -> Result<Tokens, ParserError> {
        let mut stack = Tokens::new();
        let mut output = Tokens::new();

        let mut token_iterator = tokens.iter().peekable();
        while let Some(&(position, ref token)) = token_iterator.next() {
            match *token {
                Token::Decimal(_) |
                Token::Hexadecimal(_) => output.push((position, token.clone())),
                Token::Keyword(_) =>  {
                    if tokens.len() > 1 {
                        return Err(ParserError::KeywordError(position));
                    }
                    output.push((position, token.clone()));
                    break;
                }
                Token::Operator(_) => {
                    loop {
                        match stack.last() {
                            Some(&(_, Token::Operator(_))) => {
                                if self.lower_precedence(&token, &stack.last().unwrap().1) {
                                    output.push(stack.pop().unwrap());
                                } else {
                                    break;
                                }
                            }
                            _ => break,
                        }
                    }

                    stack.push((position, token.clone()));
                }
                Token::OpenBracket => {
                    stack.push((position, token.clone()));
                }
                Token::CloseBracket => {
                    loop {
                        match stack.last() {
                            Some(&(_, Token::OpenBracket)) => {
                                stack.pop();
                                break;
                            }
                            None => return Err(ParserError::MissingOpeningBracket(position)),
                            _ => output.push(stack.pop().unwrap()),
                        }
                    }
                }
            }
        }
        loop {
            match stack.last() {
                Some(&(position, Token::OpenBracket)) => {
                    return Err(ParserError::MissingClosingBracket(position))
                }
                Some(_) => output.push(stack.pop().unwrap()),
                None => break,
            }
        }

        Ok(output)
    }
}

impl Default for Parser {
    fn default() -> Parser {
        let mut parser = Parser::new();

        parser.operators.insert(Symbol::NOT, Operator::new(2, Associativity::RightToLeft));
        parser.operators.insert(Symbol::RSHIFT, Operator::new(3, Associativity::LeftToRight));
        parser.operators.insert(Symbol::LSHIFT, Operator::new(3, Associativity::LeftToRight));
        parser.operators.insert(Symbol::AND, Operator::new(4, Associativity::LeftToRight));
        parser.operators.insert(Symbol::XOR, Operator::new(5, Associativity::LeftToRight));
        parser.operators.insert(Symbol::OR, Operator::new(6, Associativity::LeftToRight));

        parser
    }
}
<commit_msg>Parser does not mutate self<commit_after>use std::collections::HashMap;
use lexer::{Symbol, Token, Tokens};
use error::ParserError;

type Operators = HashMap<Symbol, Operator>;

#[derive(PartialEq, Debug)]
enum Associativity {
    LeftToRight,
    RightToLeft,
}

#[derive(PartialEq, Debug)]
struct Operator(usize, Associativity);

impl Operator {
    fn new(precedence: usize, associativity: Associativity) -> Operator {
        Operator(precedence, associativity)
    }
}

pub struct Parser {
    operators: Operators,
}

impl Parser {
    fn new() -> Parser {
        Parser { operators: Operators::new() }
    }

    fn lower_precedence(&self, new_token: &Token, top_token: &Token) -> bool {
        let &Operator(new_token_prec, ref new_token_assoc) = match *new_token {
            Token::Operator(ref new_token_name) => self.operators.get(&new_token_name).unwrap(),
            _ => unreachable!(),
        };

        let &Operator(top_token_prec, _) = match *top_token {
            Token::Operator(ref top_token_name) => self.operators.get(&top_token_name).unwrap(),
            _ => unreachable!(),
        };

        (*new_token_assoc == Associativity::LeftToRight && new_token_prec > top_token_prec) ||
        (*new_token_assoc == Associativity::RightToLeft && new_token_prec >= top_token_prec)
    }

    pub fn parse(&self, tokens: Tokens) -> Result<Tokens, ParserError> {
        let mut stack = Tokens::new();
        let mut output = Tokens::new();

        let mut token_iterator = tokens.iter().peekable();
        while let Some(&(position, ref token)) = token_iterator.next() {
            match *token {
                Token::Decimal(_) |
                Token::Hexadecimal(_) => output.push((position, token.clone())),
                Token::Keyword(_) =>  {
                    if tokens.len() > 1 {
                        return Err(ParserError::KeywordError(position));
                    }
                    output.push((position, token.clone()));
                    break;
                }
                Token::Operator(_) => {
                    loop {
                        match stack.last() {
                            Some(&(_, Token::Operator(_))) => {
                                if self.lower_precedence(&token, &stack.last().unwrap().1) {
                                    output.push(stack.pop().unwrap());
                                } else {
                                    break;
                                }
                            }
                            _ => break,
                        }
                    }

                    stack.push((position, token.clone()));
                }
                Token::OpenBracket => {
                    stack.push((position, token.clone()));
                }
                Token::CloseBracket => {
                    loop {
                        match stack.last() {
                            Some(&(_, Token::OpenBracket)) => {
                                stack.pop();
                                break;
                            }
                            None => return Err(ParserError::MissingOpeningBracket(position)),
                            _ => output.push(stack.pop().unwrap()),
                        }
                    }
                }
            }
        }
        loop {
            match stack.last() {
                Some(&(position, Token::OpenBracket)) => {
                    return Err(ParserError::MissingClosingBracket(position))
                }
                Some(_) => output.push(stack.pop().unwrap()),
                None => break,
            }
        }

        Ok(output)
    }
}

impl Default for Parser {
    fn default() -> Parser {
        let mut parser = Parser::new();

        parser.operators.insert(Symbol::NOT, Operator::new(2, Associativity::RightToLeft));
        parser.operators.insert(Symbol::RSHIFT, Operator::new(3, Associativity::LeftToRight));
        parser.operators.insert(Symbol::LSHIFT, Operator::new(3, Associativity::LeftToRight));
        parser.operators.insert(Symbol::AND, Operator::new(4, Associativity::LeftToRight));
        parser.operators.insert(Symbol::XOR, Operator::new(5, Associativity::LeftToRight));
        parser.operators.insert(Symbol::OR, Operator::new(6, Associativity::LeftToRight));

        parser
    }
}
<|endoftext|>