<commit_before>//! The interface for interacting with TMUX sessions. All the commands that are
/// built up during the parsing phase get matched to functions here. The
/// functions in this module all build up strings the get passed to a private
/// call. The private call converts them to CStrings and makes an "unsafe" system
/// call. All functions go through this `call` function as a common gateway to
/// system calls and can all be easily logged there.

pub mod config;

use libc::system;
use std::ffi::CString;
use std::process::{Command, ExitStatus, Output};
use std::io;

/// The program to call commands on.
static TMUX_NAME: &'static str = "tmux";

/// The gateway to calling any functions on tmux. Most public functions in this
/// module will be fed through this `call` function. This safely creates a new
/// thread to execute the command on. We say "Most" public functions will use
/// this as `attach` specificaly does not use it.
///
/// args: The command we will send to tmux on the host system for execution.
///
/// # Examples
///
/// ```
/// let _ = call(&["new-window", "-t", "muxed", "-c", "~/Projects/muxed/"]);
/// ```
fn call(args: &[&str]) -> Result<Output, io::Error> {
    println!("{:?}", &args);
    Command::new(TMUX_NAME).args(args).output()
}

/// Attach is called as the last function in a set of commands. After the tmux
/// env has been setup by all previous commands this attaches the user to their
/// daemonized tmux session.
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
/// session_name: The active tmux session name.
pub fn attach(session_name: &String) -> () {
    let line = format!("{} attach -t {} {}", TMUX_NAME, session_name, ">/dev/null");
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe { system(system_call.as_ptr()); };
}

/// New session is the first call made in any sequence of commands. It initiates
/// a new tmux session with a default first window name.
/// *Why a randomized first window name?*
///   This is because the `new-session` tmux command does not support the `-c`
/// flag as `new-window` and `split-window` do. So if the first window requires
/// being changed to a default directory `send-keys` must be used after the
/// session and first window are opened. This means the first window is treated in
/// a different code path than any other window that is opened.
/// Instead I've opted to open the session with a randomized (no conflicts)
/// named window, and we close this window out before attaching the user. This
/// allows for us to treat all user defined windows the same.
///
/// session_name: The name of the session being created.
/// tmp_name: The named first window that we will close out.
///
/// # Examples
///
/// ```
/// use rand::random;
///
/// let session_name = "muxed".to_string();
/// let tmp_window_name = random::<u16>().to_string();
/// tmux::new_session(session_name, tmp_window_name);
/// ```
///
/// session_name: The active tmux session name.
/// tmp_name: The name for the temp initial window that is created with a new
/// session.
pub fn new_session(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new", "-d", "-s", session_name, "-n", window_name]);
}

/// Split window acts as the command to target named windows in a session to
/// send a make separate a single pane into two panes. Split window should target
/// a specific pane even if only a single pane exists.
///
/// # Examples
///
/// ```
/// let target = "muxed:cargo.0".to_string();
/// tmux::split_window(target, None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn split_window(target: &String) -> () {
    let _ = call(&["split-window", "-t", target]);
}

/// New window opens a new window in the named session with the provided window
/// name.
///
/// # Examples
///
/// ```
/// tmux::new_window("muxed".to_string(), "vim".to_string(), None);
/// ```
///
/// session_name: The active tmux session name.
/// window_name: The desired window name for the new window.
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn new_window(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new-window", "-t", session_name, "-n", window_name]);
}

/// The layout function will adjust the layout of a tmux window that already has
/// multiple panes. Currently it will only accept standard tmux layout types.
///
/// Possible layout options: even-horizontal, even-vertical, main-horizontal,
/// main-vertical, or tiled.
///
/// # Examples
///
/// ```
/// tmux::layout("muxed:cargo.0".to_string(), "main-vertical".to_string(), None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// layout: The predefined tmux named layout.
pub fn layout(target: &String, layout: &String) -> () {
    let _ = call(&["select-layout", "-t", target, layout]);
}

/// Send Keys executes literal key commands in specified target windows. This is
/// the execution method to perform actions like tailing logs, starting servers,
/// or opening editors.
///
/// `KPEnter` represent the keyboard press of the "Enter" key. So the execution
/// is a user input command followed by "enter"
///
/// # Examples
///
/// ```
/// tmux::send_keys("muxed:cargo.0".to_string(), "vim .".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// exec: The system command to be executed in a particular pane.
pub fn send_keys(target: &String, exec: &String) -> () {
    let _ = call(&["send-keys", "-t", target, exec, "KPEnter"]);
}

/// This is used to re-select the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_window("muxed:cargo.0".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
pub fn select_window(target: &String) -> () {
    let _ = call(&["select-window", "-t", target]);
}

/// This is used to re-select the top pane in the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_pane("muxed:cargo.top".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
pub fn select_pane(target: &String) -> () {
    let _ = call(&["select-pane", "-t", target]);
}

/// List Windows is used firgure out if a named session is already running.
///
/// # Examples
///
/// ```
/// tmux::has_session("muxed".to_string());
/// => ExitStatus
/// ```
///
/// target: A string represented by the {named_session}
pub fn has_session(target: &String) -> ExitStatus {
    let output = call(&["has-session", "-t", target]).expect("failed to see if the session existed");
    output.status
}

/// Read the tmux config and return a config object
///
/// # Examples
///
/// ```
/// tmux::get_config();
/// => "some-option false\npane-base-index 0"
/// ```
pub fn get_config() -> String {
    let output = call(&["start-server", ";", "show-options", "-g"]).expect("couldn't get tmux options");
    String::from_utf8_lossy(&output.stdout).to_string()
}
<commit_msg>style(tmux/mod.rs): Remove un-needed println!<commit_after>//! The interface for interacting with TMUX sessions. All the commands that are
/// built up during the parsing phase get matched to functions here. The
/// functions in this module all build up strings the get passed to a private
/// call. The private call converts them to CStrings and makes an "unsafe" system
/// call. All functions go through this `call` function as a common gateway to
/// system calls and can all be easily logged there.

pub mod config;

use libc::system;
use std::ffi::CString;
use std::process::{Command, ExitStatus, Output};
use std::io;

/// The program to call commands on.
static TMUX_NAME: &'static str = "tmux";

/// The gateway to calling any functions on tmux. Most public functions in this
/// module will be fed through this `call` function. This safely creates a new
/// thread to execute the command on. We say "Most" public functions will use
/// this as `attach` specificaly does not use it.
///
/// args: The command we will send to tmux on the host system for execution.
///
/// # Examples
///
/// ```
/// let _ = call(&["new-window", "-t", "muxed", "-c", "~/Projects/muxed/"]);
/// ```
fn call(args: &[&str]) -> Result<Output, io::Error> {
    //println!("{:?}", &args);
    Command::new(TMUX_NAME).args(args).output()
}

/// Attach is called as the last function in a set of commands. After the tmux
/// env has been setup by all previous commands this attaches the user to their
/// daemonized tmux session.
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
/// session_name: The active tmux session name.
pub fn attach(session_name: &String) -> () {
    let line = format!("{} attach -t {} {}", TMUX_NAME, session_name, ">/dev/null");
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe { system(system_call.as_ptr()); };
}

/// New session is the first call made in any sequence of commands. It initiates
/// a new tmux session with a default first window name.
/// *Why a randomized first window name?*
///   This is because the `new-session` tmux command does not support the `-c`
/// flag as `new-window` and `split-window` do. So if the first window requires
/// being changed to a default directory `send-keys` must be used after the
/// session and first window are opened. This means the first window is treated in
/// a different code path than any other window that is opened.
/// Instead I've opted to open the session with a randomized (no conflicts)
/// named window, and we close this window out before attaching the user. This
/// allows for us to treat all user defined windows the same.
///
/// session_name: The name of the session being created.
/// tmp_name: The named first window that we will close out.
///
/// # Examples
///
/// ```
/// use rand::random;
///
/// let session_name = "muxed".to_string();
/// let tmp_window_name = random::<u16>().to_string();
/// tmux::new_session(session_name, tmp_window_name);
/// ```
///
/// session_name: The active tmux session name.
/// tmp_name: The name for the temp initial window that is created with a new
/// session.
pub fn new_session(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new", "-d", "-s", session_name, "-n", window_name]);
}

/// Split window acts as the command to target named windows in a session to
/// send a make separate a single pane into two panes. Split window should target
/// a specific pane even if only a single pane exists.
///
/// # Examples
///
/// ```
/// let target = "muxed:cargo.0".to_string();
/// tmux::split_window(target, None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn split_window(target: &String) -> () {
    let _ = call(&["split-window", "-t", target]);
}

/// New window opens a new window in the named session with the provided window
/// name.
///
/// # Examples
///
/// ```
/// tmux::new_window("muxed".to_string(), "vim".to_string(), None);
/// ```
///
/// session_name: The active tmux session name.
/// window_name: The desired window name for the new window.
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn new_window(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new-window", "-t", session_name, "-n", window_name]);
}

/// The layout function will adjust the layout of a tmux window that already has
/// multiple panes. Currently it will only accept standard tmux layout types.
///
/// Possible layout options: even-horizontal, even-vertical, main-horizontal,
/// main-vertical, or tiled.
///
/// # Examples
///
/// ```
/// tmux::layout("muxed:cargo.0".to_string(), "main-vertical".to_string(), None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// layout: The predefined tmux named layout.
pub fn layout(target: &String, layout: &String) -> () {
    let _ = call(&["select-layout", "-t", target, layout]);
}

