<commit_before>use expr::{Expr, Op};

#[derive(Clone, Debug, PartialEq)]
enum Token {
  Plus,
  Integer(i64),
}

struct Lexer {
  text: String,
  pos: usize,
}

impl Lexer {
  pub fn new(text: String) -> Lexer {
    Lexer { text: text, pos: 0 }
  }

  fn remove_first(&mut self, n: usize) {
    let t = self.text.clone();
    let (old, remainder) = t.split_at(n);
    self.text = remainder.to_string();
  }

  pub fn integer(&mut self) -> Option<Token> {
    let (_, s) = self.text.split_at(self.pos);

    let int_str: String = s
      .chars()
      .take_while(|c| c.is_digit(10))
      .collect();

    match int_str.parse::<i64>() {
      Ok(n) => {
        self.pos += int_str.len();
        return Some(Token::Integer(n));
      }
      Err(e) => {
        println!("---------------{:?}", e);
        panic!();
      }
    }

    None
  }

  pub fn get_next_token(&mut self) -> Option<Token> {
    let old_pos = self.pos;

    match self.text.chars().nth(self.pos) {
      Some('+') => {
        self.pos += 1;
        Some(Token::Plus)
      },
      Some(x) if x.is_numeric() => self.integer(),
      None => None,
      _ => panic!()
    }
  }
}

struct Parser {
  text: String,
  lexer: Lexer,
}

impl Parser {
  pub fn new(text: String) -> Parser {
    let mut lexer = Lexer::new(text.clone());

    Parser {
      text: text,
      lexer: lexer,
    }
  }

  fn add(&mut self, e1: Option<Expr>, e2: Option<Expr>) -> Option<Expr> {
    match (e1, e2) {
      (Some(e1), Some(e2)) => {
        Some(
          Expr::BinOp(
            Op::Plus,
            Box::new(e1),
            Box::new(e2),
          )
        )
      },
      _ => panic!()
    }
  }

  fn factor(&mut self) -> Option<Expr> {
    let token = self.lexer.get_next_token();

    match token {
      Some(Token::Integer(n)) => {
        return Some(Expr::Integer(n));
      }
      Some(t) => {
        println!("invalid factor: {:?}", t);
        panic!();
      }
      _ => panic!()
    }
  }

  pub fn expr(&mut self) -> Option<Expr> {
    let mut node = self.factor();

    let mut token = self.lexer.get_next_token();

    while token == Some(Token::Plus) {
      let right_node = self.factor();
      node = self.add(node, right_node);

      token = self.lexer.get_next_token();
    }

    node
  }
}

pub fn parse(input: &str) -> Expr {
  let mut parser = Parser::new(input.to_string());

  match parser.expr() {
    Some(e) => e,
    None => panic!(),
  }
}

#[test]
fn test_parse_int() {
  assert_eq!(
    Expr::BinOp(
      Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
      ),
    parse("3+4")
  );

  assert_eq!(
    Expr::BinOp(
      Op::Plus,
      Box::new(Expr::BinOp(
        Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
        ),
      ),
      Box::new(Expr::Integer(5)),
    ),
    parse("3+4+5")
  );
}
<commit_msg>cleaning up<commit_after>use expr::{Expr, Op};

#[derive(Clone, Debug, PartialEq)]
enum Token {
  Plus,
  Integer(i64),
}

struct Lexer {
  text: String,
}

impl Lexer {
  pub fn new(text: String) -> Lexer {
    Lexer {
      text: text,
    }
  }

  fn cut_input_by(&mut self, n: usize) {
    let text = self.text.clone();
    let (_, t) = text.split_at(n);
    self.text = t.to_string();
  }

  pub fn lex_integer(&mut self) -> Option<Token> {
    let int_str: String = self.text
      .chars()
      .take_while(|c| c.is_digit(10))
      .collect();

    match int_str.parse::<i64>() {
      Ok(n) => {
        self.cut_input_by(int_str.len());
        return Some(Token::Integer(n));
      }
      Err(e) => panic!()
    }
  }

  pub fn get_next_token(&mut self) -> Option<Token> {
    match self.text.chars().next() {
      Some('+') => {
        self.cut_input_by(1);
        Some(Token::Plus)
      },
      Some(x) if x.is_numeric() => self.lex_integer(),
      None => None,
      _ => panic!()
    }
  }
}

struct Parser {
  lexer: Lexer,
}

impl Parser {
  pub fn new(text: String) -> Parser {
    Parser { lexer: Lexer::new(text) }
  }

  fn add(&mut self, e1: Option<Expr>, e2: Option<Expr>) -> Option<Expr> {
    match (e1, e2) {
      (Some(e1), Some(e2)) => {
        Some(
          Expr::BinOp(
            Op::Plus,
            Box::new(e1),
            Box::new(e2),
          )
        )
      },
      _ => panic!()
    }
  }

  fn factor(&mut self) -> Option<Expr> {
    let token = self.lexer.get_next_token();

    match token {
      Some(Token::Integer(n)) => {
        return Some(Expr::Integer(n));
      },
      _ => {
        println!("invalid factor: {:?}", token);
        panic!();
      },
    }
  }

  pub fn expr(&mut self) -> Option<Expr> {
    let mut node = self.factor();

    let mut token = self.lexer.get_next_token();

    while token == Some(Token::Plus) {
      let right_node = self.factor();
      node = self.add(node, right_node);

      token = self.lexer.get_next_token();
    }

    node
  }
}

pub fn parse(input: &str) -> Expr {
  let mut parser = Parser::new(input.to_string());

  match parser.expr() {
    Some(e) => e,
    None => panic!(),
  }
}

#[test]
fn test_parse_int() {
  assert_eq!(
    Expr::BinOp(
      Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
      ),
    parse("3+4")
  );

  assert_eq!(
    Expr::BinOp(
      Op::Plus,
      Box::new(Expr::BinOp(
        Op::Plus,
        Box::new(Expr::Integer(3)),
        Box::new(Expr::Integer(4)),
        ),
      ),
      Box::new(Expr::Integer(5)),
    ),
    parse("3+4+5")
  );
}
<|endoftext|>


<commit_before>use lexer::{Token, TokIden, TokInt, RBRACKET, LBRACKET, LPAREN, RPAREN, COLON, INTEGER, PLUS, MINUS};
use lexer;
use ast;
use ast::{Ast, NumberExprAst, LetExprAst, BinaryExprAst};
use std::from_str;

pub struct Parser<'a> {
    lexer: lexer::Lexer<'a>
}

impl<'a> Parser<'a> {
    pub fn new(input: &'a str) -> Parser<'a> {
        Parser {
            lexer: lexer::Lexer::new(input)
        }
    }

    pub fn bump(&mut self) -> Option<Token> {
        self.lexer.next_token()
    }

    pub fn parse(&mut self) {

    }

    /// An expression is of the form:
    ///
    /// (iden K..)
    ///
    /// Where `K` is of any type.
    ///
    /// @effect(Fail)
    ///
    pub fn parse_expression(&mut self) -> Ast {
        let tok = self.bump().unwrap();
        match tok {
            lexer::LPAREN => {
                // Parse an iden
                match self.bump().unwrap() {
                    TokIden(s) => {
                        self.parse_form()
                    },
                    _ => fail!("Expected an iden token.")
                }
            },
            TokIden(s) => {
                fail!("Unexpected iden. Expected an expression.")
            }
            TokInt(i) => {
                NumberExprAst(i)
            },
            _ => fail!("Expected an expression to begin with `(` (left parentheses) or a number.")
        }
    }

    pub fn parse_form(&mut self) -> Ast {
        let tok = self.bump().unwrap();
        match tok {
            TokIden(~"defn") => fail!("Functions are not implemented yet."),
            TokIden(~"let")  => {
                // Parse the bindings.
                match self.bump() {
                    Some(r) => {
                        if r == TokIden(~"[") {
                            let bindings = ~[ast::BindingExprAst];
                            // Loop until we find the end bracket or fail.
                            loop {

                                match self.bump() {
                                    Some(tt) => {
                                        let expr = match self.bump().unwrap() {
                                            TokIden(r) => {
                                                println!("curr{}", r);
                                                // Parse another expression
                                                let value = ~self.parse_expression();
                                                let b = ast::BindingExprAst(r, value);
                                                //bindings.push();
                                            },
                                            _ => break
                                        };
                                    },
                                    None => break
                                }
                            }

                            NumberExprAst(9)
                        } else {
                            fail!("Expected `[` token.")
                        }
                    },
                    None => fail!("Expected `[` token, but found none.")
                }
            },
            _ => fail!("Unimplemented form. {}", tok)
        }
    }
}

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

    #[test]
    fn parse_expression() {
        let mut parser = Parser::new(&"(let [x 5])");
        parser.parse_expression();
        //match parser.parse_expression() {
        //    NumberExprAst(r) => assert_eq!(r, 9),
        //    _ => fail!("Not expected.")
        //}
    }
}<commit_msg>parsing works<commit_after>use lexer::{Token, TokIden, TokInt, RBRACKET, LBRACKET, LPAREN, RPAREN, COLON, INTEGER, PLUS, MINUS};
use lexer;
use ast::{Ast, NumberExprAst, BindingExprAst, LetExprAst, BinaryExprAst};
use ast;

pub struct Parser<'a> {
    lexer: lexer::Lexer<'a>
}

impl<'a> Parser<'a> {
    pub fn new(input: &'a str) -> Parser<'a> {
        Parser {
            lexer: lexer::Lexer::new(input)
        }
    }

    pub fn bump(&mut self) -> Option<Token> {
        self.lexer.next_token()
    }

    pub fn parse(&mut self) {

    }

    /// An expression is of the form:
    ///
    /// (iden K..)
    ///
    /// Where `K` is of any type.
    ///
    /// @effect(Fail)
    ///
    pub fn parse_expression(&mut self) -> Ast {
        let tok = self.bump().unwrap();
        println!("(expr {})", tok);
        match tok {
            lexer::LPAREN => {
                println!("found lparen");
                // Parse an iden
                match self.bump().unwrap() {
                    TokIden(s) => {
                        self.parse_form(TokIden(s))
                    },
                    _ => fail!("Expected an iden token.")
                }
            },
            TokIden(s) => {
                fail!("Unexpected iden. Expected an expression.")
            }
            TokInt(i) => {
                NumberExprAst(i)
            },
            _ => fail!("Expected an expression to begin with `(` (left parentheses) or a number.")
        }
    }

    pub fn parse_form(&mut self, tok: Token) -> Ast {
        match tok {
            TokIden(~"defn") => fail!("Functions are not implemented yet."),
            TokIden(~"let")  => {
                // Parse the bindings.
                match self.bump() {
                    Some(r) => {
                        if r == LBRACKET {
                            let mut bindings: ~[Ast] = ~[];
                            let mut found_ending = false;
                            // Loop until we find the end bracket or fail.
                            loop {
                                match self.bump() {
                                    Some(tt) => {
                                        let expr = match tt {
                                            TokIden(r) => {
                                                println!("(iden {})", r);
                                                // Parse another expression
                                                let value = ~self.parse_expression();
                                                let b = BindingExprAst(r, value);
                                                bindings.push(b);
                                            },
                                            RBRACKET => {
                                                found_ending = true;
                                                break
                                            },
                                            _ => break
                                        };
                                    },
                                    None => break
                                }
                            }

                            if !found_ending {
                                fail!("Expected ending `]` bracket.");
                            }

                            NumberExprAst(9)
                        } else {
                            fail!("Expected `[` token.")
                        }
                    },
                    None => fail!("Expected `[` token, but found none.")
                }
            },
            _ => fail!("Unimplemented form. {}", tok)
        }
    }
}

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

