<commit_before>extern crate teleborg;
extern crate reqwest;

#[cfg(test)]
mod tests {
    use teleborg::{Bot, ParseMode, ChatAction};
    use teleborg::objects::{Contact, InlineKeyboardButton, InlineKeyboardMarkup};
    use reqwest::Client;

    use std::env;

    const BASE_URL: &'static str = "https://api.telegram.org/bot";

    fn setup() -> (String, i64) {
        let token = env::var("TELEGRAM_BOT_TOKEN")
            .ok()
            .expect("Can't find TELEGRAM_BOT_TOKEN env variable");
        let chat_id = env::var("TELEGRAM_CHAT_ID")
            .ok()
            .expect("Can't find TELEGRAM_CHAT_ID env variable")
            .parse::<i64>()
            .unwrap();
        (token, chat_id)
    }

    #[test]
    fn test_get_me() {
        let (token, _) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let client = Client::new().unwrap();
        let json = Bot::get_me(&client, &bot_url);
        assert!(json.is_ok());
    }

    #[test]
    fn test_send_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id, "test", None, None, None, None, None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_text_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id,
                                       "test",
                                       Some(&ParseMode::Text),
                                       None,
                                       None,
                                       None,
                                       None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_markdown_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id,
                                       "*test*",
                                       Some(&ParseMode::Markdown),
                                       None,
                                       None,
                                       None,
                                       None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_html_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id,
                                       "test",
                                       Some(&ParseMode::Html),
                                       None,
                                       None,
                                       None,
                                       None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_url_chat_actions() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let message = bot.send_chat_action(&chat_id, &ChatAction::FindLocation);
        assert!(message.is_ok());
    }

    #[test]
    fn test_url_inline_keyboard() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let mut total = Vec::<Vec<InlineKeyboardButton>>::new();
        let mut row = Vec::<InlineKeyboardButton>::new();
        row.push(InlineKeyboardButton::new("TestButton".to_string(),
                                           Some("http://github.com/voider1/teleborg".to_string()),
                                           None,
                                           None,
                                           None));
        total.push(row);
        let markup = InlineKeyboardMarkup::new(total);
        let message = bot.send_message(&chat_id, "test", None, None, None, None, Some(&markup));
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_contact() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let contact = Contact {
            phone_number: "0612345678".to_string(),
            first_name: "Voider".to_string(),
            last_name: Some("1".to_string()),
            user_id: Some(666),
        };
        let message = bot.send_contact(&chat_id, &contact, None, None, None);
        assert!(message.is_ok());
    }
}
<commit_msg>changed total to buttons in test_inline_keyboard, changed message to success in test_send_chat_action<commit_after>extern crate teleborg;
extern crate reqwest;

#[cfg(test)]
mod tests {
    use teleborg::{Bot, ParseMode, ChatAction};
    use teleborg::objects::{Contact, InlineKeyboardButton, InlineKeyboardMarkup};
    use reqwest::Client;

    use std::env;

    const BASE_URL: &'static str = "https://api.telegram.org/bot";

    fn setup() -> (String, i64) {
        let token = env::var("TELEGRAM_BOT_TOKEN")
            .ok()
            .expect("Can't find TELEGRAM_BOT_TOKEN env variable");
        let chat_id = env::var("TELEGRAM_CHAT_ID")
            .ok()
            .expect("Can't find TELEGRAM_CHAT_ID env variable")
            .parse::<i64>()
            .unwrap();
        (token, chat_id)
    }

    #[test]
    fn test_get_me() {
        let (token, _) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let client = Client::new().unwrap();
        let json = Bot::get_me(&client, &bot_url);
        assert!(json.is_ok());
    }

    #[test]
    fn test_send_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id, "test", None, None, None, None, None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_text_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id,
                                       "test",
                                       Some(&ParseMode::Text),
                                       None,
                                       None,
                                       None,
                                       None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_markdown_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id,
                                       "*test*",
                                       Some(&ParseMode::Markdown),
                                       None,
                                       None,
                                       None,
                                       None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_html_message() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();
        let message = bot.send_message(&chat_id,
                                       "test",
                                       Some(&ParseMode::Html),
                                       None,
                                       None,
                                       None,
                                       None);
        assert!(message.is_ok());
    }

    #[test]
    fn test_url_chat_actions() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let success = bot.send_chat_action(&chat_id, &ChatAction::FindLocation);
        assert!(success.is_ok());
    }

    #[test]
    fn test_url_inline_keyboard() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let mut buttons = Vec::<Vec<InlineKeyboardButton>>::new();
        let mut row = Vec::<InlineKeyboardButton>::new();
        row.push(InlineKeyboardButton::new("TestButton".to_string(),
                                           Some("http://github.com/voider1/teleborg".to_string()),
                                           None,
                                           None,
                                           None));
        buttons.push(row);
        let markup = InlineKeyboardMarkup::new(buttons);
        let message = bot.send_message(&chat_id, "test", None, None, None, None, Some(&markup));
        assert!(message.is_ok());
    }

    #[test]
    fn test_send_contact() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let contact = Contact {
            phone_number: "0612345678".to_string(),
            first_name: "Voider".to_string(),
            last_name: Some("1".to_string()),
            user_id: Some(666),
        };
        let message = bot.send_contact(&chat_id, &contact, None, None, None);
        assert!(message.is_ok());
    }
}
<|endoftext|>


<commit_before>extern crate integrity_checker;

extern crate flate2;

extern crate serde_json;

extern crate tempfile;

extern crate valico;

use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};

use integrity_checker::database::Database;
use integrity_checker::error::Error;

use flate2::read::GzDecoder;

use serde_json::Value;

use tempfile::tempfile;

use valico::json_schema;

fn validate_schema(data: &[u8], schema_path: impl AsRef<Path>) -> Result<bool, Error> {
    let instance: Value = serde_json::from_slice(data)?;

    let f = File::open(schema_path)?;
    let schema: Value = serde_json::from_reader(f)?;

    let mut scope = json_schema::Scope::new();
    let schema = scope.compile_and_return(schema, true).unwrap();
    Ok(schema.validate(&instance).is_valid())
}

fn validate(root_dir: impl AsRef<Path>) -> Result<bool, Error> {
    let mut before_path = PathBuf::from(root_dir.as_ref());
    before_path.push("before");

    let mut after_path = PathBuf::from(root_dir.as_ref());
    after_path.push("after");

    let threads = 1;
    let before_db = Database::build(&before_path, false, threads)?;

    // Dump the databse to a temporary file and read it back so that
    // we can be 100% we're doing everything the same was as the main
    // client.
    let f = tempfile()?;
    let mut f = before_db.dump_json(f)?;
    f.seek(SeekFrom::Start(0))?;
    let mut d = GzDecoder::new(f);
    let mut bytes = Vec::new();
    d.read_to_end(&mut bytes)?;
    let bytes = bytes;

    // Now make sure the format validates.
    const SEP: u8 = 0x0a;
    let index = match bytes.iter().position(|&x| x == SEP) {
        Some(x) => x,
        None => return Err(Error::ParseError),
    };

    Ok(validate_schema(&bytes[..index], "schema/checksum.json")? &&
       validate_schema(&bytes[index+1..], "schema/database.json")?)
}

#[test]
fn no_changes() {
    assert!(validate("tests/nochanges").unwrap());
}

#[test]
fn changes_edit() {
    assert!(validate("tests/changes_edit").unwrap());
}

#[test]
fn changes_new() {
    assert!(validate("tests/changes_new").unwrap());
}

#[test]
fn changes_edit_bin() {
    assert!(validate("tests/changes_edit_bin").unwrap());
}

#[test]
fn changes_new_bin() {
    assert!(validate("tests/changes_new_bin").unwrap());
}

#[test]
fn changes_delete() {
    assert!(validate("tests/changes_delete").unwrap());
}

#[test]
fn suspicious_truncate() {
    assert!(validate("tests/suspicious_truncate").unwrap());
}

#[test]
fn suspicious_nul() {
    assert!(validate("tests/suspicious_nul").unwrap());
}

#[test]
fn suspicious_nonascii() {
    assert!(validate("tests/suspicious_nonascii").unwrap());
}
<commit_msg>Validate both before and after repos.<commit_after>extern crate integrity_checker;

extern crate flate2;

extern crate serde_json;

extern crate tempfile;

extern crate valico;

use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

use integrity_checker::database::Database;
use integrity_checker::error::Error;

use flate2::read::GzDecoder;

use serde_json::Value;

use tempfile::tempfile;

use valico::json_schema;

fn validate_schema(data: &[u8], schema_path: impl AsRef<Path>) -> Result<bool, Error> {
    let instance: Value = serde_json::from_slice(data)?;

    let f = File::open(schema_path)?;
    let schema: Value = serde_json::from_reader(f)?;

    let mut scope = json_schema::Scope::new();
    let schema = scope.compile_and_return(schema, true).unwrap();
    Ok(schema.validate(&instance).is_valid())
}