/// Send Keys executes literal key commands in specified target windows. This is
/// the execution method to perform actions like tailing logs, starting servers,
/// or opening editors.
///
/// `KPEnter` represent the keyboard press of the "Enter" key. So the execution
/// is a user input command followed by "enter"
///
/// # Examples
///
/// ```
/// tmux::send_keys("muxed:cargo.0".to_string(), "vim .".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// exec: The system command to be executed in a particular pane.
pub fn send_keys(target: &String, exec: &String) -> () {
    let _ = call(&["send-keys", "-t", target, exec, "KPEnter"]);
}

/// This is used to re-select the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_window("muxed:cargo.0".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
pub fn select_window(target: &String) -> () {
    let _ = call(&["select-window", "-t", target]);
}

/// This is used to re-select the top pane in the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_pane("muxed:cargo.top".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
pub fn select_pane(target: &String) -> () {
    let _ = call(&["select-pane", "-t", target]);
}

/// List Windows is used firgure out if a named session is already running.
///
/// # Examples
///
/// ```
/// tmux::has_session("muxed".to_string());
/// => ExitStatus
/// ```
///
/// target: A string represented by the {named_session}
pub fn has_session(target: &String) -> ExitStatus {
    let output = call(&["has-session", "-t", target]).expect("failed to see if the session existed");
    output.status
}

/// Read the tmux config and return a config object
///
/// # Examples
///
/// ```
/// tmux::get_config();
/// => "some-option false\npane-base-index 0"
/// ```
pub fn get_config() -> String {
    let output = call(&["start-server", ";", "show-options", "-g"]).expect("couldn't get tmux options");
    String::from_utf8_lossy(&output.stdout).to_string()
}
<|endoftext|>


<commit_before>#![doc="Defines generic traits for numbers on which
SciRust library works.
"]

// Imports
use std::fmt::Show;

// complex numbers
use external::complex::{Complex32, Complex64};

/// Defines all the traits which a matrix element must support
pub trait Number : Num+Copy+Show {

    fn is_float() -> bool {
        false
    }

    fn is_int() -> bool {
        false
    }

    fn is_complex() -> bool {
        false
    }

    fn is_signed() -> bool;
}

/// Indicate that i8 fits all requirements for being a matrix element.
impl Number for i8 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}
/// Indicate that i16 fits all requirements for being a matrix element.
impl Number for i16 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that i32 fits all requirements for being a matrix element.
impl Number for i32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that i64 fits all requirements for being a matrix element.
impl Number for i64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that int fits all requirements for being a matrix element.
impl Number for int {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that u8 fits all requirements for being a matrix element.
impl Number for u8 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that u16 fits all requirements for being a matrix element.
impl Number for u16 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that u32 fits all requirements for being a matrix element.
impl Number for u32 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that u64 fits all requirements for being a matrix element.
impl Number for u64 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that uint fits all requirements for being a matrix element.
impl Number for uint {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that f32 fits all requirements for being a matrix element.
impl Number for f32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_float() -> bool {
        true
    }

}

/// Indicate that f64 fits all requirements for being a matrix element.
impl Number for f64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_float() -> bool {
        true
    }

}

/// Indicate that Complex32 fits all requirements for being a matrix element.
impl Number for Complex32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}

/// Indicate that Complex64 fits all requirements for being a matrix element.
impl Number for Complex64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}

//TODO: figure out how to make this work.
pub fn describe<T: Number>(value : T ){
    //println!("Signed: {}", T.is_signed());
}
<commit_msg>commented unused function.<commit_after>#![doc="Defines generic traits for numbers on which
SciRust library works.
"]

// Imports
use std::fmt::Show;

// complex numbers
use external::complex::{Complex32, Complex64};

/// Defines all the traits which a matrix element must support
pub trait Number : Num+Copy+Show {

    fn is_float() -> bool {
        false
    }

    fn is_int() -> bool {
        false
    }

    fn is_complex() -> bool {
        false
    }

    fn is_signed() -> bool;
}

/// Indicate that i8 fits all requirements for being a matrix element.
impl Number for i8 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}
/// Indicate that i16 fits all requirements for being a matrix element.
impl Number for i16 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that i32 fits all requirements for being a matrix element.
impl Number for i32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that i64 fits all requirements for being a matrix element.
impl Number for i64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that int fits all requirements for being a matrix element.
impl Number for int {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that u8 fits all requirements for being a matrix element.
impl Number for u8 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that u16 fits all requirements for being a matrix element.
impl Number for u16 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that u32 fits all requirements for being a matrix element.
impl Number for u32 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that u64 fits all requirements for being a matrix element.
impl Number for u64 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that uint fits all requirements for being a matrix element.
impl Number for uint {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that f32 fits all requirements for being a matrix element.
impl Number for f32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_float() -> bool {
        true
    }

}

/// Indicate that f64 fits all requirements for being a matrix element.
impl Number for f64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_float() -> bool {
        true
    }

}

/// Indicate that Complex32 fits all requirements for being a matrix element.
impl Number for Complex32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}

/// Indicate that Complex64 fits all requirements for being a matrix element.
impl Number for Complex64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}

//TODO: figure out how to make this work.
//pub fn describe<T: Number>(){
    //println!("Signed: {}", T.is_signed());
//}
<|endoftext|>


<commit_before>use IpfsApi;

use reqwest;
use serde_json;

error_chain! {
    foreign_links {
        Reqwest(reqwest::Error);
        JsonDecode(serde_json::Error);
    }
}

#[derive(Deserialize, Debug, PartialEq, Hash)]
#[serde(rename_all="PascalCase")]
pub struct ObjectStats {
    hash: String,
    cumulative_size: u64
}

impl IpfsApi {
    /// Get stats for an IPFS hash. It can be used to get the recursive size
    /// of a hash.
    pub fn object_stats(&self, hash: &str) -> Result<ObjectStats> {
        let url = format!("http://{}:{}/api/v0/object/stat?arg={}", self.server, self.port, hash);
        let resp = reqwest::get(&url)?;
        Ok(serde_json::from_reader(resp)?)
    }
}

#[cfg(test)]
mod tests {
    use IpfsApi;
    use super::*;
    #[test]
    fn test_object_stats() {
        let api = IpfsApi::new("127.0.0.1", 5001);
        // Hello world object
        let stats = api.object_stats("QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u").unwrap();
        let desired = ObjectStats {
            hash: "QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u".to_string(),
            cumulative_size: 20,
        };

        assert_eq!(stats, desired);
    }
}
<commit_msg>Added accessor methods for the object API<commit_after>use IpfsApi;

use reqwest;
use serde_json;

error_chain! {
    foreign_links {
        Reqwest(reqwest::Error);
        JsonDecode(serde_json::Error);
    }
}

#[derive(Deserialize, Debug, PartialEq, Hash)]
#[serde(rename_all="PascalCase")]
pub struct ObjectStats {
    hash: String,
    num_links: u64,
    block_size: u64,
    links_size: u64,
    data_size: u64,
    cumulative_size: u64
}

impl ObjectStats {
    pub fn hash(&self) -> String {
        self.hash.clone()
    }

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

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

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

    /// Get the total size an IPFS hash takes. Includes all the recursive
    /// directories and files.
    pub fn cumulative_size(&self) -> u64 {
        self.cumulative_size
    }
}

impl IpfsApi {
    /// Get stats for an IPFS hash. It can be used to get the recursive size
    /// of a hash.
    pub fn object_stats(&self, hash: &str) -> Result<ObjectStats> {
        let url = format!("http://{}:{}/api/v0/object/stat?arg={}", self.server, self.port, hash);
        let resp = reqwest::get(&url)?;
        Ok(serde_json::from_reader(resp)?)
    }
}

#[cfg(test)]
mod tests {
    use IpfsApi;
    use super::*;
    #[test]
    fn test_object_stats() {
        let api = IpfsApi::new("127.0.0.1", 5001);
        // Hello world object
        let stats = api.object_stats("QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u").unwrap();
        let desired = ObjectStats {
            hash: "QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u".to_string(),
            cumulative_size: 20,
        };

        assert_eq!(stats, desired);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::collections::HashMap;

use super::ast::Expression;
use super::ast::Statement;
use super::ast::Node;

#[derive(Debug, Clone)]
pub enum Object {
    Integer(i64),
    Boolean(bool),
    String(String),
    Null,
    ReturnValue(Box<Object>),
    Error(String),
    Function {
        parameters: Vec<Expression>,
        body: Statement,
        env: Env,
    },
}

impl Object {
    pub fn inspect(&self) -> String {
        match *self {
            Object::Integer(val) => format!("{}", val),
            Object::Boolean(val) => format!("{}", val),
            Object::String(ref val) => format!("{}", val),
            Object::Null => "null".to_string(),
            Object::ReturnValue(ref obj) => obj.inspect(),
            Object::Error(ref msg) => format!("ERROR: {}", msg),
            Object::Function { ref parameters, ref body, .. } => {
                let mut params = Vec::new();
                for p in parameters {
                    params.push(p.string());
                }

                format!("fn({}){{\n{}\n}}", params.join(", "), body.string())
            }
        }
    }

    pub fn is_truthy(&self) -> bool {
        match *self {
            Object::Boolean(true) => true,
            Object::Boolean(false) => false,
            ref o if o.is_null() => false,
            _ => true,
        }
    }

    pub fn is_error(&self) -> bool {
        match *self {
            Object::Error(..) => true,
            _ => false,
        }
    }

    pub fn is_null(&self) -> bool {
        match *self {
            Object::Null => true,
            _ => false,
        }
    }
}

impl fmt::Display for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Integer(..) => write!(f, "Integer"),
            Object::Boolean(..) => write!(f, "Boolean"),
            Object::String(..) => write!(f, "String"),
            _ => write!(f, ""),
        }
    }
}

