<commit_before>extern crate rustbox;
extern crate chrono;

use rustbox::*;
use chrono::*;

use screen::common::*;
use utility::string::*;
use model::ShowItem;
use model::ShowReplyItem;
use reply_model::*;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
    scrollY: usize,
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show {
            rustbox: &rustbox,
            scrollY: 0,
        }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        self.print_header(&format!("{} - {} [{}/{}]",
                                   item.title,
                                   title,
                                   item.page,
                                   item.max_page));
        self.print_body(2, &item);
    }

    fn print_separator_top(&mut self, reply: &ShowReplyItem, y: usize) {
        if y > self.scrollY + 1 {
            let (replier_name, time) = self.build_separator_content(&reply);
            self.rustbox.print(0,
                               y - self.scrollY,
                               rustbox::RB_NORMAL,
                               Color::Green,
                               Color::Black,
                               &self.build_separator_top(&replier_name, &time));
        }
    }

    fn print_separator_bottom(&mut self, y: usize) {
        if y > self.scrollY + 1 {
            self.rustbox.print(0,
                               y - self.scrollY,
                               rustbox::RB_NORMAL,
                               Color::Green,
                               Color::Black,
                               &self.build_separator_bottom());
        }
    }

    fn print_header(&mut self, text: &str) {
        let title_len = jks_len(text);
        let padding = (if self.rustbox.width() >= title_len {
            self.rustbox.width() - title_len
        } else {
            0
        }) / 2;

        let header_bottom = seq_str_gen(0, self.rustbox.width(), "─", "");

        clearline(&self.rustbox, self.rustbox.width(), 0, 0);
        self.rustbox.print(padding,
                           0,
                           rustbox::RB_BOLD,
                           Color::White,
                           Color::Black,
                           text);
        self.rustbox.print(0,
                           1,
                           rustbox::RB_BOLD,
                           Color::Yellow,
                           Color::Black,
                           &header_bottom);
    }

    pub fn print_body(&mut self, offset_y: usize, item: &ShowItem) {
        let width = self.body_width();
        let rows = self.body_height();
        let rustbox = self.rustbox;
        let scrollY = self.scrollY;

        let mut y = offset_y;

        for (i, reply) in item.replies.iter().take(rows).enumerate() {

            y += self.print_reply(&reply.body, 0, y);

            self.print_separator_top(&reply, y);
            y += 1;

            self.print_separator_bottom(y);
            y += 1;
        }
    }

    fn print_reply(&mut self, vec: &Vec<NodeType>,
                   depth: usize,
                   y: usize)
                   -> usize {

       let rustbox = self.rustbox;
       let scrollY = self.scrollY;

        let padding = seq_str_gen(0, depth, "├─", "");
        let mut m = 0;
        let mut recursive_offset = 0;
        let mut total_y = 0;
        let mut line = String::new();
        let mut is_first = true;

        let vec_clean = clean_reply_body(vec);
        for (j, node) in vec_clean.iter().enumerate() {
            total_y = y + m + recursive_offset;
            if scrollY + 1 < total_y {
                let node2 = node.clone();
                match node2 {
                    NodeType::Text(n) => {
                        if n.data != "" {
                            line = format!("{}{}", line, n.data);
                        }
                    }
                    NodeType::Image(n) => {
                        if n.data != "" {
                            line = format!("{}[img {}]", line, n.data);
                        }
                    }
                    NodeType::BlockQuote(n) => {
                        recursive_offset += self.print_reply(&n.data, depth + 1, total_y);
                        is_first = false;
                    }
                    NodeType::Br(n) => {
                        if !line.is_empty() {
                            print_default(rustbox,
                                          0,
                                          total_y - scrollY,
                                          format!(" {}{}", padding, line));
                            line = String::new();
                            is_first = false;
                        }

                        // prevent first line empty
                        if !is_first {
                            m += 1;
                        }

                    }
                }
            }
        }

        if !line.is_empty() {
            total_y = y + m + recursive_offset;
            print_default(rustbox,
                          0,
                          total_y - scrollY,
                          format!(" {}{}  ", padding, line));
            line = String::new();
            m += 1;
        }

        m + recursive_offset
    }

    fn build_separator_content(&mut self, reply: &ShowReplyItem) -> (String, String) {
        let now = Local::now();

        let replier_name = reply.username.clone();

        let published_at = reply.published_at.clone();

        let published_at_dt = match Local.datetime_from_str(&published_at, "%d/%m/%Y %H:%M") {
            Ok(v) => v,
            Err(e) => now,
        };
        let time = published_at_format(&(now - published_at_dt));
        (replier_name, time)
    }

    fn build_separator_arguments(&mut self) -> (usize, usize, String) {
        let width = self.body_width();
        let rustbox = self.rustbox;

        let separator_width = if rustbox.width() >= 2 {
            rustbox.width() - 2
        } else {
            0
        };
        let separator_padding_width = if rustbox.width() > separator_width {
            rustbox.width() - separator_width
        } else {
            0
        } / 2;

        let separator_padding = seq_str_gen(0, separator_padding_width, " ", "");

        (separator_width, separator_padding_width, separator_padding)
    }

    fn build_separator_top(&mut self, replier_name: &str, time: &str) -> String {
        let replier_max_width = 14;
        let time_max_width = 5;
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_top(separator_width,
                           &separator_padding,
                           replier_max_width,
                           &replier_name,
                           time_max_width,
                           &time)
    }

    fn build_separator_bottom(&mut self) -> String {
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_bottom(separator_width, &separator_padding)
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp < 10000 {
            self.scrollY = tmp + value;
            return true;
        }
        false
    }

    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }
}

fn print_default(rustbox: &rustbox::RustBox, x: usize, y: usize, s: String) {
    rustbox.print(0, y, rustbox::RB_NORMAL, Color::White, Color::Black, &s);
}

fn clean_reply_body(vec: &Vec<NodeType>) -> Vec<NodeType>{
    // clean up lines (end)
    let vec2 = {
        let vec_length = vec.len();
        let vec_check_cleanup = vec.clone();

        // check if last 4 elements match the EMPTY PATTERN
        let is_last4_empty = vec_check_cleanup.iter()
                                              .rev()
                                              .take(4)
                                              .enumerate()
                                              .all(|(j, node)| match node.clone() {
                                                  NodeType::Br(n) => j == 1 || j == 2 || j == 3,
                                                  NodeType::Text(n) => j == 0 && n.data.is_empty(),
                                                  _ => false,
                                              });

        let vec_short_length = if vec_length > 4 && is_last4_empty {
            vec_length - 4
        } else {
            vec_length
        };

        vec.iter().take(vec_short_length)
    };

    // clean up lines (start)
    let vec3 = {
        let vec2_cloned = vec2.clone();
        let mut result: Vec<NodeType> = Vec::new();
        for (j, node) in vec2_cloned.enumerate() {
            let node2 = node.clone();
            let node3 = node.clone();
            match node2 {
                NodeType::Br(n) => {
                    if !result.is_empty() {
                        result.push(node3);
                    }
                }
                _ => result.push(node3),
            }
        }
        result.clone()
    };

    vec3
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = seq_str_gen(0, replier_name_left_spacing_width, "─", "");
    let replier_name_right_spacing = seq_str_gen(0, replier_name_right_spacing_width, "─", "");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = seq_str_gen(0, time_left_spacing_width, "─", "");
    let time_right_spacing = seq_str_gen(0, time_right_spacing_width, "─", "");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = seq_str_gen(0, separator_top_middle_width, " ", "");
    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = seq_str_gen(0, separator_bottom_middle_width, "─", "");

    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}