fn validate(path: impl AsRef<Path>) -> Result<bool, Error> {
    let threads = 1;
    let db = Database::build(&path, false, threads)?;

    // Dump the databse to a temporary file and read it back so that
    // we can be 100% we're doing everything the same was as the main
    // client.
    let f = tempfile()?;
    let mut f = db.dump_json(f)?;
    f.seek(SeekFrom::Start(0))?;
    let mut d = GzDecoder::new(f);
    let mut bytes = Vec::new();
    d.read_to_end(&mut bytes)?;
    let bytes = bytes;

    // Now make sure the format validates.
    const SEP: u8 = 0x0a;
    let index = match bytes.iter().position(|&x| x == SEP) {
        Some(x) => x,
        None => return Err(Error::ParseError),
    };

    Ok(validate_schema(&bytes[..index], "schema/checksum.json")? &&
       validate_schema(&bytes[index+1..], "schema/database.json")?)
}

#[test]
fn no_changes() {
    assert!(validate("tests/nochanges/before").unwrap());
    assert!(validate("tests/nochanges/after").unwrap());
}

#[test]
fn changes_edit() {
    assert!(validate("tests/changes_edit/before").unwrap());
    assert!(validate("tests/changes_edit/after").unwrap());
}

#[test]
fn changes_new() {
    assert!(validate("tests/changes_new/before").unwrap());
    assert!(validate("tests/changes_new/after").unwrap());
}

#[test]
fn changes_edit_bin() {
    assert!(validate("tests/changes_edit_bin/before").unwrap());
    assert!(validate("tests/changes_edit_bin/after").unwrap());
}

#[test]
fn changes_new_bin() {
    assert!(validate("tests/changes_new_bin/before").unwrap());
    assert!(validate("tests/changes_new_bin/after").unwrap());
}

#[test]
fn changes_delete() {
    assert!(validate("tests/changes_delete/before").unwrap());
    assert!(validate("tests/changes_delete/after").unwrap());
}

#[test]
fn suspicious_truncate() {
    assert!(validate("tests/suspicious_truncate/before").unwrap());
    assert!(validate("tests/suspicious_truncate/after").unwrap());
}

#[test]
fn suspicious_nul() {
    assert!(validate("tests/suspicious_nul/before").unwrap());
    assert!(validate("tests/suspicious_nul/after").unwrap());
}

#[test]
fn suspicious_nonascii() {
    assert!(validate("tests/suspicious_nonascii/before").unwrap());
    assert!(validate("tests/suspicious_nonascii/after").unwrap());
}
<|endoftext|>


<commit_before>use std::iter;
use super::bag::Bag;

#[derive(Clone, Debug)]
pub struct Graph {
    v: usize,
    e: usize,
    adj: Vec<Bag<usize>>
}

impl Graph {
    pub fn new(v: usize) -> Graph {
        Graph {
            v: v,
            e: 0,
            adj: iter::repeat(Bag::<usize>::new()).take(v).collect()
        }
    }

    fn validate_vertex(&self, v: usize) {
        assert!(v < self.v, "vertex is not between 0 and {}", self.v - 1)
    }

    pub fn vertices(&self) -> usize {
        self.v
    }

    pub fn edges(&self) -> usize {
        self.e
    }

    pub fn add_edge(&mut self, v: usize, w: usize) {
        self.validate_vertex(v);
        self.validate_vertex(w);

        self.e += 1;
        self.adj[v].add(w);
        self.adj[w].add(v);
    }

    pub fn degree(&self, v: usize) -> usize {
        self.validate_vertex(v);
        self.adj[v].len()
    }

    pub fn to_dot(&self) -> String {
        let mut dot = String::new();

        dot.push_str("graph G {\n");
        for i in 0 .. self.v {
            dot.push_str(&format!("  {};\n", i));
        }

        //let mut edges = Vec::new();
        for (v, adj) in self.adj.iter().enumerate() {
            for w in adj.iter() {
                // if let iter::MinMaxResult::MinMax(mi, ma) = vec![v, *w].into_iter().min_max() {
                //     if !edges.contains(&(mi, ma)) {
                //         edges.push((mi, ma))
                //     }
                // }
                dot.push_str(&format!("  {} -- {};\n", v, w));
            }
        }
        // for &(v, w) in edges.iter() {
        //     dot.push_str(&format!("  {} -- {};\n", v, w));
        // }
        dot.push_str("}\n");
        dot
    }

    pub fn adj(&self, v: usize) -> super::bag::Iter<usize> {
        self.adj[v].iter()
    }
}

#[test]
fn test_graph() {
    let mut g = Graph::new(10);
    g.add_edge(0, 3);
    g.add_edge(0, 5);
    g.add_edge(4, 5);
    g.add_edge(2, 9);
    g.add_edge(2, 8);
    g.add_edge(3, 7);

    g.add_edge(1, 6);
    g.add_edge(6, 9);
    g.add_edge(5, 8);

    println!("got => \n{}", g.to_dot());

    assert_eq!(10, g.vertices());
    assert_eq!(9, g.edges());
    assert_eq!(3, g.degree(5));

}
<commit_msg>add Graph.adj() test<commit_after>use std::iter;
use super::bag::Bag;

#[derive(Clone, Debug)]
pub struct Graph {
    v: usize,
    e: usize,
    adj: Vec<Bag<usize>>
}

impl Graph {
    pub fn new(v: usize) -> Graph {
        Graph {
            v: v,
            e: 0,
            adj: iter::repeat(Bag::<usize>::new()).take(v).collect()
        }
    }

    fn validate_vertex(&self, v: usize) {
        assert!(v < self.v, "vertex is not between 0 and {}", self.v - 1)
    }

    pub fn vertices(&self) -> usize {
        self.v
    }

    pub fn edges(&self) -> usize {
        self.e
    }

    pub fn add_edge(&mut self, v: usize, w: usize) {
        self.validate_vertex(v);
        self.validate_vertex(w);

        self.e += 1;
        self.adj[v].add(w);
        self.adj[w].add(v);
    }

    pub fn degree(&self, v: usize) -> usize {
        self.validate_vertex(v);
        self.adj[v].len()
    }

    pub fn to_dot(&self) -> String {
        let mut dot = String::new();

        dot.push_str("graph G {\n");
        for i in 0 .. self.v {
            dot.push_str(&format!("  {};\n", i));
        }

        //let mut edges = Vec::new();
        for (v, adj) in self.adj.iter().enumerate() {
            for w in adj.iter() {
                // if let iter::MinMaxResult::MinMax(mi, ma) = vec![v, *w].into_iter().min_max() {
                //     if !edges.contains(&(mi, ma)) {
                //         edges.push((mi, ma))
                //     }
                // }
                dot.push_str(&format!("  {} -- {};\n", v, w));
            }
        }
        // for &(v, w) in edges.iter() {
        //     dot.push_str(&format!("  {} -- {};\n", v, w));
        // }
        dot.push_str("}\n");
        dot
    }

    pub fn adj(&self, v: usize) -> super::bag::Iter<usize> {
        self.adj[v].iter()
    }
}

#[test]
fn test_graph() {
    let mut g = Graph::new(10);
    g.add_edge(0, 3);
    g.add_edge(0, 5);
    g.add_edge(4, 5);
    g.add_edge(2, 9);
    g.add_edge(2, 8);
    g.add_edge(3, 7);

    g.add_edge(1, 6);
    g.add_edge(6, 9);
    g.add_edge(5, 8);

    println!("got => \n{}", g.to_dot());

    assert_eq!(10, g.vertices());
    assert_eq!(9, g.edges());
    assert_eq!(3, g.degree(5));

    for w in g.adj(5) {
        assert!(vec![8, 4, 0].contains(w));
    }
}
<|endoftext|>


<commit_before>//! Having a main.rs in a directory w/ mods will force tests to be built in a single binary

extern crate either;
#[macro_use]
extern crate inkwell_internal_macros;

#[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8")))]
mod test_attributes;
mod test_basic_block;
mod test_builder;
mod test_context;
mod test_execution_engine;
mod test_instruction_values;
mod test_module;
mod test_passes;
mod test_targets;
mod test_tari_example;
mod test_types;
mod test_values;
<commit_msg>Fix build.<commit_after>//! Having a main.rs in a directory w/ mods will force tests to be built in a single binary

extern crate either;
#[macro_use]
extern crate inkwell_internals;

#[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8")))]
mod test_attributes;
mod test_basic_block;
mod test_builder;
mod test_context;
mod test_execution_engine;
mod test_instruction_values;
mod test_module;
mod test_passes;
mod test_targets;
mod test_tari_example;
mod test_types;
mod test_values;
<|endoftext|>


<commit_before>use csv;
use slog::Logger;
use std::fs::create_dir;
use std::io;
use std::error;
use std::fmt;
use std::path::Path;
use ndarray;
use ndarray::{Array3, Zip};
use ndarray_parallel::prelude::*;
use grid;
use config::Config;

