<commit_before>//! redis-rs is a rust implementation of a Redis client library.  It exposes
//! a general purpose interface to Redis and also provides specific helpers for
//! commonly used functionality.
//!
//! ```rust,no_run
//! extern crate redis;
//!
//! fn main() {
//!     let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//!     let con = client.get_connection().unwrap();
//!     redis::cmd("SET").arg("my_key").arg(42i).execute(&con);
//!     assert_eq!(redis::cmd("GET").arg("my_key").query(&con), Ok(42i));
//! }
//! ```
//!
//! Because redis inherently is mostly type-less and the protocol is not
//! exactly friendly to developers, this library provides flexible support
//! for casting values to the intended results.  This is driven through the
//! `FromRedisValue` trait.
//!
//! The `query` method of a `Cmd` can conver the value to what you expect
//! the function to return:
//!
//! ```rust,no_run
//! # use std::collections::{HashMap, HashSet};
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let count : i32 = redis::cmd("GET").arg("my_counter").query(&con).unwrap();
//! let name : String = redis::cmd("GET").arg("my_name").query(&con).unwrap();
//! let bin : Vec<u8> = redis::cmd("GET").arg("my_binary").query(&con).unwrap();
//! let map : HashMap<String, i32> = redis::cmd("HGETALL").arg("my_hash").query(&con).unwrap();
//! let keys : Vec<String> = redis::cmd("KEYS").query(&con).unwrap();
//! let mems : HashSet<i32> = redis::cmd("SMEMBERS").arg("s").query(&con).unwrap();
//! let (k1, k2) : (String, String) = redis::cmd("MGET").arg("k1").arg("k2").query(&con).unwrap();
//! ```

#![crate_name = "redis"]
#![crate_type = "lib"]
#![license = "BSD"]
#![comment = "Bindings and wrapper functions for redis."]

#![deny(non_camel_case_types)]

#![feature(macro_rules)]
#![feature(default_type_params)]

extern crate url;

/* public api */
pub use parser::{parse_redis_value, Parser};
pub use client::Client;
pub use connection::Connection;
pub use cmd::{cmd, Cmd};

#[doc(hidden)]
pub use types::{
    /* low level values */
    Value,
        Nil,
        Int,
        Data,
        Bulk,
        Okay,
        Status,

    /* error and result types */
    Error,
    RedisResult,

    /* error kinds */
    ErrorKind,
        ResponseError,
        TypeError,
        ExecAbortError,
        BusyLoadingError,
        NoScriptError,
        ExtensionError,
        InternalIoError,

    /* utility types */
    InfoDict,

    /* conversion traits */
    FromRedisValue,
    ToRedisArg
};

pub mod macros;

mod parser;
mod client;
mod connection;
mod types;
mod cmd;
<commit_msg>Marked everything as experimental.<commit_after>//! redis-rs is a rust implementation of a Redis client library.  It exposes
//! a general purpose interface to Redis and also provides specific helpers for
//! commonly used functionality.
//!
//! ```rust,no_run
//! extern crate redis;
//!
//! fn main() {
//!     let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//!     let con = client.get_connection().unwrap();
//!     redis::cmd("SET").arg("my_key").arg(42i).execute(&con);
//!     assert_eq!(redis::cmd("GET").arg("my_key").query(&con), Ok(42i));
//! }
//! ```
//!
//! Because redis inherently is mostly type-less and the protocol is not
//! exactly friendly to developers, this library provides flexible support
//! for casting values to the intended results.  This is driven through the
//! `FromRedisValue` trait.
//!
//! The `query` method of a `Cmd` can conver the value to what you expect
//! the function to return:
//!
//! ```rust,no_run
//! # use std::collections::{HashMap, HashSet};
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let count : i32 = redis::cmd("GET").arg("my_counter").query(&con).unwrap();
//! let name : String = redis::cmd("GET").arg("my_name").query(&con).unwrap();
//! let bin : Vec<u8> = redis::cmd("GET").arg("my_binary").query(&con).unwrap();
//! let map : HashMap<String, i32> = redis::cmd("HGETALL").arg("my_hash").query(&con).unwrap();
//! let keys : Vec<String> = redis::cmd("KEYS").query(&con).unwrap();
//! let mems : HashSet<i32> = redis::cmd("SMEMBERS").arg("s").query(&con).unwrap();
//! let (k1, k2) : (String, String) = redis::cmd("MGET").arg("k1").arg("k2").query(&con).unwrap();
//! ```

#![crate_name = "redis"]
#![crate_type = "lib"]
#![license = "BSD"]
#![comment = "Bindings and wrapper functions for redis."]

#![deny(non_camel_case_types)]

#![feature(macro_rules)]
#![feature(default_type_params)]

#![experimental]

extern crate url;

/* public api */
pub use parser::{parse_redis_value, Parser};
pub use client::Client;
pub use connection::Connection;
pub use cmd::{cmd, Cmd};

#[doc(hidden)]
pub use types::{
    /* low level values */
    Value,
        Nil,
        Int,
        Data,
        Bulk,
        Okay,
        Status,

    /* error and result types */
    Error,
    RedisResult,

    /* error kinds */
    ErrorKind,
        ResponseError,
        TypeError,
        ExecAbortError,
        BusyLoadingError,
        NoScriptError,
        ExtensionError,
        InternalIoError,

    /* utility types */
    InfoDict,

    /* conversion traits */
    FromRedisValue,
    ToRedisArg
};

pub mod macros;

mod parser;
mod client;
mod connection;
mod types;
mod cmd;
<|endoftext|>


<commit_before>pub mod reference;
pub mod render_spec;
mod tests;

<commit_msg>Only compile render tests for 'cargo test' (silences unused X warnings)<commit_after>pub mod reference;
pub mod render_spec;
#[cfg(test)]
mod tests;

<|endoftext|>


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

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

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

/// The [evaluation order](http://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf) 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`,
/// `HeadSpine` and `CallByValue` orders don't always normalize fully. 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. It produces an `Error` if the first `Term` is not an `Abs`traction.
///
/// # 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)
        }
    }
}

/// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit on
/// number of reductions (`0` means no limit).
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::Normal;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta(pred_one, &Normal, 0), 0.into());
/// ```
pub fn beta(mut term: Term, order: &Order, limit: usize) -> Term {
    term.beta(order, limit);
    term
}