pub const TRUE: Object = Object::Boolean(true);
pub const FALSE: Object = Object::Boolean(false);
pub const NULL: Object = Object::Null;

#[derive(Debug, Clone)]
pub struct Env {
    store: HashMap<String, Object>,
}

impl Env {
    pub fn new() -> Self {
        Env { store: HashMap::new() }
    }

    pub fn get<'a>(&'a self, name: &str) -> Object {
        match self.store.get(name) {
            Some(obj) => obj.clone(),
            None => Object::Error(format!("Identifier not found: {}", name)),
        }
    }

    pub fn set(&mut self, name: &str, val: Object) {
        self.store.insert(name.to_string(), val);
    }
}
<commit_msg>Initial BuiltinFn.<commit_after>use std::fmt;
use std::collections::HashMap;

use super::ast::Expression;
use super::ast::Statement;
use super::ast::Node;

pub struct BuiltinFn(fn(&[Object]) -> Object);

impl Clone for BuiltinFn {
    fn clone(&self) -> Self {
        BuiltinFn(self.0)
    }
}

impl fmt::Debug for BuiltinFn {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "BuiltinFn")
    }
}

#[derive(Debug, Clone)]
pub enum Object {
    Integer(i64),
    Boolean(bool),
    String(String),
    Null,
    ReturnValue(Box<Object>),
    Error(String),
    Function {
        parameters: Vec<Expression>,
        body: Statement,
        env: Env,
    },
    Builtin(BuiltinFn),
}

impl Object {
    pub fn inspect(&self) -> String {
        match *self {
            Object::Integer(val) => format!("{}", val),
            Object::Boolean(val) => format!("{}", val),
            Object::String(ref val) => format!("{}", val),
            Object::Null => "null".to_string(),
            Object::ReturnValue(ref obj) => obj.inspect(),
            Object::Error(ref msg) => format!("ERROR: {}", msg),
            Object::Function { ref parameters, ref body, .. } => {
                let mut params = Vec::new();
                for p in parameters {
                    params.push(p.string());
                }

                format!("fn({}){{\n{}\n}}", params.join(", "), body.string())
            }
            Object::Builtin(..) => "builtin function".to_string(),
        }
    }

    pub fn is_truthy(&self) -> bool {
        match *self {
            Object::Boolean(true) => true,
            Object::Boolean(false) => false,
            ref o if o.is_null() => false,
            _ => true,
        }
    }

    pub fn is_error(&self) -> bool {
        match *self {
            Object::Error(..) => true,
            _ => false,
        }
    }

    pub fn is_null(&self) -> bool {
        match *self {
            Object::Null => true,
            _ => false,
        }
    }
}

impl fmt::Display for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Integer(..) => write!(f, "Integer"),
            Object::Boolean(..) => write!(f, "Boolean"),
            Object::String(..) => write!(f, "String"),
            _ => write!(f, ""),
        }
    }
}

pub const TRUE: Object = Object::Boolean(true);
pub const FALSE: Object = Object::Boolean(false);
pub const NULL: Object = Object::Null;

#[derive(Debug, Clone)]
pub struct Env {
    store: HashMap<String, Object>,
}

impl Env {
    pub fn new() -> Self {
        Env { store: HashMap::new() }
    }

    pub fn get<'a>(&'a self, name: &str) -> Object {
        match self.store.get(name) {
            Some(obj) => obj.clone(),
            None => Object::Error(format!("Identifier not found: {}", name)),
        }
    }

    pub fn set(&mut self, name: &str, val: Object) {
        self.store.insert(name.to_string(), val);
    }
}
<|endoftext|>


<commit_before>use libc;
use {Errno, Result};
use std::mem;

#[derive(Clone, Copy)]
pub struct UContext {
    context: libc::ucontext_t,
}

impl UContext {
    pub fn get() -> Result<UContext> {
        let mut context: libc::ucontext_t = unsafe { mem::uninitialized() };
        let res = unsafe {
            libc::getcontext(&mut context as *mut libc::ucontext_t)
        };
        Errno::result(res).map(|_| UContext { context: context })
    }

    pub fn set(&self) -> Result<()> {
        let res = unsafe {
            libc::setcontext(&self.context as *const libc::ucontext_t)
        };
        Errno::result(res).map(drop)
    }
}
<commit_msg>Auto merge of #371 - justinlatimer:musl, r=kamalmarhubi<commit_after>use libc;
#[cfg(not(target_env = "musl"))]
use {Errno, Result};
#[cfg(not(target_env = "musl"))]
use std::mem;

#[derive(Clone, Copy)]
pub struct UContext {
    context: libc::ucontext_t,
}

impl UContext {
    #[cfg(not(target_env = "musl"))]
    pub fn get() -> Result<UContext> {
        let mut context: libc::ucontext_t = unsafe { mem::uninitialized() };
        let res = unsafe {
            libc::getcontext(&mut context as *mut libc::ucontext_t)
        };
        Errno::result(res).map(|_| UContext { context: context })
    }

    #[cfg(not(target_env = "musl"))]
    pub fn set(&self) -> Result<()> {
        let res = unsafe {
            libc::setcontext(&self.context as *const libc::ucontext_t)
        };
        Errno::result(res).map(drop)
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

mod varlink_grammar {

    pub enum VType<'a> {
        Bool,
        Int8,
        UInt8,
        Int16,
        UInt16,
        Int32,
        UInt32,
        Int64,
        UInt64,
        Float32,
        Float64,
        VString,
        VTypename(&'a str),
        VStruct(Box<VStruct<'a>>),
    }

    pub struct VTypeExt<'a> {
        vtype: VType<'a>,
        nullable: bool,
        isarray: Option<Option<usize>>,
    }

    pub struct Argument<'a> {
        pub name: &'a str,
        pub vtypes : Vec<VTypeExt<'a>>,
    }

    pub struct VStruct<'a> {
        pub elts: Vec<Argument<'a>>,
    }

    pub struct Typedef<'a> {
        pub name: &'a str,
        pub vstruct : VStruct<'a>,
    }

    pub struct Method<'a> {
        pub name: &'a str,
        pub input : VStruct<'a>,
        pub output : VStruct<'a>,
    }

    enum MethodOrTypedef<'a> {
        Typedef(Typedef<'a>),
        Method(Method<'a>),
    }

    pub struct Interface<'a> {
        pub name: &'a str,
        pub methods: Vec<Method<'a>>,
        pub typedefs: Vec<Typedef<'a>>,
    }

    use std::fmt;

    impl<'a> fmt::Display for VType<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match *self {
                VType::Bool => write!(f, "bool"),
                VType::Int8 => write!(f, "int8"),
                VType::UInt8 => write!(f, "uint8"),
                VType::Int16 => write!(f, "int16"),
                VType::UInt16 => write!(f, "uint16"),
                VType::Int32 => write!(f, "int32"),
                VType::UInt32 => write!(f, "uint32"),
                VType::Int64 => write!(f, "int64"),
                VType::UInt64 => write!(f, "uint64"),
                VType::Float32 => write!(f, "float32"),
                VType::Float64 => write!(f, "float64"),
                VType::VString => write!(f, "string"),
                VType::VTypename(ref s) => write!(f, "{}", s),
                VType::VStruct(ref v) => write!(f, "{}", v),
            }
        }
    }

    impl<'a> fmt::Display for VTypeExt<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{}", self.vtype)?;
            if let Some(t) = self.isarray {
                match t {
                    Some(v) => write!(f, "[{}]", v)?,
                    None    => write!(f, "[]")?,
                }
            }
            if self.nullable {
                write!(f, "?")
            } else {
                Ok(())
            }
        }
    }

    impl<'a> fmt::Display for Argument<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            if self.vtypes.len() == 1 {
                write!(f, "{}: {}", self.name, self.vtypes[0])?;
            } else {
                let mut iter = self.vtypes.iter();
                if let Some(fst) = iter.next() {
                    write!(f, "{}: {}", self.name, fst)?;
                    for elt in iter {
                        write!(f, " | {}", elt)?;
                    }
                }
            }
            Ok(())
        }
    }

    impl<'a> fmt::Display for VStruct<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "(")?;
            let mut iter = self.elts.iter();
            if let Some(fst) = iter.next() {
                write!(f, "{}", fst)?;
                for elt in iter {
                    write!(f, ", {}", elt)?;
                }
            }
            write!(f, ")")
        }
    }

    impl<'a> fmt::Display for Interface<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{} {{\n", self.name)?;
            for t in &self.typedefs {
                write!(f, "  type {} {};\n", t.name, t.vstruct)?;
            }
            for m in &self.methods {
                write!(f, "  {}{} -> {};\n", m.name, m.input, m.output)?;
            }
            write!(f, "}}\n")
        }
    }

    impl<'a> Interface<'a> {
        fn from_token(n: &'a str, ts: Vec<Typedef<'a>>, m: Method<'a>, mt: Vec<MethodOrTypedef<'a>>) -> Interface<'a> {
            let mut i = Interface { name: n, methods: Vec::new(), typedefs: Vec::new() };

            i.methods.push(m);

            for o in mt {
                match o {
                    MethodOrTypedef::Method(m) => i.methods.push(m),
                    MethodOrTypedef::Typedef(t) => i.typedefs.push(t)
                };
            }

            for t in ts {
                i.typedefs.push(t);
            }

            return i;
        }
    }

    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