#[derive(Debug,Deserialize)]
/// A simple struct to parse data from a plain csv file
struct PlainRecord {
    /// Index in *x*
    i: usize,
    /// Index in *y*
    j: usize,
    /// Index in *z*
    k: usize,
    /// Data at this position
    data: f64,
}

/// Error type for handling file output. Effectively a wapper around multiple error types we encounter.
#[derive(Debug)]
pub enum Error {
    /// From disk issues.
    Io(io::Error),
    /// From `csv`.
    Csv(csv::Error),
    /// From `ndarray`.
    Shape(ndarray::ShapeError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Csv(ref err) => err.fmt(f),
            Error::Shape(ref err) => err.fmt(f),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => err.description(),
            Error::Csv(ref err) => err.description(),
            Error::Shape(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Io(ref err) => Some(err),
            Error::Csv(ref err) => Some(err),
            Error::Shape(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<csv::Error> for Error {
    fn from(err: csv::Error) -> Error {
        Error::Csv(err)
    }
}

impl From<ndarray::ShapeError> for Error {
    fn from(err: ndarray::ShapeError) -> Error {
        Error::Shape(err)
    }
}

/// Loads potential file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn potential(target_size: [usize; 3],
                 bb: usize,
                 binary: bool,
                 log: &Logger)
                 -> Result<Array3<f64>, Error> {
    let plain_path = "./input/potential.csv";
    let binary_path = "./input/potential.mpk";
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path.to_string())
    } else {
        None
    };
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path.to_string())
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple potential files found in input directory. Chosing 'potential.{}' based on configuration settings.",
              if binary { "mpk" } else { "csv" });
        if binary {
            potential_plain(plain_file, target_size, bb)
        } else {
            potential_binary(binary_file, target_size, bb)
        }
    } else if plain_file.is_some() {
        potential_plain(plain_file, target_size, bb)
    } else {
        potential_binary(binary_file, target_size, bb)
    }
}

/// Loads a potential from a csv file on disk.
fn potential_plain(file: Option<String>,
                   target_size: [usize; 3],
                   bb: usize)
                   -> Result<Array3<f64>, Error> {
    //No need for anything more here, just call the general parser.
    parse_csv_to_array3(file, target_size, bb)
}

/// Loads a potential from a mpk file on disk.
fn potential_binary(file: Option<String>,
                    target_size: [usize; 3],
                    bb: usize)
                    -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, for now call plain
    let _none = file;
    potential_plain(Some("./input/potential.csv".to_string()), target_size, bb)
}

/// Loads previously computed wavefunctions from disk.
pub fn load_wavefunctions(config: &Config,
                          log: &Logger,
                          binary: bool,
                          w_store: &mut Vec<Array3<f64>>)
                          -> Result<(), Error> {
    let num = &config.grid.size;
    let bb = config.central_difference.bb();
    let init_size: [usize; 3] = [num.x + bb, num.y + bb, num.z + bb];
    // Load required wavefunctions. If the current state resides on disk as well, we load that later.
    for wnum in 0..config.wavenum {
        let wfn = wavefunction(wnum, init_size, bb, binary, log);
        match wfn {
            Ok(w) => w_store.push(w),
            Err(err) => return Err(err),
            //TODO: Probably need to make these errors a litte more expressive in thier format sections. For example:
            //    panic!("Cannot load any wavefunction_{}* file from input folder: {}", wnum, err)
        }
        info!(log, "Loaded (previous) wavefunction {} from disk", wnum);
    }
    Ok(())
}

/// Loads wavefunction file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `wnum` - Excited state level of the wavefunction to load.
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn wavefunction(wnum: u8,
                    target_size: [usize; 3],
                    bb: usize,
                    binary: bool,
                    log: &Logger)
                    -> Result<Array3<f64>, Error> {
    let plain_path = format!("./input/wavefunction_{}.csv", wnum);
    let plain_path_partial = format!("./input/wavefunction_{}_partial.csv", wnum);
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path)
    } else if Path::new(&plain_path_partial).exists() {
        Some(plain_path_partial)
    } else {
        None
    };

    let binary_path = format!("./input/wavefunction_{}.mpk", wnum);
    let binary_path_partial = format!("./input/wavefunction_{}_partial.mpk", wnum);
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path)
    } else if Path::new(&binary_path_partial).exists() {
        Some(binary_path_partial)
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple wavefunction_{} files found in input directory. Chosing '{}' version based on configuration settings.",
              wnum,
              if binary { "mpk" } else { "csv" });
        if binary {
            wavefunction_plain(plain_file, target_size, bb)
        } else {
            wavefunction_binary(binary_file, target_size, bb)
        }
    } else if plain_file.is_some() {
        wavefunction_plain(plain_file, target_size, bb)
    } else {
        wavefunction_binary(binary_file, target_size, bb)
    }
}

/// Loads a wafefunction from a csv file on disk.
fn wavefunction_plain(file: Option<String>,
                      target_size: [usize; 3],
                      bb: usize)
                      -> Result<Array3<f64>, Error> {
    //No more to add here, just parse the file in the generic parser.
    parse_csv_to_array3(file, target_size, bb)
}

/// Loads a wafefunction from a mpk file on disk.
fn wavefunction_binary(file: Option<String>,
                       target_size: [usize; 3],
                       bb: usize)
                       -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, call plain
    //NOTE: This will guarentee a failure from the file name.
    wavefunction_plain(file, target_size, bb)
}

/// Checks that the folder `input` exists. If not, creates it.
/// This doesn't specifically need to happen for all instances,
/// but we may want to put restart values in there later on.
pub fn check_input_dir() -> Result<(), Error> {
    //std::io::Error
    if !Path::new("./input").exists() {
        create_dir("./input")?;
    }
    Ok(())
}

/// Given a filename, this funtion reads in the data of a csv file and parses
/// the values into a 3D array. There are a few caveats to this as the file
/// may be of a different shape to the requested size in the configuration file.
/// The routine therefore attempts to resample/interpolate the data to fit the required
/// parameters.
///
/// # Arguments
///
/// * `file` - A filename wrapped in an option. This function is called from filename parsers
/// which may not be able to obtain a valid location.
/// * `target_size` - Requsted size of the resultant array. If this size does not match the data
/// pulled from the file, interpolation or resampling will occur.
///
/// # Returns
///
/// * A 3D array loaded with data from the file and resampled/interpolated if required.
/// If something goes wrong in the parsing or file handling, a `csv::Error` is passed.
fn parse_csv_to_array3(file: Option<String>,
                       target_size: [usize; 3],
                       bb: usize)
                       -> Result<Array3<f64>, Error> {
    match file {
        Some(f) => {
            let mut rdr = csv::ReaderBuilder::new().has_headers(false).from_path(f)?;
            let mut max_i = 0;
            let mut max_j = 0;
            let mut max_k = 0;
            let mut data: Vec<f64> = Vec::new();
            for result in rdr.deserialize() {
                let record: PlainRecord = result?;
                if record.i > max_i {
                    max_i = record.i
                };
                if record.j > max_j {
                    max_j = record.j
                };
                if record.k > max_k {
                    max_k = record.k
                };
                data.push(record.data);
            }
            let numx = max_i + 1;
            let numy = max_j + 1;
            let numz = max_k + 1;
            match Array3::<f64>::from_shape_vec((numx, numy, numz), data) {
                Ok(result) => {
                    //result is now a parsed Array3 with the work area inside.
                    //We must fill this into an array with CD boundaries, provided
                    //it is the correct size. If not, we must scale it.
                    let init_size: [usize; 3] = [numx + bb, numy + bb, numz + bb];
                    let mut complete = Array3::<f64>::zeros(target_size);
                    {
                        let mut work = grid::get_mut_work_area(&mut complete, bb);
                        let same: bool = init_size
                            .iter()
                            .zip(target_size.iter())
                            .all(|(a, b)| a == b);
                        let smaller: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a < b);
                        let larger: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a > b);
                        if same {
                            // Input is the same size, copy down.
                            Zip::from(&mut work)
                                .and(result.view())
                                .par_apply(|work, &result| *work = result);
                        } else if smaller {
                            //TODO: Input has lower resolution. Spread it out.
                            panic!("Wavefunction is lower in resolution than requested");
                        } else if larger {
                            //TODO: Input has higer resolution. Sample it.
                            panic!("Wavefunction is higher in resolution than requested");
                        } else {
                            //TODO: Dimensons are all over the shop. Sample and interp
                            panic!("Wavefunction differs in resolution from requested");
                        }
                    }
                    Ok(complete)
                }
                Err(err) => Err(Error::Shape(err)),
            }
        }
        None => Err(Error::Io(io::Error::from(io::ErrorKind::NotFound))),
    }
}
<commit_msg>Fixed bug in read in and work area<commit_after>use csv;
use slog::Logger;
use std::fs::create_dir;
use std::io;
use std::error;
use std::fmt;
use std::path::Path;
use ndarray;
use ndarray::{Array3, Zip};
use ndarray_parallel::prelude::*;
use grid;
use config::Config;