impl Term {
    /// Applies `self` to another `Term` and performs substitution, consuming `self` in the process.
    /// It produces an `Error` if `self` is not an `Abs`traction.
    ///
    /// # 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. It produces an `Error` if
    /// `self` is not an `App`lication.
    ///
    /// # 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)
    }

    fn eval_with_info(&mut self, depth: u32, count: &usize) {
        if SHOW_REDUCTIONS {
            print!("\n{}. {}\n=>", count + 1, show_precedence(self, 0, depth));
            let mut indent_len = ((*count + 1) as f32).log10().trunc() as usize + 3;
            if DISPLAY_CLASSIC { indent_len += 3 }
            for _ in 0..indent_len { print!(" ") };
        };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) };
    }

    fn is_reducible(&self, limit: usize, count: &usize) -> bool {
        self.lhs_ref().unwrap().unabs_ref().is_ok() &&
        (limit == 0 || *count < limit )
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional
    /// limit on number of reductions (`0` means no limit).
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order::Normal;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta(&Normal, 0);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta(&mut self, order: &Order, limit: usize) {
        if SHOW_REDUCTIONS {
            println!("reducing {} [{} order{}]:", self, order,
                if limit != 0 {
                    format!(", limit of {} reduction{}", limit,
                        if limit == 1 { "" } else { "s" }
                    )
                } else {
                    "".into()
                }
            );
        };

        let mut count = 0;

        match *order {
            CallByName        => self.beta_cbn(0, limit, &mut count),
            Normal            => self.beta_nor(0, limit, &mut count),
            CallByValue       => self.beta_cbv(0, limit, &mut count),
            Applicative       => self.beta_app(0, limit, &mut count),
            HeadSpine         => self.beta_hs(0, limit, &mut count),
            HybridNormal      => self.beta_hnor(0, limit, &mut count),
            HybridApplicative => self.beta_happ(0, limit, &mut count)
        }
        if SHOW_REDUCTIONS { println!("\nresult: {}\n", self) };
    }

    fn beta_cbn(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbn(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_nor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_nor(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_nor(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                }
            }
        }
    }

    fn beta_cbv(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit, count);
                self.rhs_ref_mut().unwrap().beta_cbv(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbv(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_app(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_app(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_app(depth, limit, count);
                self.rhs_ref_mut().unwrap().beta_app(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_app(depth, limit, count);
                }
            }
        }
    }

    fn beta_happ(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_happ(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit, count);
                self.rhs_ref_mut().unwrap().beta_happ(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_happ(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_happ(depth, limit, count);
                }
            }
        }
    }

    fn beta_hs(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hs(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hs(depth, limit, count)
                }
            }
        }
    }

    fn beta_hnor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hnor(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_hs(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hnor(depth, limit, count)
                } else {
                    self.lhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                }
            }
        }
    }
}

impl fmt::Display for Order {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            Normal => "normal",
            CallByName => "call-by-name",
            HeadSpine => "head spine",
            HybridNormal => "hybrid normal",
            Applicative => "applicative",
            CallByValue => "call-by-value",
            HybridApplicative => "hybrid applicative"
        })
    }
}

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

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

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

        let does_reduce = app(abs(Var(2)), omm());
        assert_eq!(beta(does_reduce, &Normal, 0), Var(1));
    }

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

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

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

        let mut wont_reduce = app(abs(Var(2)), omm());
        wont_reduce.beta(&Applicative, 3);
        assert_eq!(wont_reduce, app(abs(Var(2)), omm()));
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta(&CallByValue, 1);
        assert_eq!(expr, app(abs(app(i(), Var(1))), i()));
        expr.beta(&CallByValue, 1);
        assert_eq!(expr, app(i(), i()));
        expr.beta(&CallByValue, 1);
        assert_eq!(expr, i());
    }
}
<commit_msg>print reduction count in the result<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

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

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

/// The [evaluation order](http://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf) 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`,
/// `HeadSpine` and `CallByValue` orders don't always normalize fully. 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. It produces an `Error` if the first `Term` is not an `Abs`traction.
///
/// # 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)
        }
    }
}

/// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit on
/// number of reductions (`0` means no limit).
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::Normal;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta(pred_one, &Normal, 0), 0.into());
/// ```
pub fn beta(mut term: Term, order: &Order, limit: usize) -> Term {
    term.beta(order, limit);
    term
}

impl Term {
    /// Applies `self` to another `Term` and performs substitution, consuming `self` in the process.
    /// It produces an `Error` if `self` is not an `Abs`traction.
    ///
    /// # 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. It produces an `Error` if
    /// `self` is not an `App`lication.
    ///
    /// # 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)
    }

    fn eval_with_info(&mut self, depth: u32, count: &usize) {
        if SHOW_REDUCTIONS {
            print!("\n{}. {}\n=>", count + 1, show_precedence(self, 0, depth));
            let mut indent_len = ((*count + 1) as f32).log10().trunc() as usize + 3;
            if DISPLAY_CLASSIC { indent_len += 3 }
            for _ in 0..indent_len { print!(" ") };
        };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) };
    }

    fn is_reducible(&self, limit: usize, count: &usize) -> bool {
        self.lhs_ref().unwrap().unabs_ref().is_ok() &&
        (limit == 0 || *count < limit )
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional
    /// limit on number of reductions (`0` means no limit).
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order::Normal;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta(&Normal, 0);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta(&mut self, order: &Order, limit: usize) {
        if SHOW_REDUCTIONS {
            println!("reducing {} [{} order{}]:", self, order,
                if limit != 0 {
                    format!(", limit of {} reduction{}", limit,
                        if limit == 1 { "" } else { "s" }
                    )
                } else {
                    "".into()
                }
            );
        };

        let mut count = 0;

        match *order {
            CallByName        => self.beta_cbn(0, limit, &mut count),
            Normal            => self.beta_nor(0, limit, &mut count),
            CallByValue       => self.beta_cbv(0, limit, &mut count),
            Applicative       => self.beta_app(0, limit, &mut count),
            HeadSpine         => self.beta_hs(0, limit, &mut count),
            HybridNormal      => self.beta_hnor(0, limit, &mut count),
            HybridApplicative => self.beta_happ(0, limit, &mut count)
        }
        if SHOW_REDUCTIONS { println!("\nresult after {} reductions: {}\n", count, self) };
    }

    fn beta_cbn(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbn(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_nor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_nor(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_nor(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                }
            }
        }
    }

    fn beta_cbv(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit, count);
                self.rhs_ref_mut().unwrap().beta_cbv(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbv(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_app(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_app(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_app(depth, limit, count);
                self.rhs_ref_mut().unwrap().beta_app(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_app(depth, limit, count);
                }
            }
        }
    }

    fn beta_happ(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_happ(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit, count);
                self.rhs_ref_mut().unwrap().beta_happ(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_happ(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_happ(depth, limit, count);
                }
            }
        }
    }

    fn beta_hs(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hs(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hs(depth, limit, count)
                }
            }
        }
    }

    fn beta_hnor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hnor(depth + 1, limit, count),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_hs(depth, limit, count);

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hnor(depth, limit, count)
                } else {
                    self.lhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                }
            }
        }
    }
}

