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

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

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

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` order will fail to fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator (they will expand forever). `CallByName` and
/// `CallByValue` orders only produce weak normal forms. The default is `Normal`.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost outermost not inside an abstraction
    CallByName,
    /// leftmost innermost
    Applicative,
    /// leftmost innermost not inside an abstraction
    CallByValue
}

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

    lhs.unabs()
}

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

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

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

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

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self` with the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once(&Order::Normal);
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self, order: &Order) {
        self.beta_once_indicative(order);
    }

    fn beta_once_indicative(&mut self, order: &Order) -> bool {
        match *order {
            Normal => self._beta_once_normal(0),
            CallByName => self._beta_once_call_by_name(),
            Applicative => self._beta_once_applicative(0),
            CallByValue => self._beta_once_call_by_value(),
        }
    }

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

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

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_name(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_name() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_name()
                }
            }
        }
    }

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

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_value(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                   !self.rhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_value() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_value()
                }
            }
        }
    }

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

    /// Performs full β-reduction on `self` using the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full(&Order::Normal);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self, order: &Order) {
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            if !self.beta_once_indicative(order) { break }
        }
        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

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

/// Performs a single β-reduction on a `Term` using the specified evaluation `Order`, consuming its
/// argument in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::{beta_once, Order};
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one, &Order::Normal), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term, order: &Order) -> Term {
    term.beta_once(order);
    term
}

#[cfg(test)]
mod test {
    use super::*;
    use parser::parse;
    use combinators::{i, y};

    #[test]
    fn normal_order() {
        let reduces_instantly = parse(&"(λλ1)((λλλ((32)1))(λλ2))").unwrap();
        assert_eq!(beta_full(reduces_instantly.clone(), &Normal),
                   beta_once(reduces_instantly, &Normal));

        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce, &Normal), Var(1))
    }

    #[test]
    fn call_by_name_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta_once(&CallByName);
        assert_eq!(expr, app(i(), app(i(), i())));
        expr.beta_once(&CallByName);
        assert_eq!(expr, app(i(), i()));
        expr.beta_once(&CallByName);
        assert_eq!(expr, i());
    }

    #[test]
    fn applicative_order() {
        let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
        assert_eq!(&format!("{}", beta_once(expr, &Applicative)), "λ1((λλ1)((λλ21)1))");

        let expands = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(&format!("{}", beta_once(expands, &Applicative)), "(λ2)((λ111)(λ111)(λ111))")
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, app(abs(app(i(), Var(1))), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, app(i(), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, i());
    }

    #[test]
    fn reducibility() {
        assert!(y().is_beta_reducible(&Normal));
        assert!(y().is_beta_reducible(&Applicative));
        assert!(!y().is_beta_reducible(&CallByName));
        assert!(!y().is_beta_reducible(&CallByValue));
    }
}
<commit_msg>add new evaluation orders<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

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

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

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` order will fail to fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator (they will expand forever). The default is
/// `Normal`.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost outermost, but not inside abstractions
    CallByName,
    /// leftmost outermost, but abstractions reduced only in head position
    HeadSpine,
    /// `HeadSpine` + `Normal`
    HybridNormal,
    /// leftmost innermost
    Applicative,
    /// leftmost innermost, but not inside abstractions
    CallByValue,
    /// `CallByValue` + `Applicative`
    HybridApplicative
}

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

    lhs.unabs()
}

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

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

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

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

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self` with the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once(&Order::Normal);
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self, order: &Order) {
        self.beta_once_indicative(order);
    }

    fn beta_once_indicative(&mut self, order: &Order) -> bool {
        match *order {
            Normal       => self._beta_once_normal(0),
            CallByName   => self._beta_once_call_by_name(),
            HeadSpine    => self._beta_once_head_spine(0, true),
            Applicative  => self._beta_once_applicative(0),
            CallByValue  => self._beta_once_call_by_value(),
            HybridNormal => {
                self._beta_once_head_spine(0, true);
                self._beta_once_normal(0)
            },
            HybridApplicative => {
                self._beta_once_call_by_value();
                self._beta_once_applicative(0)
            }
        }
    }

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

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

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_name(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_name() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_name()
                }
            }
        }
    }

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

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

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_value(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                   !self.rhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_value() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_value()
                }
            }
        }
    }

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

    /// Performs full β-reduction on `self` using the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full(&Order::Normal);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self, order: &Order) {
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            if !self.beta_once_indicative(order) { break }
        }
        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

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

/// Performs a single β-reduction on a `Term` using the specified evaluation `Order`, consuming its
/// argument in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::{beta_once, Order};
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one, &Order::Normal), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term, order: &Order) -> Term {
    term.beta_once(order);
    term
}

#[cfg(test)]
mod test {
    use super::*;
    use parser::parse;
    use combinators::{i, y};

    #[test]
    fn normal_order() {
        let reduces_instantly = parse(&"(λλ1)((λλλ((32)1))(λλ2))").unwrap();
        assert_eq!(beta_full(reduces_instantly.clone(), &Normal),
                   beta_once(reduces_instantly, &Normal));

        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce, &Normal), Var(1))
    }

    #[test]
    fn call_by_name_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta_once(&CallByName);
        assert_eq!(expr, app(i(), app(i(), i())));
        expr.beta_once(&CallByName);
        assert_eq!(expr, app(i(), i()));
        expr.beta_once(&CallByName);
        assert_eq!(expr, i());
    }

    #[test]
    fn applicative_order() {
        let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
        assert_eq!(&format!("{}", beta_once(expr, &Applicative)), "λ1((λλ1)((λλ21)1))");

        let expands = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(&format!("{}", beta_once(expands, &Applicative)), "(λ2)((λ111)(λ111)(λ111))")
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, app(abs(app(i(), Var(1))), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, app(i(), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, i());
    }

    #[test]
    fn reducibility() {
        assert!(y().is_beta_reducible(&Normal));
        assert!(y().is_beta_reducible(&Applicative));
        assert!(!y().is_beta_reducible(&CallByName));
        assert!(!y().is_beta_reducible(&CallByValue));
    }
}
<|endoftext|>


<commit_before>use super::*;
use crate::types::{Call, RawMessage};
use crate::vim;
use crossbeam_channel::{bounded, unbounded, Receiver, Sender};

#[derive(Clone, Serialize)]
pub struct RpcClient {
    languageId: LanguageId,
    // TODO: make atomic.
    #[serde(skip_serializing)]
    id: Arc<Mutex<Id>>,
    #[serde(skip_serializing)]
    writer_tx: Sender<RawMessage>,
    #[serde(skip_serializing)]
    reader_tx: Sender<(Id, Sender<rpc::Output>)>,
    pub process_id: Option<u32>,
}

impl RpcClient {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(
        languageId: LanguageId,
        reader: impl BufRead + Send + 'static,
        writer: impl Write + Send + 'static,
        process_id: Option<u32>,
        sink: Sender<Call>,
    ) -> Fallible<Self> {
        let (reader_tx, reader_rx): (Sender<(Id, Sender<rpc::Output>)>, _) = unbounded();

        let languageId_clone = languageId.clone();
        let reader_thread_name = format!("reader-{:?}", languageId);
        thread::Builder::new()
            .name(reader_thread_name.clone())
            .spawn(move || {
                if let Err(err) = loop_read(reader, reader_rx, sink, languageId_clone) {
                    error!("Thread {} exited with error: {:?}", reader_thread_name, err);
                }
            })?;

        let (writer_tx, writer_rx) = unbounded();
        let writer_thread_name = format!("writer-{:?}", languageId);
        let languageId_clone = languageId.clone();
        thread::Builder::new()
            .name(writer_thread_name.clone())
            .spawn(move || {
                if let Err(err) = loop_write(writer, writer_rx, languageId_clone) {
                    error!("Thread {} exited with error: {:?}", writer_thread_name, err);
                }
            })?;

        Ok(RpcClient {
            languageId,
            id: Arc::new(Mutex::new(0)),
            process_id,
            reader_tx,
            writer_tx,
        })
    }

    pub fn call<R: DeserializeOwned>(
        &self,
        method: impl AsRef<str>,
        params: impl Serialize,
    ) -> Fallible<R> {
        let method = method.as_ref();
        let id = {
            let mut id = self
                .id
                .lock()
                .map_err(|err| format_err!("Failed to lock msg id: {}", err))?;
            *id += 1;
            *id
        };
        let msg = rpc::MethodCall {
            jsonrpc: Some(rpc::Version::V2),
            id: rpc::Id::Num(id),
            method: method.to_owned(),
            params: params.to_params()?,
        };
        let (tx, rx) = bounded(1);
        self.reader_tx.send((id, tx))?;
        self.writer_tx.send(RawMessage::MethodCall(msg))?;
        // TODO: duration from config.
        match rx.recv_timeout(Duration::from_secs(60))? {
            rpc::Output::Success(ok) => Ok(serde_json::from_value(ok.result)?),
            rpc::Output::Failure(err) => bail!("Error: {:?}", err),
        }
    }

