<commit_before>pub type c_char = i8;
pub type c_long = i64;
pub type c_ulong = u64;

pub type wchar_t = i16;

pub type off_t = c_long;
pub type mode_t = u16;
pub type time_t = i64;
pub type pid_t = usize;
pub type gid_t = usize;
pub type uid_t = usize;

pub type suseconds_t = i64;

s! {
    pub struct timeval {
        pub tv_sec: time_t,
        pub tv_usec: suseconds_t,
    }

    pub struct timespec {
        pub tv_sec: time_t,
        pub tv_nsec: c_long,
    }
}

pub const STDIN_FILENO: ::c_int = 0;
pub const STDOUT_FILENO: ::c_int = 1;
pub const STDERR_FILENO: ::c_int = 2;

pub const EXIT_FAILURE: ::c_int = 1;
pub const EXIT_SUCCESS: ::c_int = 0;

pub const S_ISUID: ::c_int = 0x800;
pub const S_ISGID: ::c_int = 0x400;
pub const S_ISVTX: ::c_int = 0x200;

pub const S_IFIFO: mode_t = 0x1000;
pub const S_IFCHR: mode_t = 0x2000;
pub const S_IFBLK: mode_t = 0x6000;
pub const S_IFDIR: mode_t = 0x4000;
pub const S_IFREG: mode_t = 0x8000;
pub const S_IFLNK: mode_t = 0xA000;
pub const S_IFSOCK: mode_t = 0xC000;
pub const S_IFMT: mode_t = 0xF000;
pub const S_IEXEC: mode_t = 0x40;
pub const S_IWRITE: mode_t = 0x80;
pub const S_IREAD: mode_t = 0x100;
pub const S_IRWXU: mode_t = 0x1C0;
pub const S_IXUSR: mode_t = 0x40;
pub const S_IWUSR: mode_t = 0x80;
pub const S_IRUSR: mode_t = 0x100;
pub const S_IRWXG: mode_t = 0x38;
pub const S_IXGRP: mode_t = 0x8;
pub const S_IWGRP: mode_t = 0x10;
pub const S_IRGRP: mode_t = 0x20;
pub const S_IRWXO: mode_t = 0x7;
pub const S_IXOTH: mode_t = 0x1;
pub const S_IWOTH: mode_t = 0x2;
pub const S_IROTH: mode_t = 0x4;

pub const F_DUPFD: ::c_int = 0;
pub const F_GETFD: ::c_int = 1;
pub const F_SETFD: ::c_int = 2;
pub const F_GETFL: ::c_int = 3;
pub const F_SETFL: ::c_int = 4;

pub const O_RDONLY: ::c_int =     0x0001_0000;
pub const O_WRONLY: ::c_int =     0x0002_0000;
pub const O_RDWR: ::c_int =       0x0003_0000;
pub const O_NONBLOCK: ::c_int =   0x0004_0000;
pub const O_APPEND: ::c_int =     0x0008_0000;
pub const O_SHLOCK: ::c_int =     0x0010_0000;
pub const O_EXLOCK: ::c_int =     0x0020_0000;
pub const O_ASYNC: ::c_int =      0x0040_0000;
pub const O_FSYNC: ::c_int =      0x0080_0000;
pub const O_CLOEXEC: ::c_int =    0x0100_0000;
pub const O_CREAT: ::c_int =      0x0200_0000;
pub const O_TRUNC: ::c_int =      0x0400_0000;
pub const O_EXCL: ::c_int =       0x0800_0000;
pub const O_DIRECTORY: ::c_int =  0x1000_0000;
pub const O_STAT: ::c_int =       0x2000_0000;
pub const O_SYMLINK: ::c_int =    0x4000_0000;
pub const O_NOFOLLOW: ::c_int =   0x8000_0000;
pub const O_ACCMODE: ::c_int =    O_RDONLY | O_WRONLY | O_RDWR;

extern {
    pub fn memalign(align: ::size_t, size: ::size_t) -> *mut ::c_void;
    pub fn read(fd: ::c_int, buf: *mut ::c_void, count: ::size_t)
                -> ::ssize_t;
    pub fn write(fd: ::c_int, buf: *const ::c_void, count: ::size_t)
                 -> ::ssize_t;
    pub fn fcntl(fd: ::c_int, cmd: ::c_int, ...) -> ::c_int;
    pub fn close(fd: ::c_int) -> ::c_int;
}

#[link(name = "c")]
#[link(name = "m")]
extern {}

pub use self::net::*;

mod net;
<commit_msg>Add SIG* on redox<commit_after>pub type c_char = i8;
pub type c_long = i64;
pub type c_ulong = u64;

pub type wchar_t = i16;

pub type off_t = c_long;
pub type mode_t = u16;
pub type time_t = i64;
pub type pid_t = usize;
pub type gid_t = usize;
pub type uid_t = usize;

pub type suseconds_t = i64;

s! {
    pub struct timeval {
        pub tv_sec: time_t,
        pub tv_usec: suseconds_t,
    }

    pub struct timespec {
        pub tv_sec: time_t,
        pub tv_nsec: c_long,
    }
}

pub const STDIN_FILENO: ::c_int = 0;
pub const STDOUT_FILENO: ::c_int = 1;
pub const STDERR_FILENO: ::c_int = 2;

pub const EXIT_FAILURE: ::c_int = 1;
pub const EXIT_SUCCESS: ::c_int = 0;

pub const S_ISUID: ::c_int = 0x800;
pub const S_ISGID: ::c_int = 0x400;
pub const S_ISVTX: ::c_int = 0x200;

pub const S_IFIFO: mode_t = 0x1000;
pub const S_IFCHR: mode_t = 0x2000;
pub const S_IFBLK: mode_t = 0x6000;
pub const S_IFDIR: mode_t = 0x4000;
pub const S_IFREG: mode_t = 0x8000;
pub const S_IFLNK: mode_t = 0xA000;
pub const S_IFSOCK: mode_t = 0xC000;
pub const S_IFMT: mode_t = 0xF000;
pub const S_IEXEC: mode_t = 0x40;
pub const S_IWRITE: mode_t = 0x80;
pub const S_IREAD: mode_t = 0x100;
pub const S_IRWXU: mode_t = 0x1C0;
pub const S_IXUSR: mode_t = 0x40;
pub const S_IWUSR: mode_t = 0x80;
pub const S_IRUSR: mode_t = 0x100;
pub const S_IRWXG: mode_t = 0x38;
pub const S_IXGRP: mode_t = 0x8;
pub const S_IWGRP: mode_t = 0x10;
pub const S_IRGRP: mode_t = 0x20;
pub const S_IRWXO: mode_t = 0x7;
pub const S_IXOTH: mode_t = 0x1;
pub const S_IWOTH: mode_t = 0x2;
pub const S_IROTH: mode_t = 0x4;

pub const F_DUPFD: ::c_int = 0;
pub const F_GETFD: ::c_int = 1;
pub const F_SETFD: ::c_int = 2;
pub const F_GETFL: ::c_int = 3;
pub const F_SETFL: ::c_int = 4;

pub const O_RDONLY: ::c_int =     0x0001_0000;
pub const O_WRONLY: ::c_int =     0x0002_0000;
pub const O_RDWR: ::c_int =       0x0003_0000;
pub const O_NONBLOCK: ::c_int =   0x0004_0000;
pub const O_APPEND: ::c_int =     0x0008_0000;
pub const O_SHLOCK: ::c_int =     0x0010_0000;
pub const O_EXLOCK: ::c_int =     0x0020_0000;
pub const O_ASYNC: ::c_int =      0x0040_0000;
pub const O_FSYNC: ::c_int =      0x0080_0000;
pub const O_CLOEXEC: ::c_int =    0x0100_0000;
pub const O_CREAT: ::c_int =      0x0200_0000;
pub const O_TRUNC: ::c_int =      0x0400_0000;
pub const O_EXCL: ::c_int =       0x0800_0000;
pub const O_DIRECTORY: ::c_int =  0x1000_0000;
pub const O_STAT: ::c_int =       0x2000_0000;
pub const O_SYMLINK: ::c_int =    0x4000_0000;
pub const O_NOFOLLOW: ::c_int =   0x8000_0000;
pub const O_ACCMODE: ::c_int =    O_RDONLY | O_WRONLY | O_RDWR;