impl fmt::Display for Order {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            Normal => "normal",
            CallByName => "call-by-name",
            HeadSpine => "head spine",
            HybridNormal => "hybrid normal",
            Applicative => "applicative",
            CallByValue => "call-by-value",
            HybridApplicative => "hybrid applicative"
        })
    }
}

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

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

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

        let does_reduce = app(abs(Var(2)), omm());
        assert_eq!(beta(does_reduce, &Normal, 0), Var(1));
    }

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

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

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

        let mut wont_reduce = app(abs(Var(2)), omm());
        wont_reduce.beta(&Applicative, 3);
        assert_eq!(wont_reduce, app(abs(Var(2)), omm()));
    }

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


<commit_before>use super::*;
use lsp::notification::Notification;
use lsp::request::Request;

impl State {
    pub fn handle_method_call(
        &mut self,
        languageId: Option<&str>,
        method_call: &rpc::MethodCall,
    ) -> Result<Value> {
        let params = serde_json::to_value(method_call.params.clone())?;

        let user_handler = self.get(|state| {
            state
                .user_handlers
                .get(&method_call.method)
                .cloned()
                .ok_or_else(|| err_msg("No user handler"))
        });
        if let Ok(user_handler) = user_handler {
            return self.call(None, &user_handler, params);
        }

        match method_call.method.as_str() {
            lsp::request::RegisterCapability::METHOD => {
                self.client_registerCapability(languageId.unwrap_or_default(), &params)
            }
            lsp::request::UnregisterCapability::METHOD => {
                self.client_unregisterCapability(languageId.unwrap_or_default(), &params)
            }
            lsp::request::HoverRequest::METHOD => self.textDocument_hover(&params),
            REQUEST__FindLocations => self.find_locations(&params),
            lsp::request::Rename::METHOD => self.textDocument_rename(&params),
            lsp::request::DocumentSymbolRequest::METHOD => self.textDocument_documentSymbol(&params),
            lsp::request::WorkspaceSymbol::METHOD => self.workspace_symbol(&params),
            lsp::request::CodeActionRequest::METHOD => self.textDocument_codeAction(&params),
            lsp::request::Completion::METHOD => self.textDocument_completion(&params),
            lsp::request::SignatureHelpRequest::METHOD => self.textDocument_signatureHelp(&params),
            lsp::request::References::METHOD => self.textDocument_references(&params),
            lsp::request::Formatting::METHOD => self.textDocument_formatting(&params),
            lsp::request::RangeFormatting::METHOD => self.textDocument_rangeFormatting(&params),
            lsp::request::ResolveCompletionItem::METHOD => self.completionItem_resolve(&params),
            lsp::request::ExecuteCommand::METHOD => self.workspace_executeCommand(&params),
            lsp::request::ApplyWorkspaceEdit::METHOD => self.workspace_applyEdit(&params),
            lsp::request::DocumentHighlightRequest::METHOD => {
                self.textDocument_documentHighlight(&params)
            }
            REQUEST__RustImplementations => self.rustDocument_implementations(&params),
            // Extensions.
            REQUEST__GetState => self.languageClient_getState(&params),
            REQUEST__IsAlive => self.languageClient_isAlive(&params),
            REQUEST__StartServer => self.languageClient_startServer(&params),
            REQUEST__RegisterServerCommands => self.languageClient_registerServerCommands(&params),
            REQUEST__SetLoggingLevel => self.languageClient_setLoggingLevel(&params),
            REQUEST__RegisterHandlers => self.languageClient_registerHandlers(&params),
            REQUEST__NCMRefresh => self.NCM_refresh(&params),
            REQUEST__NCM2OnComplete => self.NCM2_on_complete(&params),
            REQUEST__ExplainErrorAtPoint => self.languageClient_explainErrorAtPoint(&params),
            REQUEST__OmniComplete => self.languageClient_omniComplete(&params),
            REQUEST__ClassFileContents => self.java_classFileContents(&params),

            _ => {
                let languageId_target = if languageId.is_some() {
                    // Message from language server. No handler found.
                    let msg = format!("Message not handled: {:?}", method_call);
                    if method_call.method.starts_with('$') {
                        warn!("{}", msg);
                        return Ok(Value::default());
                    } else {
                        return Err(err_msg(msg));
                    }
                } else {
                    // Message from vim. Proxy to language server.
                    let (languageId_target,): (String,) =
                        self.gather_args(&[VimVar::LanguageId], &params)?;
                    Some(languageId_target)
                };

                self.call(languageId_target.as_deref(), &method_call.method, &params)
            }
        }
    }