    pub fn notify(&self, method: impl AsRef<str>, params: impl Serialize) -> Fallible<()> {
        let method = method.as_ref();

        let msg = rpc::Notification {
            jsonrpc: Some(rpc::Version::V2),
            method: method.to_owned(),
            params: params.to_params()?,
        };
        self.writer_tx.send(RawMessage::Notification(msg))?;
        Ok(())
    }

    pub fn output(&self, id: Id, result: Fallible<impl Serialize>) -> Fallible<()> {
        let output = match result {
            Ok(ok) => rpc::Output::Success(rpc::Success {
                jsonrpc: Some(rpc::Version::V2),
                id: rpc::Id::Num(id),
                result: serde_json::to_value(ok)?,
            }),
            Err(err) => rpc::Output::Failure(rpc::Failure {
                jsonrpc: Some(rpc::Version::V2),
                id: rpc::Id::Num(id),
                error: err.to_rpc_error(),
            }),
        };

        self.writer_tx.send(RawMessage::Output(output))?;
        Ok(())
    }
}

fn loop_read(
    reader: impl BufRead,
    reader_rx: Receiver<(Id, Sender<rpc::Output>)>,
    sink: Sender<Call>,
    languageId: LanguageId,
) -> Fallible<()> {
    let mut pending_outputs = HashMap::new();

    // Count how many consequent empty lines.
    let mut count_empty_lines = 0;

    let mut reader = reader;
    let mut content_length = 0;
    loop {
        let mut message = String::new();
        let mut line = String::new();
        if languageId.is_some() {
            reader.read_line(&mut line)?;
            let line = line.trim();
            if line.is_empty() {
                count_empty_lines += 1;
                if count_empty_lines > 5 {
                    bail!("Unable to read from language server");
                }

                let mut buf = vec![0; content_length];
                reader.read_exact(buf.as_mut_slice())?;
                message = String::from_utf8(buf)?;
            } else {
                count_empty_lines = 0;
                if !line.starts_with("Content-Length") {
                    continue;
                }

                let tokens: Vec<&str> = line.splitn(2, ':').collect();
                let len = tokens
                    .get(1)
                    .ok_or_else(|| format_err!("Failed to get length! tokens: {:?}", tokens))?
                    .trim();
                content_length = usize::from_str(len)?;
            }
        } else if reader.read_line(&mut message)? == 0 {
            break;
        }

        let message = message.trim();
        if message.is_empty() {
            continue;
        }
        info!("<= {:?} {}", languageId, message);
        // FIXME: Remove extra `meta` property from javascript-typescript-langserver.
        let s = message.replace(r#","meta":{}"#, "");
        let message = serde_json::from_str(&s);
        if let Err(ref err) = message {
            error!(
                "Failed to deserialize output: {}\n\n Message: {}\n\nError: {:?}",
                err, s, err
            );
            continue;
        }
        // TODO: cleanup.
        let message = message.unwrap();
        match message {
            RawMessage::MethodCall(method_call) => {
                sink.send(Call::MethodCall(languageId.clone(), method_call))?;
            }
            RawMessage::Notification(notification) => {
                sink.send(Call::Notification(languageId.clone(), notification))?;
            }
            RawMessage::Output(output) => {
                while let Ok((id, tx)) = reader_rx.try_recv() {
                    pending_outputs.insert(id, tx);
                }

                if let Some(tx) = pending_outputs.remove(&output.id().to_int()?) {
                    tx.send(output)
                        .map_err(|output| format_err!("Failed to send output: {:?}", output))?;
                }
            }
        };
    }

    info!("reader-{:?} terminated", languageId);
    Ok(())
}

fn loop_write(
    writer: impl Write,
    rx: Receiver<RawMessage>,
    languageId: LanguageId,
) -> Fallible<()> {
    let mut writer = writer;

    for msg in rx.iter() {
        let s = serde_json::to_string(&msg)?;
        info!("=> {:?} {}", languageId, s);
        if languageId.is_none() {
            // Use different convention for two reasons,
            // 1. If using '\r\ncontent', nvim will receive output as `\r` + `content`, while vim
            // receives `content`.
            // 2. Without last line ending, vim output handler won't be triggered.
            write!(writer, "Content-Length: {}\n\n{}\n", s.len(), s)?;
        } else {
            write!(writer, "Content-Length: {}\r\n\r\n{}", s.len(), s)?;
        };
        writer.flush()?;
    }
    Ok(())
}
<commit_msg>refactor: Delete unusing module<commit_after>use super::*;
use crate::types::{Call, RawMessage};
use crossbeam_channel::{bounded, unbounded, Receiver, Sender};

#[derive(Clone, Serialize)]
pub struct RpcClient {
    languageId: LanguageId,
    // TODO: make atomic.
    #[serde(skip_serializing)]
    id: Arc<Mutex<Id>>,
    #[serde(skip_serializing)]
    writer_tx: Sender<RawMessage>,
    #[serde(skip_serializing)]
    reader_tx: Sender<(Id, Sender<rpc::Output>)>,
    pub process_id: Option<u32>,
}

impl RpcClient {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(
        languageId: LanguageId,
        reader: impl BufRead + Send + 'static,
        writer: impl Write + Send + 'static,
        process_id: Option<u32>,
        sink: Sender<Call>,
    ) -> Fallible<Self> {
        let (reader_tx, reader_rx): (Sender<(Id, Sender<rpc::Output>)>, _) = unbounded();

        let languageId_clone = languageId.clone();
        let reader_thread_name = format!("reader-{:?}", languageId);
        thread::Builder::new()
            .name(reader_thread_name.clone())
            .spawn(move || {
                if let Err(err) = loop_read(reader, reader_rx, sink, languageId_clone) {
                    error!("Thread {} exited with error: {:?}", reader_thread_name, err);
                }
            })?;

        let (writer_tx, writer_rx) = unbounded();
        let writer_thread_name = format!("writer-{:?}", languageId);
        let languageId_clone = languageId.clone();
        thread::Builder::new()
            .name(writer_thread_name.clone())
            .spawn(move || {
                if let Err(err) = loop_write(writer, writer_rx, languageId_clone) {
                    error!("Thread {} exited with error: {:?}", writer_thread_name, err);
                }
            })?;

        Ok(RpcClient {
            languageId,
            id: Arc::new(Mutex::new(0)),
            process_id,
            reader_tx,
            writer_tx,
        })
    }

    pub fn call<R: DeserializeOwned>(
        &self,
        method: impl AsRef<str>,
        params: impl Serialize,
    ) -> Fallible<R> {
        let method = method.as_ref();
        let id = {
            let mut id = self
                .id
                .lock()
                .map_err(|err| format_err!("Failed to lock msg id: {}", err))?;
            *id += 1;
            *id
        };
        let msg = rpc::MethodCall {
            jsonrpc: Some(rpc::Version::V2),
            id: rpc::Id::Num(id),
            method: method.to_owned(),
            params: params.to_params()?,
        };
        let (tx, rx) = bounded(1);
        self.reader_tx.send((id, tx))?;
        self.writer_tx.send(RawMessage::MethodCall(msg))?;
        // TODO: duration from config.
        match rx.recv_timeout(Duration::from_secs(60))? {
            rpc::Output::Success(ok) => Ok(serde_json::from_value(ok.result)?),
            rpc::Output::Failure(err) => bail!("Error: {:?}", err),
        }
    }

    pub fn notify(&self, method: impl AsRef<str>, params: impl Serialize) -> Fallible<()> {
        let method = method.as_ref();

        let msg = rpc::Notification {
            jsonrpc: Some(rpc::Version::V2),
            method: method.to_owned(),
            params: params.to_params()?,
        };
        self.writer_tx.send(RawMessage::Notification(msg))?;
        Ok(())
    }

    pub fn output(&self, id: Id, result: Fallible<impl Serialize>) -> Fallible<()> {
        let output = match result {
            Ok(ok) => rpc::Output::Success(rpc::Success {
                jsonrpc: Some(rpc::Version::V2),
                id: rpc::Id::Num(id),
                result: serde_json::to_value(ok)?,
            }),
            Err(err) => rpc::Output::Failure(rpc::Failure {
                jsonrpc: Some(rpc::Version::V2),
                id: rpc::Id::Num(id),
                error: err.to_rpc_error(),
            }),
        };

        self.writer_tx.send(RawMessage::Output(output))?;
        Ok(())
    }
}

fn loop_read(
    reader: impl BufRead,
    reader_rx: Receiver<(Id, Sender<rpc::Output>)>,
    sink: Sender<Call>,
    languageId: LanguageId,
) -> Fallible<()> {
    let mut pending_outputs = HashMap::new();

    // Count how many consequent empty lines.
    let mut count_empty_lines = 0;

    let mut reader = reader;
    let mut content_length = 0;
    loop {
        let mut message = String::new();
        let mut line = String::new();
        if languageId.is_some() {
            reader.read_line(&mut line)?;
            let line = line.trim();
            if line.is_empty() {
                count_empty_lines += 1;
                if count_empty_lines > 5 {
                    bail!("Unable to read from language server");
                }

                let mut buf = vec![0; content_length];
                reader.read_exact(buf.as_mut_slice())?;
                message = String::from_utf8(buf)?;
            } else {
                count_empty_lines = 0;
                if !line.starts_with("Content-Length") {
                    continue;
                }

                let tokens: Vec<&str> = line.splitn(2, ':').collect();
                let len = tokens
                    .get(1)
                    .ok_or_else(|| format_err!("Failed to get length! tokens: {:?}", tokens))?
                    .trim();
                content_length = usize::from_str(len)?;
            }
        } else if reader.read_line(&mut message)? == 0 {
            break;
        }

        let message = message.trim();
        if message.is_empty() {
            continue;
        }
        info!("<= {:?} {}", languageId, message);
        // FIXME: Remove extra `meta` property from javascript-typescript-langserver.
        let s = message.replace(r#","meta":{}"#, "");
        let message = serde_json::from_str(&s);
        if let Err(ref err) = message {
            error!(
                "Failed to deserialize output: {}\n\n Message: {}\n\nError: {:?}",
                err, s, err
            );
            continue;
        }
        // TODO: cleanup.
        let message = message.unwrap();
        match message {
            RawMessage::MethodCall(method_call) => {
                sink.send(Call::MethodCall(languageId.clone(), method_call))?;
            }
            RawMessage::Notification(notification) => {
                sink.send(Call::Notification(languageId.clone(), notification))?;
            }
            RawMessage::Output(output) => {
                while let Ok((id, tx)) = reader_rx.try_recv() {
                    pending_outputs.insert(id, tx);
                }

                if let Some(tx) = pending_outputs.remove(&output.id().to_int()?) {
                    tx.send(output)
                        .map_err(|output| format_err!("Failed to send output: {:?}", output))?;
                }
            }
        };
    }

    info!("reader-{:?} terminated", languageId);
    Ok(())
}

fn loop_write(
    writer: impl Write,
    rx: Receiver<RawMessage>,
    languageId: LanguageId,
) -> Fallible<()> {
    let mut writer = writer;

    for msg in rx.iter() {
        let s = serde_json::to_string(&msg)?;
        info!("=> {:?} {}", languageId, s);
        if languageId.is_none() {
            // Use different convention for two reasons,
            // 1. If using '\r\ncontent', nvim will receive output as `\r` + `content`, while vim
            // receives `content`.
            // 2. Without last line ending, vim output handler won't be triggered.
            write!(writer, "Content-Length: {}\n\n{}\n", s.len(), s)?;
        } else {
            write!(writer, "Content-Length: {}\r\n\r\n{}", s.len(), s)?;
        };
        writer.flush()?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use libc::{c_char};
use std::ffi::{CStr, CString};
use std::ptr;
use std::u64;
use std::slice;
use std::io::Read;
use std::fs::File;
use std::path::Path;

#[repr(C)]
pub enum GstFlowReturn {
    Ok = 0,
    NotLinked = -1,
    Flushing = -2,
    Eos = -3,
    NotNegotiated = -4,
    Error = -5,
}

#[repr(C)]
pub enum GBoolean {
    False = 0,
    True = 1,
}

impl GBoolean {
    fn from_bool(v: bool) -> GBoolean {
        match v {
            true => GBoolean::True,
            false => GBoolean::False,
        }
    }
}

#[derive(Debug)]
pub struct FileSrc {
    location: Option<String>,
    file: Option<File>,
}

impl FileSrc {
    fn new() -> FileSrc {
        FileSrc { location: None, file: None }
    }

    fn set_location(&mut self, location: &Option<String>) {
        self.location = location.clone();
    }

    fn get_location(&self) -> &Option<String> {
        &self.location
    }

    fn is_seekable(&self) -> bool {
        true
    }

    fn get_size(&self) -> u64 {
        u64::MAX
    }

    fn start(&mut self) -> bool {
        if self.location.is_none() { return false; }

        self.file = Some(File::open(Path::new(&self.location.clone().unwrap())).unwrap());

        return true;
    }

    fn stop(&mut self) -> bool {
        self.file = None;

        true
    }

    fn fill(&mut self, data: &mut [u8]) -> GstFlowReturn {
        match self.file {
            None => return GstFlowReturn::Error,
            Some(ref mut f) => {
                // FIXME: Need to return the actual size, handle EOF, etc
                f.read(data);
                return GstFlowReturn::Ok;
            },
        }
    }
}

impl Drop for FileSrc {
    fn drop(&mut self) {
        println!("drop");
    }
}

#[no_mangle]
pub extern "C" fn filesrc_new() -> *mut FileSrc {
    let instance = Box::new(FileSrc::new());
    return Box::into_raw(instance);
}

#[no_mangle]
pub extern "C" fn filesrc_drop(ptr: *mut FileSrc) {
    unsafe { Box::from_raw(ptr) };
}

#[no_mangle]
pub extern "C" fn filesrc_set_location(ptr: *mut FileSrc, location_ptr: *const c_char) {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    if location_ptr.is_null() {
        filesrc.set_location(&None)
    } else {
        let location = unsafe { CStr::from_ptr(location_ptr) };
        filesrc.set_location(&Some(String::from(location.to_str().unwrap())));
    }
}

#[no_mangle]
pub extern "C" fn filesrc_get_location(ptr: *mut FileSrc) -> *mut c_char {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    match *filesrc.get_location() {
        Some(ref location) =>
            CString::new(location.clone().into_bytes()).unwrap().into_raw(),
        None =>
            ptr::null_mut()
    }
}

#[no_mangle]
pub extern "C" fn filesrc_fill(ptr: *mut FileSrc, data_ptr: *mut u8, data_len: usize) -> GstFlowReturn {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    println!("{:?}", filesrc);
    let mut data = unsafe { slice::from_raw_parts_mut(data_ptr, data_len) };
    return filesrc.fill(data);
}

#[no_mangle]
pub extern "C" fn filesrc_get_size(ptr: *mut FileSrc) -> u64 {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    return filesrc.get_size();
}

#[no_mangle]
pub extern "C" fn filesrc_start(ptr: *mut FileSrc) -> GBoolean {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    GBoolean::from_bool(filesrc.start())
}

#[no_mangle]
pub extern "C" fn filesrc_stop(ptr: *mut FileSrc) -> GBoolean {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    GBoolean::from_bool(filesrc.stop())
}

#[no_mangle]
pub extern "C" fn filesrc_is_seekable(ptr: *mut FileSrc) -> GBoolean {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    GBoolean::from_bool(filesrc.is_seekable())
}

<commit_msg>Implement get_size()<commit_after>use libc::{c_char};
use std::ffi::{CStr, CString};
use std::ptr;
use std::u64;
use std::slice;
use std::io::Read;
use std::fs::File;
use std::path::Path;

#[repr(C)]
pub enum GstFlowReturn {
    Ok = 0,
    NotLinked = -1,
    Flushing = -2,
    Eos = -3,
    NotNegotiated = -4,
    Error = -5,
}

#[repr(C)]
pub enum GBoolean {
    False = 0,
    True = 1,
}

impl GBoolean {
    fn from_bool(v: bool) -> GBoolean {
        match v {
            true => GBoolean::True,
            false => GBoolean::False,
        }
    }
}

#[derive(Debug)]
pub struct FileSrc {
    location: Option<String>,
    file: Option<File>,
}

impl FileSrc {
    fn new() -> FileSrc {
        FileSrc { location: None, file: None }
    }

    fn set_location(&mut self, location: &Option<String>) {
        self.location = location.clone();
    }

    fn get_location(&self) -> &Option<String> {
        &self.location
    }

    fn is_seekable(&self) -> bool {
        true
    }

    fn get_size(&self) -> u64 {
        match self.file {
            None => return u64::MAX,
            Some(ref f) => {
                return f.metadata().unwrap().len();
            },
        }
    }

    fn start(&mut self) -> bool {
        if self.location.is_none() { return false; }

        self.file = Some(File::open(Path::new(&self.location.clone().unwrap())).unwrap());

        return true;
    }

    fn stop(&mut self) -> bool {
        self.file = None;

        true
    }

    fn fill(&mut self, data: &mut [u8]) -> GstFlowReturn {
        match self.file {
            None => return GstFlowReturn::Error,
            Some(ref mut f) => {
                // FIXME: Need to return the actual size, handle EOF, etc
                f.read(data);
                return GstFlowReturn::Ok;
            },
        }
    }
}

impl Drop for FileSrc {
    fn drop(&mut self) {
        println!("drop");
    }
}

#[no_mangle]
pub extern "C" fn filesrc_new() -> *mut FileSrc {
    let instance = Box::new(FileSrc::new());
    return Box::into_raw(instance);
}

#[no_mangle]
pub extern "C" fn filesrc_drop(ptr: *mut FileSrc) {
    unsafe { Box::from_raw(ptr) };
}

#[no_mangle]
pub extern "C" fn filesrc_set_location(ptr: *mut FileSrc, location_ptr: *const c_char) {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    if location_ptr.is_null() {
        filesrc.set_location(&None)
    } else {
        let location = unsafe { CStr::from_ptr(location_ptr) };
        filesrc.set_location(&Some(String::from(location.to_str().unwrap())));
    }
}

#[no_mangle]
pub extern "C" fn filesrc_get_location(ptr: *mut FileSrc) -> *mut c_char {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    match *filesrc.get_location() {
        Some(ref location) =>
            CString::new(location.clone().into_bytes()).unwrap().into_raw(),
        None =>
            ptr::null_mut()
    }
}

#[no_mangle]
pub extern "C" fn filesrc_fill(ptr: *mut FileSrc, data_ptr: *mut u8, data_len: usize) -> GstFlowReturn {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    println!("{:?}", filesrc);
    let mut data = unsafe { slice::from_raw_parts_mut(data_ptr, data_len) };
    return filesrc.fill(data);
}

#[no_mangle]
pub extern "C" fn filesrc_get_size(ptr: *mut FileSrc) -> u64 {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    return filesrc.get_size();
}

#[no_mangle]
pub extern "C" fn filesrc_start(ptr: *mut FileSrc) -> GBoolean {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    GBoolean::from_bool(filesrc.start())
}

#[no_mangle]
pub extern "C" fn filesrc_stop(ptr: *mut FileSrc) -> GBoolean {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    GBoolean::from_bool(filesrc.stop())
}

#[no_mangle]
pub extern "C" fn filesrc_is_seekable(ptr: *mut FileSrc) -> GBoolean {
    let filesrc: &mut FileSrc = unsafe { &mut *ptr };

    GBoolean::from_bool(filesrc.is_seekable())
}

<|endoftext|>


<commit_before>extern crate regex;

mod matching;
mod support;
mod tokens;
mod lexer;
mod files;
mod parser;
mod symbols;
mod symbol_table;

use std::vec::Vec;
use tokens::*;
use lexer::*;
use parser::*;
use symbols::*;
use symbol_table::*;

fn main()
{
    let source_file = "sampleData/BasicArithmatic.java";
    let lex_output_file = "sampleData/output/stable/BasicArithmatic.java.lexed";
    let preprocessed_output_file = "sampleData/output/stable/BasicArithmatic.java.preprocessed";

    let mut tokens: Vec<Token> = lex_file(source_file);

    println!("\n\nFound Tokens:");
    tokens.print_to(lex_output_file, true);

    remove_meta(&mut tokens);
    println!("\n\nPerged Tokens:");
    tokens.print_to(preprocessed_output_file, true);

    let mut symbols_table = SymbolTable::new();
    parse_class(&tokens, 1, &mut symbols_table);

    if tokens[0].value != "class"
    {
        panic!("Unexpected token: {}: {}", tokens[0].value, tokens[0].name);
    }

    let (last_index, asm_string) = compile_class(&tokens, 1);
}

fn parse_class(tokens: &Vec<Token>, start_index: usize, symbols_table: &mut SymbolTable)
{
    let mut static_variables: Vec<(usize, usize)> = Vec::new();
    let mut static_methods: Vec<(usize, usize)> = Vec::new();
    let mut static_classes: Vec<(usize, usize)> = Vec::new();
    let mut non_static_variables: Vec<(usize, usize)> = Vec::new();
    let mut non_static_methods: Vec<(usize, usize)> = Vec::new();
    let mut non_static_classes: Vec<(usize, usize)> = Vec::new();

    println!("\n<------------ Parse Class --------------->");
    let mut min_value = 0;
    let mut skip_amount = 0;

    for (index, token) in tokens.iter().enumerate()
    {
        if min_value != 0
        {
            min_value -= 1;
            continue;
        }
        //Static Variable/Method/Class
        if token.name == "mod.access"
        {
            skip_amount = 3;
            if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Static Variable Decl!");

                let (low, high) = symbols_table.get_variable_locations(tokens, index + 1);
                symbols_table.static_variables.push((low, high));

                min_value =  low;
                min_value -= index;
            }
            else if tokens[index + 1].name.starts_with("construct")
            {
                if tokens[index + skip_amount].name.starts_with("control")
                {
                    println!("------Incoming Static Class Decl!");
                    min_value = find_outer_ending_brace(tokens, index);
                    static_classes.push((index + 2, min_value));
                    min_value -= index;
                }
                else
                {
                    panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
                }

            }
            else if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                static_methods.push((index + 1, min_value));
                min_value -=  index;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }

        }
        //Non-Static Variable/Method/Class
        else if token.name.starts_with("construct")
        {
            skip_amount = 2;
            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Class Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_classes.push((index + 1, min_value));
                min_value = 0;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }
        }
        //Non-Static Variable/Method/Class
        else if token.name == "type"
        {
            skip_amount = 2;

            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_methods.push((index, min_value));
                min_value -= index;
                //check for control
            }
            else if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Non-Static Variable Decl!");
                min_value =  find_next_semicolon(tokens, index);
                non_static_variables.push((index, min_value));
                min_value -= index;
            }
        }
        println!("\tIndex: {} | Token -> {} : {}", index, token.value, token.name );
        //deal with parameters
    }
    println!("\n<---------------- Static Variables --------------->");
    for &(start, end) in symbols_table.static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Classes ----------------->");
    for &(start, end) in static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Methods ----------------->");
    for &(start, end) in static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Variables --------------->");
    for &(start, end) in non_static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Classes ----------------->");
    for &(start, end) in non_static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Methods ----------------->");
    for &(start, end) in non_static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }
    println!("\n");
}