#[cfg(test)]
use self::varlink_grammar::*;

#[test]
fn test_standard() {
    let ifaces = interfaces("
/**
 * The Varlink Service Interface is added to every varlink service. It provides
 * the Introspect method to be called by a client to retrieve the bootstrap
 * information from a service.
 */
org.varlink.service {
  type Type (name: string, typestring: string)
  type Method (
    name: string,
    monitor: bool,
    type_in: string,
    type_out: string
  )
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
  type InterfaceDescription (
    description: string,
    types: string[],
    methods: string[]
  )

  /**
   * Returns the machine readable information about a service. It contains the service
   * name, all available interfaces with their defined method calls and types.
   */
  Introspect(version: uint64) -> (name: string, interfaces: Interface[])

  /**
   * Returns the human readable description of a service.
   */
  Help() -> (
    description: string,
    properties: Property[],
    interfaces: InterfaceDescription[]
  )
}
")
        .unwrap();
    assert_eq!(ifaces[0].name, "org.varlink.service");
    assert_eq!(ifaces[0].to_string(), "org.varlink.service {
  type Type (name: string, typestring: string);
  type Method (name: string, monitor: bool, type_in: string, type_out: string);
  type Interface (name: string, types: Type[], methods: Method[]);
  type Property (key: string, value: string);
  type InterfaceDescription (description: string, types: string[], methods: string[]);
  Introspect(version: uint64) -> (name: string, interfaces: Interface[]);
  Help() -> (description: string, properties: Property[], interfaces: InterfaceDescription[]);
}
");

}

#[test]
fn test_one_method() {
    assert!(interfaces("/* comment */ foo.bar{ Foo()->() }").is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(interfaces("foo.bar{ Foo()->(b:) }").is_err());
}

#[test]
fn test_domainnames() {
    assert!(interfaces("org.varlink.service {F()->()}").is_ok());
    assert!(interfaces("com.example.0example {F()->()}").is_ok());
    assert!(interfaces("com.example.example-dash {F()->()}").is_ok());
    assert!(interfaces("xn--lgbbat1ad8j.example.algeria {F()->()}").is_ok());
    assert!(interfaces("com.-example.leadinghyphen {F()->()}").is_err());
    assert!(interfaces("com.example-.danglinghyphen- {F()->()}").is_err());
    assert!(interfaces("Com.example.uppercase-toplevel {F()->()}").is_err());
    assert!(interfaces("Co9.exmaple.number-toplevel {F()->()}").is_err());
    assert!(interfaces("com.Example {F()->()}").is_err());
}

#[test]
fn test_no_method() {
    assert!(interfaces("
org.varlink.service {
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
}
")
        .is_err());
}

#[test]
fn test_type_no_args() {
    assert!(interfaces("foo.bar{ type I () F()->() }").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(interfaces("foo.bar{ type I (b:bool) F()->() }").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(interfaces("foo.bar{ type I (b:bool[]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[1]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 1 ]) F()->() }").is_err());
}

#[test]
fn test_method_struct_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)? )->()}").is_ok());
}

#[test]
fn test_method_struct_array_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)[]? )->()}").is_ok());
}

#[test]
fn test_method_struct_array_optional_wrong() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)?[]) -> ()}").is_err());
}

#[test]
fn test_format() {
    let i = interfaces("foo.bar{ type I (b:bool[1]) F()->() }").unwrap();
    assert_eq!(i[0].to_string(), "foo.bar {
  type I (b: bool[1]);
  F() -> ();
}
");
}

#[test]
fn test_union() {
    let i = interfaces("foo.bar{ F()->(s: (a: bool, b: int64), u: bool|int64|(foo: bool, bar: bool)) }").unwrap();
    println!("{}", i[0]);
    assert_eq!(i[0].to_string(), "foo.bar {
  F() -> (s: (a: bool, b: int64), u: bool | int64 | (foo: bool, bar: bool));
}
");
}
<commit_msg>reformat source<commit_after>#![allow(dead_code)]

mod varlink_grammar {

    pub enum VType<'a> {
        Bool,
        Int8,
        UInt8,
        Int16,
        UInt16,
        Int32,
        UInt32,
        Int64,
        UInt64,
        Float32,
        Float64,
        VString,
        VTypename(&'a str),
        VStruct(Box<VStruct<'a>>),
    }

    pub struct VTypeExt<'a> {
        vtype: VType<'a>,
        nullable: bool,
        isarray: Option<Option<usize>>,
    }

    pub struct Argument<'a> {
        pub name: &'a str,
        pub vtypes: Vec<VTypeExt<'a>>,
    }

    pub struct VStruct<'a> {
        pub elts: Vec<Argument<'a>>,
    }

    pub struct Typedef<'a> {
        pub name: &'a str,
        pub vstruct: VStruct<'a>,
    }

    pub struct Method<'a> {
        pub name: &'a str,
        pub input: VStruct<'a>,
        pub output: VStruct<'a>,
    }

    enum MethodOrTypedef<'a> {
        Typedef(Typedef<'a>),
        Method(Method<'a>),
    }

    pub struct Interface<'a> {
        pub name: &'a str,
        pub methods: Vec<Method<'a>>,
        pub typedefs: Vec<Typedef<'a>>,
    }

    use std::fmt;

    impl<'a> fmt::Display for VType<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match *self {
                VType::Bool => write!(f, "bool"),
                VType::Int8 => write!(f, "int8"),
                VType::UInt8 => write!(f, "uint8"),
                VType::Int16 => write!(f, "int16"),
                VType::UInt16 => write!(f, "uint16"),
                VType::Int32 => write!(f, "int32"),
                VType::UInt32 => write!(f, "uint32"),
                VType::Int64 => write!(f, "int64"),
                VType::UInt64 => write!(f, "uint64"),
                VType::Float32 => write!(f, "float32"),
                VType::Float64 => write!(f, "float64"),
                VType::VString => write!(f, "string"),
                VType::VTypename(ref s) => write!(f, "{}", s),
                VType::VStruct(ref v) => write!(f, "{}", v),
            }
        }
    }

    impl<'a> fmt::Display for VTypeExt<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{}", self.vtype)?;
            if let Some(t) = self.isarray {
                match t {
                    Some(v) => write!(f, "[{}]", v)?,
                    None => write!(f, "[]")?,
                }
            }
            if self.nullable {
                write!(f, "?")
            } else {
                Ok(())
            }
        }
    }

    impl<'a> fmt::Display for Argument<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            if self.vtypes.len() == 1 {
                write!(f, "{}: {}", self.name, self.vtypes[0])?;
            } else {
                let mut iter = self.vtypes.iter();
                if let Some(fst) = iter.next() {
                    write!(f, "{}: {}", self.name, fst)?;
                    for elt in iter {
                        write!(f, " | {}", elt)?;
                    }
                }
            }
            Ok(())
        }
    }

    impl<'a> fmt::Display for VStruct<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "(")?;
            let mut iter = self.elts.iter();
            if let Some(fst) = iter.next() {
                write!(f, "{}", fst)?;
                for elt in iter {
                    write!(f, ", {}", elt)?;
                }
            }
            write!(f, ")")
        }
    }

    impl<'a> fmt::Display for Interface<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{} {{\n", self.name)?;
            for t in &self.typedefs {
                write!(f, "  type {} {};\n", t.name, t.vstruct)?;
            }
            for m in &self.methods {
                write!(f, "  {}{} -> {};\n", m.name, m.input, m.output)?;
            }
            write!(f, "}}\n")
        }
    }

    impl<'a> Interface<'a> {
        fn from_token(n: &'a str,
                      ts: Vec<Typedef<'a>>,
                      m: Method<'a>,
                      mt: Vec<MethodOrTypedef<'a>>)
                      -> Interface<'a> {
            let mut i = Interface {
                name: n,
                methods: Vec::new(),
                typedefs: Vec::new(),
            };

            i.methods.push(m);

            for o in mt {
                match o {
                    MethodOrTypedef::Method(m) => i.methods.push(m),
                    MethodOrTypedef::Typedef(t) => i.typedefs.push(t),
                };
            }

            for t in ts {
                i.typedefs.push(t);
            }

            return i;
        }
    }

    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