#[derive(Debug,Deserialize)]
/// A simple struct to parse data from a plain csv file
struct PlainRecord {
    /// Index in *x*
    i: usize,
    /// Index in *y*
    j: usize,
    /// Index in *z*
    k: usize,
    /// Data at this position
    data: f64,
}

/// Error type for handling file output. Effectively a wapper around multiple error types we encounter.
#[derive(Debug)]
pub enum Error {
    /// From disk issues.
    Io(io::Error),
    /// From `csv`.
    Csv(csv::Error),
    /// From `ndarray`.
    Shape(ndarray::ShapeError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Csv(ref err) => err.fmt(f),
            Error::Shape(ref err) => err.fmt(f),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => err.description(),
            Error::Csv(ref err) => err.description(),
            Error::Shape(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Io(ref err) => Some(err),
            Error::Csv(ref err) => Some(err),
            Error::Shape(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<csv::Error> for Error {
    fn from(err: csv::Error) -> Error {
        Error::Csv(err)
    }
}

impl From<ndarray::ShapeError> for Error {
    fn from(err: ndarray::ShapeError) -> Error {
        Error::Shape(err)
    }
}

/// Loads potential file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn potential(target_size: [usize; 3],
                 bb: usize,
                 binary: bool,
                 log: &Logger)
                 -> Result<Array3<f64>, Error> {
    let plain_path = "./input/potential.csv";
    let binary_path = "./input/potential.mpk";
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path.to_string())
    } else {
        None
    };
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path.to_string())
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple potential files found in input directory. Chosing 'potential.{}' based on configuration settings.",
              if binary { "mpk" } else { "csv" });
        if binary {
            potential_plain(plain_file, target_size, bb)
        } else {
            potential_binary(binary_file, target_size, bb)
        }
    } else if plain_file.is_some() {
        potential_plain(plain_file, target_size, bb)
    } else {
        potential_binary(binary_file, target_size, bb)
    }
}

/// Loads a potential from a csv file on disk.
fn potential_plain(file: Option<String>,
                   target_size: [usize; 3],
                   bb: usize)
                   -> Result<Array3<f64>, Error> {
    //No need for anything more here, just call the general parser.
    parse_csv_to_array3(file, target_size, bb)
}

/// Loads a potential from a mpk file on disk.
fn potential_binary(file: Option<String>,
                    target_size: [usize; 3],
                    bb: usize)
                    -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, for now call plain
    let _none = file;
    potential_plain(Some("./input/potential.csv".to_string()), target_size, bb)
}

/// Loads previously computed wavefunctions from disk.
pub fn load_wavefunctions(config: &Config,
                          log: &Logger,
                          binary: bool,
                          w_store: &mut Vec<Array3<f64>>)
                          -> Result<(), Error> {
    let num = &config.grid.size;
    let bb = config.central_difference.bb();
    let init_size: [usize; 3] = [num.x + bb, num.y + bb, num.z + bb];
    // Load required wavefunctions. If the current state resides on disk as well, we load that later.
    for wnum in 0..config.wavenum {
        let wfn = wavefunction(wnum, init_size, bb, binary, log);
        match wfn {
            Ok(w) => w_store.push(w),
            Err(err) => return Err(err),
            //TODO: Probably need to make these errors a litte more expressive in thier format sections. For example:
            //    panic!("Cannot load any wavefunction_{}* file from input folder: {}", wnum, err)
        }
        info!(log, "Loaded (previous) wavefunction {} from disk", wnum);
    }
    Ok(())
}

/// Loads wavefunction file from disk. Handles cases where multiple files exist.
///
/// # Arguments
///
/// * `wnum` - Excited state level of the wavefunction to load.
/// * `target_size` - Size of the requested work area for this simulation. If the file on disk does
/// not meet these dimensions, it will be scaled.
/// * `binary` - Configuation flag concerning binary /  plain file output. Will be used as an arbitrator
/// when multiple files are detected.
/// * `log` - Reference to the system logger.
pub fn wavefunction(wnum: u8,
                    target_size: [usize; 3],
                    bb: usize,
                    binary: bool,
                    log: &Logger)
                    -> Result<Array3<f64>, Error> {
    let plain_path = format!("./input/wavefunction_{}.csv", wnum);
    let plain_path_partial = format!("./input/wavefunction_{}_partial.csv", wnum);
    let plain_file = if Path::new(&plain_path).exists() {
        Some(plain_path)
    } else if Path::new(&plain_path_partial).exists() {
        Some(plain_path_partial)
    } else {
        None
    };

    let binary_path = format!("./input/wavefunction_{}.mpk", wnum);
    let binary_path_partial = format!("./input/wavefunction_{}_partial.mpk", wnum);
    let binary_file = if Path::new(&binary_path).exists() {
        Some(binary_path)
    } else if Path::new(&binary_path_partial).exists() {
        Some(binary_path_partial)
    } else {
        None
    };

    if plain_file.is_some() && binary_file.is_some() {
        warn!(log,
              "Multiple wavefunction_{} files found in input directory. Chosing '{}' version based on configuration settings.",
              wnum,
              if binary { "mpk" } else { "csv" });
        if binary {
            wavefunction_plain(plain_file, target_size, bb)
        } else {
            wavefunction_binary(binary_file, target_size, bb)
        }
    } else if plain_file.is_some() {
        wavefunction_plain(plain_file, target_size, bb)
    } else {
        wavefunction_binary(binary_file, target_size, bb)
    }
}

/// Loads a wafefunction from a csv file on disk.
fn wavefunction_plain(file: Option<String>,
                      target_size: [usize; 3],
                      bb: usize)
                      -> Result<Array3<f64>, Error> {
    //No more to add here, just parse the file in the generic parser.
    parse_csv_to_array3(file, target_size, bb)
}

/// Loads a wafefunction from a mpk file on disk.
fn wavefunction_binary(file: Option<String>,
                       target_size: [usize; 3],
                       bb: usize)
                       -> Result<Array3<f64>, Error> {
    //TODO: Not implemented yet, call plain
    //NOTE: This will guarentee a failure from the file name.
    wavefunction_plain(file, target_size, bb)
}

/// Checks that the folder `input` exists. If not, creates it.
/// This doesn't specifically need to happen for all instances,
/// but we may want to put restart values in there later on.
pub fn check_input_dir() -> Result<(), Error> {
    //std::io::Error
    if !Path::new("./input").exists() {
        create_dir("./input")?;
    }
    Ok(())
}

/// Given a filename, this funtion reads in the data of a csv file and parses
/// the values into a 3D array. There are a few caveats to this as the file
/// may be of a different shape to the requested size in the configuration file.
/// The routine therefore attempts to resample/interpolate the data to fit the required
/// parameters.
///
/// # Arguments
///
/// * `file` - A filename wrapped in an option. This function is called from filename parsers
/// which may not be able to obtain a valid location.
/// * `target_size` - Requsted size of the resultant array. If this size does not match the data
/// pulled from the file, interpolation or resampling will occur.
///
/// # Returns
///
/// * A 3D array loaded with data from the file and resampled/interpolated if required.
/// If something goes wrong in the parsing or file handling, a `csv::Error` is passed.
fn parse_csv_to_array3(file: Option<String>,
                       target_size: [usize; 3],
                       bb: usize)
                       -> Result<Array3<f64>, Error> {
    match file {
        Some(f) => {
            let mut rdr = csv::ReaderBuilder::new().has_headers(false).from_path(f)?;
            let mut max_i = 0;
            let mut max_j = 0;
            let mut max_k = 0;
            let mut data: Vec<f64> = Vec::new();
            for result in rdr.deserialize() {
                let record: PlainRecord = result?;
                if record.i > max_i {
                    max_i = record.i
                };
                if record.j > max_j {
                    max_j = record.j
                };
                if record.k > max_k {
                    max_k = record.k
                };
                data.push(record.data);
            }
            let numx = max_i + 1;
            let numy = max_j + 1;
            let numz = max_k + 1;
            match Array3::<f64>::from_shape_vec((numx, numy, numz), data) {
                Ok(result) => {
                    //result is now a parsed Array3 with the work area inside.
                    //We must fill this into an array with CD boundaries, provided
                    //it is the correct size. If not, we must scale it.
                    let init_size: [usize; 3] = [numx + bb, numy + bb, numz + bb];
                    let mut complete = Array3::<f64>::zeros(target_size);
                    {
                        let mut work = grid::get_mut_work_area(&mut complete, bb/2); //NOTE: This is a bit of a hack. But it works.
                        let same: bool = init_size
                            .iter()
                            .zip(target_size.iter())
                            .all(|(a, b)| a == b);
                        let smaller: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a < b);
                        let larger: bool =
                            init_size.iter().zip(target_size.iter()).all(|(a, b)| a > b);
                        if same {
                            // Input is the same size, copy down.
                            Zip::from(&mut work)
                                .and(result.view())
                                .par_apply(|work, &result| *work = result);
                        } else if smaller {
                            //TODO: Input has lower resolution. Spread it out.
                            panic!("Wavefunction is lower in resolution than requested");
                        } else if larger {
                            //TODO: Input has higer resolution. Sample it.
                            panic!("Wavefunction is higher in resolution than requested");
                        } else {
                            //TODO: Dimensons are all over the shop. Sample and interp
                            panic!("Wavefunction differs in resolution from requested");
                        }
                    }
                    Ok(complete)
                }
                Err(err) => Err(Error::Shape(err)),
            }
        }
        None => Err(Error::Io(io::Error::from(io::ErrorKind::NotFound))),
    }
}
<|endoftext|>


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