fn compile_class(tokens: &Vec<Token>, start_index: usize) -> (usize, String)
{
    let invalid_types = get_invalid_token_types();
    let invalid_values = get_invalid_token_values();

    let mut plp_string: String = String::new();
    let mut current_index: usize = start_index;

    if tokens[current_index].value != "{" { panic!("Expected '{{' received: {}", tokens[current_index].value); }
    else { current_index += 1; }

    for (index, token) in tokens.iter().enumerate()
    {
        // Handle forward skipping
        if index < current_index { continue; }
        else { current_index = index; }

        // TODO: encapsulate into token_rules.validate(token)
        // Panic! if token type is invalid
        if invalid_types.contains(&token.name)
        {
            panic!("Unsupported token type: {}", token.name);
        }
        // Panic! if token value is invalid
        else if invalid_values.contains(&&*token.value)
        {
            panic!("Unsupported token value: {}", token.value);
        }

        if token.value == "class"
        {
            // parse class body
            let (end_index, compiled_class) = compile_class(&tokens, index + 1);
            plp_string.push_str(&*compiled_class);

            current_index = end_index;
        }
        else if token.name == "type" // || token.name == "identifier"
        {
            // look ahead
            // parse variable declaration
            // OR parse method declaration
        }
        else if token.value == "if"
        {
            // parse if body
            // Unsupported for now
        }
        else if token.name == "construct.conditional"
        {
            // parse conditional
            // Unsupported for now
        }
        else
        {
            panic!("Unexpected token: {}\t{}", token.name, token.value);
        }
    }

    (current_index + 1, plp_string)
}