fn seq_str_gen(start: usize, end: usize, sym: &str, join_sym: &str) -> String {
    (start..end).map(|_| sym.clone()).collect::<Vec<_>>().join(&join_sym)
}
<commit_msg>show - refactor - put Y as class variable<commit_after>extern crate rustbox;
extern crate chrono;

use rustbox::*;
use chrono::*;

use screen::common::*;
use utility::string::*;
use model::ShowItem;
use model::ShowReplyItem;
use reply_model::*;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
    scrollY: usize,
    y: usize
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show {
            rustbox: &rustbox,
            scrollY: 0,
            y: 0
        }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        self.y = 2;

        self.print_header(&format!("{} - {} [{}/{}]",
                                   item.title,
                                   title,
                                   item.page,
                                   item.max_page));
        self.print_body(&item);
    }

    fn print_separator_top(&mut self, reply: &ShowReplyItem) {
        if self.y > self.scrollY + 1 {
            let (replier_name, time) = self.build_separator_content(&reply);
            self.rustbox.print(0,
                               self.y - self.scrollY,
                               rustbox::RB_NORMAL,
                               Color::Green,
                               Color::Black,
                               &self.build_separator_top(&replier_name, &time));
        }
    }

    fn print_separator_bottom(&mut self) {
        if self.y > self.scrollY + 1 {
            self.rustbox.print(0,
                               self.y - self.scrollY,
                               rustbox::RB_NORMAL,
                               Color::Green,
                               Color::Black,
                               &self.build_separator_bottom());
        }
    }

    fn print_header(&mut self, text: &str) {
        let title_len = jks_len(text);
        let padding = (if self.rustbox.width() >= title_len {
            self.rustbox.width() - title_len
        } else {
            0
        }) / 2;

        let header_bottom = seq_str_gen(0, self.rustbox.width(), "─", "");

        clearline(&self.rustbox, self.rustbox.width(), 0, 0);
        self.rustbox.print(padding,
                           0,
                           rustbox::RB_BOLD,
                           Color::White,
                           Color::Black,
                           text);
        self.rustbox.print(0,
                           1,
                           rustbox::RB_BOLD,
                           Color::Yellow,
                           Color::Black,
                           &header_bottom);
    }

    pub fn print_body(&mut self, item: &ShowItem) {
        let width = self.body_width();
        let rows = self.body_height();
        let rustbox = self.rustbox;
        let scrollY = self.scrollY;

        for (i, reply) in item.replies.iter().take(rows).enumerate() {

            self.y += self.print_reply(&reply.body, 0);

            self.print_separator_top(&reply);
            self.y += 1;

            self.print_separator_bottom();
            self.y += 1;
        }
    }

    fn print_reply(&mut self, vec: &Vec<NodeType>,
                   depth: usize)
                   -> usize {

       let rustbox = self.rustbox;
       let scrollY = self.scrollY;

        let padding = seq_str_gen(0, depth, "├─", "");
        let mut m = 0;
        let mut recursive_offset = 0;
        let mut total_y = 0;
        let mut line = String::new();
        let mut is_first = true;

        let vec_clean = clean_reply_body(vec);
        for (j, node) in vec_clean.iter().enumerate() {
            total_y = self.y + m + recursive_offset;
            if total_y > scrollY + 1 {
                let node2 = node.clone();
                match node2 {
                    NodeType::Text(n) => {
                        if n.data != "" {
                            line = format!("{}{}", line, n.data);
                        }
                    }
                    NodeType::Image(n) => {
                        if n.data != "" {
                            line = format!("{}[img {}]", line, n.data);
                        }
                    }
                    NodeType::BlockQuote(n) => {
                        recursive_offset += self.print_reply(&n.data, depth + 1); // consider we pass y as total_y here previously
                        is_first = false;
                    }
                    NodeType::Br(n) => {
                        if !line.is_empty() {
                            print_default(rustbox,
                                          0,
                                          total_y - scrollY,
                                          format!(" {}{}", padding, line));
                            line = String::new();
                            is_first = false;
                        }

                        // prevent first line empty
                        if !is_first {
                            m += 1;
                        }

                    }
                }
            }
        }

        if !line.is_empty() {
            total_y = self.y + m + recursive_offset;
            print_default(rustbox,
                          0,
                          total_y - scrollY,
                          format!(" {}{}  ", padding, line));
            line = String::new();
            m += 1;
        }

        m + recursive_offset
    }

    fn build_separator_content(&mut self, reply: &ShowReplyItem) -> (String, String) {
        let now = Local::now();

        let replier_name = reply.username.clone();

        let published_at = reply.published_at.clone();

        let published_at_dt = match Local.datetime_from_str(&published_at, "%d/%m/%Y %H:%M") {
            Ok(v) => v,
            Err(e) => now,
        };
        let time = published_at_format(&(now - published_at_dt));
        (replier_name, time)
    }

    fn build_separator_arguments(&mut self) -> (usize, usize, String) {
        let width = self.body_width();
        let rustbox = self.rustbox;

        let separator_width = if rustbox.width() >= 2 {
            rustbox.width() - 2
        } else {
            0
        };
        let separator_padding_width = if rustbox.width() > separator_width {
            rustbox.width() - separator_width
        } else {
            0
        } / 2;

        let separator_padding = seq_str_gen(0, separator_padding_width, " ", "");

        (separator_width, separator_padding_width, separator_padding)
    }

    fn build_separator_top(&mut self, replier_name: &str, time: &str) -> String {
        let replier_max_width = 14;
        let time_max_width = 5;
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_top(separator_width,
                           &separator_padding,
                           replier_max_width,
                           &replier_name,
                           time_max_width,
                           &time)
    }

    fn build_separator_bottom(&mut self) -> String {
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_bottom(separator_width, &separator_padding)
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp < 10000 {
            self.scrollY = tmp + value;
            return true;
        }
        false
    }

    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }

}

fn print_default(rustbox: &rustbox::RustBox, x: usize, y: usize, s: String) {
    rustbox.print(0, y, rustbox::RB_NORMAL, Color::White, Color::Black, &s);
}

