<commit_before>use grammar::*;

use peruse::slice_parsers::*;

pub fn program() -> Box<SliceParser<I=[Token], O=Block>> {

  fn expression() -> Box<SliceParser<I=[Token], O=Expr>> {
    let simple_term = matcher(|token| match token {
      Token::Ident(name) => Some(Expr::Variable(name)),
      Token::Number(i)   => Some(Expr::Num(i)),
      _ => None
    });

    let paren_expr = || lit(Token::OpenParen).then_r(recursive(|| expression())).then_l(lit(Token::CloseParen));

    let term = paren_expr().or(simple_term);

    let multop = matcher(|token| match token {
      Token::MultSign     => Some(MultOp::Multiply),
      Token::DivideSign   => Some(MultOp::Divide),
      Token::ModuloSign   => Some(MultOp::Modulo),
      _ => None
    });

    let addop = matcher(|token| match token {
      Token::PlusSign     => Some(AddOp::Add),
      Token::MinusSign    => Some(AddOp::Subtract),
      _ => None
    });

    let mult =  {
      let p = term.clone().then(multop.then(term).repeat()).map(|(first, seq)| {
        let mut ops = Vec::new();
        ops.push(MultTerm(MultOp::Multiply, first));
        for &(ref op, ref value) in seq.iter() {
          ops.push(MultTerm(op.clone(), value.clone())); //maybe box the value instead
        }
        Expr::MultDiv(ops)
      });
      boxed(Box::new(p))
    };

    let plus = {
      let p = mult.clone().then(addop.then(mult).repeat()).map(|(first, seq)| {
        let mut ops = Vec::new();
        ops.push(AddTerm(AddOp::Add, first));
        for &(ref op, ref value) in seq.iter() {
          ops.push(AddTerm(op.clone(), value.clone()));
        }
        Expr::AddSub(ops)
      });
      boxed(Box::new(p))
    };

    Box::new(plus)
  }

  let assignment = {
    let target = matcher(|token| match token {
      Token::Ident(name) => Some(name),
      _ => None
    });
    boxed(Box::new(target
      .then_l(lit(Token::Equals))
      .then(recursive(|| expression()))
      .map(|(target, expr)| Statement::Assign(target, expr))))
  };

  let comparator = || matcher(|token| match token {
    Token::Cmp(cmp) => Some(cmp),
    _ => None
  });

  fn code_block() -> Box<SliceParser<I=[Token], O=Block>> {
    Box::new(lit(Token::OpenBrace)
    .then_r(recursive(|| program()))
    .then_l(lit(Token::CloseBrace)))
  }

  fn if_stmt() -> Box<SliceParser<I=[Token], O=Statement>> {

    let comparator = || matcher(|token| match token {
      Token::Cmp(cmp) => Some(cmp),
      _ => None
    });

    let else_block = lit(Token::ElseKeyword)
      .then_r(
        recursive(if_stmt).map(|i| Block(vec![i]))
        .or(boxed(code_block()))
      );

    let p = lit(Token::IfKeyword)
      .then_r(recursive(|| expression()))
      .then(comparator())
      .then(recursive(expression))
      .then_l(lit(Token::OpenBrace))
      .then(recursive(program))
      .then_l(lit(Token::CloseBrace))
      .then(opt(else_block))
      .map(|((((l, cmp), r), block), else_opt)| Statement::If(l, cmp, r, block, else_opt));

    Box::new(p)
  };

  let while_stmt = {
    let p = lit(Token::WhileKeyword)
      .then_r(recursive(expression))
      .then(comparator())
      .then(recursive(expression))
      .then_l(lit(Token::OpenBrace))
      .then(recursive(program))
      .then_l(lit(Token::CloseBrace))
      .map(|(((l, cmp), r), block)| Statement::While(l, cmp, r, block));
    boxed(Box::new(p))
  };

  let output = boxed(Box::new(lit(Token::OutputCmd).then_r(recursive(|| expression())).map(|e| Statement::Output(e))));

  let statements = one_of(vec![assignment, output, boxed(if_stmt()), while_stmt]).then_l(lit(Token::NewLine)).repeat();

  Box::new(statements.map(|v| Block(v)))
}

<commit_msg>Fixed arithmetic parser<commit_after>use grammar::*;

use peruse::slice_parsers::*;

pub fn program() -> Box<SliceParser<I=[Token], O=Block>> {

    fn expression() -> Box<SliceParser<I=[Token], O=Expr>> {
        let simple_term = matcher(|token| match token {
            Token::Ident(name) => Some(Expr::Variable(name)),
            Token::Number(i)   => Some(Expr::Num(i)),
            _ => None
        });

        let paren_expr = || lit(Token::OpenParen).then_r(recursive(|| expression())).then_l(lit(Token::CloseParen));

        let factor = paren_expr().or(simple_term);

        let multop = matcher(|token| match token {
            Token::MultSign     => Some(MultOp::Multiply),
            Token::DivideSign   => Some(MultOp::Divide),
            Token::ModuloSign   => Some(MultOp::Modulo),
            _ => None
        });

        let addop = matcher(|token| match token {
            Token::PlusSign     => Some(AddOp::Add),
            Token::MinusSign    => Some(AddOp::Subtract),
            _ => None
        });

        let mult =  {
            let p = factor.clone().then(multop.then(factor).repeat()).map(|(first, seq)| {
                let mut ops = Vec::new();
                ops.push(MultTerm(MultOp::Multiply, first));
                for &(ref op, ref value) in seq.iter() {
                    ops.push(MultTerm(op.clone(), value.clone())); //maybe box the value instead
                }
                if ops.len() == 1 {
                    ops[0].1.clone()
                } else{
                    Expr::MultDiv(ops)
                }
            });
            boxed(Box::new(p))
        };

        let plus = {
            let p = mult.clone().then(addop.then(mult).repeat()).map(|(first, seq)| {
                let mut ops = Vec::new();
                    ops.push(AddTerm(AddOp::Add, first));
                    for &(ref op, ref value) in seq.iter() {
                        ops.push(AddTerm(op.clone(), value.clone()));
                    }
                    if ops.len() == 1 {
                        ops[0].1.clone()
                    } else{
                        Expr::AddSub(ops)
                    }
            });
            boxed(Box::new(p))
        };

        Box::new(plus)
    }

    let assignment = {
        let target = matcher(|token| match token {
            Token::Ident(name) => Some(name),
            _ => None
        });
        boxed(Box::new(target
            .then_l(lit(Token::Equals))
            .then(recursive(|| expression()))
            .map(|(target, expr)| Statement::Assign(target, expr))))
        };

        let comparator = || matcher(|token| match token {
            Token::Cmp(cmp) => Some(cmp),
            _ => None
        });

        fn code_block() -> Box<SliceParser<I=[Token], O=Block>> {
            Box::new(lit(Token::OpenBrace)
            .then_r(recursive(|| program()))
            .then_l(lit(Token::CloseBrace)))
        }

        fn if_stmt() -> Box<SliceParser<I=[Token], O=Statement>> {

            let comparator = || matcher(|token| match token {
                Token::Cmp(cmp) => Some(cmp),
                _ => None
            });

            let else_block = lit(Token::ElseKeyword)
            .then_r(
                recursive(if_stmt).map(|i| Block(vec![i]))
                .or(boxed(code_block()))
            );

            let p = lit(Token::IfKeyword)
            .then_r(recursive(|| expression()))
            .then(comparator())
            .then(recursive(expression))
            .then_l(lit(Token::OpenBrace))
            .then(recursive(program))
            .then_l(lit(Token::CloseBrace))
            .then(opt(else_block))
            .map(|((((l, cmp), r), block), else_opt)| Statement::If(l, cmp, r, block, else_opt));

            Box::new(p)
        };

        let while_stmt = {
            let p = lit(Token::WhileKeyword)
            .then_r(recursive(expression))
            .then(comparator())
            .then(recursive(expression))
            .then_l(lit(Token::OpenBrace))
            .then(recursive(program))
            .then_l(lit(Token::CloseBrace))
            .map(|(((l, cmp), r), block)| Statement::While(l, cmp, r, block));
            boxed(Box::new(p))
        };

        let output = boxed(Box::new(lit(Token::OutputCmd).then_r(recursive(|| expression())).map(|e| Statement::Output(e))));

        let statements = one_of(vec![assignment, output, boxed(if_stmt()), while_stmt]).then_l(lit(Token::NewLine)).repeat();

        Box::new(statements.map(|v| Block(v)))
    }