/// Removes all meta tokens from the give Vector
/// Meta tokens include:
/// * comments
/// * imports
/// * permission modifiers (public, private, protected)
///
/// Note that in future versions, imports will not be removed.
fn remove_meta(tokens: &mut Vec<Token>)
{
    // Indecies of tokens vector to be removed
    let mut invalid_indecies: Vec<usize> = Vec::new();

    let mut min_index: usize = 0;
    for (index, token) in tokens.iter().enumerate()
    {
        // Allow forward skipping (by setting min_index)
        if index < min_index
        {
            invalid_indecies.push(index);
        }
        // Remove imports
        else if token.name == "special.import"
        {
            invalid_indecies.push(index);
            min_index = index + 2; // skip the next token (semi-colon)
        }
        // Remove comments
        else if token.name.starts_with("comment")
        {
            invalid_indecies.push(index);
        }
        // Remove all permission modifiers (public, private, protected)
        else if token.name == "mod.permission"
        {
            invalid_indecies.push(index);
        }
    }

    // count := how many indecies have already been removed
    // index := the index in the original tokens vector that should be removed
    for (count, index) in invalid_indecies.iter().enumerate()
    {
        // "index" refers to the index before any others were removed. Therefore, it must be offset by the number of removed tokens
        tokens.remove(index - count);
    }
}