pub const SIGHUP:    usize = 1;
pub const SIGINT:    usize = 2;
pub const SIGQUIT:   usize = 3;
pub const SIGILL:    usize = 4;
pub const SIGTRAP:   usize = 5;
pub const SIGABRT:   usize = 6;
pub const SIGBUS:    usize = 7;
pub const SIGFPE:    usize = 8;
pub const SIGKILL:   usize = 9;
pub const SIGUSR1:   usize = 10;
pub const SIGSEGV:   usize = 11;
pub const SIGUSR2:   usize = 12;
pub const SIGPIPE:   usize = 13;
pub const SIGALRM:   usize = 14;
pub const SIGTERM:   usize = 15;
pub const SIGSTKFLT: usize = 16;
pub const SIGCHLD:   usize = 17;
pub const SIGCONT:   usize = 18;
pub const SIGSTOP:   usize = 19;
pub const SIGTSTP:   usize = 20;
pub const SIGTTIN:   usize = 21;
pub const SIGTTOU:   usize = 22;
pub const SIGURG:    usize = 23;
pub const SIGXCPU:   usize = 24;
pub const SIGXFSZ:   usize = 25;
pub const SIGVTALRM: usize = 26;
pub const SIGPROF:   usize = 27;
pub const SIGWINCH:  usize = 28;
pub const SIGIO:     usize = 29;
pub const SIGPWR:    usize = 30;
pub const SIGSYS:    usize = 31;

extern {
    pub fn memalign(align: ::size_t, size: ::size_t) -> *mut ::c_void;
    pub fn read(fd: ::c_int, buf: *mut ::c_void, count: ::size_t)
                -> ::ssize_t;
    pub fn write(fd: ::c_int, buf: *const ::c_void, count: ::size_t)
                 -> ::ssize_t;
    pub fn fcntl(fd: ::c_int, cmd: ::c_int, ...) -> ::c_int;
    pub fn close(fd: ::c_int) -> ::c_int;
}

#[link(name = "c")]
#[link(name = "m")]
extern {}

pub use self::net::*;

mod net;
<|endoftext|>


<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` orders will not fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator. The default is `Normal`.
pub const EVALUATION_ORDER: Order = Normal;

/// The available variants of β-reduction evaluation order.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost innermost
    ApplicativeLeft,
    /// rightmost innermost
    ApplicativeRight
}

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        match EVALUATION_ORDER {
            Normal => self._beta_once_normal(0),
            ApplicativeLeft => self._beta_once_applicative_l(0),
            ApplicativeRight => self._beta_once_applicative_r(0)
        };
    }

    // the return value indicates if reduction was performed
    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_applicative_l(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative_l(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible() &&
                   !self.rhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_applicative_l(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_applicative_l(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_applicative_r(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative_r(depth + 1),
            App(_, _) => {
                if !self.rhs_ref().unwrap().is_beta_reducible() &&
                   !self.lhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.rhs_ref_mut().unwrap()._beta_once_applicative_r(depth) {
                        self.lhs_ref_mut().unwrap()._beta_once_applicative_r(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible());
    /// assert!(!not_reducible.is_beta_reducible());
    /// ```
    pub fn is_beta_reducible(&self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref().unwrap().is_beta_reducible(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible() ||
                    self.rhs_ref().unwrap().is_beta_reducible()
                }
            }
        }
    }

    /// Performs full β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn evaluation_order() {
        match EVALUATION_ORDER {
            Normal => {
                let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(beta_full(should_reduce), Var(1))
            },
            ApplicativeLeft => {
                let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
                assert_eq!(&format!("{}", beta_once(expr)), "λ1((λλ1)((λλ21)1))");

                let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(&format!("{}", beta_once(shouldnt_reduce)), "(λ2)((λ111)(λ111)(λ111))")
            },
            ApplicativeRight => {
                let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
                assert_eq!(&format!("{}", beta_once(expr)), "λ1((λλλ1)1(λ21))");

                let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(&format!("{}", beta_once(shouldnt_reduce)), "(λ2)((λ111)(λ111)(λ111))")
            }
        }
    }
}
<commit_msg>simplify beta reduction functions<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` orders will not fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator. The default is `Normal`.
pub const EVALUATION_ORDER: Order = Normal;

/// The available variants of β-reduction evaluation order.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost innermost
    ApplicativeLeft,
    /// rightmost innermost
    ApplicativeRight
}

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        match EVALUATION_ORDER {
            Normal => self._beta_once_normal(0),
            ApplicativeLeft => self._beta_once_applicative_l(0),
            ApplicativeRight => self._beta_once_applicative_r(0)
        };
    }

    fn reduce(&mut self, depth: u32) {
        let copy = self.clone();
        if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    self.reduce(depth);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_normal(depth) ||
                    self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_applicative_l(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative_l(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible() &&
                   !self.rhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.reduce(depth);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_applicative_l(depth) ||
                    self.rhs_ref_mut().unwrap()._beta_once_applicative_l(depth)
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_applicative_r(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative_r(depth + 1),
            App(_, _) => {
                if !self.rhs_ref().unwrap().is_beta_reducible() &&
                   !self.lhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.reduce(depth);
                    true
                } else {
                    self.rhs_ref_mut().unwrap()._beta_once_applicative_r(depth) ||
                    self.lhs_ref_mut().unwrap()._beta_once_applicative_r(depth)
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible());
    /// assert!(!not_reducible.is_beta_reducible());
    /// ```
    pub fn is_beta_reducible(&self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref().unwrap().is_beta_reducible(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible() ||
                    self.rhs_ref().unwrap().is_beta_reducible()
                }
            }
        }
    }

    /// Performs full β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn evaluation_order() {
        match EVALUATION_ORDER {
            Normal => {
                let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(beta_full(should_reduce), Var(1))
            },
            ApplicativeLeft => {
                let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
                assert_eq!(&format!("{}", beta_once(expr)), "λ1((λλ1)((λλ21)1))");

                let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(&format!("{}", beta_once(shouldnt_reduce)), "(λ2)((λ111)(λ111)(λ111))")
            },
            ApplicativeRight => {
                let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
                assert_eq!(&format!("{}", beta_once(expr)), "λ1((λλλ1)1(λ21))");

                let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(&format!("{}", beta_once(shouldnt_reduce)), "(λ2)((λ111)(λ111)(λ111))")
            }
        }
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_sort"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Yin <mikeyin@mikeyin.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![allow(dead_code)]

extern crate getopts;
extern crate libc;
extern crate semver;

#[macro_use]
extern crate uucore;

use std::cmp::Ordering;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write};
use std::path::Path;
use uucore::fs::is_stdin_interactive;
use semver::Version;

static NAME: &'static str = "sort";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

static DECIMAL_PT: char = '.';
static THOUSANDS_SEP: char = ',';

enum SortMode {
    Numeric,
    HumanNumeric,
    Month,
    Version,
    Default,
}

struct Settings {
    mode: SortMode,
    reverse: bool,
    outfile: Option<String>,
    stable: bool,
    unique: bool,
    check: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: SortMode::Default,
            reverse: false,
            outfile: None,
            stable: false,
            unique: false,
            check: false,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();
    let mut opts = getopts::Options::new();

    opts.optflag("n", "numeric-sort", "compare according to string numerical value");
    opts.optflag("h", "human-numeric-sort", "compare according to human readable sizes, eg 1M > 100k");
    opts.optflag("M", "month-sort", "compare according to month name abbreviation");
    opts.optflag("r", "reverse", "reverse the output");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("", "version", "output version information and exit");
    opts.optopt("o", "output", "write output to FILENAME instead of stdout", "FILENAME");
    opts.optflag("s", "stable", "stabilize sort by disabling last-resort comparison");
    opts.optflag("u", "unique", "output only the first of an equal run");
    opts.optflag("V", "version-sort", "Sort by SemVer version number, eg 1.12.2 > 1.1.2");
    opts.optflag("c", "check", "check for sorted input; do not sort");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };
    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
 {0} [OPTION]... [FILE]...

Write the sorted concatenation of all FILE(s) to standard output.

Mandatory arguments for long options are mandatory for short options too.

With no FILE, or when FILE is -, read standard input.", NAME, VERSION);
        print!("{}", opts.usage(&msg));
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    settings.mode = if matches.opt_present("numeric-sort") {
        SortMode::Numeric
    } else if matches.opt_present("human-numeric-sort") {
        SortMode::HumanNumeric
    } else if matches.opt_present("month-sort") {
        SortMode::Month
    } else if matches.opt_present("version-sort") {
        SortMode::Version
    } else {
        SortMode::Default
    };

    settings.reverse = matches.opt_present("reverse");
    settings.outfile = matches.opt_str("output");
    settings.stable = matches.opt_present("stable");
    settings.unique = matches.opt_present("unique");
    settings.check = matches.opt_present("check");

    let mut files = matches.free;
    if files.is_empty() {
        /* if no file, default to stdin */
        files.push("-".to_owned());
    }

    exec(files, &settings)
}