#[cfg(test)]
use self::varlink_grammar::*;

#[test]
fn test_standard() {
    let ifaces = interfaces("
/**
 * The Varlink Service Interface is added to every varlink service. It provides
 * the Introspect method to be called by a client to retrieve the bootstrap
 * information from a service.
 */
org.varlink.service {
  type Type (name: string, typestring: string)
  type Method (
    name: string,
    monitor: bool,
    type_in: string,
    type_out: string
  )
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
  type InterfaceDescription (
    description: string,
    types: string[],
    methods: string[]
  )

  /**
   * Returns the machine readable information about a service. It contains the service
   * name, all available interfaces with their defined method calls and types.
   */
  Introspect(version: uint64) -> (name: string, interfaces: Interface[])

  /**
   * Returns the human readable description of a service.
   */
  Help() -> (
    description: string,
    properties: Property[],
    interfaces: InterfaceDescription[]
  )
}
")
        .unwrap();
    assert_eq!(ifaces[0].name, "org.varlink.service");
    assert_eq!(ifaces[0].to_string(),
               "org.varlink.service {
  type Type (name: string, typestring: string);
  type \
                Method (name: string, monitor: bool, type_in: string, type_out: string);
  type \
                Interface (name: string, types: Type[], methods: Method[]);
  type Property \
                (key: string, value: string);
  type InterfaceDescription (description: string, \
                types: string[], methods: string[]);
  Introspect(version: uint64) -> (name: \
                string, interfaces: Interface[]);
  Help() -> (description: string, properties: \
                Property[], interfaces: InterfaceDescription[]);
}
");

}

#[test]
fn test_one_method() {
    assert!(interfaces("/* comment */ foo.bar{ Foo()->() }").is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(interfaces("foo.bar{ Foo()->(b:) }").is_err());
}

#[test]
fn test_domainnames() {
    assert!(interfaces("org.varlink.service {F()->()}").is_ok());
    assert!(interfaces("com.example.0example {F()->()}").is_ok());
    assert!(interfaces("com.example.example-dash {F()->()}").is_ok());
    assert!(interfaces("xn--lgbbat1ad8j.example.algeria {F()->()}").is_ok());
    assert!(interfaces("com.-example.leadinghyphen {F()->()}").is_err());
    assert!(interfaces("com.example-.danglinghyphen- {F()->()}").is_err());
    assert!(interfaces("Com.example.uppercase-toplevel {F()->()}").is_err());
    assert!(interfaces("Co9.exmaple.number-toplevel {F()->()}").is_err());
    assert!(interfaces("com.Example {F()->()}").is_err());
}

#[test]
fn test_no_method() {
    assert!(interfaces("
org.varlink.service {
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
}
")
        .is_err());
}

#[test]
fn test_type_no_args() {
    assert!(interfaces("foo.bar{ type I () F()->() }").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(interfaces("foo.bar{ type I (b:bool) F()->() }").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(interfaces("foo.bar{ type I (b:bool[]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[1]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 1 ]) F()->() }").is_err());
}

#[test]
fn test_method_struct_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)? )->()}").is_ok());
}

#[test]
fn test_method_struct_array_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)[]? )->()}").is_ok());
}

#[test]
fn test_method_struct_array_optional_wrong() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)?[]) -> ()}").is_err());
}

#[test]
fn test_format() {
    let i = interfaces("foo.bar{ type I (b:bool[1]) F()->() }").unwrap();
    assert_eq!(i[0].to_string(),
               "foo.bar {
  type I (b: bool[1]);
  F() -> ();
}
");
}

#[test]
fn test_union() {
    let i = interfaces("foo.bar{ F()->(s: (a: bool, b: int64), u: bool|int64|(foo: bool, bar: \
                        bool)) }")
        .unwrap();
    println!("{}", i[0]);
    assert_eq!(i[0].to_string(),
               "foo.bar {
  F() -> (s: (a: bool, b: int64), u: bool | int64 | (foo: bool, bar: \
                bool));
}
");
}
<|endoftext|>


<commit_before>//! A parser for lambda expressions

use term::{Term, Notation, abs, app};
use term::Term::*;
use self::Token::*;
use self::CToken::*;
use self::ParseError::*;
use self::Expression::*;
pub use term::Notation::*;