use scanner::Scanner;
use regex::Regex;

const COMPARISON           : &'static str = r"^(==|!=|<>|<=?|>=?|contains)";
const SINGLE_STRING_LITERAL: &'static str = r"^'[^']*'";
const DOUBLE_STRING_LITERAL: &'static str = r#"^"[^"]*""#;
const NUMBER_LITERAL:        &'static str = r"^-?\d+(\.\d+)?";
const IDENTIFIER:            &'static str = r"^[a-zA-Z_][\w-]*\??";
const RANGE_OP:              &'static str = r"^\.\.";

#[derive(Clone, Debug, PartialEq)]
pub enum Token {
    Comparison(String),
    Identifier(String),
    Number(String),
    String(String),
    Range(String),
    Pipe,
    Dot,
    Colon,
    Comma,
    OpenSquare,
    CloseSquare,
    OpenRound,
    CloseRound,
    Question,
    Dash
}

macro_rules! token {
    (Range)               => (token!(Range, ".."));
    ($e:ident)            => (Token::$e);
    ($e:ident, $str:expr) => (Token::$e(String::from($str)))
}

pub struct Tokens<'t> {
    scanner: &'t Scanner<'t>,
    specials: HashMap<&'t str, Token>,
    matchers: Vec<Regex>
}

impl<'t> Tokens<'t> {
    fn new<'a>(scanner: &'a Scanner<'a>) -> Tokens<'a> {
        let mut specials = HashMap::new();
        specials.insert("|", Token::Pipe);
        specials.insert(".", Token::Dot);
        specials.insert(":", Token::Colon);
        specials.insert(",", Token::Comma);
        specials.insert("[", Token::OpenSquare);
        specials.insert("]", Token::CloseSquare);
        specials.insert("(", Token::OpenRound);
        specials.insert(")", Token::CloseRound);
        specials.insert("?", Token::Question);
        specials.insert("-", Token::Dash);

        let matchers = vec![
            Regex::new(COMPARISON).unwrap(),
            Regex::new(SINGLE_STRING_LITERAL).unwrap(),
            Regex::new(DOUBLE_STRING_LITERAL).unwrap(),
            Regex::new(NUMBER_LITERAL).unwrap(),
            Regex::new(IDENTIFIER).unwrap(),
            Regex::new(RANGE_OP).unwrap()
        ];

        Tokens { scanner: scanner, specials: specials, matchers: matchers }
    }

    fn token_for(&self, pattern: &Regex, value: &str) -> Token {
        match pattern.as_str() {
            COMPARISON            => token!(Comparison, value),
            SINGLE_STRING_LITERAL => token!(String, value),
            DOUBLE_STRING_LITERAL => token!(String, value),
            NUMBER_LITERAL        => token!(Number, value),
            IDENTIFIER            => token!(Identifier, value),
            RANGE_OP              => token!(Range),
            _                     => unreachable!() // already been checked for existence
        }
    }

    fn next_match(&self) -> Option<Token> {
        match self.matchers.iter().find(|&m| self.scanner.check(m)) {
            Some(regex) => self.matched_token(&regex),
            None => self.matched_special()
        }
    }

    fn matched_token(&self, pattern: &Regex) -> Option<Token> {
        let value = self.scanner.scan(pattern).unwrap();
        Some(self.token_for(pattern, value))
    }

    fn matched_special(&self) -> Option<Token> {
        match self.scanner.get_char() {
            Some(character) => {
                match self.specials.get(character) {
                    Some(token) => Some((*token).clone()),
                    None => unreachable!("Syntax Error")
                }
            },
            None => None
        }
    }
}

impl<'t> Iterator for Tokens<'t> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        self.next_match()
    }
}

pub struct Lexer<'t> {
    scanner: Scanner<'t>
}