fn clean_reply_body(vec: &Vec<NodeType>) -> Vec<NodeType>{
    // clean up lines (end)
    let vec2 = {
        let vec_length = vec.len();
        let vec_check_cleanup = vec.clone();

        // check if last 4 elements match the EMPTY PATTERN
        let is_last4_empty = vec_check_cleanup.iter()
                                              .rev()
                                              .take(4)
                                              .enumerate()
                                              .all(|(j, node)| match node.clone() {
                                                  NodeType::Br(n) => j == 1 || j == 2 || j == 3,
                                                  NodeType::Text(n) => j == 0 && n.data.is_empty(),
                                                  _ => false,
                                              });

        let vec_short_length = if vec_length > 4 && is_last4_empty {
            vec_length - 4
        } else {
            vec_length
        };

        vec.iter().take(vec_short_length)
    };

    // clean up lines (start)
    let vec3 = {
        let vec2_cloned = vec2.clone();
        let mut result: Vec<NodeType> = Vec::new();
        for (j, node) in vec2_cloned.enumerate() {
            let node2 = node.clone();
            let node3 = node.clone();
            match node2 {
                NodeType::Br(n) => {
                    if !result.is_empty() {
                        result.push(node3);
                    }
                }
                _ => result.push(node3),
            }
        }
        result.clone()
    };

    vec3
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = seq_str_gen(0, replier_name_left_spacing_width, "─", "");
    let replier_name_right_spacing = seq_str_gen(0, replier_name_right_spacing_width, "─", "");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = seq_str_gen(0, time_left_spacing_width, "─", "");
    let time_right_spacing = seq_str_gen(0, time_right_spacing_width, "─", "");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = seq_str_gen(0, separator_top_middle_width, " ", "");
    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = seq_str_gen(0, separator_bottom_middle_width, "─", "");

    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}

fn seq_str_gen(start: usize, end: usize, sym: &str, join_sym: &str) -> String {
    (start..end).map(|_| sym.clone()).collect::<Vec<_>>().join(&join_sym)
}
<|endoftext|>


<commit_before>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

extern crate cgmath;
#[macro_use]
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate richter;

use gfx::Device;
use gfx::Factory;
use gfx::traits::FactoryExt;
use glutin::GlContext;
use glutin::GlRequest;
use glutin::Api::OpenGl;

type ColorFormat = gfx::format::Srgba8;
type DepthFormat = gfx::format::DepthStencil;

gfx_defines!{
    vertex Vertex {
        position: [f32; 3] = "v_position",
        texcoord: [f32; 2] = "v_texcoord",
    }

    constant Locals {
        transform: [[f32; 4]; 4] = "u_transform",
    }

    pipeline pipe {
        vertex_buffer: gfx::VertexBuffer<Vertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_transform",
        sampler: gfx::TextureSampler<[f32; 4]> = "u_texture",
        out_color: gfx::RenderTarget<ColorFormat> = "Target0",
        out_depth: gfx::DepthTarget<DepthFormat> = gfx::preset::depth::LESS_EQUAL_WRITE,
    }
}

impl From<[f32; 5]> for Vertex {
    fn from(src: [f32; 5]) -> Self {
        Vertex {
            position: [src[0], src[1], src[2]],
            texcoord: [src[3], src[4]],
        }
    }
}

struct Face {
    vertex_id: usize,
    vertex_count: usize,
    tex_id: usize,
}

impl From<(usize, usize, usize)> for Face {
    fn from(src: (usize, usize, usize)) -> Self {
        Face {
            vertex_id: src.0,
            vertex_count: src.1,
            tex_id: src.2,
        }
    }
}