fn compile_arithmetic_statement(tokens: &Vec<Token>,
                                start: usize,
                                temp_registers: (&str, &str),
                                var_registers: (&str, &str),
                                load_registers: (&str, &str),
                                symbols: &StaticSymbolTable)
                                -> (String, usize)
{
    let mut compiled_code = String::new();
    if tokens[start].value.starts_with("literal")
    {
        compiled_code.push_str("li ");
        compiled_code.push_str(temp_registers.0);
        compiled_code.push_str(", ");
        compiled_code.push_str(&*tokens[start].value);
        compiled_code.push_str("\n");
    }

    let mut index = start;
    while tokens[index].value != ";"
    {
        if tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
            continue;
        }
        if !tokens[index].name.starts_with("operator")
        {
            let current_token = &tokens[index];
            panic!("Unexpected token while parsing arithmetic statement: {}: {}", current_token.value, current_token.name);
        }

        let operator = &tokens[index];
        index += 1;
        while tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
        }
        let operand = &tokens[index];
        index += 1;

        if operand.name.starts_with("literal")
        {
            // TODO: use immediate operators
            compiled_code.push_str("li ");
            compiled_code.push_str(temp_registers.1);
            compiled_code.push_str(", ");
            compiled_code.push_str(&*operand.value);
            compiled_code.push_str("\n");
        }
        else if operand.name.starts_with("identifier")
        {
            let next_token = &tokens[index];
            index += 1;

            if next_token.name == "control"
            {
                // Method call
            }
            else if next_token.value == "."
            {
                // Accessor
            }

            // TODO: lookup memory location from symbols table

            // TODO: parse method calls
        }
        else
        {
            panic!("");
        }

        let line = compile_arithmetic_operation(operator, temp_registers, temp_registers.0);
        compiled_code.push_str(&*line);
    }

    (compiled_code, index + 1)
}

fn compile_arithmetic_operation(operator: &Token, operand_registers: (&str, &str), result_register: &str) -> String
{
    let mut compiled_code = String::new();

    if operator.name != "operator.binary"
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }
    else if operator.value == "+"
    {
        compiled_code.push_str("addu ");
    }
    else if operator.value == "-"
    {
        compiled_code.push_str("subu ");
    }
    else if operator.value == "*"
    {
        compiled_code.push_str("mullo ");
    }
    else
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }

    compiled_code.push_str(result_register);
    compiled_code.push_str(", ");
    compiled_code.push_str(operand_registers.0);
    compiled_code.push_str(", ");
    compiled_code.push_str(operand_registers.1);
    compiled_code.push_str("\n");

    compiled_code
}

/// @deprecated
/// Replaced by find_next using ";" as the symbol argument
fn find_next_semicolon(tokens: &Vec<Token>, start: usize) -> usize
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == ";" { return index + start; }
    }

    return 0;
}

/// @deprecated
/// Replaced by identify_body_bounds using ("{", "}") as the symbol argument
fn find_outer_ending_brace(tokens: &Vec<Token>, start: usize) -> usize
{
    let mut open_braces = 0;

    for(index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == "{"
        {
            open_braces += 1;
        }
        else if token.value == "}"
        {
            open_braces -= 1;
            if open_braces == 0
            {
                return index + start;
            }
        }
    }

    return 0;
}

/// Identifies the end index of a nestable body given the open and close symbols for the body
/// For instance, an arithmetic expression can have nested parenthesis groups (e.g. (1 + (2*(2+1))) * (2 + 5) )
/// In the example, this method would identify the start and stop as these (> and < respectively):
///     >(1 + (2*(2+1)))< * (2 + 5)
/// If the start_index was specified as the parenthesis surrounding 2 + 5, then this method would select:
///     (1 + (2*(2+1))) * >(2 + 5)<
///
/// The start index passed to this method should be the index AFTER the first open symbol
/// @return the index of the token that closes the specified body or None if the block is not closed or nested properly
fn identify_body_bounds(tokens: &Vec<Token>, start: usize, symbols: (&str, &str)) -> Option<usize>
{
    let (open, close) = symbols;
    let mut open_braces = 1;

    for(index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == open
        {
            open_braces += 1;
        }
        else if token.value == close
        {
            open_braces -= 1;
            if open_braces == 0
            {
                return Some(index + start);
            }
        }
    }

    return None;
}

/// Identifies the index of the next token matching the specified symbol
/// @return the index of the specified token (after the specified start index) or None if no such symbol exists
fn find_next(tokens: &Vec<Token>, start: usize, symbol: &str) -> Option<usize>
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == symbol { return Some(index + start); }
    }

    return None;
}
<commit_msg>Fix arithmetic parsing<commit_after>extern crate regex;

mod matching;
mod support;
mod tokens;
mod lexer;
mod files;
mod parser;
mod symbols;
mod symbol_table;

use std::vec::Vec;
use tokens::*;
use lexer::*;
use parser::*;
use symbols::*;
use symbol_table::*;

fn main()
{
    let source_file = "sampleData/BasicArithmatic.java";
    let lex_output_file = "sampleData/output/stable/BasicArithmatic.java.lexed";
    let preprocessed_output_file = "sampleData/output/stable/BasicArithmatic.java.preprocessed";

    let mut tokens: Vec<Token> = lex_file(source_file);

    println!("\n\nFound Tokens:");
    tokens.print_to(lex_output_file, true);

    remove_meta(&mut tokens);
    println!("\n\nPerged Tokens:");
    tokens.print_to(preprocessed_output_file, true);

    let mut symbols_table = SymbolTable::new();
    parse_class(&tokens, 1, &mut symbols_table);

    if tokens[0].value != "class"
    {
        panic!("Unexpected token: {}: {}", tokens[0].value, tokens[0].name);
    }

    let (last_index, asm_string) = compile_class(&tokens, 1);
}

fn parse_class(tokens: &Vec<Token>, start_index: usize, symbols_table: &mut SymbolTable)
{
    let mut static_variables: Vec<(usize, usize)> = Vec::new();
    let mut static_methods: Vec<(usize, usize)> = Vec::new();
    let mut static_classes: Vec<(usize, usize)> = Vec::new();
    let mut non_static_variables: Vec<(usize, usize)> = Vec::new();
    let mut non_static_methods: Vec<(usize, usize)> = Vec::new();
    let mut non_static_classes: Vec<(usize, usize)> = Vec::new();

    println!("\n<------------ Parse Class --------------->");
    let mut min_value = 0;
    let mut skip_amount = 0;

    for (index, token) in tokens.iter().enumerate()
    {
        if min_value != 0
        {
            min_value -= 1;
            continue;
        }
        //Static Variable/Method/Class
        if token.name == "mod.access"
        {
            skip_amount = 3;
            if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Static Variable Decl!");

                let (low, high) = symbols_table.get_variable_locations(tokens, index + 1);
                symbols_table.static_variables.push((low, high));

                min_value =  low;
                min_value -= index;
            }
            else if tokens[index + 1].name.starts_with("construct")
            {
                if tokens[index + skip_amount].name.starts_with("control")
                {
                    println!("------Incoming Static Class Decl!");
                    min_value = find_outer_ending_brace(tokens, index);
                    static_classes.push((index + 2, min_value));
                    min_value -= index;
                }
                else
                {
                    panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
                }

            }
            else if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                static_methods.push((index + 1, min_value));
                min_value -=  index;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }

        }
        //Non-Static Variable/Method/Class
        else if token.name.starts_with("construct")
        {
            skip_amount = 2;
            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Class Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_classes.push((index + 1, min_value));
                min_value = 0;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }
        }
        //Non-Static Variable/Method/Class
        else if token.name == "type"
        {
            skip_amount = 2;

            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_methods.push((index, min_value));
                min_value -= index;
                //check for control
            }
            else if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Non-Static Variable Decl!");
                min_value =  find_next_semicolon(tokens, index);
                non_static_variables.push((index, min_value));
                min_value -= index;
            }
        }
        println!("\tIndex: {} | Token -> {} : {}", index, token.value, token.name );
        //deal with parameters
    }
    println!("\n<---------------- Static Variables --------------->");
    for &(start, end) in symbols_table.static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Classes ----------------->");
    for &(start, end) in static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Methods ----------------->");
    for &(start, end) in static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Variables --------------->");
    for &(start, end) in non_static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Classes ----------------->");
    for &(start, end) in non_static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Methods ----------------->");
    for &(start, end) in non_static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }
    println!("\n");
}