    #[test]
    fn parse_expression() {
        let mut parser = Parser::new(&"(let [x 5])");
        parser.parse_expression();
        //match parser.parse_expression() {
        //    NumberExprAst(r) => assert_eq!(r, 9),
        //    _ => fail!("Not expected.")
        //}
    }
}<|endoftext|>


<commit_before>//! A parser for lambda expressions

use term::{Term, Notation, abs, app};
use term::Term::*;
use self::Token::*;
use self::CToken::*;
use self::ParseError::*;
use self::Expression::*;
pub use term::Notation::*;

/// An error returned when a parsing issue is encountered.
#[derive(Debug, PartialEq)]
pub enum ParseError {
    /// an invalid character was encountered
    InvalidCharacter((usize, char)),
    /// the expression is invalid
    InvalidExpression,
    /// the expression is empty
    EmptyExpression
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Token {
    /// the lambda symbol ('λ' or '\')
    Lambda,
    /// left parenthesis
    Lparen,
    /// right parenthesis
    Rparen,
    /// a hex-encoded digit
    Number(usize)
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum CToken {
    /// an abstraction with a bound variable
    CLambda(String),
    /// left parenthesis
    CLparen,
    /// right parenthesis
    CRparen,
    /// a variable with an identifier
    CName(String)
}

#[doc(hidden)]
pub fn tokenize_dbr(input: &str) -> Result<Vec<Token>, ParseError> {
    let chars = input.chars().enumerate();
    let mut tokens = Vec::new();

    for (i, c) in chars {
        match c {
     '\\' | 'λ' => { tokens.push(Lambda) },
            '(' => { tokens.push(Lparen) },
            ')' => { tokens.push(Rparen) },
             _  => {
                if let Some(n) = c.to_digit(16) {
                    tokens.push(Number(n as usize))
                } else if c.is_whitespace() {
                    ()
                } else {
                    return Err(InvalidCharacter((i + 1, c)))
                }
            }
        }
    }

    Ok(tokens)
}

#[doc(hidden)]
pub fn tokenize_cla(input: &str) -> Result<Vec<CToken>, ParseError> {
    let mut chars = input.chars().enumerate().peekable();
    let mut tokens = Vec::new();
    let valid_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    while let Some((i, c)) = chars.next() {
        match c {
            '\\' | 'λ' => {
                let mut name = String::new();
                while let Some((i, c)) = chars.next() {
                    if c == '.' {
                        break
                    } else if valid_chars.contains(c) {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((i + 1, c)))
                    }
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             _  => {
                if c.is_whitespace() {
                    ()
                } else if valid_chars.contains(c) {
                    let mut name = c.to_string();
                    while let Some(&(_, c)) = chars.peek() {
                        if c.is_whitespace() || c == '(' || c == ')' {
                            break
                        } else {
                            name.push(c);
                            chars.next();
                        }
                    }
                    tokens.push(CName(name))
                } else {
                    return Err(InvalidCharacter((i + 1, c)))
                }
            }
        }
    }

    Ok(tokens)
}

#[doc(hidden)]
pub fn convert_classic_tokens(tokens: &[CToken]) -> Vec<Token> {
    _convert_classic_tokens(tokens, &mut Vec::new(), &mut 0)
}

fn _convert_classic_tokens(tokens: &[CToken], stack: &mut Vec<String>, pos: &mut usize) -> Vec<Token>
{
    let mut output = Vec::with_capacity(tokens[*pos..].len());
    let mut inner_stack_count = 0;

    while let Some(token) = tokens.get(*pos) {
        match *token {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name.to_owned());
                inner_stack_count += 1;
            },
            CLparen => {
                output.push(Lparen);
                *pos += 1;
                output.append(&mut _convert_classic_tokens(tokens, stack, pos));
            },
            CRparen => {
                output.push(Rparen);
                for _ in 0..inner_stack_count { stack.pop(); }
                return output
            },
            CName(ref name) => {
                if let Some(index) = stack.iter().rev().position(|t| t == name) {
                    output.push(Number(index + 1))
                } else {
                    output.push(Number(stack.len() + 1))
                }
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Expression {
    /// an abstraction
    Abstraction,
    /// a sequence of `Expression`s
    Sequence(Vec<Expression>),
    /// a variable with a De Bruijn index
    Variable(usize)
}

#[doc(hidden)]
pub fn get_ast(tokens: &[Token]) -> Result<Expression, ParseError> {
    _get_ast(tokens, &mut 0)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, ParseError> {
    if tokens.is_empty() { return Err(EmptyExpression) }

    let mut expr = Vec::new();

    while let Some(token) = tokens.get(*pos) {
        match *token {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = _get_ast(tokens, pos)?;
                expr.push(subtree);
            },
            Rparen => {
                return Ok(Sequence(expr))
            }
        }
        *pos += 1;
    }

    Ok(Sequence(expr))
}

/// Parses the input `&str` as a lambda `Term`.
///
/// - lambdas can be represented either with the greek letter (λ) or a backslash (\\ -
/// less aesthetic, but only one byte in size)
/// - the identifiers in `Classic` mode are `String`s of ASCII alphabetic characters
/// - `Classic` mode ignores whitespaces where unambiguous
/// - the indices in the `DeBruijn` notation mode start with 1 and are hexadecimal digits
/// - `DeBruijn` mode ignores all whitespaces (since indices > 15 are very unlikely)
///
/// # Example
/// ```
/// use lambda_calculus::parser::*;
/// use lambda_calculus::combinators::{S, Y};
///
/// assert_eq!(parse(&"λf.(λx.f (x x)) (λx.f (x x))", Classic), Ok(Y()));
/// assert_eq!(parse(&"λf.(λx.f(x x))(λx.f(x x))", Classic),    Ok(Y()));
///
/// assert_eq!(parse(  &"λλλ31(21)",     DeBruijn), Ok(S()));
/// assert_eq!(parse(&r#"\\\3 1 (2 1)"#, DeBruijn), Ok(S()));
/// ```
pub fn parse(input: &str, notation: Notation) -> Result<Term, ParseError> {
    let tokens = if notation == DeBruijn {
        tokenize_dbr(input)?
    } else {
        convert_classic_tokens(&tokenize_cla(input)?)
    };
    let ast = get_ast(&tokens)?;

    let exprs = if let Sequence(exprs) = ast {
        Ok(exprs)
    } else {
        Err(InvalidExpression)
    };

    fold_exprs(&exprs?)
}

#[doc(hidden)]
pub fn fold_exprs(exprs: &[Expression]) -> Result<Term, ParseError> {
    let mut depth  = 0;
    let mut output = Vec::new();

    for expr in exprs.iter() {
        match *expr {
            Variable(i) => output.push(Var(i)),
            Abstraction => depth += 1,
            Sequence(ref exprs) => {
                let subexpr = fold_exprs(exprs)?;
                output.push(subexpr)
            }
        }
    }

    let mut ret = fold_terms(output)?;

    for _ in 0..depth {
        ret = abs(ret);
    }

    Ok(ret)
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, ParseError> {
    if terms.len() > 1 {
        let fst = terms.remove(0);
        Ok( terms.into_iter().fold(fst, app) )
    } else if terms.len() == 1 {
        Ok( terms.pop().unwrap() ) // safe; ensured above
    } else {
        Err(EmptyExpression)
    }
}

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

    #[test]
    fn tokenization_error() {
        assert_eq!(tokenize_dbr(&"λλx2"),    Err(InvalidCharacter((3, 'x'))));
        assert_eq!(tokenize_cla(&"λa.λb a"), Err(InvalidCharacter((6, ' '))));
    }

    #[test]
    fn tokenization_success() {
        let quine = "λ 1 ( (λ 1 1) (λ λ λ λ λ 1 4 (3 (5 5) 2) ) ) 1";
        let tokens = tokenize_dbr(&quine);

        assert!(tokens.is_ok());
        assert_eq!(tokens.unwrap(), vec![Lambda, Number(1), Lparen, Lparen, Lambda, Number(1),
            Number(1), Rparen, Lparen, Lambda, Lambda, Lambda, Lambda, Lambda, Number(1),
            Number(4), Lparen, Number(3), Lparen, Number(5), Number(5), Rparen, Number(2),
            Rparen, Rparen, Rparen, Number(1)]);
    }

    #[test]
    fn tokenization_success_classic() {
        let blc_dbr = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
            (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        let blc_cla = format!("{}", parse(&blc_dbr, DeBruijn).unwrap());

        let tokens_cla = tokenize_cla(&blc_cla);
        let tokens_dbr = tokenize_dbr(&blc_dbr);

        assert!(tokens_cla.is_ok());
        assert!(tokens_dbr.is_ok());

        assert_eq!(convert_classic_tokens(&tokens_cla.unwrap()), tokens_dbr.unwrap());
    }

    #[test]
    fn alternative_lambda_parsing() {
        assert_eq!(parse(&r#"\\\2(321)"#, DeBruijn), parse(&"λλλ2(321)", DeBruijn))
    }

    #[test]
    fn succ_ast() {
        let tokens = tokenize_dbr(&"λλλ2(321)").unwrap();
        let ast = get_ast(&tokens);

        assert_eq!(ast,
            Ok(Sequence(vec![
                Abstraction,
                Abstraction,
                Abstraction,
                Variable(2),
                Sequence(vec![
                    Variable(3),
                    Variable(2),
                    Variable(1)
                ])
            ])
        ));
    }

    #[test]
    fn parse_y() {
        let y = "λ(λ2(11))(λ2(11))";
        assert_eq!(parse(&y, DeBruijn).unwrap(),
            abs(
                app(
                    abs(app(Var(2), app(Var(1), Var(1)))),
                    abs(app(Var(2), app(Var(1), Var(1))))
                )
            )
        );
    }

    #[test]
    fn parse_quine() {
        let quine = "λ1((λ11)(λλλλλ14(3(55)2)))1";
        assert_eq!(parse(&quine, DeBruijn).unwrap(),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs!(5, app(app(Var(1),
            Var(4)), app(app(Var(3), app(Var(5), Var(5))), Var(2)))))), Var(1)))
        );
    }

    #[test]
    fn parse_blc() {
        let blc = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
                   (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        assert_eq!(parse(&blc, DeBruijn).unwrap(),
            app(app(abs(app(Var(1), Var(1))), abs!(3, app(app(app(Var(1),
            abs!(4, app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs!(2, app(Var(3), abs(app(app(Var(1), Var(2)), Var(3))))))), app(Var(4),
            abs(app(Var(4), abs(app(app(Var(3), Var(1)), app(Var(2), Var(1))))))))))),
            app(app(Var(1), app(Var(2), abs(app(Var(1), Var(2))))), abs(app(app(Var(4),
            abs(app(Var(4), abs(app(Var(2), app(Var(1), Var(4))))))), Var(5))))))))),
            app(Var(3), Var(3))), Var(2)))), abs(app(Var(1), app(abs(app(Var(1), Var(1))),
            abs(app(Var(1), Var(1)))))))
        );
    }
}
<commit_msg>make parser::ParseError::InvalidCharacter 0-indexed<commit_after>//! A parser for lambda expressions