fn main() {
    let mut events_loop = glutin::EventsLoop::new();
    let window_builder = glutin::WindowBuilder::new().with_title("BSP renderer: gfx-rs backend");
    let context_builder = glutin::ContextBuilder::new()
        .with_gl(GlRequest::Specific(OpenGl, (3, 3)))
        .with_vsync(true);

    let (window, mut device, mut factory, color, depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(
            window_builder,
            context_builder,
            &events_loop,
        );

    let mut encoder: gfx::Encoder<gfx_device_gl::Resources, gfx_device_gl::CommandBuffer> =
        factory.create_command_buffer().into();

    let shader_set = factory
        .create_shader_set(
            r#"
#version 430

layout (location = 0) in vec3 v_position;
layout (location = 1) in vec2 v_texcoord;

out vec2 f_texcoord;

uniform mat4 u_transform;

void main() {
    f_texcoord = v_texcoord;
    gl_Position = u_transform * vec4(v_position, 1.0);
}
"#
                .as_bytes(),
            r#"
#version 430

in vec2 f_texcoord;

uniform sampler2D u_texture;

out vec4 Target0;

void main() {
    Target0 = texture(u_texture, f_texcoord);
}"#
                .as_bytes(),
        )
        .unwrap();

    let rasterizer = gfx::state::Rasterizer {
        front_face: gfx::state::FrontFace::Clockwise,
        cull_face: gfx::state::CullFace::Back,
        method: gfx::state::RasterMethod::Fill,
        offset: None,
        samples: Some(gfx::state::MultiSample),
    };

    let pso = factory
        .create_pipeline_state(
            &shader_set,
            gfx::Primitive::TriangleList,
            rasterizer,
            pipe::new(),
        )
        .unwrap();

    let bsp = richter::bsp::Bsp::load("pak0.pak.d/maps/e1m1.bsp").unwrap();

    let textures = bsp.gen_textures(|tex| {
        // TODO: get all mipmaps
        let mipmap_0 = richter::engine::indexed_to_rgba(&tex.mipmaps[0]);

        let (_, view) = factory
            .create_texture_immutable_u8::<ColorFormat>(
                gfx::texture::Kind::D2(
                    tex.width as u16,
                    tex.height as u16,
                    gfx::texture::AaMode::Single,
                ),
                &[&mipmap_0],
            )
            .unwrap();

        view
    });

    let (face_data, vertex_data): (Vec<Face>, Vec<Vertex>) = bsp.gen_render_data_interleaved();
    let vertex_buffer = factory.create_vertex_buffer(&vertex_data);

    let sampler = factory.create_sampler(gfx::texture::SamplerInfo::new(
        gfx::texture::FilterMethod::Scale,
        gfx::texture::WrapMode::Tile,
    ));

    let (fb_width, fb_height) = window.window().get_inner_size_pixels().unwrap();

    let mut data = pipe::Data {
        vertex_buffer: vertex_buffer,
        transform: cgmath::perspective(
            cgmath::Deg(75.0),
            fb_width as f32 / fb_height as f32,
            1.0,
            1024.0,
        ).into(),
        sampler: (textures[0].clone(), sampler),
        out_color: color,
        out_depth: depth,
    };

    let mut exit = false;
    loop {
        events_loop.poll_events(|event| if let glutin::Event::WindowEvent {
            event, ..
        } = event
        {
            match event {
                glutin::WindowEvent::Closed => exit = true,
                _ => (),
            }
        });

        if exit {
            break;
        }

        encoder.clear(&data.out_color, [0.0, 0.0, 0.0, 1.0]);
        for f in face_data.iter() {
            let slice = gfx::Slice {
                start: 0,
                end: f.vertex_count as u32,
                base_vertex: f.vertex_id as u32,
                instances: None,
                buffer: gfx::IndexBuffer::Auto,
            };

            data.sampler.0 = textures[f.tex_id].clone();
            encoder.draw(&slice, &pso, &data);
        }
        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}
<commit_msg>Update bsp-gfx to new bsp::load() API<commit_after>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

extern crate cgmath;
#[macro_use]
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate richter;

use gfx::Device;
use gfx::Factory;
use gfx::traits::FactoryExt;
use glutin::GlContext;
use glutin::GlRequest;
use glutin::Api::OpenGl;

type ColorFormat = gfx::format::Srgba8;
type DepthFormat = gfx::format::DepthStencil;

gfx_defines!{
    vertex Vertex {
        position: [f32; 3] = "v_position",
        texcoord: [f32; 2] = "v_texcoord",
    }

    constant Locals {
        transform: [[f32; 4]; 4] = "u_transform",
    }

    pipeline pipe {
        vertex_buffer: gfx::VertexBuffer<Vertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_transform",
        sampler: gfx::TextureSampler<[f32; 4]> = "u_texture",
        out_color: gfx::RenderTarget<ColorFormat> = "Target0",
        out_depth: gfx::DepthTarget<DepthFormat> = gfx::preset::depth::LESS_EQUAL_WRITE,
    }
}

impl From<[f32; 5]> for Vertex {
    fn from(src: [f32; 5]) -> Self {
        Vertex {
            position: [src[0], src[1], src[2]],
            texcoord: [src[3], src[4]],
        }
    }
}

struct Face {
    vertex_id: usize,
    vertex_count: usize,
    tex_id: usize,
}

impl From<(usize, usize, usize)> for Face {
    fn from(src: (usize, usize, usize)) -> Self {
        Face {
            vertex_id: src.0,
            vertex_count: src.1,
            tex_id: src.2,
        }
    }
}

fn main() {
    let mut events_loop = glutin::EventsLoop::new();
    let window_builder = glutin::WindowBuilder::new().with_title("BSP renderer: gfx-rs backend");
    let context_builder = glutin::ContextBuilder::new()
        .with_gl(GlRequest::Specific(OpenGl, (3, 3)))
        .with_vsync(true);

    let (window, mut device, mut factory, color, depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(
            window_builder,
            context_builder,
            &events_loop,
        );

    let mut encoder: gfx::Encoder<gfx_device_gl::Resources, gfx_device_gl::CommandBuffer> =
        factory.create_command_buffer().into();

    let shader_set = factory
        .create_shader_set(
            r#"
#version 430

layout (location = 0) in vec3 v_position;
layout (location = 1) in vec2 v_texcoord;

out vec2 f_texcoord;

uniform mat4 u_transform;

void main() {
    f_texcoord = v_texcoord;
    gl_Position = u_transform * vec4(v_position, 1.0);
}
"#
                .as_bytes(),
            r#"
#version 430

in vec2 f_texcoord;

uniform sampler2D u_texture;

out vec4 Target0;

void main() {
    Target0 = texture(u_texture, f_texcoord);
}"#
                .as_bytes(),
        )
        .unwrap();

    let rasterizer = gfx::state::Rasterizer {
        front_face: gfx::state::FrontFace::Clockwise,
        cull_face: gfx::state::CullFace::Back,
        method: gfx::state::RasterMethod::Fill,
        offset: None,
        samples: Some(gfx::state::MultiSample),
    };

    let pso = factory
        .create_pipeline_state(
            &shader_set,
            gfx::Primitive::TriangleList,
            rasterizer,
            pipe::new(),
        )
        .unwrap();

    let mut pak = richter::pak::Pak::new();
    pak.add("pak0.pak").unwrap();
    let (bsp, _) = richter::bsp::load(pak.open("maps/e1m1.bsp").unwrap()).unwrap();

    let textures = bsp.gen_textures(|tex| {
        // TODO: get all mipmaps
        let mipmap_0 = richter::engine::indexed_to_rgba(&tex.mipmaps[0]);

        let (_, view) = factory
            .create_texture_immutable_u8::<ColorFormat>(
                gfx::texture::Kind::D2(
                    tex.width as u16,
                    tex.height as u16,
                    gfx::texture::AaMode::Single,
                ),
                &[&mipmap_0],
            )
            .unwrap();

        view
    });

    let (face_data, vertex_data): (Vec<Face>, Vec<Vertex>) = bsp.gen_render_data_interleaved();
    let vertex_buffer = factory.create_vertex_buffer(&vertex_data);

    let sampler = factory.create_sampler(gfx::texture::SamplerInfo::new(
        gfx::texture::FilterMethod::Scale,
        gfx::texture::WrapMode::Tile,
    ));

    let (fb_width, fb_height) = window.window().get_inner_size_pixels().unwrap();

    let mut data = pipe::Data {
        vertex_buffer: vertex_buffer,
        transform: cgmath::perspective(
            cgmath::Deg(75.0),
            fb_width as f32 / fb_height as f32,
            1.0,
            1024.0,
        ).into(),
        sampler: (textures[0].clone(), sampler),
        out_color: color,
        out_depth: depth,
    };

    let mut exit = false;
    loop {
        events_loop.poll_events(|event| if let glutin::Event::WindowEvent {
            event, ..
        } = event
        {
            match event {
                glutin::WindowEvent::Closed => exit = true,
                _ => (),
            }
        });

        if exit {
            break;
        }

        encoder.clear(&data.out_color, [0.0, 0.0, 0.0, 1.0]);
        for f in face_data.iter() {
            let slice = gfx::Slice {
                start: 0,
                end: f.vertex_count as u32,
                base_vertex: f.vertex_id as u32,
                instances: None,
                buffer: gfx::IndexBuffer::Auto,
            };

            data.sampler.0 = textures[f.tex_id].clone();
            encoder.draw(&slice, &pso, &data);
        }
        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}
<|endoftext|>


<commit_before>use ast::*;
use env::*;
use parser::*;

pub struct Evaler {
    parser: Parser
}

#[allow(dead_code)]
impl Evaler {
    pub fn new() -> Evaler {
        Evaler {
            parser: Parser::new()
        }
    }

    pub fn eval(&mut self, code: String) -> ExprAst {
        let ast = self.parser.load(code);
        self._eval(ast, &mut Env::new())
    }

    fn _eval(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        if exp.is_self() {
            return exp;
        } else if exp.is_symbol() {
            return env.lookup(exp).unwrap();
        } else if exp.is_quote() {
            return exp.cdr().car();
        } else if exp.is_assign() {
            return self._eval_assign(exp, env);
        } else if exp.is_def() {
            return self._eval_def(exp, env);
        } else if exp.is_begin() {
            return self._eval_begin(exp, env);
        } else if exp.is_if() {
            return self._eval_if(exp, env);
        } else if exp.is_lambda() {
            return self._eval_lambda(exp, env);
        } else if exp.is_and() {
            return self._eval_and(exp, env);
        } else if exp.is_or() {
            return self._eval_or(exp, env);
        } else if exp.is_cond() {
            return self._eval_cond(exp, env);
        } else if exp.is_let() {
            return self._eval_let(exp, env);
        } else if exp.is_pair() { //app
            return self._eval_app(exp, env);
        }
        return ExprAst::Symbol(SymbolNode::new("OK"));
    }

    fn _eval_assign(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let var = exp.cdr().car();
        let val = self._eval(exp.cdr().cdr().car(), env);
        env.def_var(var, val);
        return ExprAst::Symbol(SymbolNode::new("OK"));
    }

    fn _eval_def(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let var = exp.def_var();
        let val = exp.def_val();
        let val = self._eval(val, env);
        env.def_var(var, val);
        return ExprAst::Symbol(SymbolNode::new("OK"));
    }

    fn _eval_if(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let pred = exp.cdr().car();
        let t_blk = exp.cdr().cdr().car();
        let f_blk = exp.cdr().cdr().cdr();
        let res = self._eval(pred, env);
        if res.is_true() {
            return self._eval(t_blk, env);
        } else {
            if res.is_empty() {
                return ExprAst::Bool(BoolNode::new(false));
            } else {
                return self._eval(f_blk.car(), env);
            }
        }
    }

    fn _eval_and(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self._eval(elems.car(), env);
            if res.is_false() {
                return ExprAst::Bool(BoolNode::new(false));
            }
            elems = elems.cdr();
        }
        return self._eval(elems.car(), env);
    }

    fn _eval_or(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self._eval(elems.car(), env);
            if res.is_true() {
                return ExprAst::Bool(BoolNode::new(true));
            }
            elems = elems.cdr();
        }
        return self._eval(elems.car(), env);
    }

    fn _eval_cond(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut elems = exp.cdr().car();
        loop {
            if elems.is_empty() { break; }
            let cur = elems.car();
            let val = self._eval(cur.clone(), env);
            if val.is_true() || val == ExprAst::Symbol(SymbolNode::new("else")) {
                return self._eval(cur.clone().cdr().car(), env);
            }
            elems = elems.cdr();
        }
        return ExprAst::Bool(BoolNode::new(true));
    }

    fn _eval_let(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        fn bind_params(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                return ExprAst::Pair(PairNode::new(exp.car().car(),
                                                   bind_params(exp.cdr())));
            }
        }

        fn bind_argus(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                return ExprAst::Pair(PairNode::new(exp.car().cdr().car(),
                                                   bind_argus(exp.cdr())));
            }
        }

        //FIXME : remove clone?
        let bindings = exp.cdr().car();
        bindings.print();
        let obj = ExprAst::Pair(PairNode::new(
            bind_params(bindings.clone()).make_lambda(exp.cdr().cdr()),
            bind_argus(bindings)));
        return self._eval(obj, env);
    }

    fn _eval_begin(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut _exp = exp.cdr();
        loop {
            if _exp.is_last() { break; }
            self._eval(_exp.car(), env);
            _exp = _exp.cdr();
        }
        return self._eval(_exp.car(), env);
    }

    fn _eval_values(&self, exprs: ExprAst, env: &mut Env) -> ExprAst {
        if exprs.is_empty() {
            return ExprAst::Nil;
        } else {
            let first = self._eval(exprs.car(), env);
            return ExprAst::Pair(PairNode::new(first,
                                               self._eval_values(exprs.cdr(), env)));
        }
    }

    fn _make_begin(&self, exprs: ExprAst) -> ExprAst {
        let begin = ExprAst::Symbol(SymbolNode::new("begin"));
        return ExprAst::Pair(PairNode::new(begin, exprs));
    }

    fn _eval_app(&self, expr: ExprAst, env: &mut Env) -> ExprAst {
        let _proc = self._eval(expr.car(), env);
        let _args = self._eval_values(expr.cdr(), env);
        if _proc.is_proc() {
            let func = _proc.as_proc().func();
            return func(_args);
        } else {
            assert!(_proc.is_cproc());
            let _vars = _proc.params();
            let res = self._eval(self._make_begin(_proc.body()),
                                 &mut env.extend(_vars, _args));
            return res;
        }
    }

    fn _eval_lambda(&self, expr: ExprAst, env: &Env) -> ExprAst {
        //FIXME: remove clone
        return ExprAst::CompProc(CompProcNode::new(expr.cdr().car(), //vars
                                                   expr.cdr().cdr(), //body
                                                   box env.clone()));

    }
}

