<commit_before>use super::super::{
    Buffer, ComplexNumberSpace, ComplexOps, DataDomain, Domain, DspVec, ElementaryOps, ErrorReason,
    FrequencyDomain, FrequencyDomainOperations, FrequencyToTimeDomainOperations, GetMetaData,
    InsertZerosOpsBuffered, MetaData, NoTradeBuffer, NumberSpace, PaddingOption, PosEq,
    ReorganizeDataOps, ScaleOps, TimeDomain, TimeToFrequencyDomainOperations, ToComplexVector,
    ToFreqResult, ToSliceMut, Vector, VoidResult, FloatIndex, FloatIndexMut
};
use crate::numbers::*;
use std::ops::*;

/// Cross-correlation of data vectors. See also https://en.wikipedia.org/wiki/Cross-correlation
///
/// The correlation is calculated in two steps. This is done to give you more control
/// over two things:
///
/// 1. Should the correlation use zero padding or not? This is done by calling either
///    `prepare_argument`
///    or `prepare_argument_padded`.
/// 2. The lifetime of the argument. The argument needs to be transformed for the correlation and
///    depending on the application that might be just fine, or a clone needs to be created or
///    it's okay to use one argument for multiple correlations.
///
/// To get the same behavior like GNU Octave or MATLAB `prepare_argument_padded` needs to be
/// called before doing the correlation. See also the example section for how to do this.
/// # Example
///
/// ```no_run
/// use std::f32;
/// use basic_dsp_vector::*;
/// # use num_complex::Complex;
/// let mut vector = vec!(Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0)).to_complex_time_vec();
/// let argument = vec!(Complex::new(3.0, 3.0), Complex::new(2.0, 2.0), Complex::new(1.0, 1.0)).to_complex_time_vec();
/// let mut buffer = SingleBuffer::new();
/// let argument = argument.prepare_argument_padded(&mut buffer);
/// vector.correlate(&mut buffer, &argument).expect("Ignoring error handling in examples");
/// let expected = &[Complex::new(2.0, 0.0), Complex::new(8.0, 0.0), Complex::new(20.0, 0.0), Complex::new(24.0, 0.0), Complex::new(18.0, 0.0)];
/// for i in 0..vector.len() {
///     assert!((vector[i] - expected[i]).norm() < 1e-4);
/// }
/// ```
/// # Failures
/// `TransRes` may report the following `ErrorReason` members:
///
/// 1. `VectorMustBeComplex`: if `self` is in real number space.
/// 3. `VectorMetaDataMustAgree`: in case `self` and `function` are not
///    in the same number space and same domain.
pub trait CrossCorrelationArgumentOps<S, T>: ToFreqResult
where
    S: ToSliceMut<T>,
    T: RealNumber,
{
    /// Prepares an argument to be used for convolution. Preparing an argument includes two steps:
    ///
    /// 1. Calculate the plain FFT
    /// 2. Calculate the complex conjugate
    fn prepare_argument<B>(self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>;

    /// Prepares an argument to be used for convolution. The argument is zero padded to
    /// length of `2 * self.points() - 1`
    /// and then the same operations are performed as described for `prepare_argument`.
    fn prepare_argument_padded<B>(self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>;
}

/// A trait to calculate the cross correlation.
pub trait CrossCorrelationOps<A, S, T, N, D>
where
    S: ToSliceMut<T>,
    T: RealNumber,
    N: NumberSpace,
    D: Domain,
    A: GetMetaData<T, N, D>,
{
    /// Calculates the correlation between `self` and `other`. `other`
    /// needs to be a time vector which
    /// went through one of the prepare functions `prepare_argument` or `prepare_argument_padded`.
    /// See also the trait description for more details.
    fn correlate<B>(&mut self, buffer: &mut B, other: &A) -> VoidResult
    where
        B: for<'a> Buffer<'a, S, T>;
}

impl<S, T, N, D> CrossCorrelationArgumentOps<S, T> for DspVec<S, T, N, D>
where
    DspVec<S, T, N, D>: ToFreqResult + TimeToFrequencyDomainOperations<S, T>,
    <DspVec<S, T, N, D> as ToFreqResult>::FreqResult:
        FrequencyDomainOperations<S, T> + ComplexOps<T>,
    S: ToSliceMut<T>,
    T: RealNumber,
    N: ComplexNumberSpace,
    D: TimeDomain,
{
    fn prepare_argument<B>(self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>,
    {
        let mut result = self.plain_fft(buffer);
        result.conj();
        result
    }

    fn prepare_argument_padded<B>(mut self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>,
    {
        let points = self.points();
        self.zero_pad_b(buffer, 2 * points - 1, PaddingOption::Surround)
            .expect(
                "zero_pad_b shouldn't fail since the argument is always larger than the vector",
            );
        let mut result = self.plain_fft(buffer);
        result.conj();
        result
    }
}

impl<S, T, N, D, DF, O, NO> CrossCorrelationOps<O, S, T, NO, DF> for DspVec<S, T, N, D>
where
    DspVec<S, T, N, D>: ScaleOps<T>,
    S: ToSliceMut<T>,
    T: RealNumber,
    N: ComplexNumberSpace,
    D: TimeDomain,
    DF: FrequencyDomain,
    O: Vector<T> + GetMetaData<T, NO, DF> + FloatIndex<RangeFull, Output = [T]>,
    NO: PosEq<N> + NumberSpace,
{
    fn correlate<B>(&mut self, buffer: &mut B, other: &O) -> VoidResult
    where
        B: for<'a> Buffer<'a, S, T>,
    {
        if self.domain() != DataDomain::Time || !self.is_complex() {
            self.mark_vector_as_invalid();
            self.number_space.to_complex();
            self.domain.to_freq();
            return Err(ErrorReason::InputMustBeInTimeDomain);
        }

        if other.domain() != DataDomain::Frequency || !other.is_complex() {
            self.mark_vector_as_invalid();
            self.number_space.to_complex();
            self.domain.to_freq();
            return Err(ErrorReason::InputMustBeInTimeDomain);
        }

        let points = other.points();
        r#try!(self.zero_pad_b(buffer, points, PaddingOption::Surround));
        let len = self.len();
        let mut temp = buffer.borrow(len);
        // The next steps: fft, mul, ifft
        // However to keep the impl definition simpler and to avoid unnecessary copies
        // we have to to be creative with where we store our signal and what's the buffer.
        {
            let complex = (self.data_mut(..)).to_complex_time_vec();
            let mut buffer = NoTradeBuffer::new(&mut temp[..]);
            complex.plain_fft(&mut buffer); // after this operation, our result is in `temp`. See also definition of `NoTradeBuffer`.
        }
        {
            let other = (other.data(..)).to_complex_freq_vec();
            let mut complex = (&mut temp[..]).to_complex_freq_vec();
            r#try!(complex.mul(&other));
            let mut buffer = NoTradeBuffer::new(self.data_mut(..));
            complex.plain_ifft(&mut buffer); // the result is now back in `self`.
        }
        let p = self.points();
        self.scale(T::one() / T::from(p).unwrap());
        self.swap_halves();
        Ok(())
    }
}

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

    #[test]
    fn time_correlation_test() {
        let mut a = vec![
            0.0800, 0.0, 0.1876, 0.1170, 0.4601, 0.4132, 0.7700, 0.7500, 0.9723, 0.9698, 0.9723,
            0.9698, 0.7700, 0.7500, 0.4601, 0.4132, 0.1876, 0.1170, 0.0800, 0.0,
        ]
        .to_complex_time_vec();
        let b = vec![
            0.1000, -0.6366, 0.3000, 0.0, 0.5000, 0.2122, 0.7000, 0.0, 0.9000, -0.1273, 0.9000,
            0.0, 0.7000, 0.0909, 0.5000, 0.0, 0.3000, -0.0707, 0.1000, 0.0,
        ]
        .to_complex_time_vec();
        let c: &[f32] = &[
            0.0080, 0.0000, 0.0428, 0.0174, 0.1340, 0.0897, 0.3356, 0.2827, 0.7192, 0.6479, 1.3058,
            1.1946, 2.0175, 1.8757, 2.7047, 2.5665, 3.2186, 3.0874, 3.4409, 3.2994, 3.2291, 3.1287,
            2.5801, 2.7264, 1.7085, 2.1882, 0.8637, 1.6369, 0.2319, 1.1420, -0.0878, 0.7078,
            -0.1208, 0.3523, -0.0317, 0.1311, 0.0080, 0.0509,
        ];
        let mut buffer = SingleBuffer::new();
        let b = b.prepare_argument_padded(&mut buffer);
        a.correlate(&mut buffer, &b).unwrap();
        let res = a.data(..);
        let tol = 0.1;
        for i in 0..c.len() {
            if (res[i] - c[i]).abs() > tol {
                panic!("assertion failed: {:?} != {:?} at index {}", res, c, i);
            }
        }
    }

    #[test]
    fn time_correlation_test2() {
        let mut a = vec![1.0, 1.0, 2.0, 1.0, 3.0, 1.0].to_complex_time_vec();
        let b = vec![4.0, 1.0, 5.0, 1.0, 6.0, 1.0].to_complex_time_vec();
        let c: &[f32] = &[7.0, 5.0, 19.0, 8.0, 35.0, 9.0, 25.0, 4.0, 13.0, 1.0];
        let mut buffer = SingleBuffer::new();
        let b = b.prepare_argument_padded(&mut buffer);
        a.correlate(&mut buffer, &b).unwrap();
        let res = a.data(..);
        let tol = 0.1;
        for i in 0..c.len() {
            if (res[i] - c[i]).abs() > tol {
                panic!("assertion failed: {:?} != {:?} at index {}", res, c, i);
            }
        }
    }
}
<commit_msg>Moved documentation and example for correlation to the correct trait<commit_after>use super::super::{
    Buffer, ComplexNumberSpace, ComplexOps, DataDomain, Domain, DspVec, ElementaryOps, ErrorReason,
    FrequencyDomain, FrequencyDomainOperations, FrequencyToTimeDomainOperations, GetMetaData,
    InsertZerosOpsBuffered, MetaData, NoTradeBuffer, NumberSpace, PaddingOption, PosEq,
    ReorganizeDataOps, ScaleOps, TimeDomain, TimeToFrequencyDomainOperations, ToComplexVector,
    ToFreqResult, ToSliceMut, Vector, VoidResult, FloatIndex, FloatIndexMut
};
use crate::numbers::*;
use std::ops::*;

/// This trait allows to transform an argument so that it can be used for cross correlation. Refer to the description of
/// `CrossCorrelationOps` for more details.
pub trait CrossCorrelationArgumentOps<S, T>: ToFreqResult
where
    S: ToSliceMut<T>,
    T: RealNumber,
{
    /// Prepares an argument to be used for convolution. Preparing an argument includes two steps:
    ///
    /// 1. Calculate the plain FFT
    /// 2. Calculate the complex conjugate
    fn prepare_argument<B>(self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>;

    /// Prepares an argument to be used for convolution. The argument is zero padded to
    /// length of `2 * self.points() - 1`
    /// and then the same operations are performed as described for `prepare_argument`.
    fn prepare_argument_padded<B>(self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>;
}

/// Cross-correlation of data vectors. See also https://en.wikipedia.org/wiki/Cross-correlation
///
/// The correlation is calculated in two steps. This is done to give you more control
/// over two things:
///
/// 1. Should the correlation use zero padding or not? This is done by calling either
///    `prepare_argument`
///    or `prepare_argument_padded`.
/// 2. The lifetime of the argument. The argument needs to be transformed for the correlation and
///    depending on the application that might be just fine, or a clone needs to be created or
///    it's okay to use one argument for multiple correlations.
///
/// To get the same behavior like GNU Octave or MATLAB `prepare_argument_padded` needs to be
/// called before doing the correlation. See also the example section for how to do this.
/// # Example
///
/// ```no_run
/// use std::f32;
/// use basic_dsp_vector::*;
/// # use num_complex::Complex;
/// let mut vector = vec!(Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0)).to_complex_time_vec();
/// let argument = vec!(Complex::new(3.0, 3.0), Complex::new(2.0, 2.0), Complex::new(1.0, 1.0)).to_complex_time_vec();
/// let mut buffer = SingleBuffer::new();
/// let argument = argument.prepare_argument_padded(&mut buffer);
/// vector.correlate(&mut buffer, &argument).expect("Ignoring error handling in examples");
/// let expected = &[Complex::new(2.0, 0.0), Complex::new(8.0, 0.0), Complex::new(20.0, 0.0), Complex::new(24.0, 0.0), Complex::new(18.0, 0.0)];
/// for i in 0..vector.len() {
///     assert!((vector[i] - expected[i]).norm() < 1e-4);
/// }
/// ```
/// # Failures
/// `TransRes` may report the following `ErrorReason` members:
///
/// 1. `VectorMustBeComplex`: if `self` is in real number space.
/// 3. `VectorMetaDataMustAgree`: in case `self` and `function` are not
///    in the same number space and same domain.
pub trait CrossCorrelationOps<A, S, T, N, D>
where
    S: ToSliceMut<T>,
    T: RealNumber,
    N: NumberSpace,
    D: Domain,
    A: GetMetaData<T, N, D>,
{
    /// Calculates the correlation between `self` and `other`. `other`
    /// needs to be a time vector which
    /// went through one of the prepare functions `prepare_argument` or `prepare_argument_padded`.
    /// See also the trait description for more details.
    fn correlate<B>(&mut self, buffer: &mut B, other: &A) -> VoidResult
    where
        B: for<'a> Buffer<'a, S, T>;
}

impl<S, T, N, D> CrossCorrelationArgumentOps<S, T> for DspVec<S, T, N, D>
where
    DspVec<S, T, N, D>: ToFreqResult + TimeToFrequencyDomainOperations<S, T>,
    <DspVec<S, T, N, D> as ToFreqResult>::FreqResult:
        FrequencyDomainOperations<S, T> + ComplexOps<T>,
    S: ToSliceMut<T>,
    T: RealNumber,
    N: ComplexNumberSpace,
    D: TimeDomain,
{
    fn prepare_argument<B>(self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>,
    {
        let mut result = self.plain_fft(buffer);
        result.conj();
        result
    }

    fn prepare_argument_padded<B>(mut self, buffer: &mut B) -> Self::FreqResult
    where
        B: for<'a> Buffer<'a, S, T>,
    {
        let points = self.points();
        self.zero_pad_b(buffer, 2 * points - 1, PaddingOption::Surround)
            .expect(
                "zero_pad_b shouldn't fail since the argument is always larger than the vector",
            );
        let mut result = self.plain_fft(buffer);
        result.conj();
        result
    }
}

impl<S, T, N, D, DF, O, NO> CrossCorrelationOps<O, S, T, NO, DF> for DspVec<S, T, N, D>
where
    DspVec<S, T, N, D>: ScaleOps<T>,
    S: ToSliceMut<T>,
    T: RealNumber,
    N: ComplexNumberSpace,
    D: TimeDomain,
    DF: FrequencyDomain,
    O: Vector<T> + GetMetaData<T, NO, DF> + FloatIndex<RangeFull, Output = [T]>,
    NO: PosEq<N> + NumberSpace,
{
    fn correlate<B>(&mut self, buffer: &mut B, other: &O) -> VoidResult
    where
        B: for<'a> Buffer<'a, S, T>,
    {
        if self.domain() != DataDomain::Time || !self.is_complex() {
            self.mark_vector_as_invalid();
            self.number_space.to_complex();
            self.domain.to_freq();
            return Err(ErrorReason::InputMustBeInTimeDomain);
        }

        if other.domain() != DataDomain::Frequency || !other.is_complex() {
            self.mark_vector_as_invalid();
            self.number_space.to_complex();
            self.domain.to_freq();
            return Err(ErrorReason::InputMustBeInTimeDomain);
        }

        let points = other.points();
        r#try!(self.zero_pad_b(buffer, points, PaddingOption::Surround));
        let len = self.len();
        let mut temp = buffer.borrow(len);
        // The next steps: fft, mul, ifft
        // However to keep the impl definition simpler and to avoid unnecessary copies
        // we have to to be creative with where we store our signal and what's the buffer.
        {
            let complex = (self.data_mut(..)).to_complex_time_vec();
            let mut buffer = NoTradeBuffer::new(&mut temp[..]);
            complex.plain_fft(&mut buffer); // after this operation, our result is in `temp`. See also definition of `NoTradeBuffer`.
        }
        {
            let other = (other.data(..)).to_complex_freq_vec();
            let mut complex = (&mut temp[..]).to_complex_freq_vec();
            r#try!(complex.mul(&other));
            let mut buffer = NoTradeBuffer::new(self.data_mut(..));
            complex.plain_ifft(&mut buffer); // the result is now back in `self`.
        }
        let p = self.points();
        self.scale(T::one() / T::from(p).unwrap());
        self.swap_halves();
        Ok(())
    }
}

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

    #[test]
    fn time_correlation_test() {
        let mut a = vec![
            0.0800, 0.0, 0.1876, 0.1170, 0.4601, 0.4132, 0.7700, 0.7500, 0.9723, 0.9698, 0.9723,
            0.9698, 0.7700, 0.7500, 0.4601, 0.4132, 0.1876, 0.1170, 0.0800, 0.0,
        ]
        .to_complex_time_vec();
        let b = vec![
            0.1000, -0.6366, 0.3000, 0.0, 0.5000, 0.2122, 0.7000, 0.0, 0.9000, -0.1273, 0.9000,
            0.0, 0.7000, 0.0909, 0.5000, 0.0, 0.3000, -0.0707, 0.1000, 0.0,
        ]
        .to_complex_time_vec();
        let c: &[f32] = &[
            0.0080, 0.0000, 0.0428, 0.0174, 0.1340, 0.0897, 0.3356, 0.2827, 0.7192, 0.6479, 1.3058,
            1.1946, 2.0175, 1.8757, 2.7047, 2.5665, 3.2186, 3.0874, 3.4409, 3.2994, 3.2291, 3.1287,
            2.5801, 2.7264, 1.7085, 2.1882, 0.8637, 1.6369, 0.2319, 1.1420, -0.0878, 0.7078,
            -0.1208, 0.3523, -0.0317, 0.1311, 0.0080, 0.0509,
        ];
        let mut buffer = SingleBuffer::new();
        let b = b.prepare_argument_padded(&mut buffer);
        a.correlate(&mut buffer, &b).unwrap();
        let res = a.data(..);
        let tol = 0.1;
        for i in 0..c.len() {
            if (res[i] - c[i]).abs() > tol {
                panic!("assertion failed: {:?} != {:?} at index {}", res, c, i);
            }
        }
    }

    #[test]
    fn time_correlation_test2() {
        let mut a = vec![1.0, 1.0, 2.0, 1.0, 3.0, 1.0].to_complex_time_vec();
        let b = vec![4.0, 1.0, 5.0, 1.0, 6.0, 1.0].to_complex_time_vec();
        let c: &[f32] = &[7.0, 5.0, 19.0, 8.0, 35.0, 9.0, 25.0, 4.0, 13.0, 1.0];
        let mut buffer = SingleBuffer::new();
        let b = b.prepare_argument_padded(&mut buffer);
        a.correlate(&mut buffer, &b).unwrap();
        let res = a.data(..);
        let tol = 0.1;
        for i in 0..c.len() {
            if (res[i] - c[i]).abs() > tol {
                panic!("assertion failed: {:?} != {:?} at index {}", res, c, i);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Returns a reference to a variable's index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar_ref(), Ok(&1));
    /// ```
    pub fn unvar_ref(&self) -> Result<&usize, Error> {
        match *self {
            Var(ref n) => Ok(&n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(self, 0))
    }
}

fn show_precedence(term: &Term, context_precedence: usize) -> String {
    match *term {
        Var(i) => format!("{:X}", i), // max. index = 15
        Abs(ref t) => {
            let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
            parenthesize_if(&ret, context_precedence > 1).into()
        },
        App(ref t1, ref t2) => {
            let ret = format!("{}{}", show_precedence(t1, 2), show_precedence(t2, 3));
            parenthesize_if(&ret, context_precedence == 3).into()
        }
    }
}

fn parenthesize_if(input: &str, condition: bool) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

#[cfg(test)]
mod test {
    use arithmetic::{zero, succ, pred};

    #[test]
    fn displaying_terms() {
        assert_eq!(&format!("{}", zero()), "λλ1");
        assert_eq!(&format!("{}", succ()), "λλλ2(321)");
        assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
    }
}
<commit_msg>add classic display mode<commit_after>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;
use std::char::from_u32;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Returns a reference to a variable's index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar_ref(), Ok(&1));
    /// ```
    pub fn unvar_ref(&self) -> Result<&usize, Error> {
        match *self {
            Var(ref n) => Ok(&n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

/// Set to `true` for classic lambda mode or `false` for the De Bruijn index display mode when
/// displaying lambda terms. The default is `false`.
pub const DISPLAY_CLASSIC: bool = false;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(self, 0, 0))
    }
}

// TODO: make it safer (unwraps)
fn show_precedence(term: &Term, context_precedence: usize, depth: u32) -> String {
    match *term {
        Var(i) => if DISPLAY_CLASSIC {
            format!("{}", from_u32(depth + 97 - i as u32).unwrap())
        } else {
            format!("{:X}", i)
        },
        Abs(ref t) => {
            let ret = if DISPLAY_CLASSIC {
                format!("{}{}. {}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, from_u32(depth + 97).unwrap(), show_precedence(t, 0, depth + 1))
            } else {
                format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t)
            };
            parenthesize_if(&ret, context_precedence > 1).into()
        },
        App(ref t1, ref t2) => {
            let ret = if DISPLAY_CLASSIC {
                format!("{} {}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
            } else {
                format!("{}{}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
            };
            parenthesize_if(&ret, context_precedence == 3).into()
        }
    }
}

fn parenthesize_if(input: &str, condition: bool) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

#[cfg(test)]
mod test {
    use super::DISPLAY_CLASSIC;
    use arithmetic::{zero, succ, pred};

    #[test]
    fn display_modes() {
        if DISPLAY_CLASSIC {
            assert_eq!(&format!("{}", zero()), "λa. λb. b");
            assert_eq!(&format!("{}", succ()), "λa. λb. λc. b (a b c)");
            assert_eq!(&format!("{}", pred()), "λa. λb. λc. a (λd. λe. e (d b)) (λd. c) (λd. d)");
        } else {
            assert_eq!(&format!("{}", zero()), "λλ1");
            assert_eq!(&format!("{}", succ()), "λλλ2(321)");
            assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
        }
    }
}
<|endoftext|>


<commit_before>//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use std::cell::Ref;

use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::visit::{Visitor, LvalueContext};
use rustc::mir;
use rustc::ty::layout::Layout;
use rustc::ty::{subst, self};

use error::{EvalResult, EvalError};
use eval_context::{EvalContext, StackPopCleanup, MirRef};
use lvalue::{Global, GlobalId, Lvalue};
use syntax::codemap::Span;

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
        self.steps_remaining = self.steps_remaining.saturating_sub(n);
        if self.steps_remaining > 0 {
            Ok(())
        } else {
            Err(EvalError::ExecutionTimeLimitReached)
        }
    }

    /// Returns true as long as there are more things to do.
    pub fn step(&mut self) -> EvalResult<'tcx, bool> {
        // see docs on the `Memory::packed` field for why we do this
        self.memory.clear_packed();
        self.inc_step_counter_and_check_limit(1)?;
        if self.stack.is_empty() {
            return Ok(false);
        }

        let block = self.frame().block;
        let stmt_id = self.frame().stmt;
        let mir = self.mir();
        let basic_block = &mir.basic_blocks()[block];

        if let Some(stmt) = basic_block.statements.get(stmt_id) {
            let mut new = Ok(0);
            ConstantExtractor {
                span: stmt.source_info.span,
                substs: self.substs(),
                def_id: self.frame().def_id,
                ecx: self,
                mir: Ref::clone(&mir),
                new_constants: &mut new,
            }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
            if new? == 0 {
                self.statement(stmt)?;
            }
            // if ConstantExtractor added new frames, we don't execute anything here
            // but await the next call to step
            return Ok(true);
        }

        let terminator = basic_block.terminator();
        let mut new = Ok(0);
        ConstantExtractor {
            span: terminator.source_info.span,
            substs: self.substs(),
            def_id: self.frame().def_id,
            ecx: self,
            mir: Ref::clone(&mir),
            new_constants: &mut new,
        }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
        if new? == 0 {
            self.terminator(terminator)?;
        }
        // if ConstantExtractor added new frames, we don't execute anything here
        // but await the next call to step
        Ok(true)
    }

    fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", stmt);

        use rustc::mir::StatementKind::*;
        match stmt.kind {
            Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,

            SetDiscriminant { ref lvalue, variant_index } => {
                let dest = self.eval_lvalue(lvalue)?;
                let dest_ty = self.lvalue_ty(lvalue);
                let dest_layout = self.type_layout(dest_ty)?;

                match *dest_layout {
                    Layout::General { discr, ref variants, .. } => {
                        let discr_size = discr.size().bytes();
                        let discr_offset = variants[variant_index].offsets[0].bytes();

                        // FIXME(solson)
                        let dest = self.force_allocation(dest)?;
                        let discr_dest = (dest.to_ptr()).offset(discr_offset);

                        self.memory.write_uint(discr_dest, variant_index as u128, discr_size)?;
                    }

                    Layout::RawNullablePointer { nndiscr, .. } => {
                        use value::PrimVal;
                        if variant_index as u64 != nndiscr {
                            self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
                        }
                    }

                    _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
                }
            }

            // Miri can safely ignore these. Only translation needs it.
            StorageLive(_) |
            StorageDead(_) => {}

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}
        }

        self.frame_mut().stmt += 1;
        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", terminator.kind);
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            trace!("// {:?}", self.frame().block);
        }
        Ok(())
    }
}

// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
// this includes any method that might access the stack
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
    span: Span,
    ecx: &'a mut EvalContext<'b, 'tcx>,
    mir: MirRef<'tcx>,
    def_id: DefId,
    substs: &'tcx subst::Substs<'tcx>,
    new_constants: &'a mut EvalResult<'tcx, u64>,
}

impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
    fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) {
        let cid = GlobalId { def_id, substs, promoted: None };
        if self.ecx.globals.contains_key(&cid) {
            return;
        }
        self.try(|this| {
            let mir = this.ecx.load_mir(def_id)?;
            this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
            let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() {
                StackPopCleanup::Freeze
            } else {
                StackPopCleanup::None
            };
            this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new())
        });
    }
    fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
        if let Ok(ref mut n) = *self.new_constants {
            *n += 1;
        } else {
            return;
        }
        if let Err(e) = f(self) {
            *self.new_constants = Err(e);
        }
    }
}

impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
    fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
        self.super_constant(constant, location);
        match constant.literal {
            // already computed by rustc
            mir::Literal::Value { .. } => {}
            mir::Literal::Item { def_id, substs } => {
                if let ty::TyFnDef(..) = constant.ty.sty {
                    // No need to do anything here,
                    // because the type is the actual function, not the signature of the function.
                    // Thus we can simply create a zero sized allocation in `evaluate_operand`
                } else {
                    self.global_item(def_id, substs, constant.span, true);
                }
            },
            mir::Literal::Promoted { index } => {
                let cid = GlobalId {
                    def_id: self.def_id,
                    substs: self.substs,
                    promoted: Some(index),
                };
                if self.ecx.globals.contains_key(&cid) {
                    return;
                }
                let mir = Ref::clone(&self.mir);
                let mir = Ref::map(mir, |mir| &mir.promoted[index]);
                self.try(|this| {
                    let ty = this.ecx.monomorphize(mir.return_ty, this.substs);
                    this.ecx.globals.insert(cid, Global::uninitialized(ty));
                    this.ecx.push_stack_frame(this.def_id,
                                              constant.span,
                                              mir,
                                              this.substs,
                                              Lvalue::Global(cid),
                                              StackPopCleanup::Freeze,
                                              Vec::new())
                });
            }
        }
    }

    fn visit_lvalue(
        &mut self,
        lvalue: &mir::Lvalue<'tcx>,
        context: LvalueContext<'tcx>,
        location: mir::Location
    ) {
        self.super_lvalue(lvalue, context, location);
        if let mir::Lvalue::Static(def_id) = *lvalue {
            let substs = self.ecx.tcx.intern_substs(&[]);
            let span = self.span;
            if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
                if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
                    if let hir::ItemStatic(_, m, _) = *node {
                        self.global_item(def_id, substs, span, m == hir::MutImmutable);
                        return;
                    } else {
                        bug!("static def id doesn't point to static");
                    }
                } else {
                    bug!("static def id doesn't point to item");
                }
            } else {
                let def = self.ecx.tcx.sess.cstore.describe_def(def_id).expect("static not found");
                if let hir::def::Def::Static(_, mutable) = def {
                    self.global_item(def_id, substs, span, !mutable);
                } else {
                    bug!("static found but isn't a static: {:?}", def);
                }
            }
        }
    }
}
<commit_msg>Log global/promoted frame creation.<commit_after>//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use std::cell::Ref;