    pub fn handle_notification(
        &mut self,
        languageId: Option<&str>,
        notification: &rpc::Notification,
    ) -> Result<()> {
        let params = serde_json::to_value(notification.params.clone())?;

        let user_handler = self.get(|state| {
            state
                .user_handlers
                .get(&notification.method)
                .cloned()
                .ok_or_else(|| err_msg("No user handler"))
        });
        if let Ok(user_handler) = user_handler {
            self.call::<_, u8>(None, &user_handler, params.clone())?;
            return Ok(());
        }

        match notification.method.as_str() {
            lsp::notification::DidChangeConfiguration::METHOD => {
                self.workspace_didChangeConfiguration(&params)?
            }
            lsp::notification::DidOpenTextDocument::METHOD => self.textDocument_didOpen(&params)?,
            lsp::notification::DidChangeTextDocument::METHOD => {
                self.textDocument_didChange(&params)?
            }
            lsp::notification::DidSaveTextDocument::METHOD => self.textDocument_didSave(&params)?,
            lsp::notification::DidCloseTextDocument::METHOD => self.textDocument_didClose(&params)?,
            lsp::notification::PublishDiagnostics::METHOD => {
                self.textDocument_publishDiagnostics(&params)?
            }
            lsp::notification::LogMessage::METHOD => self.window_logMessage(&params)?,
            lsp::notification::ShowMessage::METHOD => self.window_showMessage(&params)?,
            lsp::notification::Exit::METHOD => self.exit(&params)?,
            // Extensions.
            NOTIFICATION__HandleBufNewFile => self.languageClient_handleBufNewFile(&params)?,
            NOTIFICATION__HandleBufReadPost => self.languageClient_handleBufReadPost(&params)?,
            NOTIFICATION__HandleTextChanged => self.languageClient_handleTextChanged(&params)?,
            NOTIFICATION__HandleBufWritePost => self.languageClient_handleBufWritePost(&params)?,
            NOTIFICATION__HandleBufDelete => self.languageClient_handleBufDelete(&params)?,
            NOTIFICATION__HandleCursorMoved => self.languageClient_handleCursorMoved(&params)?,
            NOTIFICATION__HandleCompleteDone => self.languageClient_handleCompleteDone(&params)?,
            NOTIFICATION__FZFSinkLocation => self.languageClient_FZFSinkLocation(&params)?,
            NOTIFICATION__FZFSinkCommand => self.languageClient_FZFSinkCommand(&params)?,
            NOTIFICATION__ClearDocumentHighlight => {
                self.languageClient_clearDocumentHighlight(&params)?
            }
            // Extensions by language servers.
            NOTIFICATION__LanguageStatus => self.language_status(&params)?,
            NOTIFICATION__RustBeginBuild => self.rust_handleBeginBuild(&params)?,
            NOTIFICATION__RustDiagnosticsBegin => self.rust_handleDiagnosticsBegin(&params)?,
            NOTIFICATION__RustDiagnosticsEnd => self.rust_handleDiagnosticsEnd(&params)?,
            NOTIFICATION__WindowProgress => self.window_progress(&params)?,
            NOTIFICATION__ServerExited => self.languageClient_serverExited(&params)?,

            _ => {
                let languageId_target = if languageId.is_some() {
                    // Message from language server. No handler found.
                    let msg = format!("Message not handled: {:?}", notification);
                    if notification.method.starts_with('$') {
                        warn!("{}", msg);
                        return Ok(());
                    } else {
                        return Err(err_msg(msg));
                    }
                } else {
                    // Message from vim. Proxy to language server.
                    let (languageId_target,): (String,) =
                        self.gather_args(&[VimVar::LanguageId], &params)?;
                    Some(languageId_target)
                };

                self.notify(languageId_target.as_deref(), &notification.method, &params)?;
            }
        };

        Ok(())
    }
}
<commit_msg>Add logs for directly proxyed message.<commit_after>use super::*;
use lsp::notification::Notification;
use lsp::request::Request;

impl State {
    pub fn handle_method_call(
        &mut self,
        languageId: Option<&str>,
        method_call: &rpc::MethodCall,
    ) -> Result<Value> {
        let params = serde_json::to_value(method_call.params.clone())?;

        let user_handler = self.get(|state| {
            state
                .user_handlers
                .get(&method_call.method)
                .cloned()
                .ok_or_else(|| err_msg("No user handler"))
        });
        if let Ok(user_handler) = user_handler {
            return self.call(None, &user_handler, params);
        }

        match method_call.method.as_str() {
            lsp::request::RegisterCapability::METHOD => {
                self.client_registerCapability(languageId.unwrap_or_default(), &params)
            }
            lsp::request::UnregisterCapability::METHOD => {
                self.client_unregisterCapability(languageId.unwrap_or_default(), &params)
            }
            lsp::request::HoverRequest::METHOD => self.textDocument_hover(&params),
            REQUEST__FindLocations => self.find_locations(&params),
            lsp::request::Rename::METHOD => self.textDocument_rename(&params),
            lsp::request::DocumentSymbolRequest::METHOD => self.textDocument_documentSymbol(&params),
            lsp::request::WorkspaceSymbol::METHOD => self.workspace_symbol(&params),
            lsp::request::CodeActionRequest::METHOD => self.textDocument_codeAction(&params),
            lsp::request::Completion::METHOD => self.textDocument_completion(&params),
            lsp::request::SignatureHelpRequest::METHOD => self.textDocument_signatureHelp(&params),
            lsp::request::References::METHOD => self.textDocument_references(&params),
            lsp::request::Formatting::METHOD => self.textDocument_formatting(&params),
            lsp::request::RangeFormatting::METHOD => self.textDocument_rangeFormatting(&params),
            lsp::request::ResolveCompletionItem::METHOD => self.completionItem_resolve(&params),
            lsp::request::ExecuteCommand::METHOD => self.workspace_executeCommand(&params),
            lsp::request::ApplyWorkspaceEdit::METHOD => self.workspace_applyEdit(&params),
            lsp::request::DocumentHighlightRequest::METHOD => {
                self.textDocument_documentHighlight(&params)
            }
            REQUEST__RustImplementations => self.rustDocument_implementations(&params),
            // Extensions.
            REQUEST__GetState => self.languageClient_getState(&params),
            REQUEST__IsAlive => self.languageClient_isAlive(&params),
            REQUEST__StartServer => self.languageClient_startServer(&params),
            REQUEST__RegisterServerCommands => self.languageClient_registerServerCommands(&params),
            REQUEST__SetLoggingLevel => self.languageClient_setLoggingLevel(&params),
            REQUEST__RegisterHandlers => self.languageClient_registerHandlers(&params),
            REQUEST__NCMRefresh => self.NCM_refresh(&params),
            REQUEST__NCM2OnComplete => self.NCM2_on_complete(&params),
            REQUEST__ExplainErrorAtPoint => self.languageClient_explainErrorAtPoint(&params),
            REQUEST__OmniComplete => self.languageClient_omniComplete(&params),
            REQUEST__ClassFileContents => self.java_classFileContents(&params),

            _ => {
                let languageId_target = if languageId.is_some() {
                    // Message from language server. No handler found.
                    let msg = format!("Message not handled: {:?}", method_call);
                    if method_call.method.starts_with('$') {
                        warn!("{}", msg);
                        return Ok(Value::default());
                    } else {
                        return Err(err_msg(msg));
                    }
                } else {
                    // Message from vim. Proxy to language server.
                    let (languageId_target,): (String,) =
                        self.gather_args(&[VimVar::LanguageId], &params)?;
                    info!("Proxy message directly to language server: {:?}", method_call);
                    Some(languageId_target)
                };

                self.call(languageId_target.as_deref(), &method_call.method, &params)
            }
        }
    }