fn exec(files: Vec<String>, settings: &Settings) -> i32 {
    let mut lines = Vec::new();
    for path in &files {
        let (reader, _) = match open(path) {
            Some(x) => x,
            None => continue,
        };

        let buf_reader = BufReader::new(reader);

        for line in buf_reader.lines() {
            match line {
                Ok(n) => {
                    lines.push(n);
                },
                _ => break
            }
        }
    }

    let original_lines = lines.to_vec();

    let mut compare_fns = Vec::new();

    compare_fns.push(match settings.mode {
        SortMode::Numeric => numeric_compare,
        SortMode::HumanNumeric => human_numeric_size_compare,
        SortMode::Month => month_compare,
        SortMode::Version => version_compare,
        SortMode::Default => String::cmp
    });

    if !settings.stable {
        match settings.mode {
            SortMode::Default => {}
            _ => compare_fns.push(String::cmp)
        }
    }

    sort_by(&mut lines, compare_fns);

    if settings.unique {
        lines.dedup()
    }

    if settings.reverse {
        lines.reverse()
    }

    if settings.check {
        for (i, line) in lines.iter().enumerate() {
            if line != &original_lines[i] {
                println!("sort: disorder in line {}", i);
                return 1;
            }
        }
    }
    else {
        print_sorted(lines.iter(), &settings.outfile)
    }

    0

}

fn sort_by<F>(lines: &mut Vec<String>, compare_fns: Vec<F>)
    where F: Fn( &String, &String ) -> Ordering
{
    lines.sort_by(|a, b| {
        for compare_fn in &compare_fns {
            let cmp = compare_fn(a, b);
            if cmp != Ordering::Equal {
                return cmp;
            }
        }
        return Ordering::Equal;
    })
}

/// Parse the beginning string into an f64, returning -inf instead of NaN on errors.
fn permissive_f64_parse(a: &str) -> f64 {
    // Maybe should be split on non-digit, but then 10e100 won't parse properly.
    // On the flip side, this will give NEG_INFINITY for "1,234", which might be OK
    // because there's no way to handle both CSV and thousands separators without a new flag.
    // GNU sort treats "1,234" as "1" in numeric, so maybe it's fine.
    let sa: &str = a.split_whitespace().next().unwrap();
    match sa.parse::<f64>() {
        Ok(a) => a,
        Err(_) => std::f64::NEG_INFINITY
    }
}