use term::{Term, Notation, abs, app};
use term::Term::*;
use self::Token::*;
use self::CToken::*;
use self::ParseError::*;
use self::Expression::*;
pub use term::Notation::*;

/// An error returned when a parsing issue is encountered.
#[derive(Debug, PartialEq)]
pub enum ParseError {
    /// invalid character
    InvalidCharacter((usize, char)),
    /// invalid expression
    InvalidExpression,
    /// empty expression
    EmptyExpression
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Token {
    /// the lambda symbol ('λ' or '\')
    Lambda,
    /// left parenthesis
    Lparen,
    /// right parenthesis
    Rparen,
    /// a hex-encoded digit
    Number(usize)
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum CToken {
    /// an abstraction with a bound variable
    CLambda(String),
    /// left parenthesis
    CLparen,
    /// right parenthesis
    CRparen,
    /// a variable with an identifier
    CName(String)
}

#[doc(hidden)]
pub fn tokenize_dbr(input: &str) -> Result<Vec<Token>, ParseError> {
    let chars = input.chars().enumerate();
    let mut tokens = Vec::new();

    for (i, c) in chars {
        match c {
     '\\' | 'λ' => { tokens.push(Lambda) },
            '(' => { tokens.push(Lparen) },
            ')' => { tokens.push(Rparen) },
             _  => {
                if let Some(n) = c.to_digit(16) {
                    tokens.push(Number(n as usize))
                } else if c.is_whitespace() {
                    ()
                } else {
                    return Err(InvalidCharacter((i, c)))
                }
            }
        }
    }

    Ok(tokens)
}

#[doc(hidden)]
pub fn tokenize_cla(input: &str) -> Result<Vec<CToken>, ParseError> {
    let mut chars = input.chars().enumerate().peekable();
    let mut tokens = Vec::new();
    let valid_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    while let Some((i, c)) = chars.next() {
        match c {
            '\\' | 'λ' => {
                let mut name = String::new();
                while let Some((i, c)) = chars.next() {
                    if c == '.' {
                        break
                    } else if valid_chars.contains(c) {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((i, c)))
                    }
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             _  => {
                if c.is_whitespace() {
                    ()
                } else if valid_chars.contains(c) {
                    let mut name = c.to_string();
                    while let Some(&(_, c)) = chars.peek() {
                        if c.is_whitespace() || c == '(' || c == ')' {
                            break
                        } else {
                            name.push(c);
                            chars.next();
                        }
                    }
                    tokens.push(CName(name))
                } else {
                    return Err(InvalidCharacter((i, c)))
                }
            }
        }
    }

    Ok(tokens)
}

#[doc(hidden)]
pub fn convert_classic_tokens(tokens: &[CToken]) -> Vec<Token> {
    _convert_classic_tokens(tokens, &mut Vec::new(), &mut 0)
}

fn _convert_classic_tokens(tokens: &[CToken], stack: &mut Vec<String>, pos: &mut usize) -> Vec<Token>
{
    let mut output = Vec::with_capacity(tokens[*pos..].len());
    let mut inner_stack_count = 0;

    while let Some(token) = tokens.get(*pos) {
        match *token {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name.to_owned());
                inner_stack_count += 1;
            },
            CLparen => {
                output.push(Lparen);
                *pos += 1;
                output.append(&mut _convert_classic_tokens(tokens, stack, pos));
            },
            CRparen => {
                output.push(Rparen);
                for _ in 0..inner_stack_count { stack.pop(); }
                return output
            },
            CName(ref name) => {
                if let Some(index) = stack.iter().rev().position(|t| t == name) {
                    output.push(Number(index + 1))
                } else {
                    output.push(Number(stack.len() + 1))
                }
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Expression {
    /// an abstraction
    Abstraction,
    /// a sequence of `Expression`s
    Sequence(Vec<Expression>),
    /// a variable with a De Bruijn index
    Variable(usize)
}

#[doc(hidden)]
pub fn get_ast(tokens: &[Token]) -> Result<Expression, ParseError> {
    _get_ast(tokens, &mut 0)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, ParseError> {
    if tokens.is_empty() { return Err(EmptyExpression) }

    let mut expr = Vec::new();

    while let Some(token) = tokens.get(*pos) {
        match *token {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = _get_ast(tokens, pos)?;
                expr.push(subtree);
            },
            Rparen => {
                return Ok(Sequence(expr))
            }
        }
        *pos += 1;
    }

    Ok(Sequence(expr))
}

/// Parses the input `&str` as a lambda `Term`.
///
/// - lambdas can be represented either with the greek letter (λ) or a backslash (\\ -
/// less aesthetic, but only one byte in size)
/// - the identifiers in `Classic` mode are `String`s of ASCII alphabetic characters
/// - `Classic` mode ignores whitespaces where unambiguous
/// - the indices in the `DeBruijn` notation mode start with 1 and are hexadecimal digits
/// - `DeBruijn` mode ignores all whitespaces (since indices > 15 are very unlikely)
///
/// # Example
/// ```
/// use lambda_calculus::parser::*;
/// use lambda_calculus::combinators::{S, Y};
///
/// assert_eq!(parse(&"λf.(λx.f (x x)) (λx.f (x x))", Classic), Ok(Y()));
/// assert_eq!(parse(&"λf.(λx.f(x x))(λx.f(x x))", Classic),    Ok(Y()));
///
/// assert_eq!(parse(  &"λλλ31(21)",     DeBruijn), Ok(S()));
/// assert_eq!(parse(&r#"\\\3 1 (2 1)"#, DeBruijn), Ok(S()));
/// ```
pub fn parse(input: &str, notation: Notation) -> Result<Term, ParseError> {
    let tokens = if notation == DeBruijn {
        tokenize_dbr(input)?
    } else {
        convert_classic_tokens(&tokenize_cla(input)?)
    };
    let ast = get_ast(&tokens)?;

    let exprs = if let Sequence(exprs) = ast {
        Ok(exprs)
    } else {
        Err(InvalidExpression)
    };

    fold_exprs(&exprs?)
}

#[doc(hidden)]
pub fn fold_exprs(exprs: &[Expression]) -> Result<Term, ParseError> {
    let mut depth  = 0;
    let mut output = Vec::new();

    for expr in exprs.iter() {
        match *expr {
            Variable(i) => output.push(Var(i)),
            Abstraction => depth += 1,
            Sequence(ref exprs) => {
                let subexpr = fold_exprs(exprs)?;
                output.push(subexpr)
            }
        }
    }

    let mut ret = fold_terms(output)?;

    for _ in 0..depth {
        ret = abs(ret);
    }

    Ok(ret)
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, ParseError> {
    if terms.len() > 1 {
        let fst = terms.remove(0);
        Ok( terms.into_iter().fold(fst, app) )
    } else if terms.len() == 1 {
        Ok( terms.pop().unwrap() ) // safe; ensured above
    } else {
        Err(EmptyExpression)
    }
}

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

    #[test]
    fn tokenization_error() {
        assert_eq!(tokenize_dbr(&"λλx2"),    Err(InvalidCharacter((2, 'x'))));
        assert_eq!(tokenize_cla(&"λa.λb a"), Err(InvalidCharacter((5, ' '))));
    }

    #[test]
    fn tokenization_success() {
        let quine = "λ 1 ( (λ 1 1) (λ λ λ λ λ 1 4 (3 (5 5) 2) ) ) 1";
        let tokens = tokenize_dbr(&quine);

        assert!(tokens.is_ok());
        assert_eq!(tokens.unwrap(), vec![Lambda, Number(1), Lparen, Lparen, Lambda, Number(1),
            Number(1), Rparen, Lparen, Lambda, Lambda, Lambda, Lambda, Lambda, Number(1),
            Number(4), Lparen, Number(3), Lparen, Number(5), Number(5), Rparen, Number(2),
            Rparen, Rparen, Rparen, Number(1)]);
    }

    #[test]
    fn tokenization_success_classic() {
        let blc_dbr = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
            (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        let blc_cla = format!("{}", parse(&blc_dbr, DeBruijn).unwrap());

        let tokens_cla = tokenize_cla(&blc_cla);
        let tokens_dbr = tokenize_dbr(&blc_dbr);

        assert!(tokens_cla.is_ok());
        assert!(tokens_dbr.is_ok());

        assert_eq!(convert_classic_tokens(&tokens_cla.unwrap()), tokens_dbr.unwrap());
    }

    #[test]
    fn alternative_lambda_parsing() {
        assert_eq!(parse(&r#"\\\2(321)"#, DeBruijn), parse(&"λλλ2(321)", DeBruijn))
    }

    #[test]
    fn succ_ast() {
        let tokens = tokenize_dbr(&"λλλ2(321)").unwrap();
        let ast = get_ast(&tokens);

        assert_eq!(ast,
            Ok(Sequence(vec![
                Abstraction,
                Abstraction,
                Abstraction,
                Variable(2),
                Sequence(vec![
                    Variable(3),
                    Variable(2),
                    Variable(1)
                ])
            ])
        ));
    }

    #[test]
    fn parse_y() {
        let y = "λ(λ2(11))(λ2(11))";
        assert_eq!(parse(&y, DeBruijn).unwrap(),
            abs(
                app(
                    abs(app(Var(2), app(Var(1), Var(1)))),
                    abs(app(Var(2), app(Var(1), Var(1))))
                )
            )
        );
    }

    #[test]
    fn parse_quine() {
        let quine = "λ1((λ11)(λλλλλ14(3(55)2)))1";
        assert_eq!(parse(&quine, DeBruijn).unwrap(),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs!(5, app(app(Var(1),
            Var(4)), app(app(Var(3), app(Var(5), Var(5))), Var(2)))))), Var(1)))
        );
    }

    #[test]
    fn parse_blc() {
        let blc = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
                   (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        assert_eq!(parse(&blc, DeBruijn).unwrap(),
            app(app(abs(app(Var(1), Var(1))), abs!(3, app(app(app(Var(1),
            abs!(4, app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs!(2, app(Var(3), abs(app(app(Var(1), Var(2)), Var(3))))))), app(Var(4),
            abs(app(Var(4), abs(app(app(Var(3), Var(1)), app(Var(2), Var(1))))))))))),
            app(app(Var(1), app(Var(2), abs(app(Var(1), Var(2))))), abs(app(app(Var(4),
            abs(app(Var(4), abs(app(Var(2), app(Var(1), Var(4))))))), Var(5))))))))),
            app(Var(3), Var(3))), Var(2)))), abs(app(Var(1), app(abs(app(Var(1), Var(1))),
            abs(app(Var(1), Var(1)))))))
        );
    }
}
<|endoftext|>