fn compile_class(tokens: &Vec<Token>, start_index: usize) -> (usize, String)
{
    let invalid_types = get_invalid_token_types();
    let invalid_values = get_invalid_token_values();

    let mut plp_string: String = String::new();
    let mut current_index: usize = start_index;

    if tokens[current_index].value != "{" { panic!("Expected '{{' received: {}", tokens[current_index].value); }
    else { current_index += 1; }

    for (index, token) in tokens.iter().enumerate()
    {
        // Handle forward skipping
        if index < current_index { continue; }
        else { current_index = index; }

        // TODO: encapsulate into token_rules.validate(token)
        // Panic! if token type is invalid
        if invalid_types.contains(&token.name)
        {
            panic!("Unsupported token type: {}", token.name);
        }
        // Panic! if token value is invalid
        else if invalid_values.contains(&&*token.value)
        {
            panic!("Unsupported token value: {}", token.value);
        }

        if token.value == "class"
        {
            // parse class body
            let (end_index, compiled_class) = compile_class(&tokens, index + 1);
            plp_string.push_str(&*compiled_class);

            current_index = end_index;
        }
        else if token.name == "type" // || token.name == "identifier"
        {
            // look ahead
            // parse variable declaration
            // OR parse method declaration
        }
        else if token.value == "if"
        {
            // parse if body
            // Unsupported for now
        }
        else if token.name == "construct.conditional"
        {
            // parse conditional
            // Unsupported for now
        }
        else
        {
            panic!("Unexpected token: {}\t{}", token.name, token.value);
        }
    }

    (current_index + 1, plp_string)
}

/// Removes all meta tokens from the give Vector
/// Meta tokens include:
/// * comments
/// * imports
/// * permission modifiers (public, private, protected)
///
/// Note that in future versions, imports will not be removed.
fn remove_meta(tokens: &mut Vec<Token>)
{
    // Indecies of tokens vector to be removed
    let mut invalid_indecies: Vec<usize> = Vec::new();

    let mut min_index: usize = 0;
    for (index, token) in tokens.iter().enumerate()
    {
        // Allow forward skipping (by setting min_index)
        if index < min_index
        {
            invalid_indecies.push(index);
        }
        // Remove imports
        else if token.name == "special.import"
        {
            invalid_indecies.push(index);
            min_index = index + 2; // skip the next token (semi-colon)
        }
        // Remove comments
        else if token.name.starts_with("comment")
        {
            invalid_indecies.push(index);
        }
        // Remove all permission modifiers (public, private, protected)
        else if token.name == "mod.permission"
        {
            invalid_indecies.push(index);
        }
    }

    // count := how many indecies have already been removed
    // index := the index in the original tokens vector that should be removed
    for (count, index) in invalid_indecies.iter().enumerate()
    {
        // "index" refers to the index before any others were removed. Therefore, it must be offset by the number of removed tokens
        tokens.remove(index - count);
    }
}

fn compile_arithmetic_statement(tokens: &Vec<Token>,
                                start: usize,
                                temp_registers: (&str, &str),
                                var_registers: (&str, &str),
                                load_registers: (&str, &str),
                                symbols: &StaticSymbolTable)
                                -> (String, usize)
{
    let mut compiled_code = String::new();
    if tokens[start].value.starts_with("literal")
    {
        compiled_code.push_str("li ");
        compiled_code.push_str(temp_registers.0);
        compiled_code.push_str(", ");
        compiled_code.push_str(&*tokens[start].value);
        compiled_code.push_str("\n");
    }

    let mut index = start;
    while tokens[index].value != ";"
    {
        if tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
            continue;
        }
        if !tokens[index].name.starts_with("operator")
        {
            let current_token = &tokens[index];
            panic!("Unexpected token while parsing arithmetic statement: {}: {}", current_token.value, current_token.name);
        }

        let operator = &tokens[index];
        index += 1;
        while tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
        }
        let operand = &tokens[index];
        index += 1;

        if operand.name.starts_with("literal")
        {
            // TODO: use immediate operators
            compiled_code.push_str("li ");
            compiled_code.push_str(temp_registers.1);
            compiled_code.push_str(", ");
            compiled_code.push_str(&*operand.value);
            compiled_code.push_str("\n");
        }
        else if operand.name.starts_with("identifier")
        {
            let following_token = &tokens[index];

            if following_token.name == "control"
            {
                // Method call
                let method_call_end = identify_body_bounds(&tokens, index + 1, ("(", ")")).unwrap();
                panic!("Method calls are unsupported");

            }
            else if following_token.value == "."
            {
                // Accessor
                panic!("Unsupported token: {}: {}", following_token.name, following_token.value);
            }
            else
            {
                // Variable
            }

            // TODO: lookup memory location from symbols table

            // TODO: parse method calls
        }
        else
        {
            panic!("");
        }

        let line = compile_arithmetic_operation(operator, temp_registers, temp_registers.0);
        compiled_code.push_str(&*line);
    }

    (compiled_code, index + 1)
}

fn compile_arithmetic_operation(operator: &Token, operand_registers: (&str, &str), result_register: &str) -> String
{
    let mut compiled_code = String::new();

    if operator.name != "operator.binary"
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }
    else if operator.value == "+"
    {
        compiled_code.push_str("addu ");
    }
    else if operator.value == "-"
    {
        compiled_code.push_str("subu ");
    }
    else if operator.value == "*"
    {
        compiled_code.push_str("mullo ");
    }
    else
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }

    compiled_code.push_str(result_register);
    compiled_code.push_str(", ");
    compiled_code.push_str(operand_registers.0);
    compiled_code.push_str(", ");
    compiled_code.push_str(operand_registers.1);
    compiled_code.push_str("\n");

    compiled_code
}

/// @deprecated
/// Replaced by find_next using ";" as the symbol argument
fn find_next_semicolon(tokens: &Vec<Token>, start: usize) -> usize
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == ";" { return index + start; }
    }

    return 0;
}

/// @deprecated
/// Replaced by identify_body_bounds using ("{", "}") as the symbol argument
fn find_outer_ending_brace(tokens: &Vec<Token>, start: usize) -> usize
{
    let mut open_braces = 0;

    for(index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == "{"
        {
            open_braces += 1;
        }
        else if token.value == "}"
        {
            open_braces -= 1;
            if open_braces == 0
            {
                return index + start;
            }
        }
    }

    return 0;
}

/// Identifies the end index of a nestable body given the open and close symbols for the body
/// For instance, an arithmetic expression can have nested parenthesis groups (e.g. (1 + (2*(2+1))) * (2 + 5) )
/// In the example, this method would identify the start and stop as these (> and < respectively):
///     >(1 + (2*(2+1)))< * (2 + 5)
/// If the start_index was specified as the parenthesis surrounding 2 + 5, then this method would select:
///     (1 + (2*(2+1))) * >(2 + 5)<
///
/// The start index passed to this method should be the index AFTER the first open symbol
/// @return the index of the token that closes the specified body or None if the block is not closed or nested properly
fn identify_body_bounds(tokens: &Vec<Token>, start: usize, symbols: (&str, &str)) -> Option<usize>
{
    let (open, close) = symbols;
    let mut open_braces = 1;

    for(index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == open
        {
            open_braces += 1;
        }
        else if token.value == close
        {
            open_braces -= 1;
            if open_braces == 0
            {
                return Some(index + start);
            }
        }
    }

    return None;
}

/// Identifies the index of the next token matching the specified symbol
/// @return the index of the specified token (after the specified start index) or None if no such symbol exists
fn find_next(tokens: &Vec<Token>, start: usize, symbol: &str) -> Option<usize>
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == symbol { return Some(index + start); }
    }

    return None;
}
<|endoftext|>


<commit_before>use autograd::{Function, ExecutionEngine, FuncId};
use tensor::Tensor;
use std::ops::{AddAssign, Index};
use std::collections::VecDeque;
use std::marker::PhantomData;
use tensor::*;
use num::Integer;
use ::*;

thread_local! {
    pub static VAR_TABLE_F32: RefCell<VecDeque<VariableImpl<f32>>> = RefCell::new(VecDeque::new());
    pub static VAR_TABLE_I64: RefCell<VecDeque<VariableImpl<i64>>> = RefCell::new(VecDeque::new());
}

pub type VarList<T> = Vec<Variable<T>>;
pub type VarKindList = Vec<VariableKind>;
pub type RefVarKindList<'a> = Vec<&'a VariableKind>;
pub type VarId = i32;
pub enum VariableKind {
    FloatVariable(Variable<f32>),
    LongVariable(Variable<i64>),
}