/// Compares two floating point numbers, with errors being assumed to be -inf.
/// Stops coercing at the first whitespace char, so 1e2 will parse as 100 but
/// 1,000 will parse as -inf.
fn numeric_compare(a: &String, b: &String) -> Ordering {
    let fa = permissive_f64_parse(a);
    let fb = permissive_f64_parse(b);
    // f64::cmp isn't implemented because NaN messes with it
    // but we sidestep that with permissive_f64_parse so just fake it
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

fn human_numeric_convert(a: &String) -> f64 {
    let int_iter = a.chars();
    let suffix_iter = a.chars();
    let int_str: String = int_iter.take_while(|c| c.is_numeric()).collect();
    let suffix = suffix_iter.skip_while(|c| c.is_numeric()).next();
    let int_part = match int_str.parse::<f64>() {
        Ok(i) => i,
        Err(_) => -1f64
    } as f64;
    let suffix: f64 = match suffix.unwrap_or('\0') {
        'K' => 1000f64,
        'M' => 1E6,
        'G' => 1E9,
        'T' => 1E12,
        'P' => 1E15,
        _ => 1f64
    };
    int_part * suffix
}

/// Compare two strings as if they are human readable sizes.
/// AKA 1M > 100k
fn human_numeric_size_compare(a: &String, b: &String) -> Ordering {
    let fa = human_numeric_convert(a);
    let fb = human_numeric_convert(b);
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

#[derive(Eq, Ord, PartialEq, PartialOrd)]
enum Month {
    Unknown,
    January,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December,
}

/// Parse the beginning string into a Month, returning Month::Unknown on errors.
fn month_parse(line: &String) -> Month {
    match line.split_whitespace().next().unwrap().to_uppercase().as_ref() {
        "JAN" => Month::January,
        "FEB" => Month::February,
        "MAR" => Month::March,
        "APR" => Month::April,
        "MAY" => Month::May,
        "JUN" => Month::June,
        "JUL" => Month::July,
        "AUG" => Month::August,
        "SEP" => Month::September,
        "OCT" => Month::October,
        "NOV" => Month::November,
        "DEC" => Month::December,
        _     => Month::Unknown,
    }
}

fn month_compare(a: &String, b: &String) -> Ordering {
    month_parse(a).cmp(&month_parse(b))
}

fn version_compare(a: &String, b: &String) -> Ordering {
    let ver_a = Version::parse(a);
    let ver_b = Version::parse(b);
    if ver_a > ver_b {
        Ordering::Greater
    }
    else if ver_a < ver_b {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

fn print_sorted<S, T: Iterator<Item=S>>(iter: T, outfile: &Option<String>) where S: std::fmt::Display {
    let mut file: Box<Write> = match *outfile {
        Some(ref filename) => {
            match File::create(Path::new(&filename)) {
                Ok(f) => Box::new(BufWriter::new(f)) as Box<Write>,
                Err(e) => {
                    show_error!("sort: {0}: {1}", filename, e.to_string());
                    panic!("Could not open output file");
                },
            }
        },
        None => Box::new(stdout()) as Box<Write>,
    };

    for line in iter {
        let str = format!("{}\n", line);
        match file.write_all(str.as_bytes()) {
            Err(e) => {
                show_error!("sort: {0}", e.to_string());
                panic!("Write failed");
            },
            Ok(_) => (),
        }
    }
}

// from cat.rs
fn open(path: &str) -> Option<(Box<Read>, bool)> {
    if path == "-" {
        let stdin = stdin();
        return Some((Box::new(stdin) as Box<Read>, is_stdin_interactive()));
    }

    match File::open(Path::new(path)) {
        Ok(f) => Some((Box::new(f) as Box<Read>, false)),
        Err(e) => {
            show_error!("sort: {0}: {1}", path, e.to_string());
            None
        },
    }
}
<commit_msg>sort: refactor compare_fns into Settings<commit_after>#![crate_name = "uu_sort"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Yin <mikeyin@mikeyin.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![allow(dead_code)]

extern crate getopts;
extern crate libc;
extern crate semver;

#[macro_use]
extern crate uucore;

use std::cmp::Ordering;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write};
use std::path::Path;
use uucore::fs::is_stdin_interactive;
use semver::Version;

static NAME: &'static str = "sort";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

static DECIMAL_PT: char = '.';
static THOUSANDS_SEP: char = ',';

enum SortMode {
    Numeric,
    HumanNumeric,
    Month,
    Version,
    Default,
}

struct Settings {
    mode: SortMode,
    reverse: bool,
    outfile: Option<String>,
    stable: bool,
    unique: bool,
    check: bool,
    compare_fns: Vec<fn(&String, &String) -> Ordering>,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: SortMode::Default,
            reverse: false,
            outfile: None,
            stable: false,
            unique: false,
            check: false,
            compare_fns: Vec::new(),
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();
    let mut opts = getopts::Options::new();

    opts.optflag("n", "numeric-sort", "compare according to string numerical value");
    opts.optflag("h", "human-numeric-sort", "compare according to human readable sizes, eg 1M > 100k");
    opts.optflag("M", "month-sort", "compare according to month name abbreviation");
    opts.optflag("r", "reverse", "reverse the output");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("", "version", "output version information and exit");
    opts.optopt("o", "output", "write output to FILENAME instead of stdout", "FILENAME");
    opts.optflag("s", "stable", "stabilize sort by disabling last-resort comparison");
    opts.optflag("u", "unique", "output only the first of an equal run");
    opts.optflag("V", "version-sort", "Sort by SemVer version number, eg 1.12.2 > 1.1.2");
    opts.optflag("c", "check", "check for sorted input; do not sort");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };
    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
 {0} [OPTION]... [FILE]...

Write the sorted concatenation of all FILE(s) to standard output.

Mandatory arguments for long options are mandatory for short options too.

With no FILE, or when FILE is -, read standard input.", NAME, VERSION);
        print!("{}", opts.usage(&msg));
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    settings.mode = if matches.opt_present("numeric-sort") {
        SortMode::Numeric
    } else if matches.opt_present("human-numeric-sort") {
        SortMode::HumanNumeric
    } else if matches.opt_present("month-sort") {
        SortMode::Month
    } else if matches.opt_present("version-sort") {
        SortMode::Version
    } else {
        SortMode::Default
    };

    settings.reverse = matches.opt_present("reverse");
    settings.outfile = matches.opt_str("output");
    settings.stable = matches.opt_present("stable");
    settings.unique = matches.opt_present("unique");
    settings.check = matches.opt_present("check");

    let mut files = matches.free;
    if files.is_empty() {
        /* if no file, default to stdin */
        files.push("-".to_owned());
    }

    settings.compare_fns.push(match settings.mode {
        SortMode::Numeric => numeric_compare,
        SortMode::HumanNumeric => human_numeric_size_compare,
        SortMode::Month => month_compare,
        SortMode::Version => version_compare,
        SortMode::Default => String::cmp
    });

    if !settings.stable {
        match settings.mode {
            SortMode::Default => {}
            _ => settings.compare_fns.push(String::cmp)
        }
    }

    exec(files, &settings)
}

fn exec(files: Vec<String>, settings: &Settings) -> i32 {
    let mut lines = Vec::new();
    for path in &files {
        let (reader, _) = match open(path) {
            Some(x) => x,
            None => continue,
        };

        let buf_reader = BufReader::new(reader);

        for line in buf_reader.lines() {
            match line {
                Ok(n) => {
                    lines.push(n);
                },
                _ => break
            }
        }
    }

    let original_lines = lines.to_vec();

    sort_by(&mut lines, &settings);

    if settings.unique {
        lines.dedup()
    }

    if settings.reverse {
        lines.reverse()
    }

    if settings.check {
        for (i, line) in lines.iter().enumerate() {
            if line != &original_lines[i] {
                println!("sort: disorder in line {}", i);
                return 1;
            }
        }
    }
    else {
        print_sorted(lines.iter(), &settings.outfile)
    }

    0

}

fn sort_by(lines: &mut Vec<String>, settings: &Settings) {
    lines.sort_by(|a, b| {
        compare_by(a, b, &settings)
    })
}

fn compare_by(a: &String, b: &String, settings: &Settings) -> Ordering {
    for compare_fn in &settings.compare_fns {
        let cmp = compare_fn(a, b);
        if cmp != Ordering::Equal {
            return cmp;
        }
    }
    return Ordering::Equal;
}

/// Parse the beginning string into an f64, returning -inf instead of NaN on errors.
fn permissive_f64_parse(a: &str) -> f64 {
    // Maybe should be split on non-digit, but then 10e100 won't parse properly.
    // On the flip side, this will give NEG_INFINITY for "1,234", which might be OK
    // because there's no way to handle both CSV and thousands separators without a new flag.
    // GNU sort treats "1,234" as "1" in numeric, so maybe it's fine.
    let sa: &str = a.split_whitespace().next().unwrap();
    match sa.parse::<f64>() {
        Ok(a) => a,
        Err(_) => std::f64::NEG_INFINITY
    }
}

/// Compares two floating point numbers, with errors being assumed to be -inf.
/// Stops coercing at the first whitespace char, so 1e2 will parse as 100 but
/// 1,000 will parse as -inf.
fn numeric_compare(a: &String, b: &String) -> Ordering {
    let fa = permissive_f64_parse(a);
    let fb = permissive_f64_parse(b);
    // f64::cmp isn't implemented because NaN messes with it
    // but we sidestep that with permissive_f64_parse so just fake it
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

fn human_numeric_convert(a: &String) -> f64 {
    let int_iter = a.chars();
    let suffix_iter = a.chars();
    let int_str: String = int_iter.take_while(|c| c.is_numeric()).collect();
    let suffix = suffix_iter.skip_while(|c| c.is_numeric()).next();
    let int_part = match int_str.parse::<f64>() {
        Ok(i) => i,
        Err(_) => -1f64
    } as f64;
    let suffix: f64 = match suffix.unwrap_or('\0') {
        'K' => 1000f64,
        'M' => 1E6,
        'G' => 1E9,
        'T' => 1E12,
        'P' => 1E15,
        _ => 1f64
    };
    int_part * suffix
}

/// Compare two strings as if they are human readable sizes.
/// AKA 1M > 100k
fn human_numeric_size_compare(a: &String, b: &String) -> Ordering {
    let fa = human_numeric_convert(a);
    let fb = human_numeric_convert(b);
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

#[derive(Eq, Ord, PartialEq, PartialOrd)]
enum Month {
    Unknown,
    January,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December,
}

/// Parse the beginning string into a Month, returning Month::Unknown on errors.
fn month_parse(line: &String) -> Month {
    match line.split_whitespace().next().unwrap().to_uppercase().as_ref() {
        "JAN" => Month::January,
        "FEB" => Month::February,
        "MAR" => Month::March,
        "APR" => Month::April,
        "MAY" => Month::May,
        "JUN" => Month::June,
        "JUL" => Month::July,
        "AUG" => Month::August,
        "SEP" => Month::September,
        "OCT" => Month::October,
        "NOV" => Month::November,
        "DEC" => Month::December,
        _     => Month::Unknown,
    }
}

fn month_compare(a: &String, b: &String) -> Ordering {
    month_parse(a).cmp(&month_parse(b))
}

fn version_compare(a: &String, b: &String) -> Ordering {
    let ver_a = Version::parse(a);
    let ver_b = Version::parse(b);
    if ver_a > ver_b {
        Ordering::Greater
    }
    else if ver_a < ver_b {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

fn print_sorted<S, T: Iterator<Item=S>>(iter: T, outfile: &Option<String>) where S: std::fmt::Display {
    let mut file: Box<Write> = match *outfile {
        Some(ref filename) => {
            match File::create(Path::new(&filename)) {
                Ok(f) => Box::new(BufWriter::new(f)) as Box<Write>,
                Err(e) => {
                    show_error!("sort: {0}: {1}", filename, e.to_string());
                    panic!("Could not open output file");
                },
            }
        },
        None => Box::new(stdout()) as Box<Write>,
    };

    for line in iter {
        let str = format!("{}\n", line);
        match file.write_all(str.as_bytes()) {
            Err(e) => {
                show_error!("sort: {0}", e.to_string());
                panic!("Write failed");
            },
            Ok(_) => (),
        }
    }
}

// from cat.rs
fn open(path: &str) -> Option<(Box<Read>, bool)> {
    if path == "-" {
        let stdin = stdin();
        return Some((Box::new(stdin) as Box<Read>, is_stdin_interactive()));
    }

    match File::open(Path::new(path)) {
        Ok(f) => Some((Box::new(f) as Box<Read>, false)),
        Err(e) => {
            show_error!("sort: {0}: {1}", path, e.to_string());
            None
        },
    }
}
<|endoftext|>


<commit_before>extern crate mocktopus;

mod mocking_fns;
mod mocking_methods;
mod mocking_trait_defaults;
mod mocking_traits;

use mocktopus::macros::*;
use mocktopus::mocking::*;
use mocktopus::mocking_utils::*;
use std::fmt::Display;

mod mock_safe {
    use super::*;

    #[mockable]
    pub fn no_args_returns_str() -> &'static str {
        "not mocked"
    }

    #[test]
    fn when_not_mocked_then_returns_not_mocked() {
        assert_eq!("not mocked", no_args_returns_str());
    }

    #[test]
    fn when_mocked_then_returns_mocked() {
        no_args_returns_str.mock_safe(|| MockResult::Return("mocked"));

        assert_eq!("mocked", no_args_returns_str());
    }
}

mod mocks_do_not_leak_between_tests {
    use super::*;

    #[mockable]
    pub fn no_args_returns_str() -> &'static str {
        "not mocked"
    }

    macro_rules! generate_tests {
        ($($fn_name:ident),+) => {
            $(
                #[test]
                fn $fn_name() {
                    assert_eq!("not mocked", no_args_returns_str(), "function was mocked before mocking");

                    unsafe {
                        no_args_returns_str.mock_raw(|| MockResult::Return(stringify!($fn_name)));
                    }

                    assert_eq!(stringify!($fn_name), no_args_returns_str(), "mocking failed");
                }
            )+
        }
    }

    generate_tests!(t00, t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19);
    generate_tests!(t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30, t31, t32, t33, t34, t35, t36, t37, t38, t39);
    generate_tests!(t40, t41, t42, t43, t44, t45, t46, t47, t48, t49, t50, t51, t52, t53, t54, t55, t56, t57, t58, t59);
    generate_tests!(t60, t61, t62, t63, t64, t65, t66, t67, t68, t69, t70, t71, t72, t73, t74, t75, t76, t77, t78, t79);
    generate_tests!(t80, t81, t82, t83, t84, t85, t86, t87, t88, t89, t90, t91, t92, t93, t94, t95, t96, t97, t98, t99);
}

mod panicking_inside_mock_is_safe {
    use super::*;

    #[mockable]
    fn function(_has_drop: String) {

    }

    #[test]
    #[should_panic]
    fn uninitialised_string_is_not_dropped() {
        function.mock_safe(|_| panic!("inside mock"));

        function("initialised".to_string());
    }
}

mod mocking_generic_over_a_type_with_lifetime_mocks_all_lifetime_variants {
    use super::*;
    use std::fmt::Display;

    #[mockable]
    fn function<T: Display>(generic: T) -> String {
        format!("not mocked {}", generic)
    }

    static STATIC_CHAR: char = 'S';

    #[test]
    fn all_lifetime_variants_get_mocked() {
        unsafe {
            function::<&char>.mock_raw(|c| MockResult::Return(format!("mocked {}", c)));
        }
        let local_char = 'L';

        assert_eq!("mocked L", function(&local_char));
        assert_eq!("mocked S", function(&STATIC_CHAR));
        assert_eq!("not mocked 3", function(&3));
    }
}

mod mocking_generic_over_a_reference_does_not_mock_opposite_mutability_variant {
    use super::*;
    use std::fmt::Display;

    #[mockable]
    fn function<T: Display>(generic: T) -> String {
        format!("not mocked {}", generic)
    }

    #[test]
    fn mocking_for_ref_does_not_mock_for_mut_ref() {
        unsafe {
            function::<&char>.mock_raw(|c| MockResult::Return(format!("mocked {}", c)));
        }

        assert_eq!("mocked R", function(&'R'));
        assert_eq!("not mocked M", function(&mut 'M'));
    }

    #[test]
    fn mocking_for_mut_ref_does_not_mock_for_ref() {
        unsafe {
            function::<&mut char>.mock_raw(|c| MockResult::Return(format!("mocked {}", c)));
        }

        assert_eq!("not mocked R", function(&'R'));
        assert_eq!("mocked M", function(&mut 'M'));
    }
}

mod mocking_trait_default_for_struct_does_not_mock_same_default_for_another_struct {
    use super::*;

    #[mockable]
    trait Trait {
        fn function() -> &'static str {
            "not mocked"
        }
    }

    struct Struct1;

    impl Trait for Struct1 {

    }

    struct Struct2;

    impl Trait for Struct2 {

    }

    #[test]
    fn when_not_mocked_then_both_run_normally() {
        assert_eq!("not mocked", Struct1::function());
        assert_eq!("not mocked", Struct2::function());
    }

    #[test]
    fn when_mocked_for_one_then_runs_mock_for_it_and_runs_normally_for_other() {
        unsafe {
            Struct1::function.mock_raw(|| MockResult::Return("mocked"))
        }

        assert_eq!("mocked", Struct1::function());
        assert_eq!("not mocked", Struct2::function());
    }
}

mod mock_closures_can_mutate_their_state {
    use super::*;

    #[mockable]
    fn function() -> &'static str {
        "not mocked"
    }

    #[test]
    fn when_not_mocked_then_runs_normally() {
        assert_eq!("not mocked", function());
    }

    #[test]
    fn when_mocked_then_runs_mocks_with_state() {
        let mut x = 0;
        function.mock_safe(move || {
            x += 1;
            match x {
                1 => MockResult::Return("first mock"),
                _ => MockResult::Return("other mock"),
            }
        });

        assert_eq!("first mock", function());
        assert_eq!("other mock", function());
    }
}

mod calling_mocked_function_inside_mock_closure {
    use super::*;

    #[mockable]
    fn mockable_1() -> String {
        "not mocked 1".to_string()
    }

    #[mockable]
    fn mockable_2() -> String {
        "not mocked 2".to_string()
    }

    fn not_mockable() -> String {
        "not mockable".to_string()
    }

    #[test]
    fn when_calling_itself_then_does_not_run_mock() {
        let mut first_call = true;
        mockable_1.mock_safe(move || {
            match first_call {
                true => {
                    first_call = false;
                    MockResult::Return(format!("mocked 1 {}", mockable_1()))
                },
                false => panic!("Mock called second time"),
            }
        });

        assert_eq!("mocked 1 not mocked 1", mockable_1());
    }

    #[test]
    fn when_calling_not_mocked_function_then_runs_it_normally() {
        mockable_1.mock_safe(|| MockResult::Return(format!("mocked 1 {}", mockable_2())));

        assert_eq!("mocked 1 not mocked 2", mockable_1());
    }

    #[test]
    fn when_calling_mocked_function_then_runs_its_mock() {
        mockable_1.mock_safe(|| MockResult::Return(format!("mocked 1 {}", mockable_2())));
        mockable_2.mock_safe(|| MockResult::Return("mocked 2".to_string()));

        assert_eq!("mocked 1 mocked 2", mockable_1());
    }

    #[test]
    fn when_calling_not_mockable_function_then_runs_it_normally() {
        mockable_1.mock_safe(|| MockResult::Return(format!("mocked 1 {}", not_mockable())));

        assert_eq!("mocked 1 not mockable", mockable_1());
    }
}

mod creating_mock_inside_mock_closure {
    use super::*;

    #[mockable]
    fn mockable_1() -> String {
        "not mocked 1".to_string()
    }

    #[mockable]
    fn mockable_2() -> String {
        "not mocked 2".to_string()
    }

    #[test]
    fn when_creating_mock_of_itself_then_registers_mock() {
        mockable_1.mock_safe(|| {
            mockable_1.mock_safe(|| MockResult::Return("mocked 1B".to_string()));
            MockResult::Return("mocked 1A".to_string())
        });

        assert_eq!("mocked 1A", mockable_1());
        assert_eq!("mocked 1B", mockable_1());
    }

    #[test]
    fn when_creating_mock_of_itself_and_calling_itself_then_runs_new_mock() {
        mockable_1.mock_safe(|| {
            mockable_1.mock_safe(|| MockResult::Return("mocked 1B".to_string()));
            MockResult::Return(format!("mocked 1A {}", mockable_1()))
        });

        assert_eq!("mocked 1A mocked 1B", mockable_1());
    }

    #[test]
    fn when_creating_mock_of_other_function_then_registers_mock() {
        mockable_1.mock_safe(|| {
            mockable_2.mock_safe(|| MockResult::Return("mocked 2".to_string()));
            MockResult::Return("mocked 1".to_string())
        });

        assert_eq!("not mocked 2", mockable_2());
        assert_eq!("mocked 1", mockable_1());
        assert_eq!("mocked 2", mockable_2());
    }

    #[test]
    fn when_creating_mock_of_other_function_and_calling_it_then_runs_new_mock() {
        mockable_1.mock_safe(|| {
            mockable_2.mock_safe(|| MockResult::Return("mocked 2".to_string()));
            MockResult::Return(format!("mocked 1 {}", mockable_2()))
        });

        assert_eq!("mocked 1 mocked 2", mockable_1());
    }
}

mod clear_mocks {
    use super::*;

    #[mockable]
    fn mockable_1() -> String {
        "not mocked 1".to_string()
    }

    #[mockable]
    fn mockable_2() -> String {
        "not mocked 2".to_string()
    }

    #[test]
    fn when_clearing_mocks_original_function_operations_return() {
        mockable_1.mock_safe(|| {
            MockResult::Return("mocked 1".to_string())
        });
        mockable_2.mock_safe(|| {
            MockResult::Return("mocked 2".to_string())
        });

        assert_eq!("mocked 1", mockable_1());
        assert_eq!("mocked 2", mockable_2());

        clear_mocks();

        assert_eq!("not mocked 1", mockable_1());
        assert_eq!("not mocked 2", mockable_2());
    }
}

mod clear_mock {
    use super::*;

    #[mockable]
    fn mockable_1() -> i32 {
        0
    }

    #[test]
    fn clearing_deregisters_the_mock() {
        mockable_1.mock_safe(|| MockResult::Return(1));
        assert_eq!(mockable_1(), 1);

        mockable_1.clear_mock();
        assert_eq!(mockable_1(), 0);
    }
}

mod mock_context {
    use super::*;

    #[mockable]
    fn mockable_1() -> i32 { 0 }

    #[test]
    fn test_run_mocks_the_function() {
        let mut x = 0;
        MockContext::new()
            .mock_safe(mockable_1, || {
                x += 1;
                MockResult::Return(1)
            })
            .run(|| {
                assert_eq!(mockable_1(), 1);
            });
        assert_eq!(mockable_1(), 0);
        assert_eq!(x, 1);
    }

    #[test]
    fn test_run_restores_the_function() {
        MockContext::new()
            .mock_safe(mockable_1, || {
                    MockResult::Return(1)
            })
            .run(|| {});
        assert_eq!(mockable_1(), 0);
    }

    #[test]
    fn test_run_no_mocks() {
        MockContext::new()
            .run(|| {
                assert_eq!(mockable_1(), 0);
            });
        assert_eq!(mockable_1(), 0);
    }
}
<commit_msg>Add test for mocking the same function multiple times should take the last one<commit_after>extern crate mocktopus;

mod mocking_fns;
mod mocking_methods;
mod mocking_trait_defaults;
mod mocking_traits;

use mocktopus::macros::*;
use mocktopus::mocking::*;
use mocktopus::mocking_utils::*;
use std::fmt::Display;

mod mock_safe {
    use super::*;

    #[mockable]
    pub fn no_args_returns_str() -> &'static str {
        "not mocked"
    }

    #[test]
    fn when_not_mocked_then_returns_not_mocked() {
        assert_eq!("not mocked", no_args_returns_str());
    }

    #[test]
    fn when_mocked_then_returns_mocked() {
        no_args_returns_str.mock_safe(|| MockResult::Return("mocked"));

        assert_eq!("mocked", no_args_returns_str());
    }
}

mod mocks_do_not_leak_between_tests {
    use super::*;

    #[mockable]
    pub fn no_args_returns_str() -> &'static str {
        "not mocked"
    }

    macro_rules! generate_tests {
        ($($fn_name:ident),+) => {
            $(
                #[test]
                fn $fn_name() {
                    assert_eq!("not mocked", no_args_returns_str(), "function was mocked before mocking");

                    unsafe {
                        no_args_returns_str.mock_raw(|| MockResult::Return(stringify!($fn_name)));
                    }

                    assert_eq!(stringify!($fn_name), no_args_returns_str(), "mocking failed");
                }
            )+
        }
    }

    generate_tests!(t00, t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19);
    generate_tests!(t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30, t31, t32, t33, t34, t35, t36, t37, t38, t39);
    generate_tests!(t40, t41, t42, t43, t44, t45, t46, t47, t48, t49, t50, t51, t52, t53, t54, t55, t56, t57, t58, t59);
    generate_tests!(t60, t61, t62, t63, t64, t65, t66, t67, t68, t69, t70, t71, t72, t73, t74, t75, t76, t77, t78, t79);
    generate_tests!(t80, t81, t82, t83, t84, t85, t86, t87, t88, t89, t90, t91, t92, t93, t94, t95, t96, t97, t98, t99);
}