    pub fn handle_notification(
        &mut self,
        languageId: Option<&str>,
        notification: &rpc::Notification,
    ) -> Result<()> {
        let params = serde_json::to_value(notification.params.clone())?;

        let user_handler = self.get(|state| {
            state
                .user_handlers
                .get(&notification.method)
                .cloned()
                .ok_or_else(|| err_msg("No user handler"))
        });
        if let Ok(user_handler) = user_handler {
            self.call::<_, u8>(None, &user_handler, params.clone())?;
            return Ok(());
        }

        match notification.method.as_str() {
            lsp::notification::DidChangeConfiguration::METHOD => {
                self.workspace_didChangeConfiguration(&params)?
            }
            lsp::notification::DidOpenTextDocument::METHOD => self.textDocument_didOpen(&params)?,
            lsp::notification::DidChangeTextDocument::METHOD => {
                self.textDocument_didChange(&params)?
            }
            lsp::notification::DidSaveTextDocument::METHOD => self.textDocument_didSave(&params)?,
            lsp::notification::DidCloseTextDocument::METHOD => self.textDocument_didClose(&params)?,
            lsp::notification::PublishDiagnostics::METHOD => {
                self.textDocument_publishDiagnostics(&params)?
            }
            lsp::notification::LogMessage::METHOD => self.window_logMessage(&params)?,
            lsp::notification::ShowMessage::METHOD => self.window_showMessage(&params)?,
            lsp::notification::Exit::METHOD => self.exit(&params)?,
            // Extensions.
            NOTIFICATION__HandleBufNewFile => self.languageClient_handleBufNewFile(&params)?,
            NOTIFICATION__HandleBufReadPost => self.languageClient_handleBufReadPost(&params)?,
            NOTIFICATION__HandleTextChanged => self.languageClient_handleTextChanged(&params)?,
            NOTIFICATION__HandleBufWritePost => self.languageClient_handleBufWritePost(&params)?,
            NOTIFICATION__HandleBufDelete => self.languageClient_handleBufDelete(&params)?,
            NOTIFICATION__HandleCursorMoved => self.languageClient_handleCursorMoved(&params)?,
            NOTIFICATION__HandleCompleteDone => self.languageClient_handleCompleteDone(&params)?,
            NOTIFICATION__FZFSinkLocation => self.languageClient_FZFSinkLocation(&params)?,
            NOTIFICATION__FZFSinkCommand => self.languageClient_FZFSinkCommand(&params)?,
            NOTIFICATION__ClearDocumentHighlight => {
                self.languageClient_clearDocumentHighlight(&params)?
            }
            // Extensions by language servers.
            NOTIFICATION__LanguageStatus => self.language_status(&params)?,
            NOTIFICATION__RustBeginBuild => self.rust_handleBeginBuild(&params)?,
            NOTIFICATION__RustDiagnosticsBegin => self.rust_handleDiagnosticsBegin(&params)?,
            NOTIFICATION__RustDiagnosticsEnd => self.rust_handleDiagnosticsEnd(&params)?,
            NOTIFICATION__WindowProgress => self.window_progress(&params)?,
            NOTIFICATION__ServerExited => self.languageClient_serverExited(&params)?,

            _ => {
                let languageId_target = if languageId.is_some() {
                    // Message from language server. No handler found.
                    let msg = format!("Message not handled: {:?}", notification);
                    if notification.method.starts_with('$') {
                        warn!("{}", msg);
                        return Ok(());
                    } else {
                        return Err(err_msg(msg));
                    }
                } else {
                    // Message from vim. Proxy to language server.
                    let (languageId_target,): (String,) =
                        self.gather_args(&[VimVar::LanguageId], &params)?;
                    info!("Proxy message directly to language server: {:?}", notification);
                    Some(languageId_target)
                };

                self.notify(languageId_target.as_deref(), &notification.method, &params)?;
            }
        };

        Ok(())
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use std::default::Default;
use std::hash::{Hash, Hasher};

use fxhash::FxHasher;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

#[cfg_attr(rustfmt, rustfmt_skip)]
const AdrenalineTable: [i8; 80] = [
	4, 0, 0, 0, 0,
	3, 1, 1, 1, 0,
	3, 2, 2, 2, 0,
	3, 3, 3, 3, 0,
	2, 3, 2, 0, 0,
	2, 4, 2, 0, 0,
	2, 4, 2, 0, 0,
	2, 5, 3, 0, 0,
	2, 6, 3, 0, 0,
	1, 3, 0, 0, 0,
	1, 4, 0, 0, 0,
	1, 4, 0, 0, 0,
	1, 4, 0, 0, 0,
	1, 5, 0, 0, 0,
	1, 5, 0, 0, 0,
	1, 5, 0, 0, 0,
];

pub fn countAdrenaline(x: i32) -> i32 {
	let x = x.abs();
	if x > 15 {
		1
	} else {
		AdrenalineTable[x as usize * 5] as i32 + 1
	}
}

pub fn calcAdrenaline(y: i32, dmg: i32) -> i32 {
	if y < 2 {
		dmg
	} else {
		let admg = AdrenalineTable[(dmg.abs() * 5 + y - 1) as usize] as i32;
		if dmg < 0 {
			-admg
		} else {
			admg
		}
	}
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub fn getAdrenalRow(x: i32) -> Vec<i8> {
	let xabs = x.abs() as usize;
	if xabs > 15 {
		Vec::new()
	} else {
		let mut v = Vec::<i8>::with_capacity(4);
		let vp = v.as_mut_ptr();
		let len = AdrenalineTable[xabs * 5] as usize;
		unsafe {
			v.set_len(len);
			std::ptr::copy_nonoverlapping(
				AdrenalineTable.as_ptr().offset(xabs as isize * 5 + 1),
				vp,
				4,
			);
			if x < 0 {
				let vp = vp.cast::<u32>();
				*vp = (!*vp) + 0x01010101;
			}
		}
		v
	}
}

pub fn hash<T: Hash>(obj: &T) -> i32 {
	let mut hasher: FxHasher = Default::default();
	obj.hash(&mut hasher);
	let h64 = hasher.finish();
	let h32 = (h64 >> 32) as u32 ^ h64 as u32;
	h32 as i32
}

pub const Weapon: i32 = 1;
pub const Shield: i32 = 2;
pub const Permanent: i32 = 3;
pub const Spell: i32 = 4;
pub const Creature: i32 = 5;
pub const Player: i32 = 6;

pub const Chroma: i32 = 0;
pub const Entropy: i32 = 1;
pub const Death: i32 = 2;
pub const Gravity: i32 = 3;
pub const Earth: i32 = 4;
pub const Life: i32 = 5;
pub const Fire: i32 = 6;
pub const Water: i32 = 7;
pub const Light: i32 = 8;
pub const Air: i32 = 9;
pub const Time: i32 = 10;
pub const Darkness: i32 = 11;
pub const Aether: i32 = 12;

pub const PillarList: &[i32] = &[
	5002, 5100, 5200, 5300, 5400, 5500, 5600, 5700, 5800, 5900, 6000, 6100, 6200,
];
pub const NymphList: &[i32] = &[
	0, 5120, 5220, 5320, 5420, 5520, 5620, 5720, 5820, 5920, 6020, 6120, 6220,
];
pub const AlchemyList: &[i32] = &[
	0, 5111, 5212, 5311, 5413, 5511, 5611, 5712, 5811, 5910, 6011, 6110, 6209,
];
pub const ShardList: &[i32] = &[
	0, 5130, 5230, 5330, 5430, 5530, 5630, 5730, 5830, 5930, 6030, 6130, 6230,
];
<commit_msg>Avoid out of bounds in adrenaline calcs<commit_after>#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use std::default::Default;
use std::hash::{Hash, Hasher};

use fxhash::FxHasher;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

#[cfg_attr(rustfmt, rustfmt_skip)]
const AdrenalineTable: [i8; 80] = [
	4, 0, 0, 0, 0,
	3, 1, 1, 1, 0,
	3, 2, 2, 2, 0,
	3, 3, 3, 3, 0,
	2, 3, 2, 0, 0,
	2, 4, 2, 0, 0,
	2, 4, 2, 0, 0,
	2, 5, 3, 0, 0,
	2, 6, 3, 0, 0,
	1, 3, 0, 0, 0,
	1, 4, 0, 0, 0,
	1, 4, 0, 0, 0,
	1, 4, 0, 0, 0,
	1, 5, 0, 0, 0,
	1, 5, 0, 0, 0,
	1, 5, 0, 0, 0,
];

pub fn countAdrenaline(x: i32) -> i32 {
	let x = x.abs() as usize;
	if x > 15 {
		1
	} else {
		AdrenalineTable[x * 5] as i32 + 1
	}
}

pub fn calcAdrenaline(y: i32, dmg: i32) -> i32 {
	if y < 2 {
		dmg
	} else if y > 5 {
		0
	} else {
		let absdmg = dmg.abs();
		if absdmg > 15 {
			0
		} else {
			let admg = AdrenalineTable[(absdmg * 5 + y - 1) as usize] as i32;
			if dmg < 0 {
				-admg
			} else {
				admg
			}
		}
	}
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub fn getAdrenalRow(x: i32) -> Vec<i8> {
	let xabs = x.abs() as usize;
	if xabs > 15 {
		Vec::new()
	} else {
		let mut v = Vec::<i8>::with_capacity(4);
		let vp = v.as_mut_ptr();
		let len = AdrenalineTable[xabs * 5] as usize;
		unsafe {
			v.set_len(len);
			std::ptr::copy_nonoverlapping(
				AdrenalineTable.as_ptr().offset(xabs as isize * 5 + 1),
				vp,
				4,
			);
			if x < 0 {
				let vp = vp.cast::<u32>();
				*vp = (!*vp) + 0x01010101;
			}
		}
		v
	}
}

pub fn hash<T: Hash>(obj: &T) -> i32 {
	let mut hasher: FxHasher = Default::default();
	obj.hash(&mut hasher);
	let h64 = hasher.finish();
	let h32 = (h64 >> 32) as u32 ^ h64 as u32;
	h32 as i32
}

pub const Weapon: i32 = 1;
pub const Shield: i32 = 2;
pub const Permanent: i32 = 3;
pub const Spell: i32 = 4;
pub const Creature: i32 = 5;
pub const Player: i32 = 6;

pub const Chroma: i32 = 0;
pub const Entropy: i32 = 1;
pub const Death: i32 = 2;
pub const Gravity: i32 = 3;
pub const Earth: i32 = 4;
pub const Life: i32 = 5;
pub const Fire: i32 = 6;
pub const Water: i32 = 7;
pub const Light: i32 = 8;
pub const Air: i32 = 9;
pub const Time: i32 = 10;
pub const Darkness: i32 = 11;
pub const Aether: i32 = 12;

pub const PillarList: &[i32] = &[
	5002, 5100, 5200, 5300, 5400, 5500, 5600, 5700, 5800, 5900, 6000, 6100, 6200,
];
pub const NymphList: &[i32] = &[
	0, 5120, 5220, 5320, 5420, 5520, 5620, 5720, 5820, 5920, 6020, 6120, 6220,
];
pub const AlchemyList: &[i32] = &[
	0, 5111, 5212, 5311, 5413, 5511, 5611, 5712, 5811, 5910, 6011, 6110, 6209,
];
pub const ShardList: &[i32] = &[
	0, 5130, 5230, 5330, 5430, 5530, 5630, 5730, 5830, 5930, 6030, 6130, 6230,
];
<|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::*;
use support::*;

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!");
                    // TODO: verify this index starts past the first open brace
                    min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                    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!");
                // TODO: verify this index starts past the first open brace
                min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                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!");
                // TODO: verify this index starts past the first open brace
                min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                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!");
                // TODO: verify this index starts past the first open brace
                min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                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(tokens, index, ";").unwrap();
                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)
}

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
}

/// 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);
    }
}
<commit_msg>Start variable/method access compilation<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::*;
use support::*;

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!");
                    // TODO: verify this index starts past the first open brace
                    min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                    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!");
                // TODO: verify this index starts past the first open brace
                min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                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!");
                // TODO: verify this index starts past the first open brace
                min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                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!");
                // TODO: verify this index starts past the first open brace
                min_value = identify_body_bounds(tokens, index, ("{", "}")).unwrap();
                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(tokens, index, ";").unwrap();
                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)
}