<|endoftext|>


<commit_before>//! A parser for lambda expressions

use term::*;
use term::Term::*;
use term::Notation::*;
use self::Token::*;
use self::CToken::*;
use self::Error::*;
use self::Expression::*;

/// A type to represent a parsing error.
#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidCharacter((usize, char)),
    InvalidExpression,
    EmptyExpression
}

#[derive(Debug, PartialEq)]
enum Token {
    Lambda,
    Lparen,
    Rparen,
    Number(usize)
}

#[derive(Debug, PartialEq)]
enum CToken {
    CLambda(String),
    CLparen,
    CRparen,
    CName(String)
}

fn tokenize_dbr(input: &str) -> Result<Vec<Token>, Error> {
    let mut chars = input.chars().enumerate();
    let mut tokens = Vec::new();

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

    Ok(tokens)
}

fn tokenize_cla(input: &str) -> Result<Vec<CToken>, Error> {
    let mut chars = input.chars().enumerate().peekable();
    let mut tokens = Vec::new();

    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 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(c) {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((i + 1, c)))
                    }
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             x  => {
                if x.is_whitespace() {
                    ()
                } else if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(x) {
                    let mut name = x.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, x)))
                }
            }
        }
    }

    Ok(tokens)
}

fn convert_classic_tokens(tokens: &[CToken]) -> Vec<Token> {
    let mut stack = Vec::new();
    let mut pos = 0;

    _convert_classic_tokens(tokens, &mut stack, &mut pos)
}

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

    while *pos < tokens.len() {
        match tokens[*pos] {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name.clone());
                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) => {
                let number = if stack.contains(name) {
                    stack.iter().rev().position(|t| t == name).unwrap() + 1
                } else {
                    stack.len() + 1
                };
                output.push(Number(number));
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
enum Expression {
    Abstraction,
    Sequence(Vec<Expression>),
    Variable(usize)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, Error> {
    let mut expr = Vec::new();

    if tokens.is_empty() { return Err(EmptyExpression) }

    while *pos < tokens.len() {
        match tokens[*pos] {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = try!(_get_ast(&tokens, pos));
                expr.push(subtree);
            },
            Rparen => {
                return Ok(Sequence(expr))
            }
        }
        *pos += 1;
    }

    Ok(Sequence(expr))
}

fn get_ast(tokens: &[Token]) -> Result<Expression, Error> {
    let mut pos = 0;

    _get_ast(tokens, &mut pos)
}

/// 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
/// - `DeBruijn` mode ignores all whitespaces (since indices > 15 are very unlikely)
/// - the indices in the `DeBruijn` notation mode start with 1 and are hexadecimal digits
///
/// # Example
/// ```
/// use lambda_calculus::parser::parse;
/// use lambda_calculus::term::Notation::*;
/// use lambda_calculus::arithmetic::{succ, pred};
///
/// assert_eq!(parse(&"λn. λf. λx. n (λg. λh. h (g f)) (λu. x) (λu. u)", Classic), Ok(pred()));
/// assert_eq!(parse(&"λn.λf.λx.n(λg.λh.h(g f))(λu.x)(λu.u)", Classic), Ok(pred()));
///
/// assert_eq!(parse(  &"λλλ2(321)",  DeBruijn), Ok(succ()));
/// assert_eq!(parse(&r#"\\\2(321)"#, DeBruijn), Ok(succ()));
/// ```
pub fn parse(input: &str, notation: Notation) -> Result<Term, Error> {
    let tokens = if notation == DeBruijn {
        try!(tokenize_dbr(input))
    } else {
        convert_classic_tokens(&try!(tokenize_cla(input)))
    };
    let ast = try!(get_ast(&tokens));

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

    let mut stack = Vec::new();
    let mut output = Vec::new();
    let term = fold_exprs(&exprs, &mut stack, &mut output);

    term
}

fn fold_exprs(exprs: &[Expression], stack: &mut Vec<Expression>, output: &mut Vec<Term>)
    -> Result<Term, Error>
{
    let mut iter = exprs.into_iter();

    while let Some(ref expr) = iter.next() {
        match **expr {
            Variable(i) => output.push(Var(i)),
            Abstraction => stack.push(Abstraction),
            Sequence(ref exprs) => {
                let mut stack2 = Vec::new();
                let mut output2 = Vec::new();
                let subexpr = try!(fold_exprs(&exprs, &mut stack2, &mut output2));
                output.push(subexpr)
            }
        }
    }

    let mut ret = try!(fold_terms(output.drain(..).collect()));

    while let Some(Abstraction) = stack.pop() {
        ret = abs(ret);
    }

    Ok(ret)
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, Error> {
    if terms.len() > 1 {
        let fst = terms.remove(0);
        Ok( terms.into_iter().fold(fst, |acc, t| app(acc, t)) )
    } else if terms.len() == 1 {
        Ok( terms.pop().unwrap() )
    } else {
        Err(EmptyExpression)
    }
}

#[cfg(test)]
mod test {
    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(&"\\\\\\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).expect("parsing Y failed!"),
            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).expect("parsing QUINE failed!"),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs(abs(abs(abs(abs(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).expect("parsing BLC failed!"),
            app(app(abs(app(Var(1), Var(1))), abs(abs(abs(app(app(app(Var(1),
            abs(abs(abs(abs(app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs(abs(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>improve the way some helper functions are called<commit_after>//! A parser for lambda expressions

use term::*;
use term::Term::*;
use term::Notation::*;
use self::Token::*;
use self::CToken::*;
use self::Error::*;
use self::Expression::*;

/// A type to represent a parsing error.
#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidCharacter((usize, char)),
    InvalidExpression,
    EmptyExpression
}

#[derive(Debug, PartialEq)]
enum Token {
    Lambda,
    Lparen,
    Rparen,
    Number(usize)
}

#[derive(Debug, PartialEq)]
enum CToken {
    CLambda(String),
    CLparen,
    CRparen,
    CName(String)
}

fn tokenize_dbr(input: &str) -> Result<Vec<Token>, Error> {
    let mut chars = input.chars().enumerate();
    let mut tokens = Vec::new();

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

    Ok(tokens)
}

fn tokenize_cla(input: &str) -> Result<Vec<CToken>, Error> {
    let mut chars = input.chars().enumerate().peekable();
    let mut tokens = Vec::new();

    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 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(c) {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((i + 1, c)))
                    }
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             x  => {
                if x.is_whitespace() {
                    ()
                } else if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(x) {
                    let mut name = x.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, x)))
                }
            }
        }
    }

    Ok(tokens)
}

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::new();
    let mut inner_stack_count = 0;

    while *pos < tokens.len() {
        match tokens[*pos] {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name.clone());
                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) => {
                let number = if stack.contains(name) {
                    stack.iter().rev().position(|t| t == name).unwrap() + 1
                } else {
                    stack.len() + 1
                };
                output.push(Number(number));
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
enum Expression {
    Abstraction,
    Sequence(Vec<Expression>),
    Variable(usize)
}

fn get_ast(tokens: &[Token]) -> Result<Expression, Error> {
    _get_ast(tokens, &mut 0)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, Error> {
    let mut expr = Vec::new();

    if tokens.is_empty() { return Err(EmptyExpression) }

    while *pos < tokens.len() {
        match tokens[*pos] {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = try!(_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
/// - `DeBruijn` mode ignores all whitespaces (since indices > 15 are very unlikely)
/// - the indices in the `DeBruijn` notation mode start with 1 and are hexadecimal digits
///
/// # Example
/// ```
/// use lambda_calculus::parser::parse;
/// use lambda_calculus::term::Notation::*;
/// use lambda_calculus::arithmetic::{succ, pred};
///
/// assert_eq!(parse(&"λn. λf. λx. n (λg. λh. h (g f)) (λu. x) (λu. u)", Classic), Ok(pred()));
/// assert_eq!(parse(&"λn.λf.λx.n(λg.λh.h(g f))(λu.x)(λu.u)", Classic), Ok(pred()));
///
/// assert_eq!(parse(  &"λλλ2(321)",  DeBruijn), Ok(succ()));
/// assert_eq!(parse(&r#"\\\2(321)"#, DeBruijn), Ok(succ()));
/// ```
pub fn parse(input: &str, notation: Notation) -> Result<Term, Error> {
    let tokens = if notation == DeBruijn {
        try!(tokenize_dbr(input))
    } else {
        convert_classic_tokens(&try!(tokenize_cla(input)))
    };
    let ast = try!(get_ast(&tokens));

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

    let mut stack = Vec::new();
    let mut output = Vec::new();
    let term = fold_exprs(&exprs, &mut stack, &mut output);

    term
}

fn fold_exprs(exprs: &[Expression], stack: &mut Vec<Expression>, output: &mut Vec<Term>)
    -> Result<Term, Error>
{
    let mut iter = exprs.into_iter();

    while let Some(ref expr) = iter.next() {
        match **expr {
            Variable(i) => output.push(Var(i)),
            Abstraction => stack.push(Abstraction),
            Sequence(ref exprs) => {
                let mut stack2 = Vec::new();
                let mut output2 = Vec::new();
                let subexpr = try!(fold_exprs(&exprs, &mut stack2, &mut output2));
                output.push(subexpr)
            }
        }
    }

    let mut ret = try!(fold_terms(output.drain(..).collect()));

    while let Some(Abstraction) = stack.pop() {
        ret = abs(ret);
    }

    Ok(ret)
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, Error> {
    if terms.len() > 1 {
        let fst = terms.remove(0);
        Ok( terms.into_iter().fold(fst, |acc, t| app(acc, t)) )
    } else if terms.len() == 1 {
        Ok( terms.pop().unwrap() )
    } else {
        Err(EmptyExpression)
    }
}

#[cfg(test)]
mod test {
    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(&"\\\\\\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).expect("parsing Y failed!"),
            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).expect("parsing QUINE failed!"),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs(abs(abs(abs(abs(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).expect("parsing BLC failed!"),
            app(app(abs(app(Var(1), Var(1))), abs(abs(abs(app(app(app(Var(1),
            abs(abs(abs(abs(app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs(abs(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>//! Functions for parsing a molecule or a chemical reaction.
//! Mostly based on the HTML parser from the robinson browser engine:
//! https://github.com/mbrubeck/robinson
//!
//! The parser works with the following grammar:
//! R = (R)eaction
//! S = one (S)ide of a reaction
//! M = (M)olecule
//! P = Combination of a (P)eriodic element and maybe a coefficient
//! E = Periodic (E)lement
//! C = (C)oefficient
//!
//! R --> S -> S
//! S --> M + S
//!    |  M
//! M --> PM
//!    |  P
//! P --> EC
//!    |  E
//! E --> <text>
//!    |  (M)
//! C --> <number>

use std::str::CharRange;
use token::Token;
use token::TokenKind::*;
use error::{CTResult, CTError};

pub struct Parser {
    pos: usize,
    input: String,
}

impl Parser {
    pub fn new(input: &str) -> Parser {
        Parser { pos: 0, input: String::from_str(input) }
    }

    pub fn parse_reaction(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut lhs = try!(self.parse_side());
        out.extend(lhs.drain());
        self.consume_whitespace();

        // we do not care if some of the consumes are not called, since this is
        // an error anyway, and will abort the parsing
        if self.pos + 2 >= self.input.len() || self.consume_char() != '-' ||
                                               self.consume_char() != '>' {
            return Err(CTError {
                desc: "Missing arrow (->) in chemical reaction".to_string(),
                pos: self.pos - 2,
                len: 1,
            });
        } else {
            out.push(Token { tok: LeftArrow, pos: self.pos - 2, len: 2 });
        }
        self.consume_whitespace();

        let mut rhs = try!(self.parse_side());
        out.extend(rhs.drain());

        Ok(out)
    }

    pub fn parse_side(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut molecule = try!(self.parse_molecule());
        out.extend(molecule.drain());
        self.consume_whitespace();

        if !self.eof() && self.peek_char() == '+' {
            out.push(Token { tok: Plus, pos: self.pos, len: 1 });
            self.consume_char();
            self.consume_whitespace();
            let mut rest = try!(self.parse_side());
            out.extend(rest.drain());
        }

        Ok(out)
    }

    pub fn parse_molecule(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut per = try!(self.parse_periodic());
        out.extend(per.drain());

        // TODO: Make this cleaner
        if !self.eof() && (self.peek_char().is_alphabetic() || self.peek_char() == '(') {
            let mut molecule = try!(self.parse_molecule());
            out.extend(molecule.drain());
        }

        Ok(out)
    }

    fn parse_periodic(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut elem = try!(self.parse_element());
        out.extend(elem.drain());

        if !self.eof() && self.peek_char().is_numeric() {
            let coef = try!(self.parse_coefficient());
            out.push(coef);
        }

        Ok(out)
    }

    fn parse_element(&mut self) -> CTResult<Vec<Token>> {
        if self.eof() {
            return Err(CTError {
                desc: "Found no periodic element".to_string(),
                pos: self.pos,
                len: 1,
            });
        }
        let start_pos = self.pos;
        let first = self.consume_char();
        if first == '(' {
            let mut out = Vec::new();
            out.push(Token { tok: ParenOpen, pos: start_pos, len: 1 });
            let mut molecule = try!(self.parse_molecule());
            out.extend(molecule.drain());

            if self.eof() || self.consume_char() != ')' {
                Err(CTError {
                    desc: "Missing closing parentheses".to_string(),
                    pos: self.pos - 1,
                    len: 1,
                })
            } else {
                out.push(Token { tok: ParenClose, pos: self.pos - 1, len: 1 });
                Ok(out)
            }
        } else if first.is_uppercase() {
            let mut name = String::new();
            name.push(first);
            name.push_str(self.consume_while(|ch| ch.is_lowercase()).as_slice());
            let len = name.len();
            Ok(vec!(Token{ tok: Elem(name), pos: start_pos, len: len }))
        } else {
            println!("{:?}", first);
            Err(CTError {
                desc: "Missing uppercase letter at the beginning of the element".to_string(),
                pos: self.pos - 1,
                len: 1,
            })
        }
    }

    fn parse_coefficient(&mut self) -> CTResult<Token> {
        let start_pos = self.pos;
        let num_str = self.consume_while(|ch| ch.is_numeric());
        if let Some(num) = num_str.parse::<u32>() {
            Ok(Token { tok: Coefficient(num), pos: start_pos, len: num_str.len() })
        } else {
            Err(CTError {
                desc: "Could not parse coefficient".to_string(),
                pos: start_pos,
                len: num_str.len(),
            })
        }
    }

    fn peek_char(&self) -> char {
        self.input.char_at(self.pos)
    }

    fn consume_char(&mut self) -> char {
        let CharRange { ch, next } = self.input.char_range_at(self.pos);
        self.pos = next;
        ch
    }

    fn consume_while<F>(&mut self, pred: F) -> String where F: Fn(char) -> bool {
        let mut out = String::new();
        while !self.eof() && pred(self.peek_char()) {
            out.push(self.consume_char());
        }
        out
    }

    fn consume_whitespace(&mut self) {
        self.consume_while(|ch| ch.is_whitespace());
    }

    fn eof(&mut self) -> bool {
        self.pos >= self.input.len()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use error::CTResult;
    use token::Token;
    use token::TokenKind::*;

    fn check_raw_result(raw_result: CTResult<Vec<Token>>, expected: Vec<Token>) {
        if let Ok(result) = raw_result {
            assert_eq!(result, expected);
        } else {
            panic!(raw_result);
        }
    }

    #[test]
    fn elems() {
        let mut parser = Parser::new("CHeH");
        let raw_result = parser.parse_molecule();
        let expected = vec!(Token { tok: Elem("C".to_string()), pos: 0, len: 1 },
                            Token { tok: Elem("He".to_string()), pos: 1, len: 2 },
                            Token { tok: Elem("H".to_string()), pos: 3, len: 1 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn coefs() {
        let mut parser = Parser::new("C23");
        let raw_result = parser.parse_molecule();
        let expected = vec!(Token { tok: Elem("C".to_string()), pos: 0, len: 1 },
                            Token { tok: Coefficient(23), pos: 1, len: 2 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn parens() {
        let mut parser = Parser::new("(C)2");
        let raw_result = parser.parse_molecule();
        let expected = vec!(Token { tok: ParenOpen, pos: 0, len: 1 },
                            Token { tok: Elem("C".to_string()), pos: 1, len: 1},
                            Token { tok: ParenClose, pos: 2, len: 1},
                            Token { tok: Coefficient(2), pos: 3, len: 1 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn empty() {
        let mut parser = Parser::new("");
        let raw_result = parser.parse_molecule();
        assert!(raw_result.is_err());
    }

    #[test]
    fn no_uppercase() {
        let mut parser = Parser::new("c");
        let raw_result = parser.parse_molecule();
        assert!(raw_result.is_err());
    }

    #[test]
    fn paren_error() {
        let mut parser = Parser::new("(C");
        let raw_result = parser.parse_molecule();
        assert!(raw_result.is_err());
    }

    #[test]
    fn invald_num() {
        let mut parser = Parser::new("C999999999999999999999");
        let raw_result = parser.parse_molecule();
        assert!(raw_result.is_err());
    }
}<commit_msg>Add more parser tests for reaction parsing<commit_after>//! Functions for parsing a molecule or a chemical reaction.
//! Mostly based on the HTML parser from the robinson browser engine:
//! https://github.com/mbrubeck/robinson
//!
//! The parser works with the following grammar:
//! R = (R)eaction
//! S = one (S)ide of a reaction
//! M = (M)olecule
//! P = Combination of a (P)eriodic element and maybe a coefficient
//! E = Periodic (E)lement
//! C = (C)oefficient
//!
//! R --> S -> S
//! S --> M + S
//!    |  M
//! M --> PM
//!    |  P
//! P --> EC
//!    |  E
//! E --> <text>
//!    |  (M)
//! C --> <number>

use std::str::CharRange;
use token::Token;
use token::TokenKind::*;
use error::{CTResult, CTError};

pub struct Parser {
    pos: usize,
    input: String,
}

impl Parser {
    pub fn new(input: &str) -> Parser {
        Parser { pos: 0, input: String::from_str(input) }
    }

    pub fn parse_reaction(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut lhs = try!(self.parse_side());
        out.extend(lhs.drain());
        self.consume_whitespace();

        // we do not care if some of the consumes are not called, since this is
        // an error anyway, and will abort the parsing
        if self.pos + 2 >= self.input.len() || self.consume_char() != '-' ||
                                               self.consume_char() != '>' {
            return Err(CTError {
                desc: "Missing arrow (->) in chemical reaction".to_string(),
                pos: self.pos - 2,
                len: 1,
            });
        } else {
            out.push(Token { tok: LeftArrow, pos: self.pos - 2, len: 2 });
        }
        self.consume_whitespace();

        let mut rhs = try!(self.parse_side());
        out.extend(rhs.drain());

        Ok(out)
    }

    pub fn parse_side(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut molecule = try!(self.parse_molecule());
        out.extend(molecule.drain());
        self.consume_whitespace();

        if !self.eof() && self.peek_char() == '+' {
            out.push(Token { tok: Plus, pos: self.pos, len: 1 });
            self.consume_char();
            self.consume_whitespace();
            let mut rest = try!(self.parse_side());
            out.extend(rest.drain());
        }

        Ok(out)
    }

    pub fn parse_molecule(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut per = try!(self.parse_periodic());
        out.extend(per.drain());

        // TODO: Make this cleaner
        if !self.eof() && (self.peek_char().is_alphabetic() || self.peek_char() == '(') {
            let mut molecule = try!(self.parse_molecule());
            out.extend(molecule.drain());
        }

        Ok(out)
    }

    fn parse_periodic(&mut self) -> CTResult<Vec<Token>> {
        let mut out = Vec::new();
        let mut elem = try!(self.parse_element());
        out.extend(elem.drain());

        if !self.eof() && self.peek_char().is_numeric() {
            let coef = try!(self.parse_coefficient());
            out.push(coef);
        }

        Ok(out)
    }

    fn parse_element(&mut self) -> CTResult<Vec<Token>> {
        if self.eof() {
            return Err(CTError {
                desc: "Found no periodic element".to_string(),
                pos: self.pos,
                len: 1,
            });
        }
        let start_pos = self.pos;
        let first = self.consume_char();
        if first == '(' {
            let mut out = Vec::new();
            out.push(Token { tok: ParenOpen, pos: start_pos, len: 1 });
            let mut molecule = try!(self.parse_molecule());
            out.extend(molecule.drain());

            if self.eof() || self.consume_char() != ')' {
                Err(CTError {
                    desc: "Missing closing parentheses".to_string(),
                    pos: self.pos - 1,
                    len: 1,
                })
            } else {
                out.push(Token { tok: ParenClose, pos: self.pos - 1, len: 1 });
                Ok(out)
            }
        } else if first.is_uppercase() {
            let mut name = String::new();
            name.push(first);
            name.push_str(self.consume_while(|ch| ch.is_lowercase()).as_slice());
            let len = name.len();
            Ok(vec!(Token{ tok: Elem(name), pos: start_pos, len: len }))
        } else {
            println!("{:?}", first);
            Err(CTError {
                desc: "Missing uppercase letter at the beginning of the element".to_string(),
                pos: self.pos - 1,
                len: 1,
            })
        }
    }

    fn parse_coefficient(&mut self) -> CTResult<Token> {
        let start_pos = self.pos;
        let num_str = self.consume_while(|ch| ch.is_numeric());
        if let Some(num) = num_str.parse::<u32>() {
            Ok(Token { tok: Coefficient(num), pos: start_pos, len: num_str.len() })
        } else {
            Err(CTError {
                desc: "Could not parse coefficient".to_string(),
                pos: start_pos,
                len: num_str.len(),
            })
        }
    }

    fn peek_char(&self) -> char {
        self.input.char_at(self.pos)
    }

    fn consume_char(&mut self) -> char {
        let CharRange { ch, next } = self.input.char_range_at(self.pos);
        self.pos = next;
        ch
    }

    fn consume_while<F>(&mut self, pred: F) -> String where F: Fn(char) -> bool {
        let mut out = String::new();
        while !self.eof() && pred(self.peek_char()) {
            out.push(self.consume_char());
        }
        out
    }

    fn consume_whitespace(&mut self) {
        self.consume_while(|ch| ch.is_whitespace());
    }

    fn eof(&mut self) -> bool {
        self.pos >= self.input.len()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use error::CTResult;
    use token::Token;
    use token::TokenKind::*;

    fn check_raw_result(raw_result: CTResult<Vec<Token>>, expected: Vec<Token>) {
        if let Ok(result) = raw_result {
            assert_eq!(result, expected);
        } else {
            panic!(raw_result);
        }
    }

    #[test]
    fn elems() {
        let mut parser = Parser::new("CHeH");
        let raw_result = parser.parse_molecule();
        let expected = vec!(Token { tok: Elem("C".to_string()), pos: 0, len: 1 },
                            Token { tok: Elem("He".to_string()), pos: 1, len: 2 },
                            Token { tok: Elem("H".to_string()), pos: 3, len: 1 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn coefs() {
        let mut parser = Parser::new("C23");
        let raw_result = parser.parse_molecule();
        let expected = vec!(Token { tok: Elem("C".to_string()), pos: 0, len: 1 },
                            Token { tok: Coefficient(23), pos: 1, len: 2 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn parens() {
        let mut parser = Parser::new("(C)2");
        let raw_result = parser.parse_molecule();
        let expected = vec!(Token { tok: ParenOpen, pos: 0, len: 1 },
                            Token { tok: Elem("C".to_string()), pos: 1, len: 1},
                            Token { tok: ParenClose, pos: 2, len: 1},
                            Token { tok: Coefficient(2), pos: 3, len: 1 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn multiple_elems() {
        let mut parser = Parser::new("C + H");
        let raw_result = parser.parse_side();
        let expected = vec!(Token { tok: Elem("C".to_string()), pos: 0, len: 1 },
                            Token { tok: Plus, pos: 2, len: 1 },
                            Token { tok: Elem("H".to_string()), pos: 4, len: 1 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn reaction() {
        let mut parser = Parser::new("C -> H");
        let raw_result = parser.parse_reaction();
        let expected = vec!(Token { tok: Elem("C".to_string()), pos: 0, len: 1 },
                            Token { tok: LeftArrow, pos: 2, len: 2 },
                            Token { tok: Elem("H".to_string()), pos: 5, len: 1 });
        check_raw_result(raw_result, expected);
    }

    #[test]
    fn empty() {
        let mut parser = Parser::new("");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn no_uppercase() {
        let mut parser = Parser::new("c");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn paren_error() {
        let mut parser = Parser::new("(C");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn invald_num() {
        let mut parser = Parser::new("C999999999999999999999");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn dangling_plus() {
        let mut parser = Parser::new("C + -> H");
        assert!(parser.parse_reaction().is_err());
    }
}<|endoftext|>


<commit_before>//! The puzzle's state and rules.

use std::collections::BTreeSet;
use std::rc::Rc;

use ::{Val,VarToken};

/// A collection of candidates.
#[derive(Clone,Debug,Eq,PartialEq)]
#[allow(dead_code)]
enum Candidates {
    None,                       // A variable with no candidates.
    Value(Val),                 // A variable set to its initial value.
    Set(Rc<BTreeSet<Val>>),     // A variable with a list of candidates.
}

/// The puzzle to be solved.
pub struct Puzzle {
    // The number of variables in the puzzle.
    num_vars: usize,

    // The list of candidates for each variable.
    candidates: Vec<Candidates>,
}

impl Puzzle {
    /// Allocate a new puzzle.
    ///
    /// # Examples
    ///
    /// ```
    /// puzzle_solver::Puzzle::new();
    /// ```
    pub fn new() -> Self {
        Puzzle {
            num_vars: 0,
            candidates: Vec::new(),
        }
    }

    /// Allocate a new puzzle variable, without inserting any
    /// candidates.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut puzzle = puzzle_solver::Puzzle::new();
    /// puzzle.new_var();
    /// ```
    pub fn new_var(&mut self) -> VarToken {
        let var = VarToken(self.num_vars);
        self.num_vars = self.num_vars + 1;
        self.candidates.push(Candidates::None);
        var
    }
}
<commit_msg>Add operations to manipulate candidates.<commit_after>//! The puzzle's state and rules.

use std::collections::BTreeSet;
use std::rc::Rc;

use ::{Val,VarToken};

/// A collection of candidates.
#[derive(Clone,Debug,Eq,PartialEq)]
enum Candidates {
    None,                       // A variable with no candidates.
    Value(Val),                 // A variable set to its initial value.
    Set(Rc<BTreeSet<Val>>),     // A variable with a list of candidates.
}

/// The puzzle to be solved.
pub struct Puzzle {
    // The number of variables in the puzzle.
    num_vars: usize,

    // The list of candidates for each variable.
    candidates: Vec<Candidates>,
}

impl Puzzle {
    /// Allocate a new puzzle.
    ///
    /// # Examples
    ///
    /// ```
    /// puzzle_solver::Puzzle::new();
    /// ```
    pub fn new() -> Self {
        Puzzle {
            num_vars: 0,
            candidates: Vec::new(),
        }
    }

    /// Allocate a new puzzle variable, without inserting any
    /// candidates.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut puzzle = puzzle_solver::Puzzle::new();
    /// puzzle.new_var();
    /// ```
    pub fn new_var(&mut self) -> VarToken {
        let var = VarToken(self.num_vars);
        self.num_vars = self.num_vars + 1;
        self.candidates.push(Candidates::None);
        var
    }

    /// Set a variable to a known value.
    ///
    /// This is useful when the variable is given as part of the
    /// problem.  After this operation, any subsequent attempts to set
    /// the value will panic.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut magic_square = puzzle_solver::Puzzle::new();
    /// let mut vars = Vec::new();
    /// for _ in 0..9 {
    ///     vars.push(magic_square.new_var());
    /// }
    /// magic_square.set_value(vars[4], 5);
    /// ```
    pub fn set_value(&mut self, var: VarToken, value: Val) {
        let VarToken(idx) = var;

        if let Candidates::Value(val) = self.candidates[idx] {
            if val != value {
                panic!("attempt to set fixed variable");
            }
        }

        self.candidates[idx] = Candidates::Value(value);
    }

    /// Add candidates to a variable.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut send_more_money = puzzle_solver::Puzzle::new();
    /// for _ in 0..9 {
    ///     let var = send_more_money.new_var();
    ///     send_more_money.insert_candidates(var, &[0,1,2,3,4,5,6,7,8,9]);
    /// }
    /// ```
    pub fn insert_candidates(&mut self, var: VarToken, candidates: &[Val]) {
        let VarToken(idx) = var;

        match &self.candidates[idx] {
            &Candidates::Value(_) =>
                panic!("attempt to set fixed variable"),

            &Candidates::None => {
                self.candidates[idx] = Candidates::Set(Rc::new(BTreeSet::new()));
            },

            &Candidates::Set(_) => (),
        }

        // Why you dumb Rust?
        if let Candidates::Set(ref mut rc) = self.candidates[idx] {
            let cs = Rc::get_mut(rc).expect("unique");
            cs.extend(candidates);
        }
    }

    /// Remove candidates from a variable.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut send_more_money = puzzle_solver::Puzzle::new();
    /// let mut vars = Vec::new();
    /// for _ in 0..8 { // [s,e,n,d,m,o,r,y].
    ///     let var = send_more_money.new_var();
    ///     send_more_money.insert_candidates(var, &[0,1,2,3,4,5,6,7,8,9]);
    ///     vars.push(var);
    /// }
    ///
    /// let s = vars[0];
    /// let m = vars[4];
    /// send_more_money.remove_candidates(s, &[0]);
    /// send_more_money.remove_candidates(m, &[0]);
    /// ```
    pub fn remove_candidates(&mut self, var: VarToken, candidates: &[Val]) {
        let VarToken(idx) = var;

        match self.candidates[idx] {
            Candidates::Value(_) =>
                panic!("attempt to set fixed variable"),

            Candidates::None => (),

            Candidates::Set(ref mut rc) => {
                let cs = Rc::get_mut(rc).expect("unique");
                for c in candidates.iter() {
                    cs.remove(c);
                }
            },
        }
    }

    /// Set the variable's candidates to the intersection with the
    /// given list.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut send_more_money = puzzle_solver::Puzzle::new();
    /// let mut vars = Vec::new();
    /// for _ in 0..8 { // [s,e,n,d,m,o,r,y].
    ///     let var = send_more_money.new_var();
    ///     send_more_money.insert_candidates(var, &[0,1,2,3,4,5,6,7,8,9]);
    ///     vars.push(var);
    /// }
    ///
    /// let m = vars[4];
    /// send_more_money.intersect_candidates(m, &[0,1]);
    /// ```
    pub fn intersect_candidates(&mut self, var: VarToken, candidates: &[Val]) {
        let VarToken(idx) = var;

        match self.candidates[idx] {
            Candidates::Value(_) =>
                panic!("attempt to set fixed variable"),

            Candidates::None => (),

            Candidates::Set(ref mut rc) => {
                let cs = Rc::get_mut(rc).expect("unique");
                let mut set = BTreeSet::new();
                set.extend(candidates);
                *cs = cs.intersection(&set).cloned().collect();
            },
        }
    }
}
<|endoftext|>


<commit_before>use glium::VertexBuffer;
use glium::IndexBuffer;
use glium::Program;
use glium::DrawParameters;
use glium::Surface;
use glium::Frame;
use glium::Rect;
use glium::Texture2d;
use glium::Blend;
use glium::index::PrimitiveType;
use glium::backend::Facade;

use nalgebra::Mat4;
use nalgebra::OrthoMat3;

use mem::MemSection;
use mem::AddressSpace;

pub const LCD_WIDTH: u32    = 160;
pub const LCD_HEIGHT: u32   = 144;
pub const LCD_ASPECT: f32   = (LCD_WIDTH as f32) / (LCD_HEIGHT as f32);

pub const BG_SIZE: u32      = 256;

/// Period of the V-Blank in ns. V-Blank frequency is ~59.7 Hz
pub const VBLANK_PERIOD: u64 = 16_750_419;

/// Period of the H-Blank in ns. H-Blank frequency is ~9198 Hz
pub const HBLANK_PERIOD: u64 = 108_719;

static SIMPLE_VERT: &'static str = r#"
#version 140

in vec2 coord;
in vec2 tcoord;

out vec2 tex_coord;

uniform mat4 projection;
uniform vec2 translate;

void main() {
    tex_coord = tcoord;
    gl_Position = projection * vec4(coord.x + translate.x, coord.y + translate.y, 0.0, 1.0);
}
"#;

static COLOR_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform vec4 color;
uniform vec2 translate;

void main() {
    frag_color = color;
}
"#;

static TEXTURE_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform sampler2D tex;

void main() {
    frag_color = texture(tex, tex_coord);
}
"#;

#[derive(Copy, Clone)]
struct Vertex {
    coord: [f32; 2],
    tcoord: [f32; 2],
}

implement_vertex!(Vertex, coord, tcoord);

pub struct GbDisplay {
    simple_surface: VertexBuffer<Vertex>,
    simple_surface_idx: IndexBuffer<u32>,
    color_prog: Program,
    tex_prog: Program,
    projection: Mat4<f32>,
    ly_counter: u8,
}

impl GbDisplay {

    pub fn new<F>(display: &F) -> GbDisplay where F: Facade {
        let (vertbuf, idxbuf) = {
            let topleft = Vertex {
                coord: [0.0, 0.0],
                tcoord: [0.0, 1.0],
            };
            let topright = Vertex {
                coord: [BG_SIZE as f32, 0.0],
                tcoord: [1.0, 1.0],
            };
            let bottomright = Vertex {
                coord: [BG_SIZE as f32, BG_SIZE as f32],
                tcoord: [1.0, 0.0],
            };
            let bottomleft = Vertex {
                coord: [0.0, BG_SIZE as f32],
                tcoord: [0.0, 0.0],
            };
            let vertices = vec![topleft, topright, bottomright, bottomleft];
            let indices = vec![0, 1, 3, 1, 2, 3];
            (
                VertexBuffer::new(display, &vertices).unwrap(),
                IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &indices).unwrap()
            )
        };
        let colorprog = Program::from_source(display, SIMPLE_VERT, COLOR_FRAG, None).unwrap();
        let texprog = Program::from_source(display, SIMPLE_VERT, TEXTURE_FRAG, None).unwrap();
        let projection = {
            let orthomat = OrthoMat3::new(LCD_WIDTH as f32, LCD_HEIGHT as f32, 0.0, 1.0);
            // Reverse y coord, and translate origin to top left
            let adjust = Mat4::new(
                1.0f32, 0.0, 0.0, (LCD_WIDTH as f32) / -2.0,
                0.0, -1.0, 0.0, (LCD_HEIGHT as f32) / 2.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0
                );
            orthomat.to_mat() * adjust
        };
        GbDisplay {
            simple_surface: vertbuf,
            simple_surface_idx: idxbuf,
            color_prog: colorprog,
            tex_prog: texprog,
            projection: projection,
            ly_counter: 0,
        }
    }

    pub fn inc_ly_counter(&mut self) -> u8 {
        if self.ly_counter >= 153 {
            self.ly_counter = 0;
        } else {
            self.ly_counter += 1;
        }
        self.ly_counter
    }

    pub fn set_ly_vblank(&mut self) -> u8 {
        self.ly_counter = 144;
        self.ly_counter
    }

    pub fn clear_viewport(&mut self, frame: &mut Frame, view: Rect, color: (f32, f32, f32, f32)) {
        let params = DrawParameters {
            viewport: Some(view),
            .. Default::default()
        };
        let uniforms = uniform! {
            projection: self.projection,
            translate: (0.0f32, 0.0),
            color: color,
        };
        frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.color_prog, &uniforms, &params);
    }

    pub fn draw<F>(&mut self, display: &F, frame: &mut Frame, view: Rect, mem: &AddressSpace) where F: Facade {
        let lcdc_reg = mem[0xFF40];
        let lcd_on                  = (lcdc_reg & 0x80) != 0;
        let win_map_addr            = if (lcdc_reg & 0x40) == 0 { 0x9800 } else { 0x9C00 };
        let win_on                  = (lcdc_reg & 0x20) != 0;
        let (tile_data, signed_idx) = if (lcdc_reg & 0x10) == 0 { (0x8800, false) }
                                        else { (0x8000, true) };
        let bg_map_addr             = if (lcdc_reg & 0x08) == 0 { 0x9800 } else { 0x9C00 };
        let sprite_height           = if (lcdc_reg & 0x04) == 0 { 8 } else { 16 };
        let sprite_on               = (lcdc_reg & 0x02) != 0;
        let bg_on                   = (lcdc_reg & 0x01) != 0;
        let scroll_y = mem[0xFF42];
        let scroll_x = mem[0xFF43];
        let bg_palette = build_palette(mem[0xFF47]);
        let sprite_palette0 = build_palette(mem[0xFF48]);
        let sprite_palette1 = build_palette(mem[0xFF49]);
        let win_y = mem[0xFF4A];
        let win_x = mem[0xFF4B];

        let params = DrawParameters {
            blend: Blend::alpha_blending(),
            viewport: Some(view),
            .. Default::default()
        };

        // Draw BG
        if bg_on {
            let opts = TileOpts {
                map_addr: bg_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let bg_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (scroll_x as f32, scroll_y as f32),
                tex: &bg_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }

        // Draw window
        if win_on {
            let opts = TileOpts {
                map_addr: win_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let win_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (win_x as f32, win_y as f32),
                tex: &win_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }
    }
}

const PALETTE_COLOR0: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0);
const PALETTE_COLOR1: (f32, f32, f32, f32) = (0.25, 0.25, 0.25, 1.0);
const PALETTE_COLOR2: (f32, f32, f32, f32) = (0.75, 0.75, 0.75, 1.0);
const PALETTE_COLOR3: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 1.0);
const PALETTE_COLORTEST: (f32, f32, f32, f32) = (1.0, 0.5, 0.75, 1.0);

fn build_palette(bits: u8) -> [(f32, f32, f32, f32); 4] {
    let c0 = (bits & 0x03);
    let c1 = (bits & 0x0C) >> 2;
    let c2 = (bits & 0x30) >> 4;
    let c3 = (bits & 0xC0) >> 6;
    let mut colors = [PALETTE_COLOR0; 4];
    colors[0] = match c0 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[1] = match c1 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[2] = match c2 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[3] = match c3 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors
}

struct TileOpts {
    map_addr: u16,
    tile_addr: u16,
    signed_idx: bool,
    palette: [(f32, f32, f32, f32); 4],
}

fn build_tile_tex<F>(display: &F, mem: &AddressSpace, opts: &TileOpts) -> Texture2d where F: Facade {
    // Data layout: data[y][x], origin top left
    let mut data: Vec<Vec<(f32, f32, f32, f32)>> = Vec::with_capacity(256);
    for i in 0..256 {
        data.push(Vec::with_capacity(256));
    }
    for i in 0..1024 {
        let offset: i32 = if opts.signed_idx {
            // Abuse conversion operations
            let tmp = mem[opts.map_addr + i] as i8;
            tmp as i32
        } else {
            mem[opts.map_addr + i] as i32
        };
        let addr = (opts.tile_addr as i32 + offset) as u16;
        let y_coord = i / 32;
        // Pixel format: big endian pixel pos
        for j in 0..8 {
            let lo = mem[addr + j*2];
            let hi = mem[addr + j*2 + 1];
            for k in (0..8).rev() {
                let mask = 1 << k;
                let color = ((lo & mask) >> k) | (((hi & mask) >> k) << 1);
                data[(y_coord + j) as usize].push(opts.palette[color as usize]);
            }
        }
    }
    Texture2d::new(display, data).unwrap()
}

pub fn calculate_viewport(width: u32, height: u32) -> Rect {
    let aspect = (width as f32) / (height as f32);
    if aspect > LCD_ASPECT {
        let fixwidth = ((height as f32) * LCD_ASPECT) as u32;
        Rect {
            left: (width - fixwidth) / 2,
            bottom: 0,
            width: fixwidth,
            height: height,
        }
    } else {
        let fixheight = ((width as f32) / LCD_ASPECT) as u32;
        Rect {
            left: 0,
            bottom: (height - fixheight) / 2,
            width: width,
            height: fixheight,
        }
    }
}
<commit_msg>Fix tile data address and sign options<commit_after>use glium::VertexBuffer;
use glium::IndexBuffer;
use glium::Program;
use glium::DrawParameters;
use glium::Surface;
use glium::Frame;
use glium::Rect;
use glium::Texture2d;
use glium::Blend;
use glium::index::PrimitiveType;
use glium::backend::Facade;

use nalgebra::Mat4;
use nalgebra::OrthoMat3;

use mem::MemSection;
use mem::AddressSpace;

pub const LCD_WIDTH: u32    = 160;
pub const LCD_HEIGHT: u32   = 144;
pub const LCD_ASPECT: f32   = (LCD_WIDTH as f32) / (LCD_HEIGHT as f32);

pub const BG_SIZE: u32      = 256;

/// Period of the V-Blank in ns. V-Blank frequency is ~59.7 Hz
pub const VBLANK_PERIOD: u64 = 16_750_419;

/// Period of the H-Blank in ns. H-Blank frequency is ~9198 Hz
pub const HBLANK_PERIOD: u64 = 108_719;

static SIMPLE_VERT: &'static str = r#"
#version 140

in vec2 coord;
in vec2 tcoord;

out vec2 tex_coord;

uniform mat4 projection;
uniform vec2 translate;

void main() {
    tex_coord = tcoord;
    gl_Position = projection * vec4(coord.x + translate.x, coord.y + translate.y, 0.0, 1.0);
}
"#;

static COLOR_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform vec4 color;
uniform vec2 translate;

void main() {
    frag_color = color;
}
"#;

static TEXTURE_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform sampler2D tex;

void main() {
    frag_color = texture(tex, tex_coord);
}
"#;

#[derive(Copy, Clone)]
struct Vertex {
    coord: [f32; 2],
    tcoord: [f32; 2],
}

implement_vertex!(Vertex, coord, tcoord);

pub struct GbDisplay {
    simple_surface: VertexBuffer<Vertex>,
    simple_surface_idx: IndexBuffer<u32>,
    color_prog: Program,
    tex_prog: Program,
    projection: Mat4<f32>,
    ly_counter: u8,
}

impl GbDisplay {

    pub fn new<F>(display: &F) -> GbDisplay where F: Facade {
        let (vertbuf, idxbuf) = {
            let topleft = Vertex {
                coord: [0.0, 0.0],
                tcoord: [0.0, 1.0],
            };
            let topright = Vertex {
                coord: [BG_SIZE as f32, 0.0],
                tcoord: [1.0, 1.0],
            };
            let bottomright = Vertex {
                coord: [BG_SIZE as f32, BG_SIZE as f32],
                tcoord: [1.0, 0.0],
            };
            let bottomleft = Vertex {
                coord: [0.0, BG_SIZE as f32],
                tcoord: [0.0, 0.0],
            };
            let vertices = vec![topleft, topright, bottomright, bottomleft];
            let indices = vec![0, 1, 3, 1, 2, 3];
            (
                VertexBuffer::new(display, &vertices).unwrap(),
                IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &indices).unwrap()
            )
        };
        let colorprog = Program::from_source(display, SIMPLE_VERT, COLOR_FRAG, None).unwrap();
        let texprog = Program::from_source(display, SIMPLE_VERT, TEXTURE_FRAG, None).unwrap();
        let projection = {
            let orthomat = OrthoMat3::new(LCD_WIDTH as f32, LCD_HEIGHT as f32, 0.0, 1.0);
            // Reverse y coord, and translate origin to top left
            let adjust = Mat4::new(
                1.0f32, 0.0, 0.0, (LCD_WIDTH as f32) / -2.0,
                0.0, -1.0, 0.0, (LCD_HEIGHT as f32) / 2.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0
                );
            orthomat.to_mat() * adjust
        };
        GbDisplay {
            simple_surface: vertbuf,
            simple_surface_idx: idxbuf,
            color_prog: colorprog,
            tex_prog: texprog,
            projection: projection,
            ly_counter: 0,
        }
    }

    pub fn inc_ly_counter(&mut self) -> u8 {
        if self.ly_counter >= 153 {
            self.ly_counter = 0;
        } else {
            self.ly_counter += 1;
        }
        self.ly_counter
    }

    pub fn set_ly_vblank(&mut self) -> u8 {
        self.ly_counter = 144;
        self.ly_counter
    }

    pub fn clear_viewport(&mut self, frame: &mut Frame, view: Rect, color: (f32, f32, f32, f32)) {
        let params = DrawParameters {
            viewport: Some(view),
            .. Default::default()
        };
        let uniforms = uniform! {
            projection: self.projection,
            translate: (0.0f32, 0.0),
            color: color,
        };
        frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.color_prog, &uniforms, &params);
    }

    pub fn draw<F>(&mut self, display: &F, frame: &mut Frame, view: Rect, mem: &AddressSpace) where F: Facade {
        let lcdc_reg = mem[0xFF40];
        let lcd_on                  = (lcdc_reg & 0x80) != 0;
        let win_map_addr            = if (lcdc_reg & 0x40) == 0 { 0x9800 } else { 0x9C00 };
        let win_on                  = (lcdc_reg & 0x20) != 0;
        let (tile_data, signed_idx) = if (lcdc_reg & 0x10) == 0 { (0x9000, true) }
                                        else { (0x8000, false) };
        let bg_map_addr             = if (lcdc_reg & 0x08) == 0 { 0x9800 } else { 0x9C00 };
        let sprite_height           = if (lcdc_reg & 0x04) == 0 { 8 } else { 16 };
        let sprite_on               = (lcdc_reg & 0x02) != 0;
        let bg_on                   = (lcdc_reg & 0x01) != 0;
        let scroll_y = mem[0xFF42];
        let scroll_x = mem[0xFF43];
        let bg_palette = build_palette(mem[0xFF47]);
        let sprite_palette0 = build_palette(mem[0xFF48]);
        let sprite_palette1 = build_palette(mem[0xFF49]);
        let win_y = mem[0xFF4A];
        let win_x = mem[0xFF4B];

        let params = DrawParameters {
            blend: Blend::alpha_blending(),
            viewport: Some(view),
            .. Default::default()
        };

        // Draw BG
        if bg_on {
            let opts = TileOpts {
                map_addr: bg_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let bg_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (scroll_x as f32, scroll_y as f32),
                tex: &bg_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }

        // Draw window
        if win_on {
            let opts = TileOpts {
                map_addr: win_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let win_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (win_x as f32, win_y as f32),
                tex: &win_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }
    }
}

const PALETTE_COLOR0: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0);
const PALETTE_COLOR1: (f32, f32, f32, f32) = (0.25, 0.25, 0.25, 1.0);
const PALETTE_COLOR2: (f32, f32, f32, f32) = (0.75, 0.75, 0.75, 1.0);
const PALETTE_COLOR3: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 1.0);
const PALETTE_COLORTEST: (f32, f32, f32, f32) = (1.0, 0.5, 0.75, 1.0);

fn build_palette(bits: u8) -> [(f32, f32, f32, f32); 4] {
    let c0 = (bits & 0x03);
    let c1 = (bits & 0x0C) >> 2;
    let c2 = (bits & 0x30) >> 4;
    let c3 = (bits & 0xC0) >> 6;
    let mut colors = [PALETTE_COLOR0; 4];
    colors[0] = match c0 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[1] = match c1 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[2] = match c2 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[3] = match c3 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors
}

struct TileOpts {
    map_addr: u16,
    tile_addr: u16,
    signed_idx: bool,
    palette: [(f32, f32, f32, f32); 4],
}

fn build_tile_tex<F>(display: &F, mem: &AddressSpace, opts: &TileOpts) -> Texture2d where F: Facade {
    // Data layout: data[y][x], origin top left
    let mut data: Vec<Vec<(f32, f32, f32, f32)>> = Vec::with_capacity(256);
    for i in 0..256 {
        data.push(Vec::with_capacity(256));
    }
    for i in 0..1024 {
        let offset: i32 = if opts.signed_idx {
            // Abuse conversion operations
            let tmp = mem[opts.map_addr + i] as i8;
            tmp as i32
        } else {
            mem[opts.map_addr + i] as i32
        };
        let addr = (opts.tile_addr as i32 + offset) as u16;
        let y_coord = i / 32;
        // Pixel format: big endian pixel pos
        for j in 0..8 {
            let lo = mem[addr + j*2];
            let hi = mem[addr + j*2 + 1];
            for k in (0..8).rev() {
                let mask = 1 << k;
                let color = ((lo & mask) >> k) | (((hi & mask) >> k) << 1);
                data[(y_coord + j) as usize].push(opts.palette[color as usize]);
            }
        }
    }
    Texture2d::new(display, data).unwrap()
}

pub fn calculate_viewport(width: u32, height: u32) -> Rect {
    let aspect = (width as f32) / (height as f32);
    if aspect > LCD_ASPECT {
        let fixwidth = ((height as f32) * LCD_ASPECT) as u32;
        Rect {
            left: (width - fixwidth) / 2,
            bottom: 0,
            width: fixwidth,
            height: height,
        }
    } else {
        let fixheight = ((width as f32) / LCD_ASPECT) as u32;
        Rect {
            left: 0,
            bottom: (height - fixheight) / 2,
            width: width,
            height: fixheight,
        }
    }
}
<|endoftext|>


<commit_before>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align", "width"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknwon_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("unknown"), false);
        assert_eq!(rendered.contains("alert('Hello World')"), true);
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("<a"), true);
        assert_eq!(rendered.contains("href="), true);
        assert_eq!(rendered.contains("onclick"), false);
        assert_eq!(rendered.contains("window.alert"), false);
        assert_eq!(rendered.contains("Crate page"), true);
    }
}
<commit_msg>Give tests better failure messages<commit_after>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align", "width"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::io;
use std::io::Cursor;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::rc::Rc;

use byteorder::{LittleEndian, ReadBytesExt};
use digest::digest_reader;
use filebuffer::FileBuffer;
use sha2::Sha256;

use routing::EffectId;

/// Resource manager. Where to search for various file types (e.g. Effects).
/// Uses a 'dumb' implementation - doesn't try to auto-configure paths (/usr/bin/share/[...],
/// ~/.friendship, etc). Instead, designed to be configured by the host.
#[derive(Default, Debug)]
pub struct ResMan {
    dirs: Vec<PathBuf>,
}

/// Audio that may be on-disk.
// TODO: derive Debug. FileBuffer doesn't have it implemented.
#[derive(Clone)]
pub struct AudioBuffer {
    buffer: Rc<FileBuffer>,
}

impl ResMan {
    pub fn new() -> Self {
        Default::default()
    }
    pub fn add_dir(&mut self, dir: PathBuf) {
        self.dirs.push(dir);
    }
    /// Returns all definitions of the given effect in the form of an iterator
    ///   over boxed objects implementing io::Read.
    pub fn find_effect<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=(PathBuf, File)> + 'a {
        self.iter_effect_files(id).map(|path| {
            (path.clone(), File::open(path).unwrap())
        })
    }
    fn iter_effect_files<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=PathBuf> + 'a {
        self.iter_all_files().filter(move |f| {
            trace!("Resman: testing hash for: {:?}", f);
            match *id.sha256() {
                None => true,
                Some(ref hash) => {
                    let mut file = File::open(f).unwrap();
                    let result = digest_reader::<Sha256>(&mut file).unwrap();
                    hash == result.as_slice()
                }
            }
        })
    }
    fn iter_all_files<'a>(&'a self) -> impl Iterator<Item=PathBuf> + 'a {
        // dirs as PathBuf -> valid ReadDir objects
        self.dirs.iter().filter_map(|dir_path| {
            fs::read_dir(dir_path)
                .map_err(|e| warn!("ResMan: Failed to read directory {:?}: {}", dir_path, e))
                .ok()
        })
        // ReadDir objects -> flat list of Result<DirEntry>
        .flat_map(|read_dir| {
            read_dir
        })
        // Result<DirEntry> -> DirEntry
        .filter_map(|dir_entry| {
            dir_entry
                .map_err(|e| warn!("ResMan: Failed to read directory entry: {}", e))
                .ok()
        })
        // keep only the files
        .filter(|dir_entry| {
            if let Ok(file_type)=dir_entry.file_type() {
                file_type.is_file()
            } else {
                false
            }
        })
        // DirEntry -> Path
        .map(|dir_entry| {
            dir_entry.path()
        })
    }
}

impl AudioBuffer {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
        if path.as_ref().extension().map(|e| e == "f32").unwrap_or(false) {
            // TODO: don't abort on failure; instead, treat as zero stream
            Ok(Self {
                buffer: Rc::new(FileBuffer::open(path)?)
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("Unknown audio format for file: {:?}", path.as_ref())))
        }
    }
    /// Read data from the buffer.
    pub fn get(&self, idx: u64, ch: u32) -> f32 {
        assert_eq!(ch, 0);
        // TODO: this isn't very dependable for 32-bit OSes.
        let view = &self.buffer[idx as usize..idx as usize + 4];
        let mut reader = Cursor::new(view);
        // Read float or 0f32 if error (e.g. end of file?)
        reader.read_f32::<LittleEndian>().unwrap_or(0f32)
    }
}
<commit_msg>Fix offset error in AudioBuffer<commit_after>use std::path::PathBuf;
use std::io;
use std::io::Cursor;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::rc::Rc;

use byteorder::{LittleEndian, ReadBytesExt};
use digest::digest_reader;
use filebuffer::FileBuffer;
use sha2::Sha256;

use routing::EffectId;

/// Resource manager. Where to search for various file types (e.g. Effects).
/// Uses a 'dumb' implementation - doesn't try to auto-configure paths (/usr/bin/share/[...],
/// ~/.friendship, etc). Instead, designed to be configured by the host.
#[derive(Default, Debug)]
pub struct ResMan {
    dirs: Vec<PathBuf>,
}

/// Audio that may be on-disk.
// TODO: derive Debug. FileBuffer doesn't have it implemented.
#[derive(Clone)]
pub struct AudioBuffer {
    buffer: Rc<FileBuffer>,
}

impl ResMan {
    pub fn new() -> Self {
        Default::default()
    }
    pub fn add_dir(&mut self, dir: PathBuf) {
        self.dirs.push(dir);
    }
    /// Returns all definitions of the given effect in the form of an iterator
    ///   over boxed objects implementing io::Read.
    pub fn find_effect<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=(PathBuf, File)> + 'a {
        self.iter_effect_files(id).map(|path| {
            (path.clone(), File::open(path).unwrap())
        })
    }
    fn iter_effect_files<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=PathBuf> + 'a {
        self.iter_all_files().filter(move |f| {
            trace!("Resman: testing hash for: {:?}", f);
            match *id.sha256() {
                None => true,
                Some(ref hash) => {
                    let mut file = File::open(f).unwrap();
                    let result = digest_reader::<Sha256>(&mut file).unwrap();
                    hash == result.as_slice()
                }
            }
        })
    }
    fn iter_all_files<'a>(&'a self) -> impl Iterator<Item=PathBuf> + 'a {
        // dirs as PathBuf -> valid ReadDir objects
        self.dirs.iter().filter_map(|dir_path| {
            fs::read_dir(dir_path)
                .map_err(|e| warn!("ResMan: Failed to read directory {:?}: {}", dir_path, e))
                .ok()
        })
        // ReadDir objects -> flat list of Result<DirEntry>
        .flat_map(|read_dir| {
            read_dir
        })
        // Result<DirEntry> -> DirEntry
        .filter_map(|dir_entry| {
            dir_entry
                .map_err(|e| warn!("ResMan: Failed to read directory entry: {}", e))
                .ok()
        })
        // keep only the files
        .filter(|dir_entry| {
            if let Ok(file_type)=dir_entry.file_type() {
                file_type.is_file()
            } else {
                false
            }
        })
        // DirEntry -> Path
        .map(|dir_entry| {
            dir_entry.path()
        })
    }
}

impl AudioBuffer {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
        if path.as_ref().extension().map(|e| e == "f32").unwrap_or(false) {
            // TODO: don't abort on failure; instead, treat as zero stream
            Ok(Self {
                buffer: Rc::new(FileBuffer::open(path)?)
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("Unknown audio format for file: {:?}", path.as_ref())))
        }
    }
    /// Read data from the buffer.
    pub fn get(&self, idx: u64, ch: u32) -> f32 {
        assert_eq!(ch, 0);
        // TODO: this isn't very dependable for 32-bit OSes.
        let idx = idx*4; // frame index -> byte index
        let view = &self.buffer[idx as usize..idx as usize + 4];
        let mut reader = Cursor::new(view);
        // Read float or 0f32 if error (e.g. end of file?)
        reader.read_f32::<LittleEndian>().unwrap_or(0f32)
    }
}
<|endoftext|>