<commit_before>use crate::syscall_scope::ShareList;
use crate::Syscalls;

// -----------------------------------------------------------------------------
// `Subscribe` struct
// -----------------------------------------------------------------------------

/// A `Subscribe` instance allows safe code to call Tock's Subscribe system
/// call, by guaranteeing the upcall will be cleaned up before 'scope ends. It
/// is generally used with the `syscall_scope` function, which offers a safe
/// interface for constructing `Subscribe` instances.
pub struct Subscribe<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> {
    _syscalls: core::marker::PhantomData<S>,

    // Make this struct invariant with respect to the 'scope lifetime.
    //
    // Covariance would be unsound, as that would allow code with a
    // `Subscribe<'static, ...>` to register an upcall that lasts for a shorter
    // lifetime, resulting in use-after-free if the upcall in invoked.
    // Contravariance would be sound, but is not necessary and may be confusing.
    //
    // Additionally, we want to have at least one private member of this struct
    // so that code outside this module cannot construct a `Subscribe` without
    // calling `ShareList::new`.
    _scope: core::marker::PhantomData<core::cell::Cell<&'scope ()>>,
}

impl<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> Drop
    for Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM>
{
    fn drop(&mut self) {
        S::unsubscribe(DRIVER_NUM, SUBSCRIBE_NUM);
    }
}

impl<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> ShareList<'scope>
    for Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM>
{
    unsafe fn new() -> Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM> {
        Subscribe {
            _syscalls: core::marker::PhantomData,
            _scope: core::marker::PhantomData,
        }
    }
}

// -----------------------------------------------------------------------------
// `Upcall` trait
// -----------------------------------------------------------------------------

/// A Tock kernel upcall. Upcalls are registered using the Subscribe system
/// call, and are invoked during Yield calls.
///
/// Each `Upcall` supports one or more subscribe IDs, which are indicated by the
/// `SupportedIds` parameter. The types `AnySubscribeId` and `OneSubscribeId`
/// are provided to use as `SupportedIds` parameters in `Upcall`
/// implementations.
pub trait Upcall<SupportedIds> {
    fn upcall(&self, arg0: u32, arg1: u32, arg2: u32);
}

pub trait SupportsId<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> {}

pub struct AnyId;
impl<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> SupportsId<DRIVER_NUM, SUBSCRIBE_NUM>
    for AnyId
{
}

pub struct OneId<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32>;
impl<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> SupportsId<DRIVER_NUM, SUBSCRIBE_NUM>
    for OneId<DRIVER_NUM, SUBSCRIBE_NUM>
{
}

// -----------------------------------------------------------------------------
// Upcall implementations that simply store their arguments
// -----------------------------------------------------------------------------

/// An implementation of `Upcall` that sets the contained boolean value to
/// `true` when the upcall is invoked.
impl Upcall<AnyId> for core::cell::Cell<bool> {
    fn upcall(&self, _: u32, _: u32, _: u32) {
        self.set(true);
    }
}

/// Implemented for consistency with the other `Cell<Option<...>>` `Upcall`
/// impls. Most users would prefer the `Cell<bool>` implementation over this
/// impl, but this may be useful in a generic or macro context.
impl Upcall<AnyId> for core::cell::Cell<Option<()>> {
    fn upcall(&self, _: u32, _: u32, _: u32) {
        self.set(Some(()));
    }
}

/// An `Upcall` implementation that stores its first argument when called.
impl Upcall<AnyId> for core::cell::Cell<Option<(u32,)>> {
    fn upcall(&self, arg0: u32, _: u32, _: u32) {
        self.set(Some((arg0,)));
    }
}

/// An `Upcall` implementation that stores its first two arguments when called.
impl Upcall<AnyId> for core::cell::Cell<Option<(u32, u32)>> {
    fn upcall(&self, arg0: u32, arg1: u32, _: u32) {
        self.set(Some((arg0, arg1)));
    }
}

/// An `Upcall` implementation that stores its arguments when called.
impl Upcall<AnyId> for core::cell::Cell<Option<(u32, u32, u32)>> {
    fn upcall(&self, arg0: u32, arg1: u32, arg2: u32) {
        self.set(Some((arg0, arg1, arg2)));
    }
}

#[cfg(test)]
#[test]
fn upcall_impls() {
    let cell_bool = core::cell::Cell::new(false);
    cell_bool.upcall(1, 2, 3);
    assert!(cell_bool.get());

    let cell_empty = core::cell::Cell::new(None);
    cell_empty.upcall(1, 2, 3);
    assert_eq!(cell_empty.get(), Some(()));

    let cell_one = core::cell::Cell::new(None);
    cell_one.upcall(1, 2, 3);
    assert_eq!(cell_one.get(), Some((1,)));

    let cell_two = core::cell::Cell::new(None);
    cell_two.upcall(1, 2, 3);
    assert_eq!(cell_two.get(), Some((1, 2)));

    let cell_three = core::cell::Cell::new(None);
    cell_three.upcall(1, 2, 3);
    assert_eq!(cell_three.get(), Some((1, 2, 3)));
}

// -----------------------------------------------------------------------------
// `Config` trait
// -----------------------------------------------------------------------------

/// `Config` configures the behavior of the Subscribe system call. It should
/// generally be passed through by drivers, to allow application code to
/// configure error handling.
pub trait Config {
    /// Called if a Subscribe call succeeds and returns a non-null upcall. In
    /// some applications, this may indicate unexpected reentrance. By default,
    /// the non-null upcall is ignored.
    fn returned_nonnull_upcall(_driver_num: u32, _subscribe_num: u32) {}
}
<commit_msg>Typo fix in `platform/src/subscribe.rs`.<commit_after>use crate::syscall_scope::ShareList;
use crate::Syscalls;

// -----------------------------------------------------------------------------
// `Subscribe` struct
// -----------------------------------------------------------------------------

/// A `Subscribe` instance allows safe code to call Tock's Subscribe system
/// call, by guaranteeing the upcall will be cleaned up before 'scope ends. It
/// is generally used with the `syscall_scope` function, which offers a safe
/// interface for constructing `Subscribe` instances.
pub struct Subscribe<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> {
    _syscalls: core::marker::PhantomData<S>,

    // Make this struct invariant with respect to the 'scope lifetime.
    //
    // Covariance would be unsound, as that would allow code with a
    // `Subscribe<'static, ...>` to register an upcall that lasts for a shorter
    // lifetime, resulting in use-after-free if the upcall is invoked.
    // Contravariance would be sound, but is not necessary and may be confusing.
    //
    // Additionally, we want to have at least one private member of this struct
    // so that code outside this module cannot construct a `Subscribe` without
    // calling `ShareList::new`.
    _scope: core::marker::PhantomData<core::cell::Cell<&'scope ()>>,
}

impl<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> Drop
    for Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM>
{
    fn drop(&mut self) {
        S::unsubscribe(DRIVER_NUM, SUBSCRIBE_NUM);
    }
}

impl<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> ShareList<'scope>
    for Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM>
{
    unsafe fn new() -> Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM> {
        Subscribe {
            _syscalls: core::marker::PhantomData,
            _scope: core::marker::PhantomData,
        }
    }
}

// -----------------------------------------------------------------------------
// `Upcall` trait
// -----------------------------------------------------------------------------

/// A Tock kernel upcall. Upcalls are registered using the Subscribe system
/// call, and are invoked during Yield calls.
///
/// Each `Upcall` supports one or more subscribe IDs, which are indicated by the
/// `SupportedIds` parameter. The types `AnySubscribeId` and `OneSubscribeId`
/// are provided to use as `SupportedIds` parameters in `Upcall`
/// implementations.
pub trait Upcall<SupportedIds> {
    fn upcall(&self, arg0: u32, arg1: u32, arg2: u32);
}

pub trait SupportsId<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> {}

pub struct AnyId;
impl<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> SupportsId<DRIVER_NUM, SUBSCRIBE_NUM>
    for AnyId
{
}

pub struct OneId<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32>;
impl<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> SupportsId<DRIVER_NUM, SUBSCRIBE_NUM>
    for OneId<DRIVER_NUM, SUBSCRIBE_NUM>
{
}

// -----------------------------------------------------------------------------
// Upcall implementations that simply store their arguments
// -----------------------------------------------------------------------------

/// An implementation of `Upcall` that sets the contained boolean value to
/// `true` when the upcall is invoked.
impl Upcall<AnyId> for core::cell::Cell<bool> {
    fn upcall(&self, _: u32, _: u32, _: u32) {
        self.set(true);
    }
}

/// Implemented for consistency with the other `Cell<Option<...>>` `Upcall`
/// impls. Most users would prefer the `Cell<bool>` implementation over this
/// impl, but this may be useful in a generic or macro context.
impl Upcall<AnyId> for core::cell::Cell<Option<()>> {
    fn upcall(&self, _: u32, _: u32, _: u32) {
        self.set(Some(()));
    }
}

/// An `Upcall` implementation that stores its first argument when called.
impl Upcall<AnyId> for core::cell::Cell<Option<(u32,)>> {
    fn upcall(&self, arg0: u32, _: u32, _: u32) {
        self.set(Some((arg0,)));
    }
}

/// An `Upcall` implementation that stores its first two arguments when called.
impl Upcall<AnyId> for core::cell::Cell<Option<(u32, u32)>> {
    fn upcall(&self, arg0: u32, arg1: u32, _: u32) {
        self.set(Some((arg0, arg1)));
    }
}

/// An `Upcall` implementation that stores its arguments when called.
impl Upcall<AnyId> for core::cell::Cell<Option<(u32, u32, u32)>> {
    fn upcall(&self, arg0: u32, arg1: u32, arg2: u32) {
        self.set(Some((arg0, arg1, arg2)));
    }
}