/// An error returned by `parse()` when a parsing issue is encountered.
#[derive(Debug, PartialEq)]
pub enum ParseError {
    /// lexical error; contains the invalid character and its index
    InvalidCharacter((usize, char)),
    /// syntax error; the expression is invalid
    InvalidExpression,
    /// syntax error; the expression is empty
    EmptyExpression
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Token {
    /// the lambda symbol ('λ' or '\')
    Lambda,
    /// left parenthesis
    Lparen,
    /// right parenthesis
    Rparen,
    /// a hex-encoded digit
    Number(usize)
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum CToken {
    /// an abstraction with a bound variable
    CLambda(String),
    /// left parenthesis
    CLparen,
    /// right parenthesis
    CRparen,
    /// a variable with an identifier
    CName(String)
}

#[doc(hidden)]
pub fn tokenize_dbr(input: &str) -> Result<Vec<Token>, ParseError> {
    let chars = input.chars().enumerate();
    let mut tokens = Vec::with_capacity(input.len());

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

    Ok(tokens)
}

#[doc(hidden)]
pub fn tokenize_cla(input: &str) -> Result<Vec<CToken>, ParseError> {
    let mut chars = input.chars().enumerate().peekable();
    let mut tokens = Vec::with_capacity(input.len());

    while let Some((i, c)) = chars.next() {
        match c {
            '\\' | 'λ' => {
                let mut name = String::new();
                while let Some((i, c)) = chars.next() {
                    if c == '.' {
                        break
                    } else if c.is_alphabetic() {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((i, c)))
                    }
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             _  => {
                if c.is_whitespace() {
                    ()
                } else if c.is_alphabetic() {
                    let mut name = c.to_string();
                    while let Some(&(_, c)) = chars.peek() {
                        if c.is_whitespace() || c == '(' || c == ')' {
                            break
                        } else {
                            name.push(c);
                            chars.next();
                        }
                    }
                    tokens.push(CName(name))
                } else {
                    return Err(InvalidCharacter((i, c)))
                }
            }
        }
    }

    Ok(tokens)
}

#[doc(hidden)]
pub fn convert_classic_tokens(tokens: &[CToken]) -> Vec<Token> {
    _convert_classic_tokens(tokens, &mut Vec::with_capacity(tokens.len()), &mut 0)
}

fn _convert_classic_tokens<'t, 's>(tokens: &'t [CToken], stack: &'s mut Vec<&'t str>, pos: &mut usize) -> Vec<Token>
{
    let mut output = Vec::with_capacity(tokens.len() - *pos);
    let mut inner_stack_count = 0;

    while let Some(token) = tokens.get(*pos) {
        match *token {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name);
                inner_stack_count += 1;
            },
            CLparen => {
                output.push(Lparen);
                *pos += 1;
                output.append(&mut _convert_classic_tokens(tokens, stack, pos));
            },
            CRparen => {
                output.push(Rparen);
                let l = stack.len(); // TODO: move when NLL hits stable
                stack.truncate(l - inner_stack_count);
                return output
            },
            CName(ref name) => {
                if let Some(index) = stack.iter().rev().position(|t| t == name) {
                    output.push(Number(index + 1))
                } else {
                    output.push(Number(stack.len() + 1))
                }
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Expression {
    /// an abstraction
    Abstraction,
    /// a sequence of `Expression`s
    Sequence(Vec<Expression>),
    /// a variable with a De Bruijn index
    Variable(usize)
}

#[doc(hidden)]
pub fn get_ast(tokens: &[Token]) -> Result<Expression, ParseError> {
    _get_ast(tokens, &mut 0)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, ParseError> {
    if tokens.is_empty() { return Err(EmptyExpression) }

    let mut expr = Vec::new();

    while let Some(token) = tokens.get(*pos) {
        match *token {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = _get_ast(tokens, pos)?;
                expr.push(subtree);
            },
            Rparen => {
                return Ok(Sequence(expr))
            }
        }
        *pos += 1;
    }

    Ok(Sequence(expr))
}

/// Attempts to parse the input `&str` as a lambda `Term` encoded in the given `Notation`.
///
/// - lambdas can be represented either with the greek letter (λ) or a backslash (\\ -
/// less aesthetic, but only one byte in size)
/// - the identifiers in `Classic` notation are `String`s of alphabetic Unicode characters
/// - `Classic` notation ignores whitespaces where unambiguous
/// - the indices in the `DeBruijn` notation start with 1 and are hexadecimal digits
/// - `DeBruijn` notation ignores all whitespaces (since indices > 15 are very unlikely)
///
/// # Examples
/// ```
/// use lambda_calculus::*;
/// use lambda_calculus::combinators::{S, Y};
///
/// assert_eq!(parse(&"λf.(λx.f (x x)) (λx.f (x x))", Classic), Ok(Y()));
/// assert_eq!(parse(&"λƒ.(λℵ.ƒ(ℵ ℵ))(λℵ.ƒ(ℵ ℵ))", Classic),    Ok(Y()));
///
/// assert_eq!(parse(  &"λλλ31(21)",     DeBruijn), Ok(S()));
/// assert_eq!(parse(&r#"\\\3 1 (2 1)"#, DeBruijn), Ok(S()));
/// ```
///
/// # Errors
///
/// Returns a `ParseError` when a lexing or syntax error is encountered.
pub fn parse(input: &str, notation: Notation) -> Result<Term, ParseError> {
    let tokens = if notation == DeBruijn {
        tokenize_dbr(input)?
    } else {
        convert_classic_tokens(&tokenize_cla(input)?)
    };
    let ast = get_ast(&tokens)?;

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

    fold_exprs(&exprs?)
}

#[doc(hidden)]
pub fn fold_exprs(exprs: &[Expression]) -> Result<Term, ParseError> {
    let mut depth  = 0;
    let mut output = Vec::new();

    for expr in exprs.iter() {
        match *expr {
            Variable(i) => output.push(Var(i)),
            Abstraction => depth += 1,
            Sequence(ref exprs) => {
                let subexpr = fold_exprs(exprs)?;
                output.push(subexpr)
            }
        }
    }

    Ok(abs!(depth, fold_terms(output)?))
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, ParseError> {
    if terms.len() > 1 {
        let fst = terms.remove(0);
        Ok( terms.into_iter().fold(fst, app) )
    } else if terms.len() == 1 {
        Ok( terms.pop().unwrap() ) // safe; ensured above
    } else {
        Err(EmptyExpression)
    }
}

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

    #[test]
    fn tokenization_error() {
        assert_eq!(tokenize_dbr(&"λλx2"),    Err(InvalidCharacter((2, 'x'))));
        assert_eq!(tokenize_cla(&"λa.λb a"), Err(InvalidCharacter((5, ' '))));
    }

    #[test]
    fn tokenization_success() {
        let quine = "λ 1 ( (λ 1 1) (λ λ λ λ λ 1 4 (3 (5 5) 2) ) ) 1";
        let tokens = tokenize_dbr(&quine);

        assert!(tokens.is_ok());
        assert_eq!(tokens.unwrap(), vec![Lambda, Number(1), Lparen, Lparen, Lambda, Number(1),
            Number(1), Rparen, Lparen, Lambda, Lambda, Lambda, Lambda, Lambda, Number(1),
            Number(4), Lparen, Number(3), Lparen, Number(5), Number(5), Rparen, Number(2),
            Rparen, Rparen, Rparen, Number(1)]);
    }

    #[test]
    fn tokenization_success_classic() {
        let blc_dbr = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
            (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        let blc_cla = format!("{}", parse(&blc_dbr, DeBruijn).unwrap());

        let tokens_cla = tokenize_cla(&blc_cla);
        let tokens_dbr = tokenize_dbr(&blc_dbr);

        assert!(tokens_cla.is_ok());
        assert!(tokens_dbr.is_ok());

        assert_eq!(convert_classic_tokens(&tokens_cla.unwrap()), tokens_dbr.unwrap());
    }

    #[test]
    fn alternative_lambda_parsing() {
        assert_eq!(parse(&r#"\\\2(321)"#, DeBruijn), parse(&"λλλ2(321)", DeBruijn))
    }

    #[test]
    fn succ_ast() {
        let tokens = tokenize_dbr(&"λλλ2(321)").unwrap();
        let ast = get_ast(&tokens);

        assert_eq!(ast,
            Ok(Sequence(vec![
                Abstraction,
                Abstraction,
                Abstraction,
                Variable(2),
                Sequence(vec![
                    Variable(3),
                    Variable(2),
                    Variable(1)
                ])
            ])
        ));
    }

    #[test]
    fn parse_y() {
        let y = "λ(λ2(11))(λ2(11))";
        assert_eq!(parse(&y, DeBruijn).unwrap(),
            abs(
                app(
                    abs(app(Var(2), app(Var(1), Var(1)))),
                    abs(app(Var(2), app(Var(1), Var(1))))
                )
            )
        );
    }

    #[test]
    fn parse_quine() {
        let quine = "λ1((λ11)(λλλλλ14(3(55)2)))1";
        assert_eq!(parse(&quine, DeBruijn).unwrap(),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs!(5, app(app(Var(1),
            Var(4)), app(app(Var(3), app(Var(5), Var(5))), Var(2)))))), Var(1)))
        );
    }

    #[test]
    fn parse_blc() {
        let blc = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
                   (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        assert_eq!(parse(&blc, DeBruijn).unwrap(),
            app(app(abs(app(Var(1), Var(1))), abs!(3, app(app(app(Var(1),
            abs!(4, app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs!(2, app(Var(3), abs(app(app(Var(1), Var(2)), Var(3))))))), app(Var(4),
            abs(app(Var(4), abs(app(app(Var(3), Var(1)), app(Var(2), Var(1))))))))))),
            app(app(Var(1), app(Var(2), abs(app(Var(1), Var(2))))), abs(app(app(Var(4),
            abs(app(Var(4), abs(app(Var(2), app(Var(1), Var(4))))))), Var(5))))))))),
            app(Var(3), Var(3))), Var(2)))), abs(app(Var(1), app(abs(app(Var(1), Var(1))),
            abs(app(Var(1), Var(1)))))))
        );
    }
}
<commit_msg>minor readability tweaks in parser functions<commit_after>//! A parser for lambda expressions

use term::{Term, Notation, abs, app};
use term::Term::*;
use self::Token::*;
use self::CToken::*;
use self::ParseError::*;
use self::Expression::*;
pub use term::Notation::*;