#[test]
fn test_evaler() {
    macro_rules! test_case {
        ($test_str:expr, $expect_type:ident, $expect_val:expr) => { {
            let mut evaler = Evaler::new();
            let res = evaler.eval($test_str.to_string());
            if res.$expect_type() != $expect_val {
                assert!(false);
            }
        }}
    }
    test_case!("11", as_int, 11);
    //test_case!("'a", as_char, 'a');
    test_case!(r#""hello""#, as_str, "hello");
    test_case!("#t", as_bool, true);
    test_case!("(and #t #t)", as_bool, true);
    test_case!("(and #t #f)", as_bool, false);
    test_case!("(or #t #t)", as_bool, true);
    test_case!("(or #f #f)", as_bool, false);
    test_case!("(or #t #f)", as_bool, true);
    test_case!("(set! a 1)", as_str, "OK");
    test_case!("(integer? 1)", as_bool, true);
    test_case!("(boolean? #t)", as_bool, true);
    test_case!("(boolean? #f)", as_bool, true);
    test_case!("(integer? #t)", as_bool, false);
    test_case!("(+ 1 1)", as_int, 2);
    test_case!("(+ 1 1  1)", as_int, 3);
    test_case!("(+ 1 1 -1 -1)", as_int, 0);
    test_case!("(/ 2 1)", as_int, 2);
    test_case!("(* 2 2)", as_int, 4);
    test_case!("(> 2 1)", as_bool, true);
    test_case!("(> 1 2)", as_bool, false);
    test_case!("(> 1 1)", as_bool, false);
    test_case!("(eq? 1 1)", as_bool, true);
    test_case!("(eq? 1 2)", as_bool, false);
    test_case!("(eq? 1 #f)", as_bool, false);
    test_case!("(pair? '())", as_bool, true);
    test_case!("(> (+ 1 1) 0)", as_bool, true);
    test_case!("(if (> 1 0) 1 else 2)", as_int, 1);
    test_case!("(begin (set! a 1) a)", as_int, 1);
    test_case!("(let ((a 1)) (+ a 1))", as_int, 2);
    test_case!("(let ((a 1)) (> (+ a 1) 0))", as_bool, true);
    test_case!("(let ((a 1) (b 2)) (> a b))", as_bool, false);
    test_case!("(define (add a b) (+ a b))", as_str, "OK");
    test_case!("(cond ((eq? 1 1) 1) ((> 1 2) 2) ))", as_int, 1);
    test_case!("(define add4 (let ((x 4))
               (lambda (y) (+ x y))))", as_str, "OK");
    test_case!("(begin (set! x 5) (set! x 4) (+ x 1))", as_int, 5);
    test_case!("(car '(1 2))", as_int, 1);
    test_case!("(car (cdr '(1 2)))", as_int, 2);
    test_case!("(car (cons 1 2))", as_int, 1);
    test_case!("(car (cdr (cons 1 2)))", as_int, 2);
}
<commit_msg>add cons<commit_after>use ast::*;
use env::*;
use parser::*;