#[cfg(test)]
#[test]
fn upcall_impls() {
    let cell_bool = core::cell::Cell::new(false);
    cell_bool.upcall(1, 2, 3);
    assert!(cell_bool.get());

    let cell_empty = core::cell::Cell::new(None);
    cell_empty.upcall(1, 2, 3);
    assert_eq!(cell_empty.get(), Some(()));

    let cell_one = core::cell::Cell::new(None);
    cell_one.upcall(1, 2, 3);
    assert_eq!(cell_one.get(), Some((1,)));

    let cell_two = core::cell::Cell::new(None);
    cell_two.upcall(1, 2, 3);
    assert_eq!(cell_two.get(), Some((1, 2)));

    let cell_three = core::cell::Cell::new(None);
    cell_three.upcall(1, 2, 3);
    assert_eq!(cell_three.get(), Some((1, 2, 3)));
}

// -----------------------------------------------------------------------------
// `Config` trait
// -----------------------------------------------------------------------------

/// `Config` configures the behavior of the Subscribe system call. It should
/// generally be passed through by drivers, to allow application code to
/// configure error handling.
pub trait Config {
    /// Called if a Subscribe call succeeds and returns a non-null upcall. In
    /// some applications, this may indicate unexpected reentrance. By default,
    /// the non-null upcall is ignored.
    fn returned_nonnull_upcall(_driver_num: u32, _subscribe_num: u32) {}
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use nom::{space, alphanumeric, multispace};
use nom::types::CompleteByteSlice;

use std::str;
use std::collections::HashMap;

named!(category<CompleteByteSlice, &str>, map_res!(
    delimited!(
      char!('['),
      take_while!(call!(|c| c != b']')),
      char!(']')
    ),
    complete_byte_slice_to_str
));

fn complete_byte_slice_to_str<'a>(s: CompleteByteSlice<'a>) -> Result<&'a str, str::Utf8Error> {
  str::from_utf8(s.0)
}

named!(key_value    <CompleteByteSlice,(&str,&str)>,
  do_parse!(
     key: map_res!(dbg!(alphanumeric), complete_byte_slice_to_str)
  >>      opt!(space)
  >>      char!('=')
  >>      dbg!(opt!(space))
  >> val: map_res!(
           dbg!(take_while!(call!(|c| c != b'\n' && c != b';'))),
           complete_byte_slice_to_str
         )
  >>      opt!(pair!(char!(';'), dbg!(take_while!(call!(|c| c != b'\n')))))
  >>      (key, val)
  )
);

named!(keys_and_values<CompleteByteSlice, HashMap<&str, &str> >,
  map!(
    many0!(terminated!(key_value, opt!(multispace))),
    |vec: Vec<_>| vec.into_iter().collect()
  )
);

named!(category_and_keys<CompleteByteSlice,(&str,HashMap<&str,&str>)>,
  do_parse!(
    category: category         >>
              opt!(multispace) >>
    keys: keys_and_values      >>
    (category, keys)
  )
);

named!(categories<CompleteByteSlice, HashMap<&str, HashMap<&str,&str> > >,
  map!(
    many0!(
      dbg!(separated_pair!(
        category,
        opt!(multispace),
        map!(
          dbg!(many0!(terminated!(dbg!(key_value), dbg!(opt!(multispace))))),
          |vec: Vec<_>| vec.into_iter().collect()
        )
      ))
    ),
    |vec: Vec<_>| vec.into_iter().collect()
  )
);

#[test]
fn parse_category_test() {
  let ini_file = CompleteByteSlice(
    b"[category]

parameter=value
key = value2",
  );

  let ini_without_category = CompleteByteSlice(
    b"\n\nparameter=value
key = value2",
  );

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_category, "category")));
}

#[test]
fn parse_key_value_test() {
  let ini_file = CompleteByteSlice(
    b"parameter=value
key = value2",
  );

  let ini_without_key_value = CompleteByteSlice(b"\nkey = value2");

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i.0), o1, o2),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = CompleteByteSlice(
    b"parameter = value
key = value2",
  );

  let ini_without_key_value = CompleteByteSlice(b"\nkey = value2");

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i.0), o1, o2),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = CompleteByteSlice(
    b"parameter=value;abc
key = value2",
  );

  let ini_without_key_value = CompleteByteSlice(b"\nkey = value2");

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i.0), o1, o2),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = CompleteByteSlice(
    b"parameter=value;abc

key = value2

[category]",
  );

  let ini_without_key_value = CompleteByteSlice(b"[category]");

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Ok((ini_without_key_value, expected)));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = CompleteByteSlice(
    b"[abcd]
parameter=value;abc

key = value2

[category]",
  );

  let ini_after_parser = CompleteByteSlice(b"[category]");

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h))));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = CompleteByteSlice(
    b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
",
  );

  let ini_after_parser = CompleteByteSlice(b"");

  let res = categories(ini_file);
  //println!("{:?}", res);
  match res {
    Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd", expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Ok((ini_after_parser, expected_h)));
}
<commit_msg>simplify the ini test<commit_after>#[macro_use]
extern crate nom;

use nom::{space, alphanumeric, multispace};
use nom::types::CompleteByteSlice;

use std::str;
use std::collections::HashMap;

named!(category<CompleteByteSlice, &str>, map_res!(
    delimited!(
      char!('['),
      take_while!(call!(|c| c != b']')),
      char!(']')
    ),
    complete_byte_slice_to_str
));

fn complete_byte_slice_to_str<'a>(s: CompleteByteSlice<'a>) -> Result<&'a str, str::Utf8Error> {
  str::from_utf8(s.0)
}

named!(key_value    <CompleteByteSlice,(&str,&str)>,
  do_parse!(
     key: map_res!(alphanumeric, complete_byte_slice_to_str)
  >>      opt!(space)
  >>      char!('=')
  >>      opt!(space)
  >> val: map_res!(
           take_while!(call!(|c| c != b'\n' && c != b';')),
           complete_byte_slice_to_str
         )
  >>      opt!(pair!(char!(';'), take_while!(call!(|c| c != b'\n'))))
  >>      (key, val)
  )
);

named!(keys_and_values<CompleteByteSlice, HashMap<&str, &str> >,
  map!(
    many0!(terminated!(key_value, opt!(multispace))),
    |vec: Vec<_>| vec.into_iter().collect()
  )
);

named!(category_and_keys<CompleteByteSlice,(&str,HashMap<&str,&str>)>,
  do_parse!(
    category: category         >>
              opt!(multispace) >>
    keys: keys_and_values      >>
    (category, keys)
  )
);

named!(categories<CompleteByteSlice, HashMap<&str, HashMap<&str,&str> > >,
  map!(
    many0!(
      separated_pair!(
        category,
        opt!(multispace),
        map!(
          many0!(terminated!(key_value, opt!(multispace))),
          |vec: Vec<_>| vec.into_iter().collect()
        )
      )
    ),
    |vec: Vec<_>| vec.into_iter().collect()
  )
);

#[test]
fn parse_category_test() {
  let ini_file = CompleteByteSlice(
    b"[category]

parameter=value
key = value2",
  );

  let ini_without_category = CompleteByteSlice(
    b"\n\nparameter=value
key = value2",
  );

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_category, "category")));
}

#[test]
fn parse_key_value_test() {
  let ini_file = CompleteByteSlice(
    b"parameter=value
key = value2",
  );

  let ini_without_key_value = CompleteByteSlice(b"\nkey = value2");

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i.0), o1, o2),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = CompleteByteSlice(
    b"parameter = value
key = value2",
  );

  let ini_without_key_value = CompleteByteSlice(b"\nkey = value2");

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i.0), o1, o2),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = CompleteByteSlice(
    b"parameter=value;abc
key = value2",
  );

  let ini_without_key_value = CompleteByteSlice(b"\nkey = value2");

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i.0), o1, o2),
    _ => println!("error"),
  }

  assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = CompleteByteSlice(
    b"parameter=value;abc

key = value2

[category]",
  );

  let ini_without_key_value = CompleteByteSlice(b"[category]");

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Ok((ini_without_key_value, expected)));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = CompleteByteSlice(
    b"[abcd]
parameter=value;abc

key = value2

[category]",
  );

  let ini_after_parser = CompleteByteSlice(b"[category]");

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h))));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = CompleteByteSlice(
    b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
",
  );

  let ini_after_parser = CompleteByteSlice(b"");

  let res = categories(ini_file);
  //println!("{:?}", res);
  match res {
    Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i.0), o),
    _ => println!("error"),
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd", expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Ok((ini_after_parser, expected_h)));
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate quickcheck;

use std::path::PathBuf;
use quickcheck::{quickcheck};

extern crate hyphenation;
use hyphenation::{load, Language, Corpus, Hyphenation, Standard};