impl<'t> Lexer<'t> {
    pub fn new<'a>(source: &'a str) -> Lexer<'a> {
        Lexer { scanner: Scanner::new(source) }
    }

    pub fn tokens(&self) -> Tokens {
        Tokens::new(&self.scanner)
    }
}

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

    fn compare_tokens(lexer: &Lexer, expectedTokens: Vec<Token>) {
        let zipped = lexer.tokens().zip(expectedTokens);

        for token in zipped {
            let (actual, expected) = token;
            assert_eq!(expected, actual);
        }
    }

    #[test]
    fn new_creates_a_new_instance() {
        let lexer = Lexer::new("doSomthing | filter");
        assert_eq!("doSomthing | filter", lexer.scanner.rest().unwrap());
    }

    #[test]
    fn tokens_when_given_a_blank_string() {
        let lexer              = Lexer::new("");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_when_given_a_whitespace_only_string() {
        let lexer              = Lexer::new("  \t \n\r ");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_parses_identifiers() {
        let lexer    = Lexer::new("high five?");
        let expected = vec![
            token!(Identifier, "high"),
            token!(Identifier, "five?")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_knows_that_identifiers_dont_start_with_numbers() {
        let lexer    = Lexer::new("2foo 5.0bar");
        let expected = vec![
            token!(Number, "2"),
            token!(Identifier, "foo"),
            token!(Number, "5.0"),
            token!(Identifier, "bar")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_string_literals() {
        let lexer    = Lexer::new(r#" 'this is a test""' "wat 'lol'" "#);
        let expected = vec![
            token!(String, r#"'this is a test""'"#),
            token!(String, r#""wat 'lol'""#)
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_integers() {
        let lexer    = Lexer::new("hi 50");
        let expected = vec![
            token!(Identifier, "hi"),
            token!(Number, "50")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_floats() {
        let lexer    = Lexer::new("hi 5.0");
        let expected = vec![
            token!(Identifier, "hi"),
            token!(Number, "5.0")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_comparisons() {
        let lexer    = Lexer::new("== <> contains");
        let expected = vec![
            token!(Comparison, "=="),
            token!(Comparison, "<>"),
            token!(Comparison, "contains")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_range_operator() {
        let lexer    = Lexer::new("1..10");
        let expected = vec![
            token!(Number, "1"),
            token!(Range),
            token!(Number, "10")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_special_characters() {
        let lexer    = Lexer::new("[hi], (| .:) - ?cool");
        let expected = vec![
            token!(OpenSquare),
            token!(Identifier, "hi"),
            token!(CloseSquare),
            token!(Comma),
            token!(OpenRound),
            token!(Pipe),
            token!(Dot),
            token!(Colon),
            token!(CloseRound),
            token!(Dash),
            token!(Question),
            token!(Identifier, "cool")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_skips_internal_whitespace() {
        let lexer    = Lexer::new("five|\n\t ==");
        let expected = vec![
            token!(Identifier, "five"),
            token!(Pipe),
            token!(Comparison, "==")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    #[should_panic(expected = "Syntax Error")]
    fn tokens_freaks_out_with_syntax_error() {
        let lexer              = Lexer::new("%");
        let tokens: Vec<Token> = lexer.tokens().collect();
    }
}
<commit_msg>adding LexedToken type<commit_after>use std::collections::HashMap;

use scanner::Scanner;
use regex::Regex;

const COMPARISON           : &'static str = r"^(==|!=|<>|<=?|>=?|contains)";
const SINGLE_STRING_LITERAL: &'static str = r"^'[^']*'";
const DOUBLE_STRING_LITERAL: &'static str = r#"^"[^"]*""#;
const NUMBER_LITERAL:        &'static str = r"^-?\d+(\.\d+)?";
const IDENTIFIER:            &'static str = r"^[a-zA-Z_][\w-]*\??";
const RANGE_OP:              &'static str = r"^\.\.";

#[derive(Clone, Debug, PartialEq)]
pub enum Token {
    Comparison,
    Identifier,
    Number,
    String,
    Range,
    Pipe,
    Dot,
    Colon,
    Comma,
    OpenSquare,
    CloseSquare,
    OpenRound,
    CloseRound,
    Question,
    Dash
}

pub type LexedToken = (Token, String);

macro_rules! token {
    (Range)                         => (token!(Range, ".."));
    (Pipe)                          => (token!(Pipe, "|"));
    (Dot)                           => (token!(Dot, "."));
    (Colon)                         => (token!(Colon, ":"));
    (Comma)                         => (token!(Comma, ","));
    (OpenSquare)                    => (token!(OpenSquare, "["));
    (CloseSquare)                   => (token!(CloseSquare, "]"));
    (OpenRound)                     => (token!(OpenRound, "("));
    (CloseRound)                    => (token!(CloseRound, ")"));
    (Question)                      => (token!(Question, "?"));
    (Dash)                          => (token!(Dash, "-"));
    ($tokenType:ident, $value:expr) => ((Token::$tokenType, String::from($value)));
}

pub struct Tokens<'t> {
    scanner: &'t Scanner<'t>,
    specials: HashMap<&'t str, Token>,
    matchers: Vec<Regex>
}

impl<'t> Tokens<'t> {
    fn new<'a>(scanner: &'a Scanner<'a>) -> Tokens<'a> {
        let mut specials = HashMap::new();
        specials.insert("|", Token::Pipe);
        specials.insert(".", Token::Dot);
        specials.insert(":", Token::Colon);
        specials.insert(",", Token::Comma);
        specials.insert("[", Token::OpenSquare);
        specials.insert("]", Token::CloseSquare);
        specials.insert("(", Token::OpenRound);
        specials.insert(")", Token::CloseRound);
        specials.insert("?", Token::Question);
        specials.insert("-", Token::Dash);

        let matchers = vec![
            Regex::new(COMPARISON).unwrap(),
            Regex::new(SINGLE_STRING_LITERAL).unwrap(),
            Regex::new(DOUBLE_STRING_LITERAL).unwrap(),
            Regex::new(NUMBER_LITERAL).unwrap(),
            Regex::new(IDENTIFIER).unwrap(),
            Regex::new(RANGE_OP).unwrap()
        ];

        Tokens { scanner: scanner, specials: specials, matchers: matchers }
    }

    fn token_for(&self, pattern: &Regex, value: &str) -> LexedToken {
        match pattern.as_str() {
            COMPARISON            => token!(Comparison, value),
            SINGLE_STRING_LITERAL => token!(String, value),
            DOUBLE_STRING_LITERAL => token!(String, value),
            NUMBER_LITERAL        => token!(Number, value),
            IDENTIFIER            => token!(Identifier, value),
            RANGE_OP              => token!(Range),
            _                     => unreachable!() // already been checked for existence
        }
    }

    fn next_match(&self) -> Option<LexedToken> {
        match self.matchers.iter().find(|&m| self.scanner.check(m)) {
            Some(regex) => self.matched_token(&regex),
            None => self.matched_special()
        }
    }

    fn matched_token(&self, pattern: &Regex) -> Option<LexedToken> {
        let value = self.scanner.scan(pattern).unwrap();
        Some(self.token_for(pattern, value))
    }

    fn matched_special(&self) -> Option<LexedToken> {
        match self.scanner.get_char() {
            Some(character) => {
                match self.specials.get(character) {
                    Some(token) => Some(((*token).clone(), character.into())),
                    None => unreachable!("Syntax Error")
                }
            },
            None => None
        }
    }
}

impl<'t> Iterator for Tokens<'t> {
    type Item = (Token, String);

    fn next(&mut self) -> Option<LexedToken> {
        self.next_match()
    }
}

pub struct Lexer<'t> {
    scanner: Scanner<'t>
}

impl<'t> Lexer<'t> {
    pub fn new<'a>(source: &'a str) -> Lexer<'a> {
        Lexer { scanner: Scanner::new(source) }
    }

    pub fn tokens(&self) -> Tokens {
        Tokens::new(&self.scanner)
    }
}

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

    fn compare_tokens(lexer: &Lexer, expectedTokens: Vec<LexedToken>) {
        let zipped = lexer.tokens().zip(expectedTokens);

        for token in zipped {
            let (actual, expected) = token;
            assert_eq!(expected, actual);
        }
    }

    #[test]
    fn new_creates_a_new_instance() {
        let lexer = Lexer::new("doSomthing | filter");
        assert_eq!("doSomthing | filter", lexer.scanner.rest().unwrap());
    }

    #[test]
    fn tokens_when_given_a_blank_string() {
        let lexer                   = Lexer::new("");
        let tokens: Vec<LexedToken> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_when_given_a_whitespace_only_string() {
        let lexer                   = Lexer::new("  \t \n\r ");
        let tokens: Vec<LexedToken> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_parses_identifiers() {
        let lexer    = Lexer::new("high five?");
        let expected = vec![
            token!(Identifier, "high"),
            token!(Identifier, "five?")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_knows_that_identifiers_dont_start_with_numbers() {
        let lexer    = Lexer::new("2foo 5.0bar");
        let expected = vec![
            token!(Number, "2"),
            token!(Identifier, "foo"),
            token!(Number, "5.0"),
            token!(Identifier, "bar")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_string_literals() {
        let lexer    = Lexer::new(r#" 'this is a test""' "wat 'lol'" "#);
        let expected = vec![
            token!(String, r#"'this is a test""'"#),
            token!(String, r#""wat 'lol'""#)
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_integers() {
        let lexer    = Lexer::new("hi 50");
        let expected = vec![
            token!(Identifier, "hi"),
            token!(Number, "50")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_floats() {
        let lexer    = Lexer::new("hi 5.0");
        let expected = vec![
            token!(Identifier, "hi"),
            token!(Number, "5.0")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_comparisons() {
        let lexer    = Lexer::new("== <> contains");
        let expected = vec![
            token!(Comparison, "=="),
            token!(Comparison, "<>"),
            token!(Comparison, "contains")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_range_operator() {
        let lexer    = Lexer::new("1..10");
        let expected = vec![
            token!(Number, "1"),
            token!(Range),
            token!(Number, "10")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_parses_special_characters() {
        let lexer    = Lexer::new("[hi], (| .:) - ?cool");
        let expected = vec![
            token!(OpenSquare),
            token!(Identifier, "hi"),
            token!(CloseSquare),
            token!(Comma),
            token!(OpenRound),
            token!(Pipe),
            token!(Dot),
            token!(Colon),
            token!(CloseRound),
            token!(Dash),
            token!(Question),
            token!(Identifier, "cool")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    fn tokens_skips_internal_whitespace() {
        let lexer    = Lexer::new("five|\n\t ==");
        let expected = vec![
            token!(Identifier, "five"),
            token!(Pipe),
            token!(Comparison, "==")
        ];

        compare_tokens(&lexer, expected);
    }

    #[test]
    #[should_panic(expected = "Syntax Error")]
    fn tokens_freaks_out_with_syntax_error() {
        let lexer                   = Lexer::new("%");
        let tokens: Vec<LexedToken> = lexer.tokens().collect();
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap,BTreeSet};
use std::io::prelude::*;

use source::Src;
use var::Var;
use eval::Eval;

#[derive(Debug,PartialEq)]
pub struct SrcBlock {
    pub name: String,
    pub src: Vec<Src>,
    pub await_idx: usize,
    pub visited: bool,
}

#[derive(Debug,PartialEq)]
pub struct DefBlock {
    pub name: String,
    pub def: HashMap<String,Var>
}

#[derive(Debug,PartialEq)]
pub enum Block {
    Src(SrcBlock),
    Def(DefBlock),
}

/// Intermediate Representation
///
/// This is used during the parsing stage
#[derive(Debug,Clone,PartialEq)]
pub enum IR {
    String(String),
    /// A non-quoted string turns into a symbol/token
    Sym(String),
    /// Key-Value, pre-parsed
    Map(Vec<IR>),
}

impl From<IR> for String {
    fn from(t:IR) -> String {
        match t {
            IR::String(s) => s,
            IR::Sym(s) => s,
            IR::Map(mut v) => {
                let mut s = "{".to_owned();
                for n in v.drain(..) {
                    let n:String = n.into();
                    s.push_str(&n);
                }

                s.push('}');

                s
            }
        }
    }
}
impl From<Var> for IR {
    fn from(t:Var) -> IR {
        match t {
            Var::String(t) => { IR::String(t) }
            _ => { IR::Sym(t.to_string()) }
        }
    }
}

/// Map object for Selects
pub type Map = HashMap<String,Vec<Var>>;

pub struct Parser(Vec<Block>);

use std::ops::Deref;
impl Deref for Parser {
    type Target = Vec<Block>;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl Parser {
    pub fn parse_blocks (src: &str) -> Parser {
        let mut v = vec!();
        let mut exp = String::new();
        let mut exps: Vec<IR> = vec!();
        let mut map_ir: Vec<IR> = vec!(); //contains pre-parsed map
        let mut block: Option<Block> = None;

        let mut in_string = false;
        let mut in_comment = false;
        let mut in_vec = false;
        let mut in_map = false;
        let mut was_if = false;

        let mut usyms = BTreeSet::new(); //unique set, remove dupes

        for c in src.chars() {
            if !in_comment && !in_string {
                if c == '[' { in_vec = true; continue }
                else if c == ']' { in_vec = false; }

            }

            if c == '#' && !in_string { in_comment = true; continue }
            else if  c == '\n' && in_comment && !in_string { in_comment = false; }

            if c == '\n' && (in_vec || in_map) && !in_comment && !in_string { continue }

            if (c == ']' ||
                c == '}' ||
                c == '#' ||
                c == '\n')
                && !in_string && !in_comment
            {
                for n in exp.split_whitespace() {
                    let sym = IR::Sym(n.trim().to_owned());
                    if in_map { map_ir.push(sym); }
                    else { exps.push(sym); }
                }

                if c == '}' && in_map && !in_comment{
                    in_map = false;
                    exps.push(IR::Map(map_ir));
                    map_ir = vec![];
                }

                exp = String::new();

                if exps.len() < 1 { continue }

                // determine block type
                if block.is_none() {
                    let name = exps.remove(0).into();
                    if name == "def" {
                        let b = DefBlock {
                            name: exps.pop().unwrap().into(),
                            def: HashMap::new(),
                        };

                        block = Some(Block::Def(b));
                    }
                    else {
                        let b = SrcBlock {
                            name: name,
                            src: vec!(),
                            await_idx: 0,
                            visited: false,
                        };

                        block = Some(Block::Src(b));
                    }

                }
                else { // build block type
                    let mut qsyms:Vec<(String,String)> = vec!();
                    let adjust_sym = |qsyms: &mut Vec<(String,String)>, s: &mut String| {
                        if s.chars().next().expect("ERROR: Empty Eager Symbol") == '!' {
                            let mut sym = "not_".to_owned();
                            sym.push_str(s[1..].trim());

                            let osym = s.trim().to_owned();

                            qsyms.push((sym.clone(),osym));
                            *s = sym;
                        }
                    };

                    // this builds symbol refs as a convenience
                    if exps.len() > 2 { //must be if/or/comp blocks
                        for n in exps.iter_mut() {
                            match n {
                                &mut IR::Sym(ref mut s) => {
                                    adjust_sym(&mut qsyms,s);
                                },
                                &mut IR::Map(ref mut v) => {
                                    for n in v.iter_mut() {
                                        match n {
                                            &mut IR::Sym(ref mut s) => {
                                                adjust_sym(&mut qsyms,s);
                                            },
                                            _ => {},
                                        }
                                    }
                                },
                                _ => {},
                            }
                        }
                    }

                    match block {
                        Some(Block::Def(ref mut b)) => {
                            let v = exps.pop().unwrap();
                            b.def.insert(exps.pop().unwrap().into(),
                                         Var::parse(v));
                        },
                        Some(Block::Src(ref mut b)) => {
                            let mut srcs = vec![];

                            for (qsym,sym) in qsyms.drain(..) {
                                if usyms.contains(&qsym) { continue }
                                usyms.insert(qsym.clone());

                                let src = Src::parse(vec![IR::Sym(qsym),
                                                          IR::Sym(sym)]);
                                srcs.push(src);
                            }

                            let src = Src::parse(exps);
                            srcs.push(src);

                            for src in srcs.drain(..) {
                                match &src {
                                    &Src::If(_,_,_) => { was_if = true; },
                                    &Src::Or(_,_) => {
                                        if !was_if {
                                            panic!("ERROR: IF must prepend OR");
                                        }
                                    },
                                    _ => { was_if = false; },
                                }

                                b.src.push(src);
                            }
                        },
                        _ => {}
                    }

                    exps = vec!();
                }
            }
            else if c == '"' && !in_comment {
                in_string = !in_string;
                if in_string { //starting a new quoted string? let's push this sym
                    for n in exp.split_whitespace() {
                        let sym = IR::Sym(n.trim().to_owned());
                        if in_map { map_ir.push(sym); }
                        else { exps.push(sym); }
                    }
                    exp = String::new();
                }
                else if !in_string { //finished the quoted string?
                    let sym = IR::String(exp);
                    if in_map { map_ir.push(sym); }
                    else { exps.push(sym); }
                    exp = String::new();
                }
            }
            else if c == ';' && !in_string && !in_comment {
                //fail otherwise, block should be built!
                v.push(block.expect("ERROR: Parse Block no built!"));
                usyms.clear(); //clear out on new block
                block = None;
            }
            else {
                if c == '{' && !in_comment && !in_string {
                    in_map = true;
                    // push previous symbol
                    let sym = IR::Sym(exp.trim().to_owned());
                    exps.push(sym);
                    exp.clear();
                }
                else if !in_comment {
                    exp.push(c);
                }
            }
        }

        Parser(v)
    }

    pub fn into_env (mut self) -> Env {
        let mut src = HashMap::new();
        let mut def = HashMap::new();

        for b in self.0.drain(..) {
            match b {
                Block::Def(db) => {
                    def.insert(db.name.clone(), db);
                },
                Block::Src(sb) => {
                    src.insert(sb.name.clone(), sb);
                },
            }

        }

        Env { def: def, src: src }
    }

    /// Parses a map from IR
    ///
    /// Parsed using commas for variable sized maps
    pub fn parse_map (map_ir: IR) -> Option<Map> {
        let mut map: Map = HashMap::new(); // optionally unbounded val-lengths

        match map_ir {
            IR::Map(mut exps) => {
                let mut key = "".to_owned();
                let mut vals = vec![];

                for n in exps.drain(..) {
                    if key.is_empty() { key = n.into(); continue }

                    match n {
                        IR::Sym(mut s) => {
                            if s.chars().last() == Some(',') {
                                let _ = s.pop();
                                vals.push(Var::parse(IR::Sym(s)));

                                map.insert(key,vals);
                                vals = vec![];
                                key = "".to_owned();

                                continue
                            }

                            vals.push(Var::parse(IR::Sym(s)));
                        },
                        _ => { vals.push(Var::parse(n)); },
                    }
                }

                if !key.is_empty() && vals.len() > 0 {
                    map.insert(key,vals);
                }
                else if !key.is_empty() {
                    panic!("ERROR: Unbalanced MAP at: {:?}",key);
                }

                Some(map)
            },
            _=> {None}
        }
    }
}

/// Def alias used for internal evaluation purposes
pub type Def = HashMap<String, DefBlock>;

impl Env {
    pub fn def_contains(def: &Def, path: Option<Vec<&str>>, lookup: &str) -> bool {
        if let Some(path) = path {
            if let Some(ref def) = def.get(path[0]) {
                return def.def.contains_key(lookup)
            }
        }

        false
    }
}

/// Environment containing all parsed definition and source blocks
pub struct Env {
    pub def: Def,
    pub src: HashMap<String, SrcBlock>
}

impl Eval for Def {
    fn get (&self, path: Option<Vec<&str>>, lookup: &str) -> Option<Var> {
        if let Some(path) = path {
            if let Some(ref def) = self.get(path[0]) {
                if let Some(v) = def.def.get(lookup) {
                    return Some(v.clone())
                }
            }
        }

        None
    }

    #[allow(unused_variables)]
    fn set (&mut self, path: Option<Vec<&str>>, lookup: &str, var: Var) {
        if let Some(path) = path {
            if let Some(ref mut def) = self.get_mut(path[0]) {
                if let Some(v) = def.def.get_mut(lookup) {
                    *v = var;
                }
            }
        }
    }

    #[allow(unused_variables)]
    fn call (&mut self, var: Var, fun: &str, vars: &Vec<Var>) -> Option<Var> {
        None
    }
}

pub struct StreamParser<'a, S:Read> {
    /// The non-parsed leftovers of a stream that is being buffered actively
    buf: Vec<&'a str>,
    stream: S,
}

impl<'a, S:Read> StreamParser<'a, S> {
    pub fn new (s: S) -> StreamParser<'a, S> {
        StreamParser {
            buf: vec![],
            stream: s,
        }
    }

}
<commit_msg>add stream buf to parser<commit_after>use std::collections::{HashMap,BTreeSet};
use std::io::prelude::*;

use source::Src;
use var::Var;
use eval::Eval;

#[derive(Debug,PartialEq)]
pub struct SrcBlock {
    pub name: String,
    pub src: Vec<Src>,
    pub await_idx: usize,
    pub visited: bool,
}

#[derive(Debug,PartialEq)]
pub struct DefBlock {
    pub name: String,
    pub def: HashMap<String,Var>
}

#[derive(Debug,PartialEq)]
pub enum Block {
    Src(SrcBlock),
    Def(DefBlock),
}

/// Intermediate Representation
///
/// This is used during the parsing stage
#[derive(Debug,Clone,PartialEq)]
pub enum IR {
    String(String),
    /// A non-quoted string turns into a symbol/token
    Sym(String),
    /// Key-Value, pre-parsed
    Map(Vec<IR>),
}

impl From<IR> for String {
    fn from(t:IR) -> String {
        match t {
            IR::String(s) => s,
            IR::Sym(s) => s,
            IR::Map(mut v) => {
                let mut s = "{".to_owned();
                for n in v.drain(..) {
                    let n:String = n.into();
                    s.push_str(&n);
                }

                s.push('}');

                s
            }
        }
    }
}
impl From<Var> for IR {
    fn from(t:Var) -> IR {
        match t {
            Var::String(t) => { IR::String(t) }
            _ => { IR::Sym(t.to_string()) }
        }
    }
}

/// Map object for Selects
pub type Map = HashMap<String,Vec<Var>>;

pub struct Parser(Vec<Block>);

use std::ops::Deref;
impl Deref for Parser {
    type Target = Vec<Block>;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl Parser {
    pub fn parse_blocks (src: &str) -> Parser {
        let mut v = vec!();
        let mut exp = String::new();
        let mut exps: Vec<IR> = vec!();
        let mut map_ir: Vec<IR> = vec!(); //contains pre-parsed map
        let mut block: Option<Block> = None;

        let mut in_string = false;
        let mut in_comment = false;
        let mut in_vec = false;
        let mut in_map = false;
        let mut was_if = false;

        let mut usyms = BTreeSet::new(); //unique set, remove dupes

        for c in src.chars() {
            if !in_comment && !in_string {
                if c == '[' { in_vec = true; continue }
                else if c == ']' { in_vec = false; }

            }

            if c == '#' && !in_string { in_comment = true; continue }
            else if  c == '\n' && in_comment && !in_string { in_comment = false; }

            if c == '\n' && (in_vec || in_map) && !in_comment && !in_string { continue }

            if (c == ']' ||
                c == '}' ||
                c == '#' ||
                c == '\n')
                && !in_string && !in_comment
            {
                for n in exp.split_whitespace() {
                    let sym = IR::Sym(n.trim().to_owned());
                    if in_map { map_ir.push(sym); }
                    else { exps.push(sym); }
                }

                if c == '}' && in_map && !in_comment{
                    in_map = false;
                    exps.push(IR::Map(map_ir));
                    map_ir = vec![];
                }

                exp = String::new();

                if exps.len() < 1 { continue }

                // determine block type
                if block.is_none() {
                    let name = exps.remove(0).into();
                    if name == "def" {
                        let b = DefBlock {
                            name: exps.pop().unwrap().into(),
                            def: HashMap::new(),
                        };

                        block = Some(Block::Def(b));
                    }
                    else {
                        let b = SrcBlock {
                            name: name,
                            src: vec!(),
                            await_idx: 0,
                            visited: false,
                        };

                        block = Some(Block::Src(b));
                    }

                }
                else { // build block type
                    let mut qsyms:Vec<(String,String)> = vec!();
                    let adjust_sym = |qsyms: &mut Vec<(String,String)>, s: &mut String| {
                        if s.chars().next().expect("ERROR: Empty Eager Symbol") == '!' {
                            let mut sym = "not_".to_owned();
                            sym.push_str(s[1..].trim());

                            let osym = s.trim().to_owned();

                            qsyms.push((sym.clone(),osym));
                            *s = sym;
                        }
                    };

                    // this builds symbol refs as a convenience
                    if exps.len() > 2 { //must be if/or/comp blocks
                        for n in exps.iter_mut() {
                            match n {
                                &mut IR::Sym(ref mut s) => {
                                    adjust_sym(&mut qsyms,s);
                                },
                                &mut IR::Map(ref mut v) => {
                                    for n in v.iter_mut() {
                                        match n {
                                            &mut IR::Sym(ref mut s) => {
                                                adjust_sym(&mut qsyms,s);
                                            },
                                            _ => {},
                                        }
                                    }
                                },
                                _ => {},
                            }
                        }
                    }

                    match block {
                        Some(Block::Def(ref mut b)) => {
                            let v = exps.pop().unwrap();
                            b.def.insert(exps.pop().unwrap().into(),
                                         Var::parse(v));
                        },
                        Some(Block::Src(ref mut b)) => {
                            let mut srcs = vec![];

                            for (qsym,sym) in qsyms.drain(..) {
                                if usyms.contains(&qsym) { continue }
                                usyms.insert(qsym.clone());

                                let src = Src::parse(vec![IR::Sym(qsym),
                                                          IR::Sym(sym)]);
                                srcs.push(src);
                            }

                            let src = Src::parse(exps);
                            srcs.push(src);

                            for src in srcs.drain(..) {
                                match &src {
                                    &Src::If(_,_,_) => { was_if = true; },
                                    &Src::Or(_,_) => {
                                        if !was_if {
                                            panic!("ERROR: IF must prepend OR");
                                        }
                                    },
                                    _ => { was_if = false; },
                                }

                                b.src.push(src);
                            }
                        },
                        _ => {}
                    }

                    exps = vec!();
                }
            }
            else if c == '"' && !in_comment {
                in_string = !in_string;
                if in_string { //starting a new quoted string? let's push this sym
                    for n in exp.split_whitespace() {
                        let sym = IR::Sym(n.trim().to_owned());
                        if in_map { map_ir.push(sym); }
                        else { exps.push(sym); }
                    }
                    exp = String::new();
                }
                else if !in_string { //finished the quoted string?
                    let sym = IR::String(exp);
                    if in_map { map_ir.push(sym); }
                    else { exps.push(sym); }
                    exp = String::new();
                }
            }
            else if c == ';' && !in_string && !in_comment {
                //fail otherwise, block should be built!
                v.push(block.expect("ERROR: Parse Block no built!"));
                usyms.clear(); //clear out on new block
                block = None;
            }
            else {
                if c == '{' && !in_comment && !in_string {
                    in_map = true;
                    // push previous symbol
                    let sym = IR::Sym(exp.trim().to_owned());
                    exps.push(sym);
                    exp.clear();
                }
                else if !in_comment {
                    exp.push(c);
                }
            }
        }

        Parser(v)
    }

    pub fn into_env (mut self) -> Env {
        let mut src = HashMap::new();
        let mut def = HashMap::new();

        for b in self.0.drain(..) {
            match b {
                Block::Def(db) => {
                    def.insert(db.name.clone(), db);
                },
                Block::Src(sb) => {
                    src.insert(sb.name.clone(), sb);
                },
            }

        }

        Env { def: def, src: src }
    }

    /// Parses a map from IR
    ///
    /// Parsed using commas for variable sized maps
    pub fn parse_map (map_ir: IR) -> Option<Map> {
        let mut map: Map = HashMap::new(); // optionally unbounded val-lengths

        match map_ir {
            IR::Map(mut exps) => {
                let mut key = "".to_owned();
                let mut vals = vec![];

                for n in exps.drain(..) {
                    if key.is_empty() { key = n.into(); continue }

                    match n {
                        IR::Sym(mut s) => {
                            if s.chars().last() == Some(',') {
                                let _ = s.pop();
                                vals.push(Var::parse(IR::Sym(s)));

                                map.insert(key,vals);
                                vals = vec![];
                                key = "".to_owned();

                                continue
                            }

                            vals.push(Var::parse(IR::Sym(s)));
                        },
                        _ => { vals.push(Var::parse(n)); },
                    }
                }

                if !key.is_empty() && vals.len() > 0 {
                    map.insert(key,vals);
                }
                else if !key.is_empty() {
                    panic!("ERROR: Unbalanced MAP at: {:?}",key);
                }

                Some(map)
            },
            _=> {None}
        }
    }
}

/// Def alias used for internal evaluation purposes
pub type Def = HashMap<String, DefBlock>;

impl Env {
    pub fn def_contains(def: &Def, path: Option<Vec<&str>>, lookup: &str) -> bool {
        if let Some(path) = path {
            if let Some(ref def) = def.get(path[0]) {
                return def.def.contains_key(lookup)
            }
        }

        false
    }
}

/// Environment containing all parsed definition and source blocks
pub struct Env {
    pub def: Def,
    pub src: HashMap<String, SrcBlock>
}

impl Eval for Def {
    fn get (&self, path: Option<Vec<&str>>, lookup: &str) -> Option<Var> {
        if let Some(path) = path {
            if let Some(ref def) = self.get(path[0]) {
                if let Some(v) = def.def.get(lookup) {
                    return Some(v.clone())
                }
            }
        }

        None
    }

    #[allow(unused_variables)]
    fn set (&mut self, path: Option<Vec<&str>>, lookup: &str, var: Var) {
        if let Some(path) = path {
            if let Some(ref mut def) = self.get_mut(path[0]) {
                if let Some(v) = def.def.get_mut(lookup) {
                    *v = var;
                }
            }
        }
    }

    #[allow(unused_variables)]
    fn call (&mut self, var: Var, fun: &str, vars: &Vec<Var>) -> Option<Var> {
        None
    }
}

pub struct StreamParser<S:Read> {
    /// The non-parsed leftovers of a stream that is being buffered actively
    buf: String,
    stream: S,
}

impl<S:Read> StreamParser<S> {
    pub fn new (s: S) -> StreamParser<S> {
        StreamParser {
            buf: String::new(),
            stream: s,
        }
    }

    pub fn parse (&mut self) -> Option<Parser> {
        let mut buf = vec![];
        if let Ok(n) = self.stream.read(&mut buf[..]) {
            if n > 0 {
                if let Ok(s) = String::from_utf8(buf) {
                    self.buf.push_str(&s);
                }
            }
        }

        let mut parser = None;
        let mut block = String::new();
        for c in self.buf.drain(..) {
            block.push(c);
            if c == ';' {
                parser = Some(Parser::parse_blocks(&block));
            }
        }

        if !block.is_empty() { self.buf.push_str(&block); } //put back anything if needed

        parser
    }
}
<|endoftext|>