/// Write PLP code to evaluate the given symbol sequence, and load the result into a specific register
/// A sequence can be:
/// * a  simple variable reference,
/// * a method call,
/// * a variable accessed from another symbol (e.g. foo.bar or Foo.staticBar),
/// * a method accessed from another symbol (e.g. foo.bar() or Foo.staticBar()), or
/// * a complex chain of the above (e.g. foo.method().valueInReturnValue.value.method())
///
/// The start index should be the first symbol in the sequence
/// @return (plp_code, the first index AFTER this symbol sequence)
fn compile_symbol_sequence(tokens: &Vec<Token>,
                                start: usize,
                                temp_registers: (&str, &str),
                                var_registers: (&str, &str),
                                load_registers: (&str, &str),
                                symbols: &StaticSymbolTable)
                                -> (String, usize)
{
    // Each element in this vec represents the start and end indecies (start_inclusive, end_exclusive) of a subsequence of this sequence
    // Each subsequence can reference either a variable (or static structure acting as a variable/namespace) or a method
    // Accessor tokens (.) are not included in this stack
    // Consequently, elements with multiple tokens are method calls; a single token means it is a variable (or class acting as a variable/namespace)
    let mut stack: Vec<(usize, usize)> = Vec::new();

    let mut code: String = String::new();

    // Push sequence to stack
    let mut subsequence_start = start;
    let mut index = start;
    loop
    {
        let token = &tokens[index];
        if index >= tokens.len()
        {
            break;
        }
        if token.value == "."
        {
            let subsequence = (subsequence_start, index);
            stack.push(subsequence);
        }
        else if token.name == "identifier"
        {
            subsequence_start = index;
        }
        else if token.value == "("
        {
            index = identify_body_bounds(&tokens, index + 1, ("(", ")")).unwrap();
        }
        else
        {
            break;
        }

        index += 1;
    }
    // first index AFTER the sequence
    index += 1;
    let subsequence = (subsequence_start, index);
    stack.push(subsequence);

    // TODO: compile

    (code, index)
}

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
}