use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::visit::{Visitor, LvalueContext};
use rustc::mir;
use rustc::ty::layout::Layout;
use rustc::ty::{subst, self};

use error::{EvalResult, EvalError};
use eval_context::{EvalContext, StackPopCleanup, MirRef};
use lvalue::{Global, GlobalId, Lvalue};
use syntax::codemap::Span;

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
        self.steps_remaining = self.steps_remaining.saturating_sub(n);
        if self.steps_remaining > 0 {
            Ok(())
        } else {
            Err(EvalError::ExecutionTimeLimitReached)
        }
    }

    /// Returns true as long as there are more things to do.
    pub fn step(&mut self) -> EvalResult<'tcx, bool> {
        // see docs on the `Memory::packed` field for why we do this
        self.memory.clear_packed();
        self.inc_step_counter_and_check_limit(1)?;
        if self.stack.is_empty() {
            return Ok(false);
        }

        let block = self.frame().block;
        let stmt_id = self.frame().stmt;
        let mir = self.mir();
        let basic_block = &mir.basic_blocks()[block];

        if let Some(stmt) = basic_block.statements.get(stmt_id) {
            let mut new = Ok(0);
            ConstantExtractor {
                span: stmt.source_info.span,
                substs: self.substs(),
                def_id: self.frame().def_id,
                ecx: self,
                mir: Ref::clone(&mir),
                new_constants: &mut new,
            }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
            if new? == 0 {
                self.statement(stmt)?;
            }
            // if ConstantExtractor added new frames, we don't execute anything here
            // but await the next call to step
            return Ok(true);
        }

        let terminator = basic_block.terminator();
        let mut new = Ok(0);
        ConstantExtractor {
            span: terminator.source_info.span,
            substs: self.substs(),
            def_id: self.frame().def_id,
            ecx: self,
            mir: Ref::clone(&mir),
            new_constants: &mut new,
        }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
        if new? == 0 {
            self.terminator(terminator)?;
        }
        // if ConstantExtractor added new frames, we don't execute anything here
        // but await the next call to step
        Ok(true)
    }

    fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", stmt);

        use rustc::mir::StatementKind::*;
        match stmt.kind {
            Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,

            SetDiscriminant { ref lvalue, variant_index } => {
                let dest = self.eval_lvalue(lvalue)?;
                let dest_ty = self.lvalue_ty(lvalue);
                let dest_layout = self.type_layout(dest_ty)?;

                match *dest_layout {
                    Layout::General { discr, ref variants, .. } => {
                        let discr_size = discr.size().bytes();
                        let discr_offset = variants[variant_index].offsets[0].bytes();

                        // FIXME(solson)
                        let dest = self.force_allocation(dest)?;
                        let discr_dest = (dest.to_ptr()).offset(discr_offset);

                        self.memory.write_uint(discr_dest, variant_index as u128, discr_size)?;
                    }

                    Layout::RawNullablePointer { nndiscr, .. } => {
                        use value::PrimVal;
                        if variant_index as u64 != nndiscr {
                            self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
                        }
                    }

                    _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
                }
            }

            // Miri can safely ignore these. Only translation needs it.
            StorageLive(_) |
            StorageDead(_) => {}

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}
        }

        self.frame_mut().stmt += 1;
        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", terminator.kind);
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            trace!("// {:?}", self.frame().block);
        }
        Ok(())
    }
}

// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
// this includes any method that might access the stack
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
    span: Span,
    ecx: &'a mut EvalContext<'b, 'tcx>,
    mir: MirRef<'tcx>,
    def_id: DefId,
    substs: &'tcx subst::Substs<'tcx>,
    new_constants: &'a mut EvalResult<'tcx, u64>,
}

impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
    fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) {
        let cid = GlobalId { def_id, substs, promoted: None };
        if self.ecx.globals.contains_key(&cid) {
            return;
        }
        self.try(|this| {
            let mir = this.ecx.load_mir(def_id)?;
            this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
            let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() {
                StackPopCleanup::Freeze
            } else {
                StackPopCleanup::None
            };
            let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
            trace!("pushing stack frame for global: {}", name);
            this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new())
        });
    }
    fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
        if let Ok(ref mut n) = *self.new_constants {
            *n += 1;
        } else {
            return;
        }
        if let Err(e) = f(self) {
            *self.new_constants = Err(e);
        }
    }
}

impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
    fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
        self.super_constant(constant, location);
        match constant.literal {
            // already computed by rustc
            mir::Literal::Value { .. } => {}
            mir::Literal::Item { def_id, substs } => {
                if let ty::TyFnDef(..) = constant.ty.sty {
                    // No need to do anything here,
                    // because the type is the actual function, not the signature of the function.
                    // Thus we can simply create a zero sized allocation in `evaluate_operand`
                } else {
                    self.global_item(def_id, substs, constant.span, true);
                }
            },
            mir::Literal::Promoted { index } => {
                let cid = GlobalId {
                    def_id: self.def_id,
                    substs: self.substs,
                    promoted: Some(index),
                };
                if self.ecx.globals.contains_key(&cid) {
                    return;
                }
                let mir = Ref::clone(&self.mir);
                let mir = Ref::map(mir, |mir| &mir.promoted[index]);
                self.try(|this| {
                    let ty = this.ecx.monomorphize(mir.return_ty, this.substs);
                    this.ecx.globals.insert(cid, Global::uninitialized(ty));
                    trace!("pushing stack frame for {:?}", index);
                    this.ecx.push_stack_frame(this.def_id,
                                              constant.span,
                                              mir,
                                              this.substs,
                                              Lvalue::Global(cid),
                                              StackPopCleanup::Freeze,
                                              Vec::new())
                });
            }
        }
    }

    fn visit_lvalue(
        &mut self,
        lvalue: &mir::Lvalue<'tcx>,
        context: LvalueContext<'tcx>,
        location: mir::Location
    ) {
        self.super_lvalue(lvalue, context, location);
        if let mir::Lvalue::Static(def_id) = *lvalue {
            let substs = self.ecx.tcx.intern_substs(&[]);
            let span = self.span;
            if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
                if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
                    if let hir::ItemStatic(_, m, _) = *node {
                        self.global_item(def_id, substs, span, m == hir::MutImmutable);
                        return;
                    } else {
                        bug!("static def id doesn't point to static");
                    }
                } else {
                    bug!("static def id doesn't point to item");
                }
            } else {
                let def = self.ecx.tcx.sess.cstore.describe_def(def_id).expect("static not found");
                if let hir::def::Def::Static(_, mutable) = def {
                    self.global_item(def_id, substs, span, !mutable);
                } else {
                    bug!("static found but isn't a static: {:?}", def);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use heartbeats_simple::HeartbeatPow as Heartbeat;
use heartbeats_simple::HeartbeatPowContext as HeartbeatContext;
use profile_traits::time::ProfilerCategory;
use servo_config::opts;
use std::collections::HashMap;
use std::env::var_os;
use std::error::Error;
use std::fs::File;
use std::path::Path;
use std::sync::atomic::{ATOMIC_BOOL_INIT, AtomicBool, Ordering};

static mut HBS: Option<*mut HashMap<ProfilerCategory, Heartbeat>> = None;

// unfortunately can't encompass the actual hashmap in a Mutex (Heartbeat isn't Send/Sync), so we'll use a spinlock
static mut HBS_SPINLOCK: AtomicBool = ATOMIC_BOOL_INIT;

fn lock_and_work<F, R>(work: F) -> R where F: FnOnce() -> R {
    unsafe {
        while HBS_SPINLOCK.compare_and_swap(false, true, Ordering::SeqCst) {}
    }
    let result = work();
    unsafe {
        HBS_SPINLOCK.store(false, Ordering::SeqCst);
    }
    result
}

/// Initialize heartbeats
pub fn init() {
    let mut hbs: Box<HashMap<ProfilerCategory, Heartbeat>> = Box::new(HashMap::new());
    maybe_create_heartbeat(&mut hbs, ProfilerCategory::ApplicationHeartbeat);
    lock_and_work(||
        unsafe {
            HBS = Some(Box::into_raw(hbs));
        }
    );
}

/// Log regmaining buffer data and cleanup heartbeats
pub fn cleanup() {
    let hbs_opt: Option<Box<HashMap<ProfilerCategory, Heartbeat>>> = lock_and_work(||
        unsafe {
            match HBS {
                Some(hbs_ptr) => {
                    let ret = Some(Box::from_raw(hbs_ptr));
                    HBS = None;
                    ret
                },
                None => None,
            }
        }
    );
    if let Some(mut hbs) = hbs_opt {
        for (_, mut v) in hbs.iter_mut() {
            // log any remaining heartbeat records before dropping
            log_heartbeat_records(v);
        }
        hbs.clear();
    }
}

/// Check if a heartbeat exists for the given category
pub fn is_heartbeat_enabled(category: &ProfilerCategory) -> bool {
    let is_enabled = lock_and_work(||
        unsafe {
            HBS.map_or(false, |hbs_ptr| (*hbs_ptr).contains_key(category))
        }
    );
    is_enabled || is_create_heartbeat(category)
}

/// Issue a heartbeat (if one exists) for the given category
pub fn maybe_heartbeat(category: &ProfilerCategory,
                       start_time: u64,
                       end_time: u64,
                       start_energy: u64,
                       end_energy: u64) {
    lock_and_work(||
        unsafe {
            if let Some(hbs_ptr) = HBS {
                if !(*hbs_ptr).contains_key(category) {
                    maybe_create_heartbeat(&mut (*hbs_ptr), category.clone());
                }
                if let Some(mut h) = (*hbs_ptr).get_mut(category) {
                    (*h).heartbeat(0, 1, start_time, end_time, start_energy, end_energy);
                }
            }
        }
    );
}

// TODO(cimes): Android doesn't really do environment variables. Need a better way to configure dynamically.

fn is_create_heartbeat(category: &ProfilerCategory) -> bool {
    opts::get().profile_heartbeats || var_os(format!("SERVO_HEARTBEAT_ENABLE_{:?}", category)).is_some()
}

fn open_heartbeat_log<P: AsRef<Path>>(name: P) -> Option<File> {
    match File::create(name) {
        Ok(f) => Some(f),
        Err(e) => {
            warn!("Failed to open heartbeat log: {}", Error::description(&e));
            None
        },
    }
}

#[cfg(target_os = "android")]
fn get_heartbeat_log(category: &ProfilerCategory) -> Option<File> {
    open_heartbeat_log(format!("/sdcard/servo/heartbeat-{:?}.log", category))
}

#[cfg(not(target_os = "android"))]
fn get_heartbeat_log(category: &ProfilerCategory) -> Option<File> {
    var_os(format!("SERVO_HEARTBEAT_LOG_{:?}", category)).and_then(|name| open_heartbeat_log(&name))
}

fn get_heartbeat_window_size(category: &ProfilerCategory) -> usize {
    const WINDOW_SIZE_DEFAULT: usize = 1;
    match var_os(format!("SERVO_HEARTBEAT_WINDOW_{:?}", category)) {
        Some(w) => match w.into_string() {
            Ok(s) => s.parse::<usize>().unwrap_or(WINDOW_SIZE_DEFAULT),
            _ => WINDOW_SIZE_DEFAULT,
        },
        None => WINDOW_SIZE_DEFAULT,
    }
}

/// Possibly create a heartbeat
fn maybe_create_heartbeat(hbs: &mut HashMap<ProfilerCategory, Heartbeat>,
                          category: ProfilerCategory) {
    if is_create_heartbeat(&category) {
        // get optional log file
        let logfile: Option<File> = get_heartbeat_log(&category);
        // window size
        let window_size: usize = get_heartbeat_window_size(&category);
        // create the heartbeat
        match Heartbeat::new(window_size, Some(heartbeat_window_callback), logfile) {
            Ok(hb) => {
                debug!("Created heartbeat for {:?}", category);
                hbs.insert(category, hb);
            },
            Err(e) => warn!("Failed to create heartbeat for {:?}: {}", category, e),
        }
    };
}

/// Log heartbeat records up to the buffer index
fn log_heartbeat_records(hb: &mut Heartbeat) {
    match hb.log_to_buffer_index() {
        Ok(_) => (),
        Err(e) => warn!("Failed to write heartbeat log: {}", Error::description(&e)),
    }
}

/// Callback function used to log the window buffer.
/// When this is called from native C, the heartbeat is safely locked internally and the global lock is held.
/// If calling from this file, you must already hold the global lock!
extern fn heartbeat_window_callback(hb: *const HeartbeatContext) {
    unsafe {
        if let Some(hbs_ptr) = HBS {
            for (_, v) in (*hbs_ptr).iter_mut() {
                if &v.hb as *const HeartbeatContext == hb {
                    log_heartbeat_records(v);
                }
            }
        }
    }
}
<commit_msg>Handle synchronization in a private module with safer closure<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use heartbeats_simple::HeartbeatPow as Heartbeat;
use profile_traits::time::ProfilerCategory;
use self::synchronized_heartbeat::{heartbeat_window_callback, lock_and_work};
use servo_config::opts;
use std::collections::HashMap;
use std::env::var_os;
use std::error::Error;
use std::fs::File;
use std::path::Path;

/// Initialize heartbeats
pub fn init() {
    lock_and_work(|hbs_opt|
        if hbs_opt.is_none() {
            let mut hbs: Box<HashMap<ProfilerCategory, Heartbeat>> = Box::new(HashMap::new());
            maybe_create_heartbeat(&mut hbs, ProfilerCategory::ApplicationHeartbeat);
            *hbs_opt = Some(Box::into_raw(hbs))
        }
    );
}

/// Log regmaining buffer data and cleanup heartbeats
pub fn cleanup() {
    let hbs_opt_box: Option<Box<HashMap<ProfilerCategory, Heartbeat>>> = lock_and_work(|hbs_opt|
        hbs_opt.take().map(|hbs_ptr|
            unsafe {
                Box::from_raw(hbs_ptr)
            }
        )
    );
    if let Some(mut hbs) = hbs_opt_box {
        for (_, mut v) in hbs.iter_mut() {
            // log any remaining heartbeat records before dropping
            log_heartbeat_records(v);
        }
        hbs.clear();
    }
}

/// Check if a heartbeat exists for the given category
pub fn is_heartbeat_enabled(category: &ProfilerCategory) -> bool {
    let is_enabled = lock_and_work(|hbs_opt|
        hbs_opt.map_or(false, |hbs_ptr|
            unsafe {
                (*hbs_ptr).contains_key(category)
            }
        )
    );
    is_enabled || is_create_heartbeat(category)
}

/// Issue a heartbeat (if one exists) for the given category
pub fn maybe_heartbeat(category: &ProfilerCategory,
                       start_time: u64,
                       end_time: u64,
                       start_energy: u64,
                       end_energy: u64) {
    lock_and_work(|hbs_opt|
        if let Some(hbs_ptr) = *hbs_opt {
            unsafe {
                if !(*hbs_ptr).contains_key(category) {
                    maybe_create_heartbeat(&mut (*hbs_ptr), category.clone());
                }
                if let Some(mut h) = (*hbs_ptr).get_mut(category) {
                    (*h).heartbeat(0, 1, start_time, end_time, start_energy, end_energy);
                }
            }
        }
    );
}

// TODO(cimes): Android doesn't really do environment variables. Need a better way to configure dynamically.

fn is_create_heartbeat(category: &ProfilerCategory) -> bool {
    opts::get().profile_heartbeats || var_os(format!("SERVO_HEARTBEAT_ENABLE_{:?}", category)).is_some()
}

fn open_heartbeat_log<P: AsRef<Path>>(name: P) -> Option<File> {
    match File::create(name) {
        Ok(f) => Some(f),
        Err(e) => {
            warn!("Failed to open heartbeat log: {}", Error::description(&e));
            None
        },
    }
}

#[cfg(target_os = "android")]
fn get_heartbeat_log(category: &ProfilerCategory) -> Option<File> {
    open_heartbeat_log(format!("/sdcard/servo/heartbeat-{:?}.log", category))
}

#[cfg(not(target_os = "android"))]
fn get_heartbeat_log(category: &ProfilerCategory) -> Option<File> {
    var_os(format!("SERVO_HEARTBEAT_LOG_{:?}", category)).and_then(|name| open_heartbeat_log(&name))
}

fn get_heartbeat_window_size(category: &ProfilerCategory) -> usize {
    const WINDOW_SIZE_DEFAULT: usize = 1;
    match var_os(format!("SERVO_HEARTBEAT_WINDOW_{:?}", category)) {
        Some(w) => match w.into_string() {
            Ok(s) => s.parse::<usize>().unwrap_or(WINDOW_SIZE_DEFAULT),
            _ => WINDOW_SIZE_DEFAULT,
        },
        None => WINDOW_SIZE_DEFAULT,
    }
}

/// Possibly create a heartbeat
fn maybe_create_heartbeat(hbs: &mut HashMap<ProfilerCategory, Heartbeat>,
                          category: ProfilerCategory) {
    if is_create_heartbeat(&category) {
        // get optional log file
        let logfile: Option<File> = get_heartbeat_log(&category);
        // window size
        let window_size: usize = get_heartbeat_window_size(&category);
        // create the heartbeat
        match Heartbeat::new(window_size, Some(heartbeat_window_callback), logfile) {
            Ok(hb) => {
                debug!("Created heartbeat for {:?}", category);
                hbs.insert(category, hb);
            },
            Err(e) => warn!("Failed to create heartbeat for {:?}: {}", category, e),
        }
    };
}

/// Log heartbeat records up to the buffer index
fn log_heartbeat_records(hb: &mut Heartbeat) {
    match hb.log_to_buffer_index() {
        Ok(_) => (),
        Err(e) => warn!("Failed to write heartbeat log: {}", Error::description(&e)),
    }
}

mod synchronized_heartbeat {
    use heartbeats_simple::HeartbeatPow as Heartbeat;
    use heartbeats_simple::HeartbeatPowContext as HeartbeatContext;
    use profile_traits::time::ProfilerCategory;
    use std::collections::HashMap;
    use std::sync::atomic::{ATOMIC_BOOL_INIT, AtomicBool, Ordering};
    use super::log_heartbeat_records;

    static mut HBS: Option<*mut HashMap<ProfilerCategory, Heartbeat>> = None;

    // unfortunately can't encompass the actual hashmap in a Mutex (Heartbeat isn't Send/Sync), so we'll use a spinlock
    static HBS_SPINLOCK: AtomicBool = ATOMIC_BOOL_INIT;

    pub fn lock_and_work<F, R>(work: F) -> R
        where F: FnOnce(&mut Option<*mut HashMap<ProfilerCategory, Heartbeat>>) -> R {
        while HBS_SPINLOCK.compare_and_swap(false, true, Ordering::SeqCst) {}
        let result = unsafe {
            work(&mut HBS)
        };
        HBS_SPINLOCK.store(false, Ordering::SeqCst);
        result
    }

    /// Callback function used to log the window buffer.
    /// When this is called from native C, the heartbeat is safely locked internally and the global lock is held.
    /// If calling from this file, you must already hold the global lock!
    pub extern fn heartbeat_window_callback(hb: *const HeartbeatContext) {
        unsafe {
            if let Some(hbs_ptr) = HBS {
                for (_, v) in (*hbs_ptr).iter_mut() {
                    if &v.hb as *const HeartbeatContext == hb {
                        log_heartbeat_records(v);
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::TimeZone;
use chrono::offset::fixed::FixedOffset;
use chrono::UTC;
use serde_json::value::Value;
use uuid::Uuid;

use error::TaskError;
use error::TaskErrorKind;
use priority::TaskPriority;
use result::Result;

pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type Urgency = f64;

pub static TASKWARRIOR_DATETIME_TEMPLATE : &'static str = "%Y%m%dT%H%M%SZ";

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: NaiveDateTime,
    modified: Option<NaiveDateTime>,
    priority: Option<TaskPriority>,
    project: Option<Project>,
    status: Status,
    tags: Vec<Tag>,
    uuid: Uuid,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64,
                desc: String,
                entry: NaiveDateTime,
                modified: Option<NaiveDateTime>,
                priority: Option<TaskPriority>,
                project: Option<Project>,
                status: Status,
                tags: Vec<Tag>,
                uuid: Uuid,
                urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Result<Task> {
        if !v.is_object() {
            trace!("JSON Value is not an object, cannot parse into Task");
            return Err(TaskError::new(TaskErrorKind::ParserError, None));
        }
        trace!("Found object");

        let map = v.as_object().unwrap();
        trace!("map = {:?}", map);

        let entry_dt = get_entry(&map);
        trace!("entry_dt = {:?}", entry_dt);
        if entry_dt.is_err() {
            return Err(entry_dt.err().unwrap());
        }
        let entry_dt = entry_dt.unwrap();

        let modified_dt = match get_modified(&map) {
            Some(Err(e)) => return Err(e),
            Some(Ok(x))  => Some(x),
            None         => None,
        };

        Ok(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : entry_dt,
            modified : modified_dt,
            priority : get_priority(&map),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &NaiveDateTime {
        &self.entry
    }

    pub fn modified(&self) -> Option<&NaiveDateTime> {
        self.modified.as_ref()
    }

    pub fn priority(&self) -> Option<&TaskPriority> {
        self.priority.as_ref()
    }

    pub fn project(&self) -> Option<&Project> {
        self.project.as_ref()
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &Uuid {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(format!("{}", self.uuid));
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        {
            let v = Value::String(format!("{:?}", self.entry));
            map.insert(String::from("entry"), v);
        }
        if self.modified.is_some() {
            let v = Value::String(format!("{:?}", self.modified.unwrap()));
            map.insert(String::from("modified"), v);
        }
        if self.priority.is_some() {
            map.insert(String::from("priority") , Value::String(self.priority.unwrap().into()));
        }
        if self.project.is_some() {
            map.insert(String::from("project"), Value::String(self.project.unwrap()));
        }
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> Result<NaiveDateTime> {
    if let Some(s) = map.get("entry").unwrap().as_string() {
        trace!("Found 'entry': {:?}", s);
        NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE)
            .map_err(|e| TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e))))
    } else {
        trace!("No 'entry'");
        Err(TaskError::new(TaskErrorKind::ParserError, None))
    }
}

fn get_modified(map: &BTreeMap<String, Value>) -> Option<Result<NaiveDateTime>> {
    if let Some(modif) = map.get("modified").and_then(|m| m.as_string()) {
        Some(NaiveDateTime::parse_from_str(modif, TASKWARRIOR_DATETIME_TEMPLATE)
            .map_err(|e| TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e)))))

    } else {
        None
    }
}

fn get_priority(map: &BTreeMap<String, Value>) -> Option<TaskPriority> {
    map.get("priority")
        .map(|m| m.as_string().map(String::from).unwrap())
        .map(|s| TaskPriority::from(&s[..]))
}

fn get_project(map: &BTreeMap<String, Value>) -> Option<String> {
    map.get("project")
        .map(|p| p.as_string().map(String::from).unwrap())
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> Uuid {
    map.get("uuid")
        .unwrap()
        .as_string()
        .and_then(|s| Uuid::parse_str(s).ok())
        .unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    extern crate uuid;

    use chrono::NaiveDateTime;
    use chrono::UTC;
    use uuid::Uuid;

    use core::reader::Reader;
    use core::reader::JsonObjectReader;
    use super::Task;
    use super::TASKWARRIOR_DATETIME_TEMPLATE;
    use priority::TaskPriority;
    use std::borrow::Borrow;

    use std::error::Error;

    #[test]
    fn test_from_json() {
        env_logger::init().unwrap();

        let json = String::from("{\"id\":1,\"description\":\"desc\",\"entry\":\"20150612T164806Z\",\"modified\":\"20160315T215656Z\",\"priority\":\"L\",\"project\":\"someproj\",\"status\":\"pending\",\"tags\":[\"test\",\"task\"],\"uuid\":\"93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3\",\"urgency\":1.0}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        debug!("{:?}", v);
        let t = Task::from_value(v);
        debug!("{:?}", t);
        assert!(t.is_ok());
        let t = t.unwrap();

        debug!("Task created successfully!");

        assert_eq!(t.id(), 1);
        assert_eq!(t.desc().clone(), String::from("desc"));
        assert_eq!(t.entry().clone(), NaiveDateTime::parse_from_str("20150612T164806Z", TASKWARRIOR_DATETIME_TEMPLATE).unwrap());
        assert_eq!(t.modified().clone(), NaiveDateTime::parse_from_str("20160315T215656Z", TASKWARRIOR_DATETIME_TEMPLATE).ok().as_ref());
        assert_eq!(t.priority(), Some(&TaskPriority::Low));
        assert_eq!(t.project().clone(), Some(&String::from("someproj")));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["test", "task"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), Uuid::parse_str("93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3").unwrap());
        assert_eq!(t.urgency() as u64, 1.0 as u64);
    }

}
<commit_msg>Add a test<commit_after>use std::collections::BTreeMap;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::TimeZone;
use chrono::offset::fixed::FixedOffset;
use chrono::UTC;
use serde_json::value::Value;
use uuid::Uuid;

use error::TaskError;
use error::TaskErrorKind;
use priority::TaskPriority;
use result::Result;

pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type Urgency = f64;

pub static TASKWARRIOR_DATETIME_TEMPLATE : &'static str = "%Y%m%dT%H%M%SZ";

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: NaiveDateTime,
    modified: Option<NaiveDateTime>,
    priority: Option<TaskPriority>,
    project: Option<Project>,
    status: Status,
    tags: Vec<Tag>,
    uuid: Uuid,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64,
                desc: String,
                entry: NaiveDateTime,
                modified: Option<NaiveDateTime>,
                priority: Option<TaskPriority>,
                project: Option<Project>,
                status: Status,
                tags: Vec<Tag>,
                uuid: Uuid,
                urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Result<Task> {
        if !v.is_object() {
            trace!("JSON Value is not an object, cannot parse into Task");
            return Err(TaskError::new(TaskErrorKind::ParserError, None));
        }
        trace!("Found object");

        let map = v.as_object().unwrap();
        trace!("map = {:?}", map);

        let entry_dt = get_entry(&map);
        trace!("entry_dt = {:?}", entry_dt);
        if entry_dt.is_err() {
            return Err(entry_dt.err().unwrap());
        }
        let entry_dt = entry_dt.unwrap();

        let modified_dt = match get_modified(&map) {
            Some(Err(e)) => return Err(e),
            Some(Ok(x))  => Some(x),
            None         => None,
        };

        Ok(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : entry_dt,
            modified : modified_dt,
            priority : get_priority(&map),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &NaiveDateTime {
        &self.entry
    }

    pub fn modified(&self) -> Option<&NaiveDateTime> {
        self.modified.as_ref()
    }

    pub fn priority(&self) -> Option<&TaskPriority> {
        self.priority.as_ref()
    }

    pub fn project(&self) -> Option<&Project> {
        self.project.as_ref()
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &Uuid {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(format!("{}", self.uuid));
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        {
            let v = Value::String(format!("{:?}", self.entry));
            map.insert(String::from("entry"), v);
        }
        if self.modified.is_some() {
            let v = Value::String(format!("{:?}", self.modified.unwrap()));
            map.insert(String::from("modified"), v);
        }
        if self.priority.is_some() {
            map.insert(String::from("priority") , Value::String(self.priority.unwrap().into()));
        }
        if self.project.is_some() {
            map.insert(String::from("project"), Value::String(self.project.unwrap()));
        }
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> Result<NaiveDateTime> {
    if let Some(s) = map.get("entry").unwrap().as_string() {
        trace!("Found 'entry': {:?}", s);
        NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE)
            .map_err(|e| TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e))))
    } else {
        trace!("No 'entry'");
        Err(TaskError::new(TaskErrorKind::ParserError, None))
    }
}

fn get_modified(map: &BTreeMap<String, Value>) -> Option<Result<NaiveDateTime>> {
    if let Some(modif) = map.get("modified").and_then(|m| m.as_string()) {
        Some(NaiveDateTime::parse_from_str(modif, TASKWARRIOR_DATETIME_TEMPLATE)
            .map_err(|e| TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e)))))

    } else {
        None
    }
}

fn get_priority(map: &BTreeMap<String, Value>) -> Option<TaskPriority> {
    map.get("priority")
        .map(|m| m.as_string().map(String::from).unwrap())
        .map(|s| TaskPriority::from(&s[..]))
}

fn get_project(map: &BTreeMap<String, Value>) -> Option<String> {
    map.get("project")
        .map(|p| p.as_string().map(String::from).unwrap())
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> Uuid {
    map.get("uuid")
        .unwrap()
        .as_string()
        .and_then(|s| Uuid::parse_str(s).ok())
        .unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    extern crate uuid;

    use chrono::NaiveDateTime;
    use chrono::UTC;
    use uuid::Uuid;

    use core::reader::Reader;
    use core::reader::JsonObjectReader;
    use super::Task;
    use super::TASKWARRIOR_DATETIME_TEMPLATE;
    use priority::TaskPriority;
    use std::borrow::Borrow;

    use std::error::Error;

    fn mkdate(s: &str) -> NaiveDateTime {
        NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE).unwrap()
    }

    #[test]
    fn test_from_json() {
        env_logger::init();

        let json = String::from("{\"id\":1,\"description\":\"desc\",\"entry\":\"20150612T164806Z\",\"modified\":\"20160315T215656Z\",\"priority\":\"L\",\"project\":\"someproj\",\"status\":\"pending\",\"tags\":[\"test\",\"task\"],\"uuid\":\"93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3\",\"urgency\":1.0}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        debug!("{:?}", v);
        let t = Task::from_value(v);
        debug!("{:?}", t);
        assert!(t.is_ok());
        let t = t.unwrap();

        debug!("Task created successfully!");

        assert_eq!(t.id(), 1);
        assert_eq!(t.desc().clone(), String::from("desc"));
        assert_eq!(t.entry().clone(), mkdate("20150612T164806Z"));
        assert_eq!(t.modified().clone(), Some(&mkdate("20160315T215656Z")));
        assert_eq!(t.priority(), Some(&TaskPriority::Low));
        assert_eq!(t.project().clone(), Some(&String::from("someproj")));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["test", "task"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), Uuid::parse_str("93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3").unwrap());
        assert_eq!(t.urgency() as u64, 1.0 as u64);
    }

    #[test]
    fn test_from_json_2() {
        env_logger::init();

        let json = String::from("{\"id\":123,\"description\":\"Read some papers\",\"entry\":\"20160320T214333Z\",\"modified\":\"20160320T214357Z\",\"project\":\"self.learning\",\"status\":\"pending\",\"tags\":[\"learning\",\"edu\",\"read\"],\"uuid\":\"b4e58537-96f6-4f87-bb12-ea760ca838a6\",\"urgency\":1.04931}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        debug!("{:?}", v);
        let t = Task::from_value(v);
        debug!("{:?}", t);
        assert!(t.is_ok());
        let t = t.unwrap();

        debug!("Task created successfully!");

        assert_eq!(t.id(), 123);
        assert_eq!(t.desc().clone(), String::from("Read some papers"));
        assert_eq!(t.entry().clone(), mkdate("20160320T214333Z"));
        assert_eq!(t.modified().clone(), Some(&mkdate("20160320T214357Z")));
        assert_eq!(t.priority(), None);
        assert_eq!(t.project().clone(), Some(&String::from("self.learning")));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["learning", "edu", "read"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), Uuid::parse_str("b4e58537-96f6-4f87-bb12-ea760ca838a6").unwrap());
        assert_eq!(t.urgency() as u64, 1.04931 as u64);
    }

}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::io::Write;

use parity_wasm;
use cargo_shim::BuildConfig;

use wasm_gc;

use wasm_context::Context;
use wasm_inline_js;
use wasm_export_main;
use wasm_export_table;
use wasm_hook_grow;
use wasm_intrinsics;
use wasm_runtime;

pub fn process_wasm_file< P: AsRef< Path > + ?Sized >( build: &BuildConfig, artifact: &P ) -> Option< PathBuf > {
    if !build.triplet.as_ref().map( |triplet| triplet == "wasm32-unknown-unknown" ).unwrap_or( false ) {
        return None;
    }

    let path = artifact.as_ref();
    if !path.extension().map( |ext| ext == "wasm" ).unwrap_or( false ) {
        return None;
    }

    let js_path = path.with_extension( "js" );
    if js_path.exists() {
        let js_mtime = fs::metadata( &js_path ).unwrap().modified().unwrap();
        let wasm_mtime = fs::metadata( path ).unwrap().modified().unwrap();
        if js_mtime >= wasm_mtime {
            // We've already ran; nothing to do here.
            return None;
        }
    }

    println_err!( "    Garbage collecting {:?}...", path.file_name().unwrap() );
    wasm_gc::run( &path, &path );

    println_err!( "    Processing {:?}...", path.file_name().unwrap() );
    let mut module = parity_wasm::deserialize_file( &path ).unwrap();
    let mut ctx = Context::from_module( module );
    let snippets = wasm_inline_js::process_and_extract( &mut ctx );
    let intrinsics = wasm_intrinsics::process( &mut ctx );
    wasm_export_main::process( &mut ctx );
    wasm_export_table::process( &mut ctx );
    wasm_hook_grow::process( &mut ctx );
    module = ctx.into_module();

    parity_wasm::serialize_to_file( path, module ).unwrap();

    let all_snippets: Vec< _ > = snippets.into_iter().chain( intrinsics.into_iter() ).collect();
    let js = wasm_runtime::generate_js( path, &all_snippets );
    let mut fp = File::create( &js_path ).unwrap();
    fp.write_all( js.as_bytes() ).unwrap();

    println_err!( "    Finished processing of {:?}!", path.file_name().unwrap() );
    Some( js_path )
}
<commit_msg>Don't forget about autogenerated `.js` file when `.wasm` didn't change<commit_after>use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::io::Write;

use parity_wasm;
use cargo_shim::BuildConfig;

use wasm_gc;

use wasm_context::Context;
use wasm_inline_js;
use wasm_export_main;
use wasm_export_table;
use wasm_hook_grow;
use wasm_intrinsics;
use wasm_runtime;

pub fn process_wasm_file< P: AsRef< Path > + ?Sized >( build: &BuildConfig, artifact: &P ) -> Option< PathBuf > {
    if !build.triplet.as_ref().map( |triplet| triplet == "wasm32-unknown-unknown" ).unwrap_or( false ) {
        return None;
    }

    let path = artifact.as_ref();
    if !path.extension().map( |ext| ext == "wasm" ).unwrap_or( false ) {
        return None;
    }

    let js_path = path.with_extension( "js" );
    if js_path.exists() {
        let js_mtime = fs::metadata( &js_path ).unwrap().modified().unwrap();
        let wasm_mtime = fs::metadata( path ).unwrap().modified().unwrap();
        if js_mtime >= wasm_mtime {
            // We've already ran; nothing to do here.
            return Some( js_path );
        }
    }

    println_err!( "    Garbage collecting {:?}...", path.file_name().unwrap() );
    wasm_gc::run( &path, &path );

    println_err!( "    Processing {:?}...", path.file_name().unwrap() );
    let mut module = parity_wasm::deserialize_file( &path ).unwrap();
    let mut ctx = Context::from_module( module );
    let snippets = wasm_inline_js::process_and_extract( &mut ctx );
    let intrinsics = wasm_intrinsics::process( &mut ctx );
    wasm_export_main::process( &mut ctx );
    wasm_export_table::process( &mut ctx );
    wasm_hook_grow::process( &mut ctx );
    module = ctx.into_module();

    parity_wasm::serialize_to_file( path, module ).unwrap();

    let all_snippets: Vec< _ > = snippets.into_iter().chain( intrinsics.into_iter() ).collect();
    let js = wasm_runtime::generate_js( path, &all_snippets );
    let mut fp = File::create( &js_path ).unwrap();
    fp.write_all( js.as_bytes() ).unwrap();

    println_err!( "    Finished processing of {:?}!", path.file_name().unwrap() );
    Some( js_path )
}
<|endoftext|>