impl<T> From<Variable<T>> for VariableKind {
    default fn from(input: Variable<T>) -> Self {
        panic!("bad cast")
    }
}

impl From<Variable<f32>> for VariableKind {
    fn from(input: Variable<f32>) -> Self {
        VariableKind::FloatVariable(input)
    }
}
impl From<Variable<i64>> for VariableKind {
    fn from(input: Variable<i64>) -> Self {
        VariableKind::LongVariable(input)
    }
}

impl<T> From<VariableKind> for Variable<T> {
    default fn from(input: VariableKind) -> Self {
        panic!("bad cast");
    }
}

impl From<VariableKind> for Variable<f32> {
    fn from(input: VariableKind) -> Self {
        if let VariableKind::FloatVariable(v) = input {
            v
        } else {
            panic!("bad cast")
        }
    }
}

impl From<VariableKind> for Variable<i64> {
    fn from(input: VariableKind) -> Self {
        if let VariableKind::LongVariable(v) = input {
            v
        } else {
            panic!("bad cast")
        }
    }
}

impl Clone for VariableKind {
    fn clone(&self) -> Self {
        use self::VariableKind::{FloatVariable, LongVariable};
        match *self {
            FloatVariable(ref v) => FloatVariable(v.clone()),
            LongVariable(ref v) => LongVariable(v.clone()),
            _ => unimplemented!(),
        }
    }
}

pub trait VarAccess<T> {
    fn access(&self) -> &mut VariableImpl<T>;
    fn borrow(&self) -> &VariableImpl<T>;
    fn new_args(data: Tensor<T>, args: &VariableArgs) -> Self;
}

impl<T> VarAccess<T> for Variable<T> {
    default fn access(&self) -> &mut VariableImpl<T> {
        panic!("unsupported Tensor type")
    }
    default fn borrow(&self) -> &VariableImpl<T> {
        panic!("unsupported Tensor type")
    }
    default fn new_args(data: Tensor<T>, args: &VariableArgs) -> Self {
        panic!("unsupported Tensor type")
    }
}