fn fiat_io(lang: Language) -> Corpus {
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("patterns");

    hyphenation::set_pattern_folder(path.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref EN_US: Corpus = fiat_io(Language::English_US);
}

#[test]
fn collected_equals_original() {
    fn property(original: String) -> bool {
        let collected: String = original.hyphenate(&EN_US).collect();

        collected == original
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn opportunities_within_bounds() {
    fn property(s: String) -> bool {
        let os = s.opportunities(&EN_US);
        let l = s.len();

        os.iter().all(|&i| i < l)
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn punctuated_count() {
    fn property(s: String) -> bool {
        let l = s.chars().count();
        let os = s.opportunities(&EN_US);
        let h: String = s.hyphenate(&EN_US).punctuate().collect();

        h.chars().count() == l + os.len()
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn basics() {
    let h1: Standard = "hyphenation".hyphenate(&EN_US);
    let h2: Standard = "project".hyphenate(&EN_US);

    let v1: Vec<&str> = h1.clone().collect();
    let v2: Vec<&str> = h2.clone().collect();
    assert_eq!(v1, vec!["hy", "phen", "ation"]);
    assert_eq!(v2, vec!["project"]);

    let s1: String = h1.punctuate().collect();
    assert_eq!(s1, "hy\u{ad}phen\u{ad}ation".to_owned());
}
<commit_msg>Basic test for left and right hyphenation bounds<commit_after>#[macro_use]
extern crate lazy_static;
extern crate quickcheck;

use std::path::PathBuf;
use quickcheck::{quickcheck};

extern crate hyphenation;
use hyphenation::{load, Language, Corpus, Hyphenation, Standard};

fn fiat_io(lang: Language) -> Corpus {
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("patterns");

    hyphenation::set_pattern_folder(path.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref EN_US: Corpus = fiat_io(Language::English_US);
}

#[test]
fn collected_equals_original() {
    fn property(original: String) -> bool {
        let collected: String = original.hyphenate(&EN_US).collect();

        collected == original
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn opportunities_within_bounds() {
    fn property(s: String) -> bool {
        let os = s.opportunities(&EN_US);
        let l = s.len();

        os.iter().all(|&i| i < l)
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn punctuated_count() {
    fn property(s: String) -> bool {
        let l = s.chars().count();
        let os = s.opportunities(&EN_US);
        let h: String = s.hyphenate(&EN_US).punctuate().collect();

        h.chars().count() == l + os.len()
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn basics() {
    let h1: Standard = "hyphenation".hyphenate(&EN_US);
    let h2: Standard = "project".hyphenate(&EN_US);
    let h3: Standard = "hypha".hyphenate(&EN_US);

    let v1: Vec<&str> = h1.clone().collect();
    let v2: Vec<&str> = h2.clone().collect();
    let v3: Vec<&str> = h3.clone().collect();
    assert_eq!(v1, vec!["hy", "phen", "ation"]);
    assert_eq!(v2, vec!["project"]);
    assert_eq!(v3, vec!["hy", "pha"]);

    let s1: String = h1.punctuate().collect();
    assert_eq!(s1, "hy\u{ad}phen\u{ad}ation".to_owned());
}
<|endoftext|>


<commit_before>#![allow(unstable)]

use std::io::process::Command;
use std::str;

static PROGNAME: &'static str = "./target/rust-cat";

#[test]
fn test_output_multi_files_print_all_chars() {
    let po = match Command::new(PROGNAME)
                                .arg("tests/fixtures/alpha.txt")
                                .arg("tests/fixtures/256.txt")
                                .arg("-vt")
                                .arg("-n").output() {

        Ok(p) => p,
        Err(err) => panic!("{}", err),
    };

    let out = str::from_utf8(po.output.as_slice()).unwrap();
    assert_eq!(out,
               "     1\tabcde$\n     2\tfghij$\n     3\tklmno$\n     4\tpqrst$\n     5\tuvwxyz$\n     6\t^@^A^B^C^D^E^F^G^H^I$\n     7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^VM-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-UM-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?");
}

#[test]
fn test_stdin_squeeze() {
    let mut process= Command::new(PROGNAME).arg("-vt").spawn().unwrap();

    process.stdin.take().unwrap().write(b"\x00\x01\x02").unwrap();
    let po = process.wait_with_output().unwrap();
    let out = str::from_utf8(po.output.as_slice()).unwrap();

    assert_eq!(out, "^@^A^B");
}

#[test]
fn test_stdin_number_non_blank() {
    let mut process = Command::new(PROGNAME).arg("-b").spawn().unwrap();

    process.stdin.take().unwrap().write(b"\na\nb\n\n\nc").unwrap();
    let po = process.wait_with_output().unwrap();
    let out =  str::from_utf8(po.output.as_slice()).unwrap();

    assert_eq!(out, "\n     1\ta\n     2\tb\n\n\n     3\tc");
}
<commit_msg>Include MIT license from coreutils in tests<commit_after>// Copyright (c) Jordi Boggiano
// With modifications by Luke Rodgers
// https://github.com/uutils/coreutils/blob/2f0d8c89c94ee4b6c1d911c48d6e56f002e2def7/test/cat.rs

// 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.

#![allow(unstable)]

use std::io::process::Command;
use std::str;

static PROGNAME: &'static str = "./target/rust-cat";

#[test]
fn test_output_multi_files_print_all_chars() {
    let po = match Command::new(PROGNAME)
                                .arg("tests/fixtures/alpha.txt")
                                .arg("tests/fixtures/256.txt")
                                .arg("-vt")
                                .arg("-n").output() {

        Ok(p) => p,
        Err(err) => panic!("{}", err),
    };

    let out = str::from_utf8(po.output.as_slice()).unwrap();
    assert_eq!(out,
               "     1\tabcde$\n     2\tfghij$\n     3\tklmno$\n     4\tpqrst$\n     5\tuvwxyz$\n     6\t^@^A^B^C^D^E^F^G^H^I$\n     7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^VM-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-UM-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?");
}

#[test]
fn test_stdin_squeeze() {
    let mut process= Command::new(PROGNAME).arg("-vt").spawn().unwrap();

    process.stdin.take().unwrap().write(b"\x00\x01\x02").unwrap();
    let po = process.wait_with_output().unwrap();
    let out = str::from_utf8(po.output.as_slice()).unwrap();

    assert_eq!(out, "^@^A^B");
}

#[test]
fn test_stdin_number_non_blank() {
    let mut process = Command::new(PROGNAME).arg("-b").spawn().unwrap();

    process.stdin.take().unwrap().write(b"\na\nb\n\n\nc").unwrap();
    let po = process.wait_with_output().unwrap();
    let out =  str::from_utf8(po.output.as_slice()).unwrap();

    assert_eq!(out, "\n     1\ta\n     2\tb\n\n\n     3\tc");
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate entity_rust;
#[macro_use]
extern crate lazy_static;
extern crate shared_mutex;
extern crate uuid;

// use std::any::Any;

use entity_rust::{ events };

event!{ test_event , x: i64, y: i64 }
component! { test_component, a: i64, b: i64 }

system!( test_system {
	state! { x: i64 }

	on!( test_event, { positions: super::test_component::Component }, {}) self, data => {
		assert!(data.len() > 0);
		self.x += data[0].x;
		assert!(positions.len() > 0);
		self.x += positions[0].1.a;
	}
});

fn reset_state() {
	let mut state = test_system::STATE.write().expect("System lock corrupted.");
	state.x = 0;
}

#[test]
fn run_event_runs_system_events() {
	reset_state();
	test_system::register();
	test_component::register();

	test_component::add(1, test_component::Component { a: 2, b: 10 });

	test_event::trigger(test_event::Data { x: 1, y: 6 });
	events::run_events();
	let state = test_system::STATE.read().expect("System lock corrupted");
	assert!(state.x == 3);
}<commit_msg>fix integration test<commit_after>#[macro_use]
extern crate entity_rust;
#[macro_use]
extern crate lazy_static;
extern crate shared_mutex;
extern crate uuid;

// use std::any::Any;

use entity_rust::{ events };

event!{ test_event , x: i64, y: i64 }
component! { test_component, a: i64, b: i64 }

system!( test_system {
	use super::test_component;

	state! { x: i64 }

	on!( test_event, { positions: test_component::Component }, {}) self, data => {
		assert!(data.len() > 0);
		self.x += data[0].x;
		assert!(positions.len() > 0);
		self.x += positions[0].1.a;
	}
});

fn reset_state() {
	let mut state = test_system::STATE.write().expect("System lock corrupted.");
	state.x = 0;
}

#[test]
fn run_event_runs_system_events() {
	reset_state();
	test_system::register();
	test_component::register();

	test_component::add(1, test_component::Component { a: 2, b: 10 });

	test_event::trigger(1, 6);
	events::run_events();
	let state = test_system::STATE.read().expect("System lock corrupted");
	assert!(state.x == 3);
}<|endoftext|>


<commit_before>use std::cmp;
use rand::{
    Rng,
    seq::SliceRandom,
    thread_rng,
};
use crate::{
    direction::Direction,
    ship::Ship,
    space::Space,
};

pub struct Player {
    is_cpu: bool,
    spaces: Vec<Space>,
    ships: Vec<Ship>,
    grid_size: [u8; 2],
    grid_cursor: [u8; 2],
    temp_ship: Ship,
}

impl Player {
    pub fn new(grid_size: [u8; 2], is_cpu: bool) -> Player {
        let spaces_x = grid_size[0] as usize;
        let spaces_y = grid_size[1] as usize;
        let mut spaces = Vec::with_capacity(spaces_x * spaces_y);

        for col in 0..grid_size[0] {
            for row in 0..grid_size[1] {
                spaces.push(Space::new([col, row]));
            }
        }

        Player {
            is_cpu: is_cpu,
            spaces: spaces,
            ships: vec![],
            grid_size: grid_size,
            grid_cursor: [0, 0],
            temp_ship: Ship::new(vec![[0, 0], [1, 0]], Direction::West),
        }
    }

    /// Selects a space.
    pub fn select_space(&mut self, pos: &[u8; 2]) {
        let space_index = self.space_index(pos);
        let ship_hit = self.ships.iter().position(|s| s.pos().contains(pos));
        self.spaces[space_index].set_checked(ship_hit.is_some());
    }

    /// Checks whether a ship at the given position is sunk.
    ///
    /// If all spaces have been hit but the ship has not been set to sunk, then
    /// it will be sunk on execution of this method.
    pub fn is_ship_sunk_by_pos(&mut self, pos: &[u8; 2]) -> bool {
        // TODO return Result; send error if no ship at pos
        let hit_ship = self.ships.iter().position(|s| s.pos().contains(pos));
        let mut ship_sunk = false;

        if let Some(ship) = hit_ship {
            ship_sunk = self.ships[ship].pos()
                .iter()
                .all(|p| self.space(p).is_hit());

            if ship_sunk && self.ships[ship].is_active() {
                self.ships[ship].sink();
            }
        }

        ship_sunk
    }

    /// Returns whether all of the player's ships have been sunk.
    pub fn all_ships_sunk(&self) -> bool {
        self.ships.iter().all(|ship| ship.is_sunk())
    }

    /// Determines the next space a CPU player will select.
    pub fn cpu_select_space(&self) -> [u8; 2] {
        let mut rng = thread_rng();
        let mut select = Vec::new();
        let mut directions = Direction::all();

        directions.shuffle(&mut rng);

        let mut hit_spaces = self.spaces.iter()
            .filter(|s| s.is_hit() && self.ship(s.pos()).unwrap().is_active())
            .collect::<Vec<&Space>>();

        hit_spaces.shuffle(&mut rng);

        // Check for a line of hit spaces.
        for space in &hit_spaces {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    space.pos(),
                    *direction,
                    true
                );

                if let Some(pos) = unchecked {
                    if !select.contains(&pos) {
                        select.push(pos);
                    }
                }
            }
        }

        // If a hit space was found, but no hit spaces next to it, find any
        // unchecked spaces next to it.
        if hit_spaces.len() > 0 && select.is_empty() {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    hit_spaces[0].pos(),
                    *direction,
                    false
                );

                if let Some(pos) = unchecked {
                    select.push(pos);
                }
            }
        }

        // If no spaces have been selected, just select any available space.
        if select.is_empty() {
            let pos: [u8; 2] = loop {
                let space = self.rng_pos();

                if self.space(&space).is_unchecked() {
                    break space;
                }
            };

            select.push(pos);
        }

        if select.len() > 1 {
            select.shuffle(&mut rng);
        }

        select[0]
    }

    fn rng_pos(&self) -> [u8; 2] {
        let mut rng = thread_rng();

        [
            rng.gen_range(0, self.grid_size[0]),
            rng.gen_range(0, self.grid_size[1]),
        ]
    }

    pub fn move_temp_ship(&mut self, direction: Direction) {
        let old_head = self.temp_ship.pos()[0];

        if let Some(new_head) = self.movement(&old_head, direction) {
            if let Some(ship) = self.get_ship_position(
                new_head,
                self.temp_ship.dir(),
                self.temp_ship.len() as u8
            ) {
                self.temp_ship = Ship::new(ship, self.temp_ship.dir());
            }
        }
    }

    pub fn place_temp_ship(&mut self) {
        let ship = self.temp_ship.pos().clone();

        if self.valid_ship_position(&ship) {
            self.ships.push(Ship::new(ship.to_vec(), self.temp_ship.dir()));

            self.temp_ship = Ship::new(self.get_ship_position(
                [0, 0],
                Direction::West,
                self.ships[self.ships.len() - 1].len() as u8 + 1
            ).unwrap(), Direction::West);
        }
    }

    /// Rotates a ship during the ship placement game state.
    pub fn rotate_temp_ship(&mut self) {
        let ship_len = self.temp_ship.len() as u8;
        let dir = match self.temp_ship.dir() {
            Direction::North => Direction::East,
            Direction::East => Direction::South,
            Direction::South => Direction::West,
            Direction::West => Direction::North,
        };

        // If the current starting position would cause the rotation to position
        // the ship partially out of bounds, adjust the starting position such
        // that the ship will be entirely within bounds.
        let old_start_pos = self.temp_ship.pos()[0];
        let start_pos = match dir {
            Direction::North => [
                old_start_pos[0],
                cmp::min(old_start_pos[1], self.grid_size[1] - ship_len),
            ],
            Direction::East => [
                cmp::max(old_start_pos[0], ship_len - 1),
                old_start_pos[1],
            ],
            Direction::South => [
                old_start_pos[0],
                cmp::max(old_start_pos[1], ship_len - 1),
            ],
            Direction::West => [
                cmp::min(old_start_pos[0], self.grid_size[0] - ship_len),
                old_start_pos[1],
            ],
        };

        if let Some(ship) = self.get_ship_position(start_pos, dir, ship_len) {
            self.temp_ship = Ship::new(ship, dir);
        }
    }

    pub fn cpu_place_ships(&mut self) {
        for length in 2..6 {
            let (pos, dir) = self.cpu_place_ship(length);
            self.ships.push(Ship::new(pos, dir));
        }
    }

    /// RNGs ship locations for CPU players.
    fn cpu_place_ship(&self, length: u8) -> (Vec<[u8; 2]>, Direction) {
        // RNG a position and direction, then make sure it's valid.
        loop {
            let pos = self.rng_pos();
            let direction = Direction::random();

            if let Some(s) = self.get_ship_position(pos, direction, length) {
                if self.valid_ship_position(&s) {
                    break (s, direction);
                }
            }
        }
    }

    /// Returns a ship position, given its head position, direction and length.
    ///
    /// `direction` refers to the direction the ship is facing, not the
    /// direction in which positions are generated.
    /// Returns `None` if the resulting ship position would not be contained
    /// within the grid.
    pub fn get_ship_position(
        &self,
        head: [u8; 2],
        direction: Direction,
        length: u8
    ) -> Option<Vec<[u8; 2]>> {
        let valid = match direction {
            Direction::North => head[1] + length <= self.grid_size[1],
            Direction::East => head[0] >= length - 1,
            Direction::South => head[1] >= length - 1,
            Direction::West => head[0] + length <= self.grid_size[0],
        };

        let ship_opt = if valid {
            let mut ship = vec![head];

            for pos in 1..length {
                let pos_u8 = pos as u8;
                let space = match direction {
                    Direction::North => [head[0], head[1] + pos_u8],
                    Direction::East => [head[0] - pos_u8, head[1]],
                    Direction::South => [head[0], head[1] - pos_u8],
                    Direction::West => [head[0] + pos_u8, head[1]],
                };
                ship.push(space);
            }
            Some(ship)
        } else {
            None
        };

        ship_opt
    }

    /// Checks that the given ship position is valid.
    ///
    /// If the player is CPU-controlled, a ship in a space next to another ship
    /// will be considered invalid.
    pub fn valid_ship_position(&self, new_ship: &[[u8; 2]]) -> bool {
        new_ship.iter()
            .all(|s| self.valid_space(s) && !self.ship_is_in_space(s)
                 && !(self.ship_is_next_to(s) && self.is_cpu))
    }

    /// Gets a reference to the ships.
    pub fn ships(&self) -> &[Ship] {
        &self.ships
    }

    /// Gets a reference to a ship if it is in the given position.
    fn ship(&self, pos: &[u8; 2]) -> Option<&Ship> {
        self.ships.iter().find(|s| s.pos().contains(pos))
    }

    /// Returns whether a ship occupies the specified grid coordinates.
    pub fn ship_is_in_space(&self, pos: &[u8; 2]) -> bool {
        self.ships.iter().any(|s| s.pos().contains(pos))
    }

    /// Returns whether there is a ship next to the specified grid coordinates.
    fn ship_is_next_to(&self, pos: &[u8; 2]) -> bool {
        let mut result = false;
        let x = pos[0];
        let y = pos[1];

        // Left
        if x > 0 {
            result = self.ship_is_in_space(&[x - 1, y]);
        }

        // Right
        if x < self.grid_size[0] - 1 && !result {
            result = self.ship_is_in_space(&[x + 1, y]);
        }

        // Above
        if y > 0 && !result {
            result = self.ship_is_in_space(&[x, y - 1]);
        }

        // Below
        if y < self.grid_size[1] - 1 && !result {
            result = self.ship_is_in_space(&[x, y + 1]);
        }

        result
    }

    /// Gets a reference to the spaces.
    pub fn spaces(&self) -> &[Space] {
        &self.spaces
    }

    /// Returns whether the given position is valid.
    fn valid_space(&self, pos: &[u8; 2]) -> bool {
        pos[0] < self.grid_size[0] && pos[1] < self.grid_size[1]
    }

    /// Gets a reference to the space with the given position.
    pub fn space(&self, pos: &[u8; 2]) -> &Space {
        self.spaces.get(self.space_index(pos)).unwrap()
    }

    /// Calculates the index of the given position in the spaces vector.
    fn space_index(&self, pos: &[u8; 2]) -> usize {
        self.grid_size[0] as usize * pos[0] as usize + pos[1] as usize
    }

    /// Returns the coordinates of the player's grid cursor.
    pub fn get_grid_cursor(&self) -> [u8; 2] {
        self.grid_cursor.clone()
    }

    /// Returns the coordinates of a movement from `pos` in a `direction`.
    /// Returns `None` if the movement is not possible.
    fn movement(&self, pos: &[u8; 2], direction: Direction) -> Option<[u8; 2]> {
        let valid = match direction {
            Direction::North => pos[1] > 0,
            Direction::East => pos[0] < self.grid_size[0] - 1,
            Direction::South => pos[1] < self.grid_size[1] - 1,
            Direction::West => pos[0] > 0,
        };

        match valid {
            true => Some(match direction {
                Direction::North => [pos[0], pos[1] - 1],
                Direction::East => [pos[0] + 1, pos[1]],
                Direction::South => [pos[0], pos[1] + 1],
                Direction::West => [pos[0] - 1, pos[1]],
            }),
            false => None,
        }
    }

    /// Finds the first non-hit, unchecked space in a `direction` from `pos`.
    /// Can also make sure the space is at the end of a `line` of hit spaces.
    /// Returns `None` if the first non-hit space has been checked or if a grid
    /// boundary is reached.
    fn find_unchecked_space(
        &self,
        pos: &[u8; 2],
        direction: Direction,
        check_for_line: bool
    ) -> Option<[u8; 2]> {
        let mut check_pos = self.movement(pos, direction);

        while let Some(next_pos) = check_pos {
            let next_space = self.space(&next_pos);

            match next_space.is_hit() {
                true => check_pos = self.movement(&next_pos, direction),
                false => {
                    if !next_space.is_unchecked() {
                        check_pos = None;
                    }
                    break;
                }
            };
        }

        if check_for_line && check_pos.is_some() {
            let unchecked = check_pos.unwrap();
            let opposite_dir = direction.opposite();
            let prev_pos = self.movement(&unchecked, opposite_dir).unwrap();

            if &prev_pos == pos {
                check_pos = None;
            }
        }

        check_pos
    }

    /// Moves the player's grid cursor in the given `direction` if possible.
    pub fn move_grid_cursor(&mut self, direction: Direction) {
        if let Some(new_cursor) = self.movement(&self.grid_cursor, direction) {
            self.set_grid_cursor(&new_cursor);
        }
    }

    /// Sets the player's grid cursor coordinates.
    pub fn set_grid_cursor(&mut self, new_cursor: &[u8; 2]) {
        self.grid_cursor = *new_cursor;
    }

    pub fn is_cpu(&self) -> bool {
        self.is_cpu
    }

    pub fn temp_ship(&self) -> &Ship {
        &self.temp_ship
    }

    pub fn set_temp_ship_pos(&mut self, ship: Vec<[u8; 2]>) {
        self.temp_ship = Ship::new(ship, self.temp_ship.dir());
    }
}

<commit_msg>Player: add placement_ship_mut()<commit_after>use std::cmp;
use rand::{
    Rng,
    seq::SliceRandom,
    thread_rng,
};
use crate::{
    direction::Direction,
    ship::Ship,
    space::Space,
};

pub struct Player {
    is_cpu: bool,
    spaces: Vec<Space>,
    ships: Vec<Ship>,
    grid_size: [u8; 2],
    grid_cursor: [u8; 2],
    temp_ship: Ship,
}

impl Player {
    pub fn new(grid_size: [u8; 2], is_cpu: bool) -> Player {
        let spaces_x = grid_size[0] as usize;
        let spaces_y = grid_size[1] as usize;
        let mut spaces = Vec::with_capacity(spaces_x * spaces_y);

        for col in 0..grid_size[0] {
            for row in 0..grid_size[1] {
                spaces.push(Space::new([col, row]));
            }
        }

        Player {
            is_cpu: is_cpu,
            spaces: spaces,
            ships: vec![],
            grid_size: grid_size,
            grid_cursor: [0, 0],
            temp_ship: Ship::new(vec![[0, 0], [1, 0]], Direction::West),
        }
    }

    /// Selects a space.
    pub fn select_space(&mut self, pos: &[u8; 2]) {
        let space_index = self.space_index(pos);
        let ship_hit = self.ships.iter().position(|s| s.pos().contains(pos));
        self.spaces[space_index].set_checked(ship_hit.is_some());
    }

    /// Checks whether a ship at the given position is sunk.
    ///
    /// If all spaces have been hit but the ship has not been set to sunk, then
    /// it will be sunk on execution of this method.
    pub fn is_ship_sunk_by_pos(&mut self, pos: &[u8; 2]) -> bool {
        // TODO return Result; send error if no ship at pos
        let hit_ship = self.ships.iter().position(|s| s.pos().contains(pos));
        let mut ship_sunk = false;

        if let Some(ship) = hit_ship {
            ship_sunk = self.ships[ship].pos()
                .iter()
                .all(|p| self.space(p).is_hit());

            if ship_sunk && self.ships[ship].is_active() {
                self.ships[ship].sink();
            }
        }

        ship_sunk
    }

    /// Returns whether all of the player's ships have been sunk.
    pub fn all_ships_sunk(&self) -> bool {
        self.ships.iter().all(|ship| ship.is_sunk())
    }

    /// Determines the next space a CPU player will select.
    pub fn cpu_select_space(&self) -> [u8; 2] {
        let mut rng = thread_rng();
        let mut select = Vec::new();
        let mut directions = Direction::all();

        directions.shuffle(&mut rng);

        let mut hit_spaces = self.spaces.iter()
            .filter(|s| s.is_hit() && self.ship(s.pos()).unwrap().is_active())
            .collect::<Vec<&Space>>();

        hit_spaces.shuffle(&mut rng);

        // Check for a line of hit spaces.
        for space in &hit_spaces {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    space.pos(),
                    *direction,
                    true
                );

                if let Some(pos) = unchecked {
                    if !select.contains(&pos) {
                        select.push(pos);
                    }
                }
            }
        }

        // If a hit space was found, but no hit spaces next to it, find any
        // unchecked spaces next to it.
        if hit_spaces.len() > 0 && select.is_empty() {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    hit_spaces[0].pos(),
                    *direction,
                    false
                );

                if let Some(pos) = unchecked {
                    select.push(pos);
                }
            }
        }

        // If no spaces have been selected, just select any available space.
        if select.is_empty() {
            let pos: [u8; 2] = loop {
                let space = self.rng_pos();

                if self.space(&space).is_unchecked() {
                    break space;
                }
            };

            select.push(pos);
        }

        if select.len() > 1 {
            select.shuffle(&mut rng);
        }

        select[0]
    }

    fn rng_pos(&self) -> [u8; 2] {
        let mut rng = thread_rng();

        [
            rng.gen_range(0, self.grid_size[0]),
            rng.gen_range(0, self.grid_size[1]),
        ]
    }

    pub fn move_temp_ship(&mut self, direction: Direction) {
        let old_head = self.temp_ship.pos()[0];

        if let Some(new_head) = self.movement(&old_head, direction) {
            if let Some(ship) = self.get_ship_position(
                new_head,
                self.temp_ship.dir(),
                self.temp_ship.len() as u8
            ) {
                self.temp_ship = Ship::new(ship, self.temp_ship.dir());
            }
        }
    }

    pub fn place_temp_ship(&mut self) {
        let ship = self.temp_ship.pos().clone();

        if self.valid_ship_position(&ship) {
            self.ships.push(Ship::new(ship.to_vec(), self.temp_ship.dir()));

            self.temp_ship = Ship::new(self.get_ship_position(
                [0, 0],
                Direction::West,
                self.ships[self.ships.len() - 1].len() as u8 + 1
            ).unwrap(), Direction::West);
        }
    }

    /// Rotates a ship during the ship placement game state.
    pub fn rotate_temp_ship(&mut self) {
        let ship_len = self.temp_ship.len() as u8;
        let dir = match self.temp_ship.dir() {
            Direction::North => Direction::East,
            Direction::East => Direction::South,
            Direction::South => Direction::West,
            Direction::West => Direction::North,
        };

        // If the current starting position would cause the rotation to position
        // the ship partially out of bounds, adjust the starting position such
        // that the ship will be entirely within bounds.
        let old_start_pos = self.temp_ship.pos()[0];
        let start_pos = match dir {
            Direction::North => [
                old_start_pos[0],
                cmp::min(old_start_pos[1], self.grid_size[1] - ship_len),
            ],
            Direction::East => [
                cmp::max(old_start_pos[0], ship_len - 1),
                old_start_pos[1],
            ],
            Direction::South => [
                old_start_pos[0],
                cmp::max(old_start_pos[1], ship_len - 1),
            ],
            Direction::West => [
                cmp::min(old_start_pos[0], self.grid_size[0] - ship_len),
                old_start_pos[1],
            ],
        };

        if let Some(ship) = self.get_ship_position(start_pos, dir, ship_len) {
            self.temp_ship = Ship::new(ship, dir);
        }
    }

    pub fn cpu_place_ships(&mut self) {
        for length in 2..6 {
            let (pos, dir) = self.cpu_place_ship(length);
            self.ships.push(Ship::new(pos, dir));
        }
    }

    /// RNGs ship locations for CPU players.
    fn cpu_place_ship(&self, length: u8) -> (Vec<[u8; 2]>, Direction) {
        // RNG a position and direction, then make sure it's valid.
        loop {
            let pos = self.rng_pos();
            let direction = Direction::random();

            if let Some(s) = self.get_ship_position(pos, direction, length) {
                if self.valid_ship_position(&s) {
                    break (s, direction);
                }
            }
        }
    }

    /// Returns a ship position, given its head position, direction and length.
    ///
    /// `direction` refers to the direction the ship is facing, not the
    /// direction in which positions are generated.
    /// Returns `None` if the resulting ship position would not be contained
    /// within the grid.
    pub fn get_ship_position(
        &self,
        head: [u8; 2],
        direction: Direction,
        length: u8
    ) -> Option<Vec<[u8; 2]>> {
        let valid = match direction {
            Direction::North => head[1] + length <= self.grid_size[1],
            Direction::East => head[0] >= length - 1,
            Direction::South => head[1] >= length - 1,
            Direction::West => head[0] + length <= self.grid_size[0],
        };

        let ship_opt = if valid {
            let mut ship = vec![head];

            for pos in 1..length {
                let pos_u8 = pos as u8;
                let space = match direction {
                    Direction::North => [head[0], head[1] + pos_u8],
                    Direction::East => [head[0] - pos_u8, head[1]],
                    Direction::South => [head[0], head[1] - pos_u8],
                    Direction::West => [head[0] + pos_u8, head[1]],
                };
                ship.push(space);
            }
            Some(ship)
        } else {
            None
        };

        ship_opt
    }

    /// Checks that the given ship position is valid.
    ///
    /// If the player is CPU-controlled, a ship in a space next to another ship
    /// will be considered invalid.
    pub fn valid_ship_position(&self, new_ship: &[[u8; 2]]) -> bool {
        new_ship.iter()
            .all(|s| self.valid_space(s) && !self.ship_is_in_space(s)
                 && !(self.ship_is_next_to(s) && self.is_cpu))
    }

    /// Gets a reference to the ships.
    pub fn ships(&self) -> &[Ship] {
        &self.ships
    }

    /// Gets a reference to a ship if it is in the given position.
    fn ship(&self, pos: &[u8; 2]) -> Option<&Ship> {
        self.ships.iter().find(|s| s.pos().contains(pos))
    }

    /// Returns whether a ship occupies the specified grid coordinates.
    pub fn ship_is_in_space(&self, pos: &[u8; 2]) -> bool {
        self.ships.iter().any(|s| s.pos().contains(pos))
    }

    /// Returns whether there is a ship next to the specified grid coordinates.
    fn ship_is_next_to(&self, pos: &[u8; 2]) -> bool {
        let mut result = false;
        let x = pos[0];
        let y = pos[1];

        // Left
        if x > 0 {
            result = self.ship_is_in_space(&[x - 1, y]);
        }

        // Right
        if x < self.grid_size[0] - 1 && !result {
            result = self.ship_is_in_space(&[x + 1, y]);
        }

        // Above
        if y > 0 && !result {
            result = self.ship_is_in_space(&[x, y - 1]);
        }

        // Below
        if y < self.grid_size[1] - 1 && !result {
            result = self.ship_is_in_space(&[x, y + 1]);
        }

        result
    }

    /// Gets a reference to the spaces.
    pub fn spaces(&self) -> &[Space] {
        &self.spaces
    }

    /// Returns whether the given position is valid.
    fn valid_space(&self, pos: &[u8; 2]) -> bool {
        pos[0] < self.grid_size[0] && pos[1] < self.grid_size[1]
    }

    /// Gets a reference to the space with the given position.
    pub fn space(&self, pos: &[u8; 2]) -> &Space {
        self.spaces.get(self.space_index(pos)).unwrap()
    }

    /// Calculates the index of the given position in the spaces vector.
    fn space_index(&self, pos: &[u8; 2]) -> usize {
        self.grid_size[0] as usize * pos[0] as usize + pos[1] as usize
    }

    /// Returns the coordinates of the player's grid cursor.
    pub fn get_grid_cursor(&self) -> [u8; 2] {
        self.grid_cursor.clone()
    }

    /// Returns the coordinates of a movement from `pos` in a `direction`.
    /// Returns `None` if the movement is not possible.
    fn movement(&self, pos: &[u8; 2], direction: Direction) -> Option<[u8; 2]> {
        let valid = match direction {
            Direction::North => pos[1] > 0,
            Direction::East => pos[0] < self.grid_size[0] - 1,
            Direction::South => pos[1] < self.grid_size[1] - 1,
            Direction::West => pos[0] > 0,
        };

        match valid {
            true => Some(match direction {
                Direction::North => [pos[0], pos[1] - 1],
                Direction::East => [pos[0] + 1, pos[1]],
                Direction::South => [pos[0], pos[1] + 1],
                Direction::West => [pos[0] - 1, pos[1]],
            }),
            false => None,
        }
    }

    /// Finds the first non-hit, unchecked space in a `direction` from `pos`.
    /// Can also make sure the space is at the end of a `line` of hit spaces.
    /// Returns `None` if the first non-hit space has been checked or if a grid
    /// boundary is reached.
    fn find_unchecked_space(
        &self,
        pos: &[u8; 2],
        direction: Direction,
        check_for_line: bool
    ) -> Option<[u8; 2]> {
        let mut check_pos = self.movement(pos, direction);

        while let Some(next_pos) = check_pos {
            let next_space = self.space(&next_pos);

            match next_space.is_hit() {
                true => check_pos = self.movement(&next_pos, direction),
                false => {
                    if !next_space.is_unchecked() {
                        check_pos = None;
                    }
                    break;
                }
            };
        }

        if check_for_line && check_pos.is_some() {
            let unchecked = check_pos.unwrap();
            let opposite_dir = direction.opposite();
            let prev_pos = self.movement(&unchecked, opposite_dir).unwrap();

            if &prev_pos == pos {
                check_pos = None;
            }
        }

        check_pos
    }

    /// Moves the player's grid cursor in the given `direction` if possible.
    pub fn move_grid_cursor(&mut self, direction: Direction) {
        if let Some(new_cursor) = self.movement(&self.grid_cursor, direction) {
            self.set_grid_cursor(&new_cursor);
        }
    }

    /// Sets the player's grid cursor coordinates.
    pub fn set_grid_cursor(&mut self, new_cursor: &[u8; 2]) {
        self.grid_cursor = *new_cursor;
    }

    pub fn is_cpu(&self) -> bool {
        self.is_cpu
    }

    pub fn temp_ship(&self) -> &Ship {
        &self.temp_ship
    }

    pub fn placement_ship_mut(&mut self) -> &mut Ship {
        &mut self.temp_ship
    }

    pub fn set_temp_ship_pos(&mut self, ship: Vec<[u8; 2]>) {
        self.temp_ship = Ship::new(ship, self.temp_ship.dir());
    }
}

<|endoftext|>