mod panicking_inside_mock_is_safe {
    use super::*;

    #[mockable]
    fn function(_has_drop: String) {

    }

    #[test]
    #[should_panic]
    fn uninitialised_string_is_not_dropped() {
        function.mock_safe(|_| panic!("inside mock"));

        function("initialised".to_string());
    }
}

mod mocking_generic_over_a_type_with_lifetime_mocks_all_lifetime_variants {
    use super::*;
    use std::fmt::Display;

    #[mockable]
    fn function<T: Display>(generic: T) -> String {
        format!("not mocked {}", generic)
    }

    static STATIC_CHAR: char = 'S';

    #[test]
    fn all_lifetime_variants_get_mocked() {
        unsafe {
            function::<&char>.mock_raw(|c| MockResult::Return(format!("mocked {}", c)));
        }
        let local_char = 'L';

        assert_eq!("mocked L", function(&local_char));
        assert_eq!("mocked S", function(&STATIC_CHAR));
        assert_eq!("not mocked 3", function(&3));
    }
}

mod mocking_generic_over_a_reference_does_not_mock_opposite_mutability_variant {
    use super::*;
    use std::fmt::Display;

    #[mockable]
    fn function<T: Display>(generic: T) -> String {
        format!("not mocked {}", generic)
    }

    #[test]
    fn mocking_for_ref_does_not_mock_for_mut_ref() {
        unsafe {
            function::<&char>.mock_raw(|c| MockResult::Return(format!("mocked {}", c)));
        }

        assert_eq!("mocked R", function(&'R'));
        assert_eq!("not mocked M", function(&mut 'M'));
    }