pub struct Evaler {
    parser: Parser
}

#[allow(dead_code)]
impl Evaler {
    pub fn new() -> Evaler {
        Evaler {
            parser: Parser::new()
        }
    }

    pub fn eval(&mut self, code: String) -> ExprAst {
        let ast = self.parser.load(code);
        self._eval(ast, &mut Env::new())
    }

    fn _eval(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        if exp.is_self() {
            return exp;
        } else if exp.is_symbol() {
            return env.lookup(exp).unwrap();
        } else if exp.is_quote() {
            return exp.cdr().car();
        } else if exp.is_assign() {
            return self._eval_assign(exp, env);
        } else if exp.is_def() {
            return self._eval_def(exp, env);
        } else if exp.is_begin() {
            return self._eval_begin(exp, env);
        } else if exp.is_if() {
            return self._eval_if(exp, env);
        } else if exp.is_lambda() {
            return self._eval_lambda(exp, env);
        } else if exp.is_and() {
            return self._eval_and(exp, env);
        } else if exp.is_or() {
            return self._eval_or(exp, env);
        } else if exp.is_cond() {
            return self._eval_cond(exp, env);
        } else if exp.is_let() {
            return self._eval_let(exp, env);
        } else if exp.is_pair() { //app
            return self._eval_app(exp, env);
        }
        return ExprAst::Symbol(SymbolNode::new("OK"));
    }

    fn _eval_assign(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let var = exp.cdr().car();
        let val = self._eval(exp.cdr().cdr().car(), env);
        env.def_var(var, val);
        return ExprAst::Symbol(SymbolNode::new("OK"));
    }

    fn _eval_def(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let var = exp.def_var();
        let val = exp.def_val();
        let val = self._eval(val, env);
        env.def_var(var, val);
        return ExprAst::Symbol(SymbolNode::new("OK"));
    }

    fn _eval_if(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let pred = exp.cdr().car();
        let t_blk = exp.cdr().cdr().car();
        let f_blk = exp.cdr().cdr().cdr();
        let res = self._eval(pred, env);
        if res.is_true() {
            return self._eval(t_blk, env);
        } else {
            if res.is_empty() {
                return ExprAst::Bool(BoolNode::new(false));
            } else {
                return self._eval(f_blk.car(), env);
            }
        }
    }

    fn _eval_and(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self._eval(elems.car(), env);
            if res.is_false() {
                return ExprAst::Bool(BoolNode::new(false));
            }
            elems = elems.cdr();
        }
        return self._eval(elems.car(), env);
    }

    fn _eval_or(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self._eval(elems.car(), env);
            if res.is_true() {
                return ExprAst::Bool(BoolNode::new(true));
            }
            elems = elems.cdr();
        }
        return self._eval(elems.car(), env);
    }

    fn _eval_cond(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut elems = exp.cdr().car();
        loop {
            if elems.is_empty() { break; }
            let cur = elems.car();
            let val = self._eval(cur.clone(), env);
            if val.is_true() || val == ExprAst::Symbol(SymbolNode::new("else")) {
                return self._eval(cur.clone().cdr().car(), env);
            }
            elems = elems.cdr();
        }
        return ExprAst::Bool(BoolNode::new(true));
    }

    fn _eval_let(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        fn bind_params(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                return ExprAst::Pair(PairNode::new(exp.car().car(),
                                                   bind_params(exp.cdr())));
            }
        }

        fn bind_argus(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                return ExprAst::Pair(PairNode::new(exp.car().cdr().car(),
                                                   bind_argus(exp.cdr())));
            }
        }

        //FIXME : remove clone?
        let bindings = exp.cdr().car();
        bindings.print();
        let obj = ExprAst::Pair(PairNode::new(
            bind_params(bindings.clone()).make_lambda(exp.cdr().cdr()),
            bind_argus(bindings)));
        return self._eval(obj, env);
    }

    fn _eval_begin(&self, exp: ExprAst, env: &mut Env) -> ExprAst {
        let mut _exp = exp.cdr();
        loop {
            if _exp.is_last() { break; }
            self._eval(_exp.car(), env);
            _exp = _exp.cdr();
        }
        return self._eval(_exp.car(), env);
    }

    fn _eval_values(&self, exprs: ExprAst, env: &mut Env) -> ExprAst {
        if exprs.is_empty() {
            return ExprAst::Nil;
        } else {
            let first = self._eval(exprs.car(), env);
            return ExprAst::Pair(PairNode::new(first,
                                               self._eval_values(exprs.cdr(), env)));
        }
    }

    fn _make_begin(&self, exprs: ExprAst) -> ExprAst {
        let begin = ExprAst::Symbol(SymbolNode::new("begin"));
        return ExprAst::Pair(PairNode::new(begin, exprs));
    }

    fn _eval_app(&self, expr: ExprAst, env: &mut Env) -> ExprAst {
        let _proc = self._eval(expr.car(), env);
        let _args = self._eval_values(expr.cdr(), env);
        if _proc.is_proc() {
            let func = _proc.as_proc().func();
            return func(_args);
        } else {
            assert!(_proc.is_cproc());
            let _vars = _proc.params();
            let res = self._eval(self._make_begin(_proc.body()),
                                 &mut env.extend(_vars, _args));
            return res;
        }
    }

    fn _eval_lambda(&self, expr: ExprAst, env: &Env) -> ExprAst {
        //FIXME: remove clone
        return ExprAst::CompProc(CompProcNode::new(expr.cdr().car(), //vars
                                                   expr.cdr().cdr(), //body
                                                   box env.clone()));

    }
}