/// 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);
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

// pub use self::blosum62::blosum62;
// pub use self::pam40::pam40;
// pub use self::pam120::pam120;
// pub use self::pam200::pam200;
// pub use self::pam250::pam250;
//
// pub mod blosum62;
// pub mod pam40;
// pub mod pam120;
// pub mod pam200;
// pub mod pam250;
<commit_msg>chore(scores/mod.rs): enable the scores module<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::blosum62::blosum62;
pub use self::pam40::pam40;
pub use self::pam120::pam120;
pub use self::pam200::pam200;
pub use self::pam250::pam250;

pub mod blosum62;
pub mod pam40;
pub mod pam120;
pub mod pam200;
pub mod pam250;
<|endoftext|>


<commit_before>use std::sync::Arc;
use bifrost::raft::state_machine::master::ExecError;
use neb::client::{Client as NebClient, NebClientError};
use neb::server::{ServerOptions, NebServer, ServerError};

use graph::Graph;

pub mod general;
pub mod schema;
pub mod traversal;

#[derive(Debug)]
pub enum MorpheusServerError {
    ServerError(ServerError),
    ClientError(NebClientError),
    InitSchemaError(ExecError)
}

pub struct MorpheusServer {
    pub neb_server: Arc<NebServer>,
    pub neb_client: Arc<NebClient>,
    pub schema_container: Arc<schema::SchemaContainer>,
    pub graph: Arc<Graph>
}

impl MorpheusServer {
    pub fn new(opts: &ServerOptions)
        -> Result<Arc<MorpheusServer>, MorpheusServerError> {
        let neb_server = match NebServer::new(opts) {
            Ok(server) => server,
            Err(e) => return Err(MorpheusServerError::ServerError(e))
        };
        let neb_client = match NebClient::new(
            &neb_server.rpc, &opts.meta_members,
            &opts.group_name) {
            Ok(client) => Arc::new(client),
            Err(e) => return Err(MorpheusServerError::ClientError(e))
        };
        if opts.is_meta {
            if let &Some(ref raft_service) = &neb_server.raft_service {
                schema::SchemaContainer::new_meta_service(raft_service);
            } else {
                panic!("raft service should be ready for meta server");
            }
        }
        let schema_container = match schema::SchemaContainer::new_client(
            &neb_client.raft_client, &neb_client, &neb_server.meta
        ) {
            Ok(container) => container,
            Err(e) => return Err(MorpheusServerError::InitSchemaError(e))
        };
        let graph = Arc::new(Graph::new(&schema_container, &neb_client)
            .map_err(MorpheusServerError::InitSchemaError)?);
        Ok(Arc::new(MorpheusServer {
            neb_server: neb_server,
            neb_client: neb_client,
            schema_container: schema_container,
            graph: graph
        }))
    }
}<commit_msg>concise error result<commit_after>use std::sync::Arc;
use bifrost::raft::state_machine::master::ExecError;
use neb::client::{Client as NebClient, NebClientError};
use neb::server::{ServerOptions, NebServer, ServerError};

use graph::Graph;

pub mod general;
pub mod schema;
pub mod traversal;

#[derive(Debug)]
pub enum MorpheusServerError {
    ServerError(ServerError),
    ClientError(NebClientError),
    InitSchemaError(ExecError)
}

pub struct MorpheusServer {
    pub neb_server: Arc<NebServer>,
    pub neb_client: Arc<NebClient>,
    pub schema_container: Arc<schema::SchemaContainer>,
    pub graph: Arc<Graph>
}

impl MorpheusServer {
    pub fn new(opts: &ServerOptions)
               -> Result<Arc<MorpheusServer>, MorpheusServerError> {
        let neb_server = NebServer::new(opts).map_err(MorpheusServerError::ServerError)?;
        let neb_client = Arc::new(NebClient::new(
            &neb_server.rpc, &opts.meta_members,
            &opts.group_name).map_err(MorpheusServerError::ClientError)?);
        if opts.is_meta {
            if let &Some(ref raft_service) = &neb_server.raft_service {
                schema::SchemaContainer::new_meta_service(raft_service);
            } else {
                panic!("raft service should be ready for meta server");
            }
        }
        let schema_container = schema::SchemaContainer::new_client(
            &neb_client.raft_client, &neb_client, &neb_server.meta
        ).map_err(MorpheusServerError::InitSchemaError)?;
        let graph = Arc::new(Graph::new(&schema_container, &neb_client)
            .map_err(MorpheusServerError::InitSchemaError)?);
        Ok(Arc::new(MorpheusServer {
            neb_server: neb_server,
            neb_client: neb_client,
            schema_container: schema_container,
            graph: graph
        }))
    }
}<|endoftext|>


<commit_before>use super::config::TlsServerIdentityConfig;
use rustls::{Certificate, ResolvesServerCert, SignatureScheme, sign};
use rustls::internal::pemfile;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::sync::Arc;