/// An error returned by `parse()` when a parsing issue is encountered.
#[derive(Debug, PartialEq)]
pub enum ParseError {
    /// lexical error; contains the invalid character and its index
    InvalidCharacter((usize, char)),
    /// syntax error; the expression is invalid
    InvalidExpression,
    /// syntax error; the expression is empty
    EmptyExpression
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Token {
    /// the lambda symbol ('λ' or '\')
    Lambda,
    /// left parenthesis
    Lparen,
    /// right parenthesis
    Rparen,
    /// a hex-encoded digit
    Number(usize)
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum CToken {
    /// an abstraction with a bound variable
    CLambda(String),
    /// left parenthesis
    CLparen,
    /// right parenthesis
    CRparen,
    /// a variable with an identifier
    CName(String)
}

#[doc(hidden)]
pub fn tokenize_dbr(input: &str) -> Result<Vec<Token>, ParseError> {
    let chars = input.chars().enumerate();
    let mut tokens = Vec::with_capacity(input.len());

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

    Ok(tokens)
}

#[doc(hidden)]
pub fn tokenize_cla(input: &str) -> Result<Vec<CToken>, ParseError> {
    let mut chars = input.chars().enumerate().peekable();
    let mut tokens = Vec::with_capacity(input.len());

    while let Some((i, c)) = chars.next() {
        match c {
            '\\' | 'λ' => {
                let mut name = String::new();
                while let Some((i, c)) = chars.next() {
                    if c == '.' {
                        break
                    } else if c.is_alphabetic() {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((i, c)))
                    }
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             _  => {
                if c.is_whitespace() {
                    ()
                } else if c.is_alphabetic() {
                    let mut name = c.to_string();
                    while let Some(&(_, c)) = chars.peek() {
                        if c.is_whitespace() || c == '(' || c == ')' {
                            break
                        } else {
                            name.push(c);
                            chars.next();
                        }
                    }
                    tokens.push(CName(name))
                } else {
                    return Err(InvalidCharacter((i, c)))
                }
            }
        }
    }

    Ok(tokens)
}

#[doc(hidden)]
pub fn convert_classic_tokens(tokens: &[CToken]) -> Vec<Token> {
    _convert_classic_tokens(tokens, &mut Vec::with_capacity(tokens.len()), &mut 0)
}

fn _convert_classic_tokens<'t, 's>(tokens: &'t [CToken], stack: &'s mut Vec<&'t str>, pos: &mut usize) -> Vec<Token>
{
    let mut output = Vec::with_capacity(tokens.len() - *pos);
    let mut inner_stack_count = 0;

    while let Some(token) = tokens.get(*pos) {
        match *token {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name);
                inner_stack_count += 1;
            },
            CLparen => {
                output.push(Lparen);
                *pos += 1;
                output.append(&mut _convert_classic_tokens(tokens, stack, pos));
            },
            CRparen => {
                output.push(Rparen);
                let l = stack.len(); // TODO: move when NLL hits stable
                stack.truncate(l - inner_stack_count);
                return output
            },
            CName(ref name) => {
                if let Some(index) = stack.iter().rev().position(|t| t == name) {
                    output.push(Number(index + 1))
                } else {
                    output.push(Number(stack.len() + 1))
                }
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
#[doc(hidden)]
pub enum Expression {
    /// an abstraction
    Abstraction,
    /// a sequence of `Expression`s
    Sequence(Vec<Expression>),
    /// a variable with a De Bruijn index
    Variable(usize)
}

#[doc(hidden)]
pub fn get_ast(tokens: &[Token]) -> Result<Expression, ParseError> {
    _get_ast(tokens, &mut 0)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, ParseError> {
    if tokens.is_empty() { return Err(EmptyExpression) }

    let mut expr = Vec::new();

    while let Some(token) = tokens.get(*pos) {
        match *token {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = _get_ast(tokens, pos)?;
                expr.push(subtree);
            },
            Rparen => {
                return Ok(Sequence(expr))
            }
        }
        *pos += 1;
    }

    Ok(Sequence(expr))
}

/// Attempts to parse the input `&str` as a lambda `Term` encoded in the given `Notation`.
///
/// - lambdas can be represented either with the greek letter (λ) or a backslash (\\ -
/// less aesthetic, but only one byte in size)
/// - the identifiers in `Classic` notation are `String`s of alphabetic Unicode characters
/// - `Classic` notation ignores whitespaces where unambiguous
/// - the indices in the `DeBruijn` notation start with 1 and are hexadecimal digits
/// - `DeBruijn` notation ignores all whitespaces (since indices > 15 are very unlikely)
///
/// # Examples
/// ```
/// use lambda_calculus::*;
/// use lambda_calculus::combinators::{S, Y};
///
/// assert_eq!(parse(&"λf.(λx.f (x x)) (λx.f (x x))", Classic), Ok(Y()));
/// assert_eq!(parse(&"λƒ.(λℵ.ƒ(ℵ ℵ))(λℵ.ƒ(ℵ ℵ))", Classic),  Ok(Y()));
///
/// assert_eq!(parse(  &"λλλ31(21)",     DeBruijn), Ok(S()));
/// assert_eq!(parse(&r#"\\\3 1 (2 1)"#, DeBruijn), Ok(S()));
/// ```
///
/// # Errors
///
/// Returns a `ParseError` when a lexing or syntax error is encountered.
pub fn parse(input: &str, notation: Notation) -> Result<Term, ParseError> {
    let tokens = if notation == DeBruijn {
        tokenize_dbr(input)?
    } else {
        convert_classic_tokens(&tokenize_cla(input)?)
    };
    let ast = get_ast(&tokens)?;

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

    fold_exprs(&exprs?)
}

#[doc(hidden)]
pub fn fold_exprs(exprs: &[Expression]) -> Result<Term, ParseError> {
    let mut depth  = 0;
    let mut output = Vec::new();

    for expr in exprs.iter() {
        match *expr {
            Abstraction         => depth += 1,
            Variable(i)         => output.push(Var(i)),
            Sequence(ref exprs) => output.push(fold_exprs(exprs)?)
        }
    }

    Ok(abs!(depth, fold_terms(output)?))
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, ParseError> {
    if terms.is_empty() {
        Err(EmptyExpression)
    } else {
        let fst = terms.remove(0);
        if terms.is_empty() {
            Ok( fst )
        } else {
            Ok( terms.into_iter().fold(fst, app) )
        }
    }
}

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

    #[test]
    fn tokenization_error() {
        assert_eq!(tokenize_dbr(&"λλx2"),    Err(InvalidCharacter((2, 'x'))));
        assert_eq!(tokenize_cla(&"λa.λb a"), Err(InvalidCharacter((5, ' '))));
    }

    #[test]
    fn tokenization_success() {
        let quine = "λ 1 ( (λ 1 1) (λ λ λ λ λ 1 4 (3 (5 5) 2) ) ) 1";
        let tokens = tokenize_dbr(&quine);

        assert!(tokens.is_ok());
        assert_eq!(tokens.unwrap(), vec![Lambda, Number(1), Lparen, Lparen, Lambda, Number(1),
            Number(1), Rparen, Lparen, Lambda, Lambda, Lambda, Lambda, Lambda, Number(1),
            Number(4), Lparen, Number(3), Lparen, Number(5), Number(5), Rparen, Number(2),
            Rparen, Rparen, Rparen, Number(1)]);
    }

    #[test]
    fn tokenization_success_classic() {
        let blc_dbr = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
            (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        let blc_cla = format!("{}", parse(&blc_dbr, DeBruijn).unwrap());

        let tokens_cla = tokenize_cla(&blc_cla);
        let tokens_dbr = tokenize_dbr(&blc_dbr);

        assert!(tokens_cla.is_ok());
        assert!(tokens_dbr.is_ok());

        assert_eq!(convert_classic_tokens(&tokens_cla.unwrap()), tokens_dbr.unwrap());
    }

    #[test]
    fn alternative_lambda_parsing() {
        assert_eq!(parse(&r#"\\\2(321)"#, DeBruijn), parse(&"λλλ2(321)", DeBruijn))
    }

    #[test]
    fn succ_ast() {
        let tokens = tokenize_dbr(&"λλλ2(321)").unwrap();
        let ast = get_ast(&tokens);

        assert_eq!(ast,
            Ok(Sequence(vec![
                Abstraction,
                Abstraction,
                Abstraction,
                Variable(2),
                Sequence(vec![
                    Variable(3),
                    Variable(2),
                    Variable(1)
                ])
            ])
        ));
    }

    #[test]
    fn parse_y() {
        let y = "λ(λ2(11))(λ2(11))";
        assert_eq!(parse(&y, DeBruijn).unwrap(),
            abs(
                app(
                    abs(app(Var(2), app(Var(1), Var(1)))),
                    abs(app(Var(2), app(Var(1), Var(1))))
                )
            )
        );
    }

    #[test]
    fn parse_quine() {
        let quine = "λ1((λ11)(λλλλλ14(3(55)2)))1";
        assert_eq!(parse(&quine, DeBruijn).unwrap(),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs!(5, app(app(Var(1),
            Var(4)), app(app(Var(3), app(Var(5), Var(5))), Var(2)))))), Var(1)))
        );
    }

    #[test]
    fn parse_blc() {
        let blc = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
                   (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        assert_eq!(parse(&blc, DeBruijn).unwrap(),
            app(app(abs(app(Var(1), Var(1))), abs!(3, app(app(app(Var(1),
            abs!(4, app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs!(2, app(Var(3), abs(app(app(Var(1), Var(2)), Var(3))))))), app(Var(4),
            abs(app(Var(4), abs(app(app(Var(3), Var(1)), app(Var(2), Var(1))))))))))),
            app(app(Var(1), app(Var(2), abs(app(Var(1), Var(2))))), abs(app(app(Var(4),
            abs(app(Var(4), abs(app(Var(2), app(Var(1), Var(4))))))), Var(5))))))))),
            app(Var(3), Var(3))), Var(2)))), abs(app(Var(1), app(abs(app(Var(1), Var(1))),
            abs(app(Var(1), Var(1)))))))
        );
    }
}
<|endoftext|>


<commit_before>use nom::IResult;

use ::event::Event;
use ::game::{Game, GameError};
use ::parsers::event;

#[derive(Clone, Debug, PartialEq)]
/// An error that occurs while parsing an event file.
pub enum Error {
    /// The parser received an unexpected `game_id` event.
    UnexpectedGameId {
        /// The id of the game already registered.
        current_game_id: String
    },
    /// The parser received an game event without a game having been started.
    NoGame(Event),
    /// There were bytes remaining after attempting to parse the event file.
    BytesRemaining(Vec<u8>),
    /// An error occurred while processing events for a game.
    GameProcessingError(GameError),
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Error::UnexpectedGameId { ref current_game_id } => {
                write!(f, "game_id event was received before finishing game '{}'", current_game_id)
            }
            Error::NoGame(ref evt) => {
                write!(f, "event {:?} was received before a game was registered", evt)
            }
            Error::BytesRemaining(ref bytes) => {
                write!(f, "bytes remaining after parsing completed: {:?}", bytes)
            }
            Error::GameProcessingError(ref e) => write!(f, "{}", e)
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
enum State {
    Empty,
    Game(Game),
}

#[derive(Clone, Debug, PartialEq)]
/// An event file parser for Retrosheet event files.
pub struct Parser {
    state: State,
}

impl Parser {
    /// Creates a new parser instance.
    pub fn new() -> Parser {
        Parser { state: State::Empty }
    }

    /// Resets the parser to the initial state.
    pub fn reset(&mut self) {
        self.state = State::Empty;
    }

    /// Parses an event file.
    ///
    /// This returns a list of the games in the event file.
    ///
    /// # Arguments
    /// * bytes - A stream of byte-data containing the entirety of the event file.
    ///
    /// # Errors
    /// The parser expects the byte-data coming in to be a *complete* event file, so any bytes
    /// remaining are counted as an error. Any other errors are from invalid events.
    pub fn parse(&mut self, mut bytes: &[u8]) -> Result<Vec<Game>, Error> {
        let mut result = Vec::new();
        while let IResult::Done(rest, evt) = event(bytes) {
            match self.state {
                State::Empty => {
                    // The only valid event when the state is empty is a game ID (which marks a new
                    // game).
                    if let Event::GameId { id } = evt {
                        let game = Game::new(id);
                        self.state = State::Game(game);
                    } else {
                        return Err(Error::NoGame(evt.clone()));
                    }
                }
                State::Game(ref mut game) => {
                    match evt {
                        Event::GameId { .. } => {
                            // Game IDs can't appear until the previous game is done.
                            // TODO: The way to actually check that the previous game is done is by
                            //       confirming it is in the Data stage.
                            return Err(Error::UnexpectedGameId { current_game_id: game.id.clone() });
                        }
                        Event::Version { .. } => {
                            // We actually don't care about the version as long as it came in a
                            // valid location.
                        }
                        _ => {
                            try!(game.process_event(evt).map_err(Error::GameProcessingError));
                        }
                    }
                }
            }
            bytes = rest;
        }
        if !bytes.is_empty() {
            Err(Error::BytesRemaining(bytes.to_vec()))
        } else {
            if self.state != State::Empty {
                // We reached the end of input after a game, so make sure the game can be validly
                // finished, and then add it.
                if let State::Game(mut game) = ::std::mem::replace(&mut self.state, State::Empty) {
                    try!(game.finish().map_err(Error::GameProcessingError));
                    result.push(game);
                }
            }
            Ok(result)
        }
    }
}

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