    #[test]
    fn mocking_for_mut_ref_does_not_mock_for_ref() {
        unsafe {
            function::<&mut char>.mock_raw(|c| MockResult::Return(format!("mocked {}", c)));
        }

        assert_eq!("not mocked R", function(&'R'));
        assert_eq!("mocked M", function(&mut 'M'));
    }
}

mod mocking_trait_default_for_struct_does_not_mock_same_default_for_another_struct {
    use super::*;

    #[mockable]
    trait Trait {
        fn function() -> &'static str {
            "not mocked"
        }
    }

    struct Struct1;

    impl Trait for Struct1 {

    }

    struct Struct2;

    impl Trait for Struct2 {

    }

    #[test]
    fn when_not_mocked_then_both_run_normally() {
        assert_eq!("not mocked", Struct1::function());
        assert_eq!("not mocked", Struct2::function());
    }

    #[test]
    fn when_mocked_for_one_then_runs_mock_for_it_and_runs_normally_for_other() {
        unsafe {
            Struct1::function.mock_raw(|| MockResult::Return("mocked"))
        }

        assert_eq!("mocked", Struct1::function());
        assert_eq!("not mocked", Struct2::function());
    }
}

mod mock_closures_can_mutate_their_state {
    use super::*;

    #[mockable]
    fn function() -> &'static str {
        "not mocked"
    }

    #[test]
    fn when_not_mocked_then_runs_normally() {
        assert_eq!("not mocked", function());
    }

    #[test]
    fn when_mocked_then_runs_mocks_with_state() {
        let mut x = 0;
        function.mock_safe(move || {
            x += 1;
            match x {
                1 => MockResult::Return("first mock"),
                _ => MockResult::Return("other mock"),
            }
        });

        assert_eq!("first mock", function());
        assert_eq!("other mock", function());
    }
}

mod calling_mocked_function_inside_mock_closure {
    use super::*;

    #[mockable]
    fn mockable_1() -> String {
        "not mocked 1".to_string()
    }

    #[mockable]
    fn mockable_2() -> String {
        "not mocked 2".to_string()
    }

    fn not_mockable() -> String {
        "not mockable".to_string()
    }

    #[test]
    fn when_calling_itself_then_does_not_run_mock() {
        let mut first_call = true;
        mockable_1.mock_safe(move || {
            match first_call {
                true => {
                    first_call = false;
                    MockResult::Return(format!("mocked 1 {}", mockable_1()))
                },
                false => panic!("Mock called second time"),
            }
        });

        assert_eq!("mocked 1 not mocked 1", mockable_1());
    }

    #[test]
    fn when_calling_not_mocked_function_then_runs_it_normally() {
        mockable_1.mock_safe(|| MockResult::Return(format!("mocked 1 {}", mockable_2())));

        assert_eq!("mocked 1 not mocked 2", mockable_1());
    }

    #[test]
    fn when_calling_mocked_function_then_runs_its_mock() {
        mockable_1.mock_safe(|| MockResult::Return(format!("mocked 1 {}", mockable_2())));
        mockable_2.mock_safe(|| MockResult::Return("mocked 2".to_string()));

        assert_eq!("mocked 1 mocked 2", mockable_1());
    }

    #[test]
    fn when_calling_not_mockable_function_then_runs_it_normally() {
        mockable_1.mock_safe(|| MockResult::Return(format!("mocked 1 {}", not_mockable())));

        assert_eq!("mocked 1 not mockable", mockable_1());
    }
}

mod creating_mock_inside_mock_closure {
    use super::*;

    #[mockable]
    fn mockable_1() -> String {
        "not mocked 1".to_string()
    }

    #[mockable]
    fn mockable_2() -> String {
        "not mocked 2".to_string()
    }

    #[test]
    fn when_creating_mock_of_itself_then_registers_mock() {
        mockable_1.mock_safe(|| {
            mockable_1.mock_safe(|| MockResult::Return("mocked 1B".to_string()));
            MockResult::Return("mocked 1A".to_string())
        });

        assert_eq!("mocked 1A", mockable_1());
        assert_eq!("mocked 1B", mockable_1());
    }

    #[test]
    fn when_creating_mock_of_itself_and_calling_itself_then_runs_new_mock() {
        mockable_1.mock_safe(|| {
            mockable_1.mock_safe(|| MockResult::Return("mocked 1B".to_string()));
            MockResult::Return(format!("mocked 1A {}", mockable_1()))
        });

        assert_eq!("mocked 1A mocked 1B", mockable_1());
    }