impl VarAccess<f32> for Variable<f32> {
    fn access(&self) -> &mut VariableImpl<f32> {
        let vecp = VAR_TABLE_F32.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &mut vec[self.id as usize]
    }
    fn borrow(&self) -> &VariableImpl<f32> {
        let vecp = VAR_TABLE_F32.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &vec[self.id as usize]
    }
    fn new_args(data: Tensor<f32>, args: &VariableArgs) -> Self {
        let mut id = ::std::usize::MAX;
        let value = VariableImpl::new(data, args);

        VAR_TABLE_F32.with(|f| {
                               let mut table = f.borrow_mut();
                               id = table.len();
                               table.push_back(value);
                           });
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}

impl VarAccess<i64> for Variable<i64> {
    fn access(&self) -> &mut VariableImpl<i64> {
        let vecp = VAR_TABLE_I64.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &mut vec[self.id as usize]
    }
    fn borrow(&self) -> &VariableImpl<i64> {
        let vecp = VAR_TABLE_I64.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &vec[self.id as usize]
    }
    fn new_args(data: Tensor<i64>, args: &VariableArgs) -> Self {
        let mut id = ::std::usize::MAX;
        let value = VariableImpl::new(data, args);

        VAR_TABLE_I64.with(|f| {
                               let mut table = f.borrow_mut();
                               id = table.len();
                               table.push_back(value);
                           });
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}

pub struct VariableImpl<T> {
    pub data: Tensor<T>,
    // AKA Creator Id
    grad_fn: Option<Function>,
    grad: Option<VarId>,
    // version_counter etc ...
    dirty: bool,
    volatile: bool,
    requires_grad: bool,
}

impl<T> VariableImpl<T> {
    fn new(data_: Tensor<T>, args: &VariableArgs) -> Self {
        let creator = match args.creator {
            Some(ref f) => Some(f.clone()),
            None => None,
        };
        VariableImpl {
            data: data_,
            grad_fn: creator,
            grad: None,
            dirty: false,
            volatile: args.volatile,
            requires_grad: args.requires_grad,
        }
    }
}

pub struct Variable<T> {
    pub id: VarId,
    phantom: PhantomData<T>,
}

impl<T> Default for Variable<T> {
    fn default() -> Self {
        Variable {
            id: -1,
            phantom: PhantomData,
        }
    }
}
impl<T> Clone for Variable<T> {
    fn clone(&self) -> Self {
        Variable {
            id: self.id,
            phantom: PhantomData,
        }
    }
}
impl<T> From<u32> for Variable<T> {
    fn from(id: u32) -> Self {
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}
impl<T> From<i32> for Variable<T> {
    fn from(id: i32) -> Self {
        Variable {
            id: id,
            phantom: PhantomData,
        }
    }
}
impl<'a, T: 'a> From<&'a i32> for Variable<T> {
    fn from(id: &'a i32) -> Self {
        Variable {
            id: *id,
            phantom: PhantomData,
        }
    }
}
impl<T> From<usize> for Variable<T> {
    fn from(id: usize) -> Self {
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}

#[derive(Default, Clone)]
pub struct BackwardArgs {}

#[derive(Builder)]
#[builder(pattern="owned")]
pub struct VariableArgs {
    pub creator: Option<Function>,
    pub volatile: bool,
    pub requires_grad: bool,
}

impl Default for VariableArgs {
    fn default() -> Self {
        VariableArgs {
            creator: None,
            volatile: false,
            requires_grad: true,
        }
    }
}
impl VariableArgs {
    pub fn build() -> VariableArgsBuilder {
        VariableArgsBuilder::default()
    }
}
impl VariableArgsBuilder {
    pub fn done(self) -> VariableArgs {
        self.build().unwrap()
    }
}

impl<T> Variable<T> {
    pub fn new(data: Tensor<T>) -> Self {
        Variable::new_args(data, &VariableArgs::default())
    }
    pub fn is_volatile(&self) -> bool {
        self.access().volatile
    }
    pub fn requires_grad(&self) -> bool {
        self.access().requires_grad
    }
    pub fn grad_fn(&self) -> Option<Function> {
        match self.access().grad_fn {
            Some(ref func) => Some(func.clone()),
            None => None,
        }
    }
    pub fn data(&mut self) -> &mut Tensor<T> {
        &mut self.access().data
    }
    pub fn data_borrow(&self) -> &Tensor<T> {
        &self.borrow().data
    }
    pub fn apply(&mut self, callback: fn(&mut Tensor<T>)) {
        callback(&mut self.access().data);
    }
    pub fn mark_dirty(&mut self) {
        self.access().dirty = true;
    }
    pub fn requires_nograd(&mut self) {
        self.access().requires_grad = false;
    }
    pub fn view(&self, dims: &[i32]) -> Self {
        unimplemented!()
    }
    // Computes the gradient of current variable w.r.t. graph leaves
    pub fn backward_args(&mut self, gradient_: Option<&mut Tensor<T>>, retain_variables: bool) {
        let mut store;
        if self.access().volatile {
            panic!("calling backward on a volatile variable")
        }
        if !self.access().requires_grad {
            panic!("calling backward on a variable that doesn't require a gradient")
        }
        let mut gradient = match gradient_ {
            Some(gradient) => gradient,
            None => {
                store = self.access().data.new_(1);
                &mut store
            }
        };
        ExecutionEngine::run_backward(self, &mut gradient, retain_variables)
    }
    pub fn backward(&mut self) {
        self.backward_args(None, false)
    }
    // Detach from graph
    pub fn detach_(&mut self) {
        unimplemented!()
    }
    // return a new variable detached from graph
    pub fn detach(&self) -> Variable<T> {
        unimplemented!()
    }
}

impl<T: Copy> Index<isize> for Variable<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

impl AddAssign<Variable<f32>> for f32 {
    fn add_assign(&mut self, rhs: Variable<f32>) {
        *self = *self + rhs[0]
    }
}
<commit_msg>eliminate duplicate access calls<commit_after>use autograd::{Function, ExecutionEngine, FuncId};
use tensor::Tensor;
use std::ops::{AddAssign, Index};
use std::collections::VecDeque;
use std::marker::PhantomData;
use tensor::*;
use num::Integer;
use ::*;

thread_local! {
    pub static VAR_TABLE_F32: RefCell<VecDeque<VariableImpl<f32>>> = RefCell::new(VecDeque::new());
    pub static VAR_TABLE_I64: RefCell<VecDeque<VariableImpl<i64>>> = RefCell::new(VecDeque::new());
}

pub type VarList<T> = Vec<Variable<T>>;
pub type VarKindList = Vec<VariableKind>;
pub type RefVarKindList<'a> = Vec<&'a VariableKind>;
pub type VarId = i32;
pub enum VariableKind {
    FloatVariable(Variable<f32>),
    LongVariable(Variable<i64>),
}

impl<T> From<Variable<T>> for VariableKind {
    default fn from(input: Variable<T>) -> Self {
        panic!("bad cast")
    }
}

impl From<Variable<f32>> for VariableKind {
    fn from(input: Variable<f32>) -> Self {
        VariableKind::FloatVariable(input)
    }
}
impl From<Variable<i64>> for VariableKind {
    fn from(input: Variable<i64>) -> Self {
        VariableKind::LongVariable(input)
    }
}

impl<T> From<VariableKind> for Variable<T> {
    default fn from(input: VariableKind) -> Self {
        panic!("bad cast");
    }
}

impl From<VariableKind> for Variable<f32> {
    fn from(input: VariableKind) -> Self {
        if let VariableKind::FloatVariable(v) = input {
            v
        } else {
            panic!("bad cast")
        }
    }
}

impl From<VariableKind> for Variable<i64> {
    fn from(input: VariableKind) -> Self {
        if let VariableKind::LongVariable(v) = input {
            v
        } else {
            panic!("bad cast")
        }
    }
}

impl Clone for VariableKind {
    fn clone(&self) -> Self {
        use self::VariableKind::{FloatVariable, LongVariable};
        match *self {
            FloatVariable(ref v) => FloatVariable(v.clone()),
            LongVariable(ref v) => LongVariable(v.clone()),
            _ => unimplemented!(),
        }
    }
}

pub trait VarAccess<T> {
    fn access(&self) -> &mut VariableImpl<T>;
    fn borrow(&self) -> &VariableImpl<T>;
    fn new_args(data: Tensor<T>, args: &VariableArgs) -> Self;
}

impl<T> VarAccess<T> for Variable<T> {
    default fn access(&self) -> &mut VariableImpl<T> {
        panic!("unsupported Tensor type")
    }
    default fn borrow(&self) -> &VariableImpl<T> {
        panic!("unsupported Tensor type")
    }
    default fn new_args(data: Tensor<T>, args: &VariableArgs) -> Self {
        panic!("unsupported Tensor type")
    }
}

impl VarAccess<f32> for Variable<f32> {
    fn access(&self) -> &mut VariableImpl<f32> {
        let vecp = VAR_TABLE_F32.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &mut vec[self.id as usize]
    }
    fn borrow(&self) -> &VariableImpl<f32> {
        let vecp = VAR_TABLE_F32.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &vec[self.id as usize]
    }
    fn new_args(data: Tensor<f32>, args: &VariableArgs) -> Self {
        let mut id = ::std::usize::MAX;
        let value = VariableImpl::new(data, args);

        VAR_TABLE_F32.with(|f| {
                               let mut table = f.borrow_mut();
                               id = table.len();
                               table.push_back(value);
                           });
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}

impl VarAccess<i64> for Variable<i64> {
    fn access(&self) -> &mut VariableImpl<i64> {
        let vecp = VAR_TABLE_I64.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &mut vec[self.id as usize]
    }
    fn borrow(&self) -> &VariableImpl<i64> {
        let vecp = VAR_TABLE_I64.with(|f| f.as_ptr());
        let vec = unsafe { &mut *vecp };
        &vec[self.id as usize]
    }
    fn new_args(data: Tensor<i64>, args: &VariableArgs) -> Self {
        let mut id = ::std::usize::MAX;
        let value = VariableImpl::new(data, args);

        VAR_TABLE_I64.with(|f| {
                               let mut table = f.borrow_mut();
                               id = table.len();
                               table.push_back(value);
                           });
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}

pub struct VariableImpl<T> {
    pub data: Tensor<T>,
    // AKA Creator Id
    grad_fn: Option<Function>,
    grad: Option<VarId>,
    // version_counter etc ...
    dirty: bool,
    volatile: bool,
    requires_grad: bool,
}

impl<T> VariableImpl<T> {
    fn new(data_: Tensor<T>, args: &VariableArgs) -> Self {
        let creator = match args.creator {
            Some(ref f) => Some(f.clone()),
            None => None,
        };
        VariableImpl {
            data: data_,
            grad_fn: creator,
            grad: None,
            dirty: false,
            volatile: args.volatile,
            requires_grad: args.requires_grad,
        }
    }
}

pub struct Variable<T> {
    pub id: VarId,
    phantom: PhantomData<T>,
}

impl<T> Default for Variable<T> {
    fn default() -> Self {
        Variable {
            id: -1,
            phantom: PhantomData,
        }
    }
}
impl<T> Clone for Variable<T> {
    fn clone(&self) -> Self {
        Variable {
            id: self.id,
            phantom: PhantomData,
        }
    }
}
impl<T> From<u32> for Variable<T> {
    fn from(id: u32) -> Self {
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}
impl<T> From<i32> for Variable<T> {
    fn from(id: i32) -> Self {
        Variable {
            id: id,
            phantom: PhantomData,
        }
    }
}
impl<'a, T: 'a> From<&'a i32> for Variable<T> {
    fn from(id: &'a i32) -> Self {
        Variable {
            id: *id,
            phantom: PhantomData,
        }
    }
}
impl<T> From<usize> for Variable<T> {
    fn from(id: usize) -> Self {
        Variable {
            id: id as i32,
            phantom: PhantomData,
        }
    }
}

#[derive(Default, Clone)]
pub struct BackwardArgs {}

#[derive(Builder)]
#[builder(pattern="owned")]
pub struct VariableArgs {
    pub creator: Option<Function>,
    pub volatile: bool,
    pub requires_grad: bool,
}

impl Default for VariableArgs {
    fn default() -> Self {
        VariableArgs {
            creator: None,
            volatile: false,
            requires_grad: true,
        }
    }
}
impl VariableArgs {
    pub fn build() -> VariableArgsBuilder {
        VariableArgsBuilder::default()
    }
}
impl VariableArgsBuilder {
    pub fn done(self) -> VariableArgs {
        self.build().unwrap()
    }
}

impl<T> Variable<T> {
    pub fn new(data: Tensor<T>) -> Self {
        Variable::new_args(data, &VariableArgs::default())
    }
    pub fn is_volatile(&self) -> bool {
        self.access().volatile
    }
    pub fn requires_grad(&self) -> bool {
        self.access().requires_grad
    }
    pub fn grad_fn(&self) -> Option<Function> {
        match self.access().grad_fn {
            Some(ref func) => Some(func.clone()),
            None => None,
        }
    }
    pub fn data(&mut self) -> &mut Tensor<T> {
        &mut self.access().data
    }
    pub fn data_borrow(&self) -> &Tensor<T> {
        &self.borrow().data
    }
    pub fn apply(&mut self, callback: fn(&mut Tensor<T>)) {
        callback(&mut self.access().data);
    }
    pub fn mark_dirty(&mut self) {
        self.access().dirty = true;
    }
    pub fn requires_nograd(&mut self) {
        self.access().requires_grad = false;
    }
    pub fn view(&self, dims: &[i32]) -> Self {
        unimplemented!()
    }
    // Computes the gradient of current variable w.r.t. graph leaves
    pub fn backward_args(&mut self, gradient_: Option<&mut Tensor<T>>, retain_variables: bool) {
        let mut store;
        let mut gradient;
        {
            let parent = self.access();
            if parent.volatile {
                panic!("calling backward on a volatile variable")
            }
            if !parent.requires_grad {
                panic!("calling backward on a variable that doesn't require a gradient")
            }
            gradient = match gradient_ {
                Some(gradient) => gradient,
                None => {
                    store = parent.data.new_(1);
                    &mut store
                }
            };
        }
        ExecutionEngine::run_backward(self, &mut gradient, retain_variables)
    }
    pub fn backward(&mut self) {
        self.backward_args(None, false)
    }
    // Detach from graph
    pub fn detach_(&mut self) {
        unimplemented!()
    }
    // return a new variable detached from graph
    pub fn detach(&self) -> Variable<T> {
        unimplemented!()
    }
}

impl<T: Copy> Index<isize> for Variable<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

impl AddAssign<Variable<f32>> for f32 {
    fn add_assign(&mut self, rhs: Variable<f32>) {
        *self = *self + rhs[0]
    }
}
<|endoftext|>


<commit_before>
extern crate linuxdvb;

fn main() {}<commit_msg>Make ugly, but working version of frontend-info.<commit_after>
extern crate linuxdvb;

use linuxdvb::{Frontend,ReadWriteMode,BlockMode};

fn main() {
    let frontend = Frontend::open("/dev/dvb/adapter0/frontend0", ReadWriteMode::ReadOnly, BlockMode::NonBlocking).unwrap();
    println!("{:?}", frontend.get_info().unwrap());
}<|endoftext|>