pub fn new(
    identities: &Option<HashMap<String, TlsServerIdentityConfig>>,
    default: &Option<TlsServerIdentityConfig>,
) -> Result<Sni, Error> {
    let n_identities = identities.as_ref().map(|ids| ids.len()).unwrap_or(0);
    if default.is_none() && n_identities > 0 {
        return Err(Error::NoIdentities);
    }
    let sni = Sni {
        default: default.as_ref().map(|c| ServerIdentity::load(c)),
        identities: {
            let mut ids = HashMap::with_capacity(n_identities);
            if let Some(identities) = identities.as_ref() {
                for (k, c) in identities {
                    let k: String = (*k).clone();
                    let v = ServerIdentity::load(c);
                    ids.insert(k, v);
                }
            }
            Arc::new(ids)
        },
    };
    Ok(sni)
}

#[derive(Debug)]
pub enum Error {
    NoIdentities,
}

pub struct Sni {
    default: Option<ServerIdentity>,
    identities: Arc<HashMap<String, ServerIdentity>>,
}

impl ResolvesServerCert for Sni {
    fn resolve(
        &self,
        server_name: Option<&str>,
        _sigschemes: &[SignatureScheme],
    ) -> Option<sign::CertifiedKey> {
        debug!("finding cert resolver for {:?}", server_name);
        server_name
            .and_then(|n| {
                debug!("found match for {}", n);
                self.identities.get(n)
            })
            .or_else(|| {
                debug!("reverting to default");
                self.default.as_ref()
            })
            .map(|id| id.key.clone())
    }
}

struct ServerIdentity {
    key: sign::CertifiedKey,
}

impl ServerIdentity {
    fn load(c: &TlsServerIdentityConfig) -> ServerIdentity {
        let mut certs = vec![];
        for p in &c.certs {
            certs.append(&mut load_certs(p));
        }
        let key = Arc::new(load_private_key(&c.private_key));
        ServerIdentity {
            key: sign::CertifiedKey::new(certs, key),
        }
    }
}

// from rustls example
fn load_certs(filename: &str) -> Vec<Certificate> {
    let certfile = File::open(filename).expect("cannot open certificate file");
    let mut r = BufReader::new(certfile);
    pemfile::certs(&mut r).unwrap()
}

// from rustls example
fn load_private_key(filename: &str) -> Box<sign::SigningKey> {
    let keyfile = File::open(filename).expect("cannot open private key file");
    let mut r = BufReader::new(keyfile);
    let keys = pemfile::rsa_private_keys(&mut r).unwrap();
    assert_eq!(keys.len(), 1);
    Box::new(sign::RSASigningKey::new(&keys[0]).expect(
        "Invalid RSA private key",
    ))
}
<commit_msg>Improve error handling during reading of TLS configuration. (#75)<commit_after>use super::config::TlsServerIdentityConfig;
use rustls::{Certificate, ResolvesServerCert, SignatureScheme, sign};
use rustls::internal::pemfile;
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::sync::Arc;

pub fn new(
    identities: &Option<HashMap<String, TlsServerIdentityConfig>>,
    default: &Option<TlsServerIdentityConfig>,
) -> Result<Sni, Error> {
    let n_identities = identities.as_ref().map(|ids| ids.len()).unwrap_or(0);
    let default = match default {
        &Some(ref c) => Some(ServerIdentity::load(c)?),
        &None if n_identities > 0 => {
            return Err(Error::NoIdentities)
        },
        &None => None,
    };
    let sni = Sni {
        default,
        identities: {
            let mut ids = HashMap::with_capacity(n_identities);
            if let Some(identities) = identities.as_ref() {
                for (k, c) in identities {
                    let k: String = (*k).clone();
                    let v = ServerIdentity::load(c)?;
                    ids.insert(k, v);
                }
            }
            Arc::new(ids)
        },
    };
    Ok(sni)
}

#[derive(Debug)]
pub enum Error {
    NoIdentities,
    FailedToOpenCertificateFile(String, io::Error),
    FailedToReadCertificateFile(String),
    FailedToOpenPrivateKeyFile(String, io::Error),
    FailedToReadPrivateKeyFile(String),
    WrongNumberOfKeysInPrivateKeyFile(String, usize),
    FailedToConstructPrivateKey(String),
}

pub struct Sni {
    default: Option<ServerIdentity>,
    identities: Arc<HashMap<String, ServerIdentity>>,
}

impl ResolvesServerCert for Sni {
    fn resolve(
        &self,
        server_name: Option<&str>,
        _sigschemes: &[SignatureScheme],
    ) -> Option<sign::CertifiedKey> {
        debug!("finding cert resolver for {:?}", server_name);
        server_name
            .and_then(|n| {
                debug!("found match for {}", n);
                self.identities.get(n)
            })
            .or_else(|| {
                debug!("reverting to default");
                self.default.as_ref()
            })
            .map(|id| id.key.clone())
    }
}

struct ServerIdentity {
    key: sign::CertifiedKey,
}

impl ServerIdentity {
    fn load(c: &TlsServerIdentityConfig) -> Result<ServerIdentity, Error> {
        let mut certs = vec![];
        for p in &c.certs {
            certs.append(&mut load_certs(p)?);
        }
        let key = load_private_key(&c.private_key)?;
        Ok(ServerIdentity {
            key: sign::CertifiedKey::new(certs, Arc::new(key)),
        })
    }
}

// from rustls example
fn load_certs(cert_file_path: &String) -> Result<Vec<Certificate>, Error> {
    let file = File::open(&cert_file_path)
        .map_err(|e| Error::FailedToOpenCertificateFile(cert_file_path.clone(), e))?;
    let mut r = io::BufReader::new(file);
    pemfile::certs(&mut r)
        .map_err(|()| Error::FailedToReadCertificateFile(cert_file_path.clone()))
}

// from rustls example
fn load_private_key(key_file_path: &String)
                    -> Result<Box<sign::SigningKey>, Error> {
    let file = File::open(&key_file_path)
        .map_err(|e| Error::FailedToOpenPrivateKeyFile(key_file_path.clone(), e))?;
    let mut r = io::BufReader::new(file);
    let keys = pemfile::rsa_private_keys(&mut r)
        .map_err(|()| Error::FailedToReadPrivateKeyFile(key_file_path.clone()))?;
    if keys.len() != 1 {
        return Err(Error::WrongNumberOfKeysInPrivateKeyFile(key_file_path.clone(), keys.len()));
    }
    let key = sign::RSASigningKey::new(&keys[0])
        .map_err(|()| Error::FailedToConstructPrivateKey(key_file_path.clone()))?;
    Ok(Box::new(key))
}
<|endoftext|>