    use ::event::Event;

    #[test]
    fn test_unexpected_id() {
        let mut parser = Parser::new();
        let result = parser.parse(b"id,CHN201506130
version,2
id,CHN201604110");
        assert_eq!(Err(Error::UnexpectedGameId { current_game_id: "CHN201506130".into() }), result);
    }

    #[test]
    fn test_unexpected_version() {
        let mut parser = Parser::new();
        let result = parser.parse(b"version,3");
        assert_eq!(Err(Error::NoGame(Event::Version { version: 3 })), result);
    }

    #[test]
    fn test_bytes_remaining() {
        let mut parser = Parser::new();
        let result = parser.parse(b"id,CHN201506130
version,2
foo\n\n");
        assert_eq!(Err(Error::BytesRemaining(b"foo\n\n".to_vec())), result);
    }

    #[test]
    fn test_reset() {
        let mut parser = Parser::new();
        let _ = parser.parse(b"id,CHN201506130");
        parser.reset();
        let result = parser.parse(b"id,CHN201506130
version,2
info,number,0
start,foo,\"Bar\",0,1,6
play,4,1,buz,00,,NP
data,er,foo,2");
        assert!(result.is_ok(), "resetting parser did not allow new game: {}", result.err().unwrap());
    }
}<commit_msg>Add a short game unit test to parser tests<commit_after>use nom::IResult;

use ::event::Event;
use ::game::{Game, GameError};
use ::parsers::event;

#[derive(Clone, Debug, PartialEq)]
/// An error that occurs while parsing an event file.
pub enum Error {
    /// The parser received an unexpected `game_id` event.
    UnexpectedGameId {
        /// The id of the game already registered.
        current_game_id: String
    },
    /// The parser received an game event without a game having been started.
    NoGame(Event),
    /// There were bytes remaining after attempting to parse the event file.
    BytesRemaining(Vec<u8>),
    /// An error occurred while processing events for a game.
    GameProcessingError(GameError),
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Error::UnexpectedGameId { ref current_game_id } => {
                write!(f, "game_id event was received before finishing game '{}'", current_game_id)
            }
            Error::NoGame(ref evt) => {
                write!(f, "event {:?} was received before a game was registered", evt)
            }
            Error::BytesRemaining(ref bytes) => {
                write!(f, "bytes remaining after parsing completed: {:?}", bytes)
            }
            Error::GameProcessingError(ref e) => write!(f, "{}", e)
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
enum State {
    Empty,
    Game(Game),
}

#[derive(Clone, Debug, PartialEq)]
/// An event file parser for Retrosheet event files.
pub struct Parser {
    state: State,
}

impl Parser {
    /// Creates a new parser instance.
    pub fn new() -> Parser {
        Parser { state: State::Empty }
    }

    /// Resets the parser to the initial state.
    pub fn reset(&mut self) {
        self.state = State::Empty;
    }

    /// Parses an event file.
    ///
    /// This returns a list of the games in the event file.
    ///
    /// # Arguments
    /// * bytes - A stream of byte-data containing the entirety of the event file.
    ///
    /// # Errors
    /// The parser expects the byte-data coming in to be a *complete* event file, so any bytes
    /// remaining are counted as an error. Any other errors are from invalid events.
    pub fn parse(&mut self, mut bytes: &[u8]) -> Result<Vec<Game>, Error> {
        let mut result = Vec::new();
        while let IResult::Done(rest, evt) = event(bytes) {
            match self.state {
                State::Empty => {
                    // The only valid event when the state is empty is a game ID (which marks a new
                    // game).
                    if let Event::GameId { id } = evt {
                        let game = Game::new(id);
                        self.state = State::Game(game);
                    } else {
                        return Err(Error::NoGame(evt.clone()));
                    }
                }
                State::Game(ref mut game) => {
                    match evt {
                        Event::GameId { .. } => {
                            // Game IDs can't appear until the previous game is done.
                            // TODO: The way to actually check that the previous game is done is by
                            //       confirming it is in the Data stage.
                            return Err(Error::UnexpectedGameId { current_game_id: game.id.clone() });
                        }
                        Event::Version { .. } => {
                            // We actually don't care about the version as long as it came in a
                            // valid location.
                        }
                        _ => {
                            try!(game.process_event(evt).map_err(Error::GameProcessingError));
                        }
                    }
                }
            }
            bytes = rest;
        }
        if !bytes.is_empty() {
            Err(Error::BytesRemaining(bytes.to_vec()))
        } else {
            if self.state != State::Empty {
                // We reached the end of input after a game, so make sure the game can be validly
                // finished, and then add it.
                if let State::Game(mut game) = ::std::mem::replace(&mut self.state, State::Empty) {
                    try!(game.finish().map_err(Error::GameProcessingError));
                    result.push(game);
                }
            }
            Ok(result)
        }
    }
}

#[cfg(test)]
mod tests {
    const SHORT_GAME: &'static [u8] = b"id,CHN201506130
version,2
info,number,0
start,foo,\"Bar\",0,1,6
play,4,1,meh,12,CCX,S8.B-2
play,4,1,buz,00,,NP
sub,buz,\"Bax\",1,3,4
data,er,foo,2";

    use super::*;

    use std::collections::{HashMap, HashSet};
    use std::iter::FromIterator;

    use ::event::{Advance, Base, DataEventType, Event, Info, Pitch, Player, PlayDescription,
                  PlayEvent, Team};
    use ::game::{Game, Substitution};

    #[test]
    fn test_game_parse() {
        let expected_info = {
            let mut map = HashMap::new();
            map.insert(Info::Number, "0".into());
            map
        };
        let expected_starters = HashSet::from_iter(vec![
            Player {
                id: "foo".into(),
                name: "Bar".into(),
                team: Team::Visiting,
                batting_pos: 1,
                fielding_pos: 6,
            }
        ].into_iter());
        let expected_plays = vec![
            Event::Play {
                inning: 4,
                team: Team::Home,
                player: "meh".into(),
                count: Some((1, 2)),
                pitches: vec![
                    Pitch::CalledStrike,
                    Pitch::CalledStrike,
                    Pitch::BallInPlayBatter,
                ],
                event: PlayEvent {
                    description: PlayDescription::Single(vec![8]),
                    modifiers: vec![],
                    advances: vec![
                        Advance {
                            from: Base::Home,
                            to: Base::Second,
                            success: true,
                            parameters: vec![],
                        }
                    ],
                },
            }
            // The NP should be gone because of the sub!
        ];
        let expected_data = vec![
            Event::Data {
                data_type: DataEventType::EarnedRuns,
                player: "foo".into(),
                value: "2".into(),
            }
        ];
        let expected_substitutions = vec![
            Substitution {
                inning: 4,
                batting_team: Team::Home,
                player: Player {
                    id: "buz".into(),
                    name: "Bax".into(),
                    team: Team::Home,
                    batting_pos: 3,
                    fielding_pos: 4,
                },
            }
        ];

        let mut parser = Parser::new();
        assert_eq!(State::Empty, parser.state);

        let result = parser.parse(SHORT_GAME).unwrap();
        let ref game_result: Game = result[0];
        assert_eq!(expected_info, game_result.info);
        assert_eq!(expected_starters, game_result.starters);
        assert_eq!(expected_plays, game_result.plays);
        assert_eq!(expected_substitutions, game_result.substitutions);
        assert_eq!(expected_data, game_result.data);
    }

    #[test]
    fn test_unexpected_id() {
        let mut parser = Parser::new();
        let result = parser.parse(b"id,CHN201506130
version,2
id,CHN201604110");
        assert_eq!(Err(Error::UnexpectedGameId { current_game_id: "CHN201506130".into() }), result);
    }

    #[test]
    fn test_unexpected_version() {
        let mut parser = Parser::new();
        let result = parser.parse(b"version,3");
        assert_eq!(Err(Error::NoGame(Event::Version { version: 3 })), result);
    }

    #[test]
    fn test_bytes_remaining() {
        let mut parser = Parser::new();
        let result = parser.parse(b"id,CHN201506130
version,2
foo\n\n");
        assert_eq!(Err(Error::BytesRemaining(b"foo\n\n".to_vec())), result);
    }

    #[test]
    fn test_reset() {
        let mut parser = Parser::new();
        let _ = parser.parse(b"id,CHN201506130");
        parser.reset();
        let result = parser.parse(SHORT_GAME);
        assert!(result.is_ok(), "resetting parser did not allow new game: {}", result.err().unwrap());
    }
}<|endoftext|>