    #[test]
    fn when_creating_mock_of_other_function_then_registers_mock() {
        mockable_1.mock_safe(|| {
            mockable_2.mock_safe(|| MockResult::Return("mocked 2".to_string()));
            MockResult::Return("mocked 1".to_string())
        });

        assert_eq!("not mocked 2", mockable_2());
        assert_eq!("mocked 1", mockable_1());
        assert_eq!("mocked 2", mockable_2());
    }

    #[test]
    fn when_creating_mock_of_other_function_and_calling_it_then_runs_new_mock() {
        mockable_1.mock_safe(|| {
            mockable_2.mock_safe(|| MockResult::Return("mocked 2".to_string()));
            MockResult::Return(format!("mocked 1 {}", mockable_2()))
        });

        assert_eq!("mocked 1 mocked 2", mockable_1());
    }
}

mod clear_mocks {
    use super::*;

    #[mockable]
    fn mockable_1() -> String {
        "not mocked 1".to_string()
    }

    #[mockable]
    fn mockable_2() -> String {
        "not mocked 2".to_string()
    }

    #[test]
    fn when_clearing_mocks_original_function_operations_return() {
        mockable_1.mock_safe(|| {
            MockResult::Return("mocked 1".to_string())
        });
        mockable_2.mock_safe(|| {
            MockResult::Return("mocked 2".to_string())
        });

        assert_eq!("mocked 1", mockable_1());
        assert_eq!("mocked 2", mockable_2());

        clear_mocks();

        assert_eq!("not mocked 1", mockable_1());
        assert_eq!("not mocked 2", mockable_2());
    }
}

mod clear_mock {
    use super::*;

    #[mockable]
    fn mockable_1() -> i32 {
        0
    }

    #[test]
    fn clearing_deregisters_the_mock() {
        mockable_1.mock_safe(|| MockResult::Return(1));
        assert_eq!(mockable_1(), 1);

        mockable_1.clear_mock();
        assert_eq!(mockable_1(), 0);
    }
}

mod mock_context {
    use super::*;

    #[mockable]
    fn mockable_1() -> i32 { 0 }

    #[test]
    fn test_run_mocks_the_function() {
        let mut x = 0;
        MockContext::new()
            .mock_safe(mockable_1, || {
                x += 1;
                MockResult::Return(1)
            })
            .run(|| {
                assert_eq!(mockable_1(), 1);
            });
        assert_eq!(mockable_1(), 0);
        assert_eq!(x, 1);
    }

    #[test]
    fn test_run_restores_the_function() {
        MockContext::new()
            .mock_safe(mockable_1, || MockResult::Return(1))
            .run(|| {});
        assert_eq!(mockable_1(), 0);
    }

    #[test]
    fn test_run_no_mocks() {
        MockContext::new()
            .run(|| {
                assert_eq!(mockable_1(), 0);
            });
        assert_eq!(mockable_1(), 0);
    }

    #[test]
    fn test_mock_the_same_function_multiple_times() {
        MockContext::new()
            .mock_safe(mockable_1, || MockResult::Return(1))
            .mock_safe(mockable_1, || MockResult::Return(2))
            .mock_safe(mockable_1, || MockResult::Return(3))
            .run(|| {
                assert_eq!(mockable_1(), 3);
            });
        assert_eq!(mockable_1(), 0);
    }
}
<|endoftext|>


<commit_before>use token::Token;
use error::Result;

use std::str::CharIndices;
use std::iter::Peekable;

pub struct Tokenizer<'a> {
    text: &'a str,
    chars: Peekable<CharIndices<'a>>,
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Token>;

    fn next(&mut self) -> Option<Result<Token>> {
        match self.parse() {
            Ok(Some(t)) => Some(Ok(t)),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

impl<'a> Tokenizer<'a> {
    pub fn new(text: &str) -> Tokenizer {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    pub fn parse(&mut self) -> Result<Option<Token>> {
        let mut value = String::new();
        let mut start;

        while let Some((char_offset, curr_char)) = self.chars.next() {
            // Skip leading whitespace
            if curr_char.is_whitespace() {
                continue;
            } else {
                if is_single_char_token(curr_char) {
                    return Ok(Some(Token {
                        value: curr_char.to_string(),
                        offset: Some(char_offset),
                    }))
                } else if curr_char == '-' {
                    value.push(curr_char);
                    start = char_offset;

                    if let Some(&(_, peek_char)) = self.chars.peek() {
                        if peek_char.is_numeric() {
                            while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                                // Stop when we reach a non-numeric char
                                if !next_char.is_numeric() && next_char != '.' {
                                    break;
                                } else {
                                    value.push(next_char);
                                    self.chars.next();
                                }
                            }
                        } else {
                            while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                                // Stop when we reach a non-word char
                                if next_char.is_whitespace() || is_single_char_token(next_char) {
                                    break;
                                } else {
                                    value.push(next_char);
                                    self.chars.next();
                                }
                            }
                        }
                    }

                    return Ok(Some(Token { value: value, offset: Some(start) }))
                } else if curr_char.is_numeric() {
                    // Start of a multi-char numeric token
                    value.push(curr_char);
                    start = char_offset;

                    while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                        // Stop when we reach a non-numeric char
                        if !next_char.is_numeric() && next_char != '.' {
                            break;
                        } else {
                            value.push(next_char);
                            self.chars.next();
                        }
                    }
                    return Ok(Some(Token { value: value, offset: Some(start) }))
                } else {
                    // Start of a multi-char non-numeric token
                    value.push(curr_char);
                    start = char_offset;
                    while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                        // Stop when we reach a non-word char
                        if next_char.is_whitespace() || is_single_char_token(next_char) {
                            break;
                        } else {
                            value.push(next_char);
                            self.chars.next();
                        }
                    }
                    return Ok(Some(Token { value: value, offset: Some(start) }))
                }
            }
        }
        return Ok(None)
    }
}