#[test]
fn test_evaler() {
    macro_rules! test_case {
        ($test_str:expr, $expect_type:ident, $expect_val:expr) => { {
            let mut evaler = Evaler::new();
            let res = evaler.eval($test_str.to_string());
            if res.$expect_type() != $expect_val {
                assert!(false);
            }
        }}
    }
    test_case!("11", as_int, 11);
    //test_case!("'a", as_char, 'a');
    test_case!(r#""hello""#, as_str, "hello");
    test_case!("#t", as_bool, true);
    test_case!("(and #t #t)", as_bool, true);
    test_case!("(and #t #f)", as_bool, false);
    test_case!("(or #t #t)", as_bool, true);
    test_case!("(or #f #f)", as_bool, false);
    test_case!("(or #t #f)", as_bool, true);
    test_case!("(set! a 1)", as_str, "OK");
    test_case!("(integer? 1)", as_bool, true);
    test_case!("(boolean? #t)", as_bool, true);
    test_case!("(boolean? #f)", as_bool, true);
    test_case!("(integer? #t)", as_bool, false);
    test_case!("(+ 1 1)", as_int, 2);
    test_case!("(+ 1 1  1)", as_int, 3);
    test_case!("(+ 1 1 -1 -1)", as_int, 0);
    test_case!("(/ 2 1)", as_int, 2);
    test_case!("(* 2 2)", as_int, 4);
    test_case!("(> 2 1)", as_bool, true);
    test_case!("(> 1 2)", as_bool, false);
    test_case!("(> 1 1)", as_bool, false);
    test_case!("(eq? 1 1)", as_bool, true);
    test_case!("(eq? 1 2)", as_bool, false);
    test_case!("(eq? 1 #f)", as_bool, false);
    test_case!("(pair? '())", as_bool, true);
    test_case!("(> (+ 1 1) 0)", as_bool, true);
    test_case!("(if (> 1 0) 1 else 2)", as_int, 1);
    test_case!("(begin (set! a 1) a)", as_int, 1);
    test_case!("(let ((a 1)) (+ a 1))", as_int, 2);
    test_case!("(let ((a 1)) (> (+ a 1) 0))", as_bool, true);
    test_case!("(let ((a 1) (b 2)) (> a b))", as_bool, false);
    test_case!("(define (add a b) (+ a b))", as_str, "OK");
    test_case!("(cond ((eq? 1 1) 1) ((> 1 2) 2) ))", as_int, 1);
    test_case!("(define add4 (let ((x 4))
               (lambda (y) (+ x y))))", as_str, "OK");
    test_case!("(begin (set! x 5) (set! x 4) (+ x 1))", as_int, 5);
    test_case!("(car '(1 2))", as_int, 1);
    test_case!("(car (cdr '(1 2)))", as_int, 2);
    test_case!("(car (cons 1 2))", as_int, 1);
    test_case!("(car (cdr (cons 1 2)))", as_int, 2);
    test_case!("(car (car (cons (cons 2 3) (cons 1 2))))", as_int, 2);
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate sync;

#[cfg(test)]
extern crate test;

use std::slice;
use std::cmp;
use std::str;
use std::fmt;
use std::rt;
use std::option;
use std::io::Timer;
use rand::{task_rng, Rng};
use sync::{Arc, Future};

#[cfg(test)]
use test::BenchHarness;

static LIVE_CELL: char = '@';
static DEAD_CELL: char = '.';

fn main() {
  let mut brd = Board::new(65, 250).random();
  let mut timer = Timer::new().unwrap();

  let periodic = timer.periodic(64);
  loop {
    println!("\x1b[H\x1b[2J{}", brd);
    periodic.recv();
    brd = brd.parallel_next_generation();
  }
}

#[deriving(Eq, Clone)]
struct Board {
  board: ~[bool],
  rows: uint,
  cols: uint
}

impl Board {
  fn new(rows: uint, cols: uint) -> Board {
    let new_board = slice::from_elem(rows * cols, false);
    Board { board: new_board, rows: rows, cols: cols }
  }

  fn len(&self) -> uint {
    self.rows * self.cols
  }

  fn random(&self) -> Board {
    let board = task_rng().gen_vec(self.len());

    Board { board: board, rows: self.rows, cols: self.cols }
  }

  fn next_generation(&self) -> Board {
    let new_brd = slice::from_fn(self.len(), |cell| self.successor_cell(cell));
    Board { board: new_brd, rows: self.rows, cols: self.cols }
  }

  fn parallel_next_generation(&self) -> Board {
    let length = self.len();
    let num_tasks = cmp::min(rt::default_sched_threads(), length);
    let shared_brd = Arc::new(self.clone());
    let all_tasks = slice::from_fn(length, |i| i);
    let tasks: ~[&[uint]] = all_tasks.chunks(length / num_tasks).collect();

    fn future_batch(task_brd: Arc<Board>, task: ~[uint]) -> Future<~[bool]> {
      Future::spawn(proc()
        task.iter().map(|&idx| task_brd.successor_cell(idx)).collect()
      )
    }

    let future_batches: ~[Future<~[bool]>] = tasks.move_iter().map(|task| {
      future_batch(shared_brd.clone(), task.into_owned())
    }).collect();

    let mut new_brd: ~[bool] = slice::with_capacity(length);

    for b in future_batches.move_iter() {
      new_brd = slice::append(new_brd, b.unwrap());
    }

    Board { board: new_brd, rows: self.rows, cols: self.cols }
  }

  fn cell_live(&self, x: uint, y: uint) -> bool {
    !(x >= self.cols || y >= self.rows) && self.board[y * self.cols + x]
  }

  fn living_neighbors(&self, x: uint, y: uint) -> uint {
    let neighbors = [
      self.cell_live(x-1, y-1), self.cell_live(x, y-1), self.cell_live(x+1, y-1),
      self.cell_live(x-1, y+0),                         self.cell_live(x+1, y+0),
      self.cell_live(x-1, y+1), self.cell_live(x, y+1), self.cell_live(x+1, y+1),
    ];
    neighbors.iter().count(|&x| x)
  }

  fn successor_cell(&self, cell:uint) -> bool {
    self.successor(cell % self.cols, cell / self.cols)
  }

  fn successor(&self, x:uint, y:uint) -> bool {
    let neighbors = self.living_neighbors(x, y);
    if self.cell_live(x, y) {
      neighbors == 2 || neighbors == 3
    } else {
      neighbors == 3
    }
  }

  fn from_str(string: &str) -> Option<Board> {
    let rows: ~[&str] = string.split_terminator('\n').collect();
    let (row_cnt, col_cnt) = (rows[0].len(), rows.len());

    if rows.iter().any(|x| x.len() != row_cnt) { return None; };

    let brd = option::collect(
      rows.concat().chars().map(|c| match c {
        LIVE_CELL => Some(true),
        DEAD_CELL => Some(false),
        _         => None
      })
    );

    match brd {
      Some(board) => Some(Board { board: board, rows: row_cnt, cols: col_cnt }),
      None        => None
    }
  }
}

impl fmt::Show for Board {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

    fn row_to_str(row: &[bool]) -> ~str {
      let chars: ~[char] = row.iter().map(|&cell|
        if cell {LIVE_CELL} else {DEAD_CELL}
      ).collect();
      str::from_chars(chars)
    }

    let rows: ~[~str] = self.board.as_slice().chunks(self.cols).map(|row|
      row_to_str(row)
    ).collect();

    write!(f.buf, "{}", rows.connect("\n"))
  }
}

#[cfg(test)]
fn testing_board(n: int) -> Board {
  let brds = [
    ".@.\n" +
    ".@@\n" +
    ".@@\n"
    ,
    "...\n" +
    "@@@\n" +
    "...\n"
    ,
    ".@.\n" +
    ".@.\n" +
    ".@.\n" ];
  Board::from_str(brds[n]).unwrap()
}

#[test]
fn test_cell_live() {
  let brd = testing_board(0);
  assert!(!brd.cell_live(0, 0));
  assert!(brd.cell_live(2, 2));
}

#[test]
fn test_live_count() {
  let brd = testing_board(0);
  assert!(brd.living_neighbors(0, 0) == 2);
  assert!(brd.living_neighbors(2, 2) == 3);
}

#[test]
fn test_next_generation() {
  assert!(testing_board(1).next_generation() == testing_board(2))
}

#[test]
fn test_parallel_next_generation() {
  assert!(testing_board(1).parallel_next_generation() == testing_board(2))
}

#[bench]
fn bench_random(b: &mut BenchHarness) {
  let brd = Board::new(200,200);
  b.iter(|| {brd.random();})
}

#[bench]
fn bench_hundred_generations(b: &mut BenchHarness) {
  let mut brd = Board::new(200,200).random();
  b.iter(|| {
    for _ in range(0,100) { brd = brd.next_generation() }
  });
}

#[bench]
fn bench_hundred_parallel_generations(b: &mut BenchHarness) {
  let mut brd = Board::new(200,200).random();
  b.iter(|| {
    for _ in range(0,100) { brd = brd.parallel_next_generation() }
  });
}
<commit_msg>move from 0.10 -> master; ~[bool] back to Vec<bool><commit_after>extern crate rand;
extern crate sync;

#[cfg(test)]
extern crate test;

use std::cmp;
use std::str;
use std::fmt;
use std::rt;
use std::option;
use std::io::Timer;
use rand::{task_rng, Rng};
use sync::{Arc, Future};

#[cfg(test)]
use test::Bencher;

static LIVE_CELL: char = '@';
static DEAD_CELL: char = '.';

fn main() {
  let mut brd = Board::new(65, 250).random();
  let mut timer = Timer::new().unwrap();

  let periodic = timer.periodic(64);
  loop {
    println!("\x1b[H\x1b[2J{}", brd);
    periodic.recv();
    brd = brd.parallel_next_generation();
  }
}

#[deriving(Eq, Clone)]
struct Board {
  board: Vec<bool>,
  rows: uint,
  cols: uint
}

impl Board {
  fn new(rows: uint, cols: uint) -> Board {
    let new_board = Vec::from_elem(rows * cols, false);
    Board { board: new_board, rows: rows, cols: cols }
  }

  fn len(&self) -> uint {
    self.rows * self.cols
  }

  fn random(&self) -> Board {
    let board = task_rng().gen_vec(self.len());

    Board { board: board, rows: self.rows, cols: self.cols }
  }

  fn next_generation(&self) -> Board {
    let new_brd = Vec::from_fn(self.len(), |cell| self.successor_cell(cell));
    Board { board: new_brd, rows: self.rows, cols: self.cols }
  }

  fn parallel_next_generation(&self) -> Board {
    let length = self.len();
    let num_tasks = cmp::min(rt::default_sched_threads(), length);
    let shared_brd = Arc::new(self.clone());
    let all_tasks: ~[uint] = range(0, length).collect();
    let tasks: ~[&[uint]] = all_tasks.chunks(length / num_tasks).collect();

    fn future_batch(task_brd: Arc<Board>, task: ~[uint]) -> Future<~[bool]> {
      Future::spawn(proc()
        task.iter().map(|&idx| task_brd.successor_cell(idx)).collect()
      )
    }

    let future_batches: ~[Future<~[bool]>] = tasks.move_iter().map(|task| {
      future_batch(shared_brd.clone(), task.into_owned())
    }).collect();

    let mut new_brd: Vec<bool> = Vec::with_capacity(length);

    for b in future_batches.move_iter() {
      new_brd = new_brd.append(b.unwrap());
    }

    Board { board: new_brd, rows: self.rows, cols: self.cols }
  }

  fn cell_live(&self, x: uint, y: uint) -> bool {
    !(x >= self.cols || y >= self.rows) && *self.board.get(y * self.cols + x)
  }

  fn living_neighbors(&self, x: uint, y: uint) -> uint {
    let neighbors = [
      self.cell_live(x-1, y-1), self.cell_live(x, y-1), self.cell_live(x+1, y-1),
      self.cell_live(x-1, y+0),                         self.cell_live(x+1, y+0),
      self.cell_live(x-1, y+1), self.cell_live(x, y+1), self.cell_live(x+1, y+1),
    ];
    neighbors.iter().count(|&x| x)
  }

  fn successor_cell(&self, cell:uint) -> bool {
    self.successor(cell % self.cols, cell / self.cols)
  }

  fn successor(&self, x:uint, y:uint) -> bool {
    let neighbors = self.living_neighbors(x, y);
    if self.cell_live(x, y) {
      neighbors == 2 || neighbors == 3
    } else {
      neighbors == 3
    }
  }

  fn from_str(string: &str) -> Option<Board> {
    let rows: ~[&str] = string.split_terminator('\n').collect();
    let (row_cnt, col_cnt) = (rows[0].len(), rows.len());

    if rows.iter().any(|x| x.len() != row_cnt) { return None; };

    let brd = option::collect(
      rows.concat().chars().map(|c| match c {
        LIVE_CELL => Some(true),
        DEAD_CELL => Some(false),
        _         => None
      })
    );

    match brd {
      Some(board) => Some(Board { board: board, rows: row_cnt, cols: col_cnt }),
      None        => None
    }
  }
}

impl fmt::Show for Board {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

    fn row_to_str(row: &[bool]) -> ~str {
      let chars: ~[char] = row.iter().map(|&cell|
        if cell {LIVE_CELL} else {DEAD_CELL}
      ).collect();
      str::from_chars(chars)
    }

    let rows: ~[~str] = self.board.as_slice().chunks(self.cols).map(|row|
      row_to_str(row)
    ).collect();

    write!(f.buf, "{}", rows.connect("\n"))
  }
}

#[cfg(test)]
fn testing_board(n: uint) -> Board {
  let brds = [
    ".@.\n" +
    ".@@\n" +
    ".@@\n"
    ,
    "...\n" +
    "@@@\n" +
    "...\n"
    ,
    ".@.\n" +
    ".@.\n" +
    ".@.\n" ];
  Board::from_str(brds[n]).unwrap()
}

#[test]
fn test_cell_live() {
  let brd = testing_board(0);
  assert!(!brd.cell_live(0, 0));
  assert!(brd.cell_live(2, 2));
}

#[test]
fn test_live_count() {
  let brd = testing_board(0);
  assert!(brd.living_neighbors(0, 0) == 2);
  assert!(brd.living_neighbors(2, 2) == 3);
}

#[test]
fn test_next_generation() {
  assert!(testing_board(1).next_generation() == testing_board(2))
}

#[test]
fn test_parallel_next_generation() {
  assert!(testing_board(1).parallel_next_generation() == testing_board(2))
}

#[bench]
fn bench_random(b: &mut Bencher) {
  let brd = Board::new(200,200);
  b.iter(|| {brd.random();})
}

#[bench]
fn bench_hundred_generations(b: &mut Bencher) {
  let mut brd = Board::new(200,200).random();
  b.iter(|| {
    for _ in range(0,100) { brd = brd.next_generation() }
  });
}

#[bench]
fn bench_hundred_parallel_generations(b: &mut Bencher) {
  let mut brd = Board::new(200,200).random();
  b.iter(|| {
    for _ in range(0,100) { brd = brd.parallel_next_generation() }
  });
}
<|endoftext|>