fn is_single_char_token(ch: char) -> bool {
    "{}():;".chars().any(|c| c == ch)
}

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

    fn assert_expected_token(
        actual: Option<Result<Token>>,
        expected_value: &str,
        expected_offset: usize) {
        let actual = actual.unwrap().unwrap();
        assert_eq!(actual.value, expected_value);
        assert_eq!(actual.offset, Some(expected_offset));
    }

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_none_for_only_whitespace() {
        let mut tokenizer = Tokenizer::new("     ");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_words() {
        // Without regard to Sass word validity
        let mut tokenizer = Tokenizer::new(" \n  div   aoeu  ");
        assert_expected_token(tokenizer.next(), "div", 4);
        assert_expected_token(tokenizer.next(), "aoeu", 10);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_curly_braces() {
        // Without regard to matching
        let mut tokenizer = Tokenizer::new("{}}a{ blue}");
        assert_expected_token(tokenizer.next(), "{", 0);
        assert_expected_token(tokenizer.next(), "}", 1);
        assert_expected_token(tokenizer.next(), "}", 2);
        assert_expected_token(tokenizer.next(), "a", 3);
        assert_expected_token(tokenizer.next(), "{", 4);
        assert_expected_token(tokenizer.next(), "blue", 6);
        assert_expected_token(tokenizer.next(), "}", 10);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_colon() {
        let mut tokenizer = Tokenizer::new(":invalid: property::");
        assert_expected_token(tokenizer.next(), ":", 0);
        assert_expected_token(tokenizer.next(), "invalid", 1);
        assert_expected_token(tokenizer.next(), ":", 8);
        assert_expected_token(tokenizer.next(), "property", 10);
        assert_expected_token(tokenizer.next(), ":", 18);
        assert_expected_token(tokenizer.next(), ":", 19);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_semicolon() {
        let mut tokenizer = Tokenizer::new(";;\na;\nb\n;");
        assert_expected_token(tokenizer.next(), ";", 0);
        assert_expected_token(tokenizer.next(), ";", 1);
        assert_expected_token(tokenizer.next(), "a", 3);
        assert_expected_token(tokenizer.next(), ";", 4);
        assert_expected_token(tokenizer.next(), "b", 6);
        assert_expected_token(tokenizer.next(), ";", 8);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_numbers() {
        let mut tokenizer = Tokenizer::new("border: 0px 1.5 11em;");
        assert_expected_token(tokenizer.next(), "border", 0);
        assert_expected_token(tokenizer.next(), ":", 6);
        assert_expected_token(tokenizer.next(), "0", 8);
        assert_expected_token(tokenizer.next(), "px", 9);
        assert_expected_token(tokenizer.next(), "1.5", 12);
        assert_expected_token(tokenizer.next(), "11", 16);
        assert_expected_token(tokenizer.next(), "em", 18);
        assert_expected_token(tokenizer.next(), ";", 20);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_hyphen() {
        let mut tokenizer = Tokenizer::new("font-weight -webkit -3 - 4-5 a-1");
        assert_expected_token(tokenizer.next(), "font-weight", 0);
        assert_expected_token(tokenizer.next(), "-webkit", 12);
        assert_expected_token(tokenizer.next(), "-3", 20);
        assert_expected_token(tokenizer.next(), "-", 23);
        assert_expected_token(tokenizer.next(), "4", 25);
        assert_expected_token(tokenizer.next(), "-5", 26);
        assert_expected_token(tokenizer.next(), "a-1", 29);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_parens() {
        let mut tokenizer = Tokenizer::new("() rgb)()(");
        assert_expected_token(tokenizer.next(), "(", 0);
        assert_expected_token(tokenizer.next(), ")", 1);
        assert_expected_token(tokenizer.next(), "rgb", 3);
        assert_expected_token(tokenizer.next(), ")", 6);
        assert_expected_token(tokenizer.next(), "(", 7);
        assert_expected_token(tokenizer.next(), ")", 8);
        assert_expected_token(tokenizer.next(), "(", 9);
        assert_eq!(tokenizer.next(), None);
    }
}
<commit_msg>Tokenize slash<commit_after>use token::Token;
use error::Result;

use std::str::CharIndices;
use std::iter::Peekable;

pub struct Tokenizer<'a> {
    text: &'a str,
    chars: Peekable<CharIndices<'a>>,
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Token>;

    fn next(&mut self) -> Option<Result<Token>> {
        match self.parse() {
            Ok(Some(t)) => Some(Ok(t)),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

impl<'a> Tokenizer<'a> {
    pub fn new(text: &str) -> Tokenizer {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    pub fn parse(&mut self) -> Result<Option<Token>> {
        let mut value = String::new();
        let mut start;

        while let Some((char_offset, curr_char)) = self.chars.next() {
            // Skip leading whitespace
            if curr_char.is_whitespace() {
                continue;
            } else {
                if is_single_char_token(curr_char) {
                    return Ok(Some(Token {
                        value: curr_char.to_string(),
                        offset: Some(char_offset),
                    }))
                } else if curr_char == '-' {
                    value.push(curr_char);
                    start = char_offset;

                    if let Some(&(_, peek_char)) = self.chars.peek() {
                        if peek_char.is_numeric() {
                            while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                                // Stop when we reach a non-numeric char
                                if !next_char.is_numeric() && next_char != '.' {
                                    break;
                                } else {
                                    value.push(next_char);
                                    self.chars.next();
                                }
                            }
                        } else {
                            while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                                // Stop when we reach a non-word char
                                if next_char.is_whitespace() || is_single_char_token(next_char) {
                                    break;
                                } else {
                                    value.push(next_char);
                                    self.chars.next();
                                }
                            }
                        }
                    }

                    return Ok(Some(Token { value: value, offset: Some(start) }))
                } else if curr_char.is_numeric() {
                    // Start of a multi-char numeric token
                    value.push(curr_char);
                    start = char_offset;

                    while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                        // Stop when we reach a non-numeric char
                        if !next_char.is_numeric() && next_char != '.' {
                            break;
                        } else {
                            value.push(next_char);
                            self.chars.next();
                        }
                    }
                    return Ok(Some(Token { value: value, offset: Some(start) }))
                } else {
                    // Start of a multi-char non-numeric token
                    value.push(curr_char);
                    start = char_offset;
                    while let Some(&(next_char_offset, next_char)) = self.chars.peek() {
                        // Stop when we reach a non-word char
                        if next_char.is_whitespace() || is_single_char_token(next_char) {
                            break;
                        } else {
                            value.push(next_char);
                            self.chars.next();
                        }
                    }
                    return Ok(Some(Token { value: value, offset: Some(start) }))
                }
            }
        }
        return Ok(None)
    }
}

fn is_single_char_token(ch: char) -> bool {
    "{}():;/".chars().any(|c| c == ch)
}

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

    fn assert_expected_token(
        actual: Option<Result<Token>>,
        expected_value: &str,
        expected_offset: usize) {
        let actual = actual.unwrap().unwrap();
        assert_eq!(actual.value, expected_value);
        assert_eq!(actual.offset, Some(expected_offset));
    }

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_none_for_only_whitespace() {
        let mut tokenizer = Tokenizer::new("     ");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_words() {
        // Without regard to Sass word validity
        let mut tokenizer = Tokenizer::new(" \n  div   aoeu  ");
        assert_expected_token(tokenizer.next(), "div", 4);
        assert_expected_token(tokenizer.next(), "aoeu", 10);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_curly_braces() {
        // Without regard to matching
        let mut tokenizer = Tokenizer::new("{}}a{ blue}");
        assert_expected_token(tokenizer.next(), "{", 0);
        assert_expected_token(tokenizer.next(), "}", 1);
        assert_expected_token(tokenizer.next(), "}", 2);
        assert_expected_token(tokenizer.next(), "a", 3);
        assert_expected_token(tokenizer.next(), "{", 4);
        assert_expected_token(tokenizer.next(), "blue", 6);
        assert_expected_token(tokenizer.next(), "}", 10);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_colon() {
        let mut tokenizer = Tokenizer::new(":invalid: property::");
        assert_expected_token(tokenizer.next(), ":", 0);
        assert_expected_token(tokenizer.next(), "invalid", 1);
        assert_expected_token(tokenizer.next(), ":", 8);
        assert_expected_token(tokenizer.next(), "property", 10);
        assert_expected_token(tokenizer.next(), ":", 18);
        assert_expected_token(tokenizer.next(), ":", 19);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_semicolon() {
        let mut tokenizer = Tokenizer::new(";;\na;\nb\n;");
        assert_expected_token(tokenizer.next(), ";", 0);
        assert_expected_token(tokenizer.next(), ";", 1);
        assert_expected_token(tokenizer.next(), "a", 3);
        assert_expected_token(tokenizer.next(), ";", 4);
        assert_expected_token(tokenizer.next(), "b", 6);
        assert_expected_token(tokenizer.next(), ";", 8);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_numbers() {
        let mut tokenizer = Tokenizer::new("border: 0px 1.5 11em;");
        assert_expected_token(tokenizer.next(), "border", 0);
        assert_expected_token(tokenizer.next(), ":", 6);
        assert_expected_token(tokenizer.next(), "0", 8);
        assert_expected_token(tokenizer.next(), "px", 9);
        assert_expected_token(tokenizer.next(), "1.5", 12);
        assert_expected_token(tokenizer.next(), "11", 16);
        assert_expected_token(tokenizer.next(), "em", 18);
        assert_expected_token(tokenizer.next(), ";", 20);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_hyphen() {
        let mut tokenizer = Tokenizer::new("font-weight -webkit -3 - 4-5 a-1");
        assert_expected_token(tokenizer.next(), "font-weight", 0);
        assert_expected_token(tokenizer.next(), "-webkit", 12);
        assert_expected_token(tokenizer.next(), "-3", 20);
        assert_expected_token(tokenizer.next(), "-", 23);
        assert_expected_token(tokenizer.next(), "4", 25);
        assert_expected_token(tokenizer.next(), "-5", 26);
        assert_expected_token(tokenizer.next(), "a-1", 29);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_parens() {
        let mut tokenizer = Tokenizer::new("() rgb)()(");
        assert_expected_token(tokenizer.next(), "(", 0);
        assert_expected_token(tokenizer.next(), ")", 1);
        assert_expected_token(tokenizer.next(), "rgb", 3);
        assert_expected_token(tokenizer.next(), ")", 6);
        assert_expected_token(tokenizer.next(), "(", 7);
        assert_expected_token(tokenizer.next(), ")", 8);
        assert_expected_token(tokenizer.next(), "(", 9);
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_slash() {
        let mut tokenizer = Tokenizer::new("/ / 3/4 / 8");
        assert_expected_token(tokenizer.next(), "/", 0);
        assert_expected_token(tokenizer.next(), "/", 2);
        assert_expected_token(tokenizer.next(), "3", 4);
        assert_expected_token(tokenizer.next(), "/", 5);
        assert_expected_token(tokenizer.next(), "4", 6);
        assert_expected_token(tokenizer.next(), "/", 8);
        assert_expected_token(tokenizer.next(), "8", 10);
        assert_eq!(tokenizer.next(), None);
    }
}
<|endoftext|>


<commit_before>pub mod download;
mod hash;
pub mod unpack;

pub use hash::Hash as Hash;<commit_msg>Fixing typo<commit_after>pub mod download;
mod hash;
pub mod unpack;

pub use self::hash::Hash as Hash;<|endoftext|>