<commit_before>use state::editor::Editor;
use io::parse::{Inst, Parameter};
use state::mode::{CommandMode, Mode, PrimitiveMode};
use edit::insert::{InsertMode, InsertOptions};
use io::redraw::RedrawTask;
use edit::buffer::TextBuffer;
use core::prompt::PromptCommand;

// TODO: Move the command definitions outta here
impl Editor {
    /// Execute an instruction
    pub fn exec(&mut self, Inst(para, cmd): Inst) {
        use io::key::Key::*;
        use state::mode::Mode::*;
        use state::mode::PrimitiveMode::*;
        use state::mode::CommandMode::*;

        let n = para.d();
        let bef = self.pos();
        let mut mov = false;

        match (self.cursor().mode, cmd.key) {
            (Primitive(Prompt), Char(' ')) if self.key_state.shift => {
                self.prompt.insert(0, String::new());
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (Primitive(Insert(_)), Escape) => {
                let left = self.left(1);
                self.goto(left);
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (Primitive(Insert(_)), Char(' ')) if self.key_state.shift => {
                let left = self.left(1);
                self.goto(left);
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (_, Char(' ')) if self.key_state.shift => {
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal)
            }
            (_, Char(' ')) if self.key_state.alt => self.next_cursor(),
            _ if self.key_state.alt => if let Some(m) = self.to_motion(Inst(para, cmd)) {
                self.goto(m);
            },
            (Command(Normal), Char('i')) => {
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('I')) => {
                self.cursor_mut().x = 0;
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('a')) => {
                let pos = self.right(1, false);
                self.goto(pos);
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('A')) => {
                let pos = (self.buffers.current_buffer()[self.y()].len(), self.y());
                //let pos = self.right(1, false);
                self.goto(pos);
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('o')) => {
                let y = self.y();
                let ind = if self.options.autoindent {
                    self.buffers.current_buffer().get_indent(y).to_owned()
                } else {
                    String::new()
                };
                let last = ind.len();
                self.buffers
                    .current_buffer_mut()
                    .insert_line(y + 1, ind.into());
                self.goto((last, y + 1));
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('h')) => {
                let left = self.left(n);
                self.goto(left);
                mov = true;
            }
            (Command(Normal), Char('j')) => {
                let down = self.down(n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Char('k')) => {
                let up = self.up(n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Char('l')) => {
                let right = self.right(n, true);
                self.goto(right);
                mov = true;
            }
            (Command(Normal), Char('J')) => {
                let down = self.down(15 * n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Char('K')) => {
                let up = self.up(15 * n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Char('x')) => {
                self.delete();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('X')) => {
                self.backspace();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('L')) => {
                if self.buffers.current_buffer()[self.y()].len() != 0 {
                    let ln_end = (self.buffers.current_buffer()[self.y()].len() - 1, self.y());
                    self.goto(ln_end);
                    mov = true;
                }
            }
            (Command(Normal), Char('H')) => {
                self.cursor_mut().x = 0;
                mov = true;
            }
            (Command(Normal), Char('r')) => {
                let (x, y) = self.pos();
                let c = self.get_char();
                let current_buffer = self.buffers.current_buffer_info_mut();
                // If there is nothing in the current buffer
                // ignore the command
                if current_buffer.raw_buffer[y].len() > 0 {
                    current_buffer.raw_buffer[y].remove(x);
                }
                current_buffer.raw_buffer[y].insert(x, c);
            }
            (Command(Normal), Char('R')) => {
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Replace,
                }));
            }
            (Command(Normal), Char('d')) => {
                let ins = self.get_inst();
                if let Some(m) = self.to_motion_unbounded(ins) {
                    self.remove_rb(m);
                }
            }
            (Command(Normal), Char('c')) => {
                let ins = self.get_inst();
                if let Some(m) = self.to_motion_unbounded(ins) {
                    self.remove_rb(m);
                    self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                        mode: InsertMode::Insert,
                    }));
                }
            }
            (Command(Normal), Char('G')) => {
                let last = self.buffers.current_buffer().len() - 1;
                self.goto((0, last));
                mov = true;
            }
            (Command(Normal), Char('g')) => {
                if let Parameter::Int(n) = para {
                    self.goto((0, n.wrapping_sub(1)));
                    mov = true;
                } else {
                    let inst = self.get_inst();
                    if let Some(m) = self.to_motion(inst) {
                        self.goto(m); // fix
                        mov = true;
                    }
                }
            }
            (Command(Normal), Char('b')) => {
                // Branch cursor
                if self.buffers.current_buffer_info().cursors.len() < 255 {
                    let cursor = self.cursor().clone();
                    let current_cursor_index =
                        self.buffers.current_buffer_info().current_cursor as usize;
                    self.buffers
                        .current_buffer_info_mut()
                        .cursors
                        .insert(current_cursor_index, cursor);
                    self.next_cursor();
                } else {
                    self.status_bar.msg = format!("At max 255 cursors");
                }
            }
            (Command(Normal), Char('B')) => {
                // Delete cursor
                if self.buffers.current_buffer_info().cursors.len() > 1 {
                    let current_cursor_index = self.buffers.current_buffer_info().current_cursor;
                    self.buffers
                        .current_buffer_info_mut()
                        .cursors
                        .remove(current_cursor_index as usize);
                    self.prev_cursor();
                } else {
                    self.status_bar.msg = format!("No other cursors!");
                }
            }
            (Command(Normal), Char('t')) => {
                let ch = self.get_char();

                let pos = self.next_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('f')) => {
                let ch = self.get_char();

                let pos = self.previous_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('W')) => {
                let pos = self._next_word_forward(n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char(';')) => {
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Prompt)
            }
            (Command(Normal), Char(' ')) => self.next_cursor(),
            (Command(Normal), Char('z')) => {
                let Inst(param, cmd) = self.get_inst();
                match param {
                    Parameter::Null => if let Some(m) = self.to_motion(Inst(param, cmd)) {
                        self.buffers.current_buffer_info_mut().scroll_y = m.1;
                        self.goto(m);
                    },
                    Parameter::Int(n) => {
                        self.buffers.current_buffer_info_mut().scroll_y = n;
                    }
                }
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('Z')) => {
                self.buffers.current_buffer_info_mut().scroll_y = self.y() - 3;
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('~')) => {
                self.invert_chars(n);
            }
            (Command(Normal), Char('.')) => if let Some(inst) = self.previous_instruction {
                self.exec(inst);
            } else {
                self.status_bar.msg = "No previous command".into();
                self.redraw_task = RedrawTask::StatusBar;
            },
            (Command(Normal), Char(c)) => {
                self.status_bar.msg = format!("Unknown command: {}", c);
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Insert(opt)), k) => self.insert(k, opt),
            (Primitive(Prompt), Char('\n')) => {
                self.cursor_mut().mode = Command(Normal);
                if let Some(cmd) = PromptCommand::parse(&self.prompt[self.prompt_index].clone()) {
                    self.invoke(cmd);
                    self.redraw_task = RedrawTask::StatusBar;
                } else {
                    self.status_bar.msg = format!("Unknown command: {}", self.prompt[self.prompt_index]);
                }

                // If we use a command we used before, don't add a new line to the vec
                let cmd = self.prompt[self.prompt_index].clone();
                if self.prompt_index != 0 {
                    self.prompt[0] = cmd;
                }
                // Don't insert anything if the user didn't write anything
                if self.prompt[self.prompt_index] != "" {
                    self.prompt.insert(0, String::new());
                }
                self.prompt_index = 0;
            }
            (Primitive(Prompt), Backspace) => {
                self.prompt[self.prompt_index].pop();
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Prompt), Up) => {
                if self.prompt_index < self.prompt.len() - 1 {
                    self.prompt_index += 1;
                }
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Prompt), Down) => {
                if self.prompt_index > 0 {
                    self.prompt_index -= 1;
                }
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Prompt), Char(c)) => {
                self.prompt[self.prompt_index].push(c);
                self.redraw_task = RedrawTask::StatusBar;
            }
            _ => {
                self.status_bar.msg = format!("Unknown command");
                self.redraw_task = RedrawTask::StatusBar;
            }
        }
        if mov {
            self.redraw_task = RedrawTask::Cursor(bef, self.pos());
        }

        if !(self.cursor().mode == Command(Normal) && cmd.key == Char('.')) {
            self.previous_instruction = Some(Inst(para, cmd));
        }
    }
}
<commit_msg>Adding in support for arrow keys in normal mode for moving.<commit_after>use state::editor::Editor;
use io::parse::{Inst, Parameter};
use state::mode::{CommandMode, Mode, PrimitiveMode};
use edit::insert::{InsertMode, InsertOptions};
use io::redraw::RedrawTask;
use edit::buffer::TextBuffer;
use core::prompt::PromptCommand;

// TODO: Move the command definitions outta here
impl Editor {
    /// Execute an instruction
    pub fn exec(&mut self, Inst(para, cmd): Inst) {
        use io::key::Key::*;
        use state::mode::Mode::*;
        use state::mode::PrimitiveMode::*;
        use state::mode::CommandMode::*;

        let n = para.d();
        let bef = self.pos();
        let mut mov = false;

        match (self.cursor().mode, cmd.key) {
            (Primitive(Prompt), Char(' ')) if self.key_state.shift => {
                self.prompt.insert(0, String::new());
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (Primitive(Insert(_)), Escape) => {
                let left = self.left(1);
                self.goto(left);
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (Primitive(Insert(_)), Char(' ')) if self.key_state.shift => {
                let left = self.left(1);
                self.goto(left);
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (_, Char(' ')) if self.key_state.shift => {
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal)
            }
            (_, Char(' ')) if self.key_state.alt => self.next_cursor(),
            _ if self.key_state.alt => if let Some(m) = self.to_motion(Inst(para, cmd)) {
                self.goto(m);
            },
            (Command(Normal), Char('i')) => {
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('I')) => {
                self.cursor_mut().x = 0;
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('a')) => {
                let pos = self.right(1, false);
                self.goto(pos);
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('A')) => {
                let pos = (self.buffers.current_buffer()[self.y()].len(), self.y());
                //let pos = self.right(1, false);
                self.goto(pos);
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Char('o')) => {
                let y = self.y();
                let ind = if self.options.autoindent {
                    self.buffers.current_buffer().get_indent(y).to_owned()
                } else {
                    String::new()
                };
                let last = ind.len();
                self.buffers
                    .current_buffer_mut()
                    .insert_line(y + 1, ind.into());
                self.goto((last, y + 1));
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Insert,
                }));
            }
            (Command(Normal), Left) | (Command(Normal), Char('h')) => {
                let left = self.left(n);
                self.goto(left);
                mov = true;
            }
            (Command(Normal), Down) | (Command(Normal), Char('j')) => {
                let down = self.down(n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Up) | (Command(Normal), Char('k')) => {
                let up = self.up(n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Right) | (Command(Normal), Char('l')) => {
                let right = self.right(n, true);
                self.goto(right);
                mov = true;
            }
            (Command(Normal), Char('J')) => {
                let down = self.down(15 * n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Char('K')) => {
                let up = self.up(15 * n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Char('x')) => {
                self.delete();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('X')) => {
                self.backspace();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('L')) => {
                if self.buffers.current_buffer()[self.y()].len() != 0 {
                    let ln_end = (self.buffers.current_buffer()[self.y()].len() - 1, self.y());
                    self.goto(ln_end);
                    mov = true;
                }
            }
            (Command(Normal), Char('H')) => {
                self.cursor_mut().x = 0;
                mov = true;
            }
            (Command(Normal), Char('r')) => {
                let (x, y) = self.pos();
                let c = self.get_char();
                let current_buffer = self.buffers.current_buffer_info_mut();
                // If there is nothing in the current buffer
                // ignore the command
                if current_buffer.raw_buffer[y].len() > 0 {
                    current_buffer.raw_buffer[y].remove(x);
                }
                current_buffer.raw_buffer[y].insert(x, c);
            }
            (Command(Normal), Char('R')) => {
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                    mode: InsertMode::Replace,
                }));
            }
            (Command(Normal), Char('d')) => {
                let ins = self.get_inst();
                if let Some(m) = self.to_motion_unbounded(ins) {
                    self.remove_rb(m);
                }
            }
            (Command(Normal), Char('c')) => {
                let ins = self.get_inst();
                if let Some(m) = self.to_motion_unbounded(ins) {
                    self.remove_rb(m);
                    self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                        mode: InsertMode::Insert,
                    }));
                }
            }
            (Command(Normal), Char('G')) => {
                let last = self.buffers.current_buffer().len() - 1;
                self.goto((0, last));
                mov = true;
            }
            (Command(Normal), Char('g')) => {
                if let Parameter::Int(n) = para {
                    self.goto((0, n.wrapping_sub(1)));
                    mov = true;
                } else {
                    let inst = self.get_inst();
                    if let Some(m) = self.to_motion(inst) {
                        self.goto(m); // fix
                        mov = true;
                    }
                }
            }
            (Command(Normal), Char('b')) => {
                // Branch cursor
                if self.buffers.current_buffer_info().cursors.len() < 255 {
                    let cursor = self.cursor().clone();
                    let current_cursor_index =
                        self.buffers.current_buffer_info().current_cursor as usize;
                    self.buffers
                        .current_buffer_info_mut()
                        .cursors
                        .insert(current_cursor_index, cursor);
                    self.next_cursor();
                } else {
                    self.status_bar.msg = format!("At max 255 cursors");
                }
            }
            (Command(Normal), Char('B')) => {
                // Delete cursor
                if self.buffers.current_buffer_info().cursors.len() > 1 {
                    let current_cursor_index = self.buffers.current_buffer_info().current_cursor;
                    self.buffers
                        .current_buffer_info_mut()
                        .cursors
                        .remove(current_cursor_index as usize);
                    self.prev_cursor();
                } else {
                    self.status_bar.msg = format!("No other cursors!");
                }
            }
            (Command(Normal), Char('t')) => {
                let ch = self.get_char();

                let pos = self.next_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('f')) => {
                let ch = self.get_char();

                let pos = self.previous_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('W')) => {
                let pos = self._next_word_forward(n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char(';')) => {
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Prompt)
            }
            (Command(Normal), Char(' ')) => self.next_cursor(),
            (Command(Normal), Char('z')) => {
                let Inst(param, cmd) = self.get_inst();
                match param {
                    Parameter::Null => if let Some(m) = self.to_motion(Inst(param, cmd)) {
                        self.buffers.current_buffer_info_mut().scroll_y = m.1;
                        self.goto(m);
                    },
                    Parameter::Int(n) => {
                        self.buffers.current_buffer_info_mut().scroll_y = n;
                    }
                }
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('Z')) => {
                self.buffers.current_buffer_info_mut().scroll_y = self.y() - 3;
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('~')) => {
                self.invert_chars(n);
            }
            (Command(Normal), Char('.')) => if let Some(inst) = self.previous_instruction {
                self.exec(inst);
            } else {
                self.status_bar.msg = "No previous command".into();
                self.redraw_task = RedrawTask::StatusBar;
            },
            (Command(Normal), Char(c)) => {
                self.status_bar.msg = format!("Unknown command: {}", c);
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Insert(opt)), k) => self.insert(k, opt),
            (Primitive(Prompt), Char('\n')) => {
                self.cursor_mut().mode = Command(Normal);
                if let Some(cmd) = PromptCommand::parse(&self.prompt[self.prompt_index].clone()) {
                    self.invoke(cmd);
                    self.redraw_task = RedrawTask::StatusBar;
                } else {
                    self.status_bar.msg = format!("Unknown command: {}", self.prompt[self.prompt_index]);
                }

                // If we use a command we used before, don't add a new line to the vec
                let cmd = self.prompt[self.prompt_index].clone();
                if self.prompt_index != 0 {
                    self.prompt[0] = cmd;
                }
                // Don't insert anything if the user didn't write anything
                if self.prompt[self.prompt_index] != "" {
                    self.prompt.insert(0, String::new());
                }
                self.prompt_index = 0;
            }
            (Primitive(Prompt), Backspace) => {
                self.prompt[self.prompt_index].pop();
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Prompt), Up) => {
                if self.prompt_index < self.prompt.len() - 1 {
                    self.prompt_index += 1;
                }
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Prompt), Down) => {
                if self.prompt_index > 0 {
                    self.prompt_index -= 1;
                }
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Prompt), Char(c)) => {
                self.prompt[self.prompt_index].push(c);
                self.redraw_task = RedrawTask::StatusBar;
            }
            _ => {
                self.status_bar.msg = format!("Unknown command");
                self.redraw_task = RedrawTask::StatusBar;
            }
        }
        if mov {
            self.redraw_task = RedrawTask::Cursor(bef, self.pos());
        }

        if !(self.cursor().mode == Command(Normal) && cmd.key == Char('.')) {
            self.previous_instruction = Some(Inst(para, cmd));
        }
    }
}
<|endoftext|>


<commit_before>#![feature(default_type_params)]

extern crate collections;
extern crate serialize;
extern crate test;
extern crate time;

use benchmark::{Metric,benchmark};
use json::{load_hashes,save_hashes,update_metrics};
use language::supported_languages;
use problem::Problem;
use solution::Solution;

mod benchmark;
mod compiler;
mod executable;
mod file;
mod hash;
mod interpreter;
mod json;
mod language;
mod problem;
mod solution;

fn main() {
    let languages = supported_languages();

    for problem in range(1u, 1000).filter_map(|i| Problem::new_opt(i)) {
        let pid = problem.id();
        let mut hashes = load_hashes(pid);

        println!("{}", pid);
        let metrics: Vec<Metric> = languages.iter().filter_map(|language| {
            Solution::new_opt(language, &problem).and_then(|solution| {
                print!("> {:<13}", solution.language().name());

                benchmark(&solution, &mut hashes)
            })
        }).collect();

        save_hashes(pid, &hashes);
        update_metrics(pid, metrics);
    }
}
<commit_msg>eulermark: make sure language name is shown, while benchmarking<commit_after>#![feature(default_type_params)]

extern crate collections;
extern crate serialize;
extern crate test;
extern crate time;

use benchmark::{Metric,benchmark};
use json::{load_hashes,save_hashes,update_metrics};
use language::supported_languages;
use problem::Problem;
use solution::Solution;
use std::io::stdio;

mod benchmark;
mod compiler;
mod executable;
mod file;
mod hash;
mod interpreter;
mod json;
mod language;
mod problem;
mod solution;

fn main() {
    let languages = supported_languages();

    for problem in range(1u, 1000).filter_map(|i| Problem::new_opt(i)) {
        let pid = problem.id();
        let mut hashes = load_hashes(pid);

        println!("{}", pid);
        let metrics: Vec<Metric> = languages.iter().filter_map(|language| {
            Solution::new_opt(language, &problem).and_then(|solution| {
                print!("> {:<13}", solution.language().name());
                stdio::flush();

                benchmark(&solution, &mut hashes)
            })
        }).collect();

        save_hashes(pid, &hashes);
        update_metrics(pid, metrics);
    }
}
<|endoftext|>


<commit_before>use calx_grid::Dir6;
use display;
use euclid::{Point2D, Rect, vec2};
use rand;
use scancode::Scancode;
use std::fs::File;
use std::io::prelude::*;
use vitral::{Context, FracPoint2D, FracSize2D, FracRect, Align};
use world::{Command, CommandResult, Event, ItemType, Location, Query, Slot, TerrainQuery, World, on_screen};

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum State {
    Main,
    Inventory(InventoryAction),
    Console,
    Aim(AimAction),
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum InventoryAction {
    Drop,
    Equip,
    Use,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum AimAction {
    Zap(Slot),
    // Maybe add intrinsic abilities not tied to a specific entity later
}

pub struct GameLoop {
    pub world: World,
    pub console: display::Console,
    state: State,
}

impl GameLoop {
    pub fn new(world: World) -> GameLoop {
        GameLoop {
            world,
            console: display::Console::default(),
            state: State::Main,
        }
    }

    /// Step command that turns into melee attack if an enemy is in the way.
    fn smart_step(&mut self, dir: Dir6) -> CommandResult {
        let player = self.world.player().ok_or(())?;
        let loc = self.world.location(player).ok_or(())?;
        let destination = loc + dir;

        if let Some(mob) = self.world.mob_at(destination) {
            if self.world.is_hostile_to(player, mob) {
                // Fight on!
                self.world.melee(dir)
            } else {
                // Do we want to do something smarter than walk into friendlies?
                // The world might treat this as a displace action so keep it like this for now.
                self.world.step(dir)
            }
        } else {
            self.world.step(dir)
        }
    }

    fn game_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.state = State::Console;
                Ok(Vec::new())
            }
            Q => self.smart_step(Dir6::Northwest),
            W => self.smart_step(Dir6::North),
            E => self.smart_step(Dir6::Northeast),
            A => self.smart_step(Dir6::Southwest),
            S => self.smart_step(Dir6::South),
            D => self.smart_step(Dir6::Southeast),
            I => {
                self.state = State::Inventory(InventoryAction::Equip);
                Ok(Vec::new())
            }
            // XXX: Key mnemonics, bit awkward when D is taken by movement.
            B => {
                self.state = State::Inventory(InventoryAction::Drop);
                Ok(Vec::new())
            }
            U => {
                self.state = State::Inventory(InventoryAction::Use);
                Ok(Vec::new())
            }
            G => self.world.take(),
            F5 => {
                self.world
                    .save(&mut File::create("save.gam").unwrap())
                    .unwrap();
                Ok(Vec::new())
            }
            F9 => {
                let mut savefile = File::open("save.gam").unwrap();
                self.world = World::load(&mut savefile).unwrap();
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn aim_input(&mut self, slot: Slot, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Q => self.world.zap_item(slot, Dir6::Northwest),
            W => self.world.zap_item(slot, Dir6::North),
            E => self.world.zap_item(slot, Dir6::Northeast),
            A => self.world.zap_item(slot, Dir6::Southwest),
            S => self.world.zap_item(slot, Dir6::South),
            D => self.world.zap_item(slot, Dir6::Southeast),
            Escape => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn inventory_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        for slot in SLOT_DATA.iter() {
            if scancode == slot.code {
                if let State::Inventory(action) = self.state {
                    let ret = self.inventory_action(slot.slot, action);
                    if ret.is_ok() {
                        return ret;
                    }
                }
            }
        }

        match scancode {
            Escape => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn inventory_action(&mut self, slot: Slot, action: InventoryAction) -> CommandResult {
        match action {
            InventoryAction::Drop => {
                let ret = self.world.drop(slot);
                // After succesful drop, go back to main state.
                if ret.is_ok() {
                    self.state = State::Main;
                }
                ret
            }
            // Can equip multiple items in one go, wait for ESC to return to main state.
            InventoryAction::Equip => self.world.equip(slot),
            InventoryAction::Use => {
                let player = self.world.player().ok_or(())?;

                if let Some(item) = self.world.entity_equipped(player, slot) {
                    match self.world.item_type(item) {
                        Some(ItemType::UntargetedUsable(_)) => {
                            return self.world.use_item(slot);
                        }
                        Some(ItemType::TargetedUsable(_)) => {
                            // If we need to aim, switch to aim state before calling world.
                            self.state = State::Aim(AimAction::Zap(slot));
                            return Ok(Vec::new());
                        }
                        _ => {}
                    }
                }
                return Err(());
            }
        }
    }

    fn console_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            Enter | PadEnter => {
                let input = self.console.get_input();
                let _ = writeln!(&mut self.console, "{}", input);
                if let Err(e) = self.parse(&input) {
                    let _ = writeln!(&mut self.console, "{}", e);
                }
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn dump(&mut self) { dump_map(&self.world); }

    /// Generate a new random cave map.
    fn cave(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::caves(
            &mut self.world,
            &mut rand::thread_rng(),
            Location::new(0, 0, 0),
            300,
        );
    }

    /// Generate a new random maze map.
    fn maze(&mut self, sparseness: usize) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::maze(&mut self.world, &mut rand::thread_rng(), sparseness);
    }

    /// Generate a new random rooms and corridors
    fn rooms(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::rooms(&mut self.world, &mut rand::thread_rng());
    }

    command_parser!{
        fn cave(&mut self);
        fn maze(&mut self, sparseness: usize);
        fn rooms(&mut self);

        fn dump(&mut self);
    }

    fn draw_inventory(&mut self, c: &mut display::Backend) -> Result<(), ()> {
        let player = self.world.player().ok_or(())?;

        // Start with hardcoded invetory data to test the UI logic.
        c.fill_rect(
            FracRect::new(FracPoint2D::new(0.0, 0.0), FracSize2D::new(1.0, 1.0)),
            [0.0, 0.0, 0.0, 0.99],
        );

        let mut letter_pos = Point2D::new(0.0, 0.0);
        let mut slot_name_pos = Point2D::new(20.0, 0.0);
        let mut item_name_pos = Point2D::new(80.0, 0.0);
        let text_color = [1.0, 1.0, 1.0, 1.0];

        for slot in SLOT_DATA.iter() {
            // TODO: Bounding box for these is a button...
            letter_pos = c.draw_text(
                letter_pos,
                Align::Left,
                text_color,
                &format!("{})", slot.key),
            );
            slot_name_pos = c.draw_text(slot_name_pos, Align::Left, text_color, slot.name);
            let item_name = if let Some(item) = self.world.entity_equipped(player, slot.slot) {
                self.world.entity_name(item)
            } else {
                "".to_string()
            };

            item_name_pos = c.draw_text(item_name_pos, Align::Left, text_color, &item_name);
        }

        Ok(())
    }

    pub fn draw(&mut self, context: &mut display::Backend, screen_area: &Rect<f32>) {
        let camera_loc = Location::new(0, 0, 0);
        let mut view = display::WorldView::new(camera_loc, *screen_area);
        view.show_cursor = true;

        view.draw(&self.world, context);

        match self.state {
            State::Inventory(_) => {
                let _ = self.draw_inventory(context);
            }
            State::Console => {
                let mut console_area = *screen_area;
                console_area.size.height /= 2.0;
                self.console.draw_large(context, &console_area);
            }
            _ => {
                self.console.draw_small(context, screen_area);
            }
        }

        if let Some(scancode) = context.poll_key().and_then(|k| Scancode::new(k.scancode)) {
            let ret = match self.state {
                State::Inventory(_) => self.inventory_input(scancode),
                State::Console => self.console_input(scancode),
                State::Aim(AimAction::Zap(slot)) => self.aim_input(slot, scancode),
                _ => self.game_input(scancode),
            };

            if let Ok(events) = ret {
                // Input event caused a successful world step and we got an event sequence out.
                // Convert events into UI display effects.
                for e in events {
                    match e {
                        Event::Msg(text) => {
                            let _ = writeln!(&mut self.console, "{}", text);
                        }
                    }
                }
            }
        }
    }
}

/// Print the world map as ASCII.
fn dump_map(world: &World) {
    for y in -21..21 {
        for x in -39..41 {
            if (x + y) % 2 != 0 {
                print!(" ");
                continue;
            }
            let pos = vec2((x + y) / 2, y);
            if on_screen(pos) {
                let t = world.terrain(Location::new(0, 0, 0) + pos);
                if t.is_open() {
                    print!(".");
                } else if t.is_door() {
                    print!("+");
                } else if t.is_wall() {
                    print!("#");
                } else {
                    print!("*");
                }
            } else {
                print!(" ");
            }
        }
        println!("");
    }
}

struct SlotData {
    key: char,
    code: Scancode,
    slot: Slot,
    name: &'static str,
}

#[cfg_attr(rustfmt, rustfmt_skip)]
static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', code: Scancode::Num1, slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', code: Scancode::Num2, slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', code: Scancode::Num3, slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', code: Scancode::Num4, slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', code: Scancode::Num5, slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', code: Scancode::Num6, slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', code: Scancode::Num7, slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', code: Scancode::Num8, slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', code: Scancode::A,    slot: Slot::Melee,      name: "Weapon" },
    SlotData { key: 'b', code: Scancode::B,    slot: Slot::Ranged,     name: "Ranged" },
    SlotData { key: 'c', code: Scancode::C,    slot: Slot::Head,       name: "Head" },
    SlotData { key: 'd', code: Scancode::D,    slot: Slot::Body,       name: "Body" },
    SlotData { key: 'e', code: Scancode::E,    slot: Slot::Feet,       name: "Feet" },
    SlotData { key: 'f', code: Scancode::F,    slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', code: Scancode::G,    slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', code: Scancode::H,    slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', code: Scancode::I,    slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', code: Scancode::J,    slot: Slot::InventoryJ, name: "" },
    SlotData { key: 'k', code: Scancode::K,    slot: Slot::InventoryK, name: "" },
    SlotData { key: 'l', code: Scancode::L,    slot: Slot::InventoryL, name: "" },
    SlotData { key: 'm', code: Scancode::M,    slot: Slot::InventoryM, name: "" },
    SlotData { key: 'n', code: Scancode::N,    slot: Slot::InventoryN, name: "" },
    SlotData { key: 'o', code: Scancode::O,    slot: Slot::InventoryO, name: "" },
    SlotData { key: 'p', code: Scancode::P,    slot: Slot::InventoryP, name: "" },
    SlotData { key: 'q', code: Scancode::Q,    slot: Slot::InventoryQ, name: "" },
    SlotData { key: 'r', code: Scancode::R,    slot: Slot::InventoryR, name: "" },
    SlotData { key: 's', code: Scancode::S,    slot: Slot::InventoryS, name: "" },
    SlotData { key: 't', code: Scancode::T,    slot: Slot::InventoryT, name: "" },
    SlotData { key: 'u', code: Scancode::U,    slot: Slot::InventoryU, name: "" },
    SlotData { key: 'v', code: Scancode::V,    slot: Slot::InventoryV, name: "" },
    SlotData { key: 'w', code: Scancode::W,    slot: Slot::InventoryW, name: "" },
    SlotData { key: 'x', code: Scancode::X,    slot: Slot::InventoryX, name: "" },
    SlotData { key: 'y', code: Scancode::Y,    slot: Slot::InventoryY, name: "" },
    SlotData { key: 'z', code: Scancode::Z,    slot: Slot::InventoryZ, name: "" },
];
<commit_msg>Messages won't cover the entire screen<commit_after>use calx_grid::Dir6;
use display;
use euclid::{Point2D, Rect, vec2};
use rand;
use scancode::Scancode;
use std::fs::File;
use std::io::prelude::*;
use vitral::{Context, FracPoint2D, FracSize2D, FracRect, Align};
use world::{Command, CommandResult, Event, ItemType, Location, Query, Slot, TerrainQuery, World, on_screen};

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum State {
    Main,
    Inventory(InventoryAction),
    Console,
    Aim(AimAction),
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum InventoryAction {
    Drop,
    Equip,
    Use,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum AimAction {
    Zap(Slot),
    // Maybe add intrinsic abilities not tied to a specific entity later
}

pub struct GameLoop {
    pub world: World,
    pub console: display::Console,
    state: State,
}

impl GameLoop {
    pub fn new(world: World) -> GameLoop {
        GameLoop {
            world,
            console: display::Console::default(),
            state: State::Main,
        }
    }

    /// Step command that turns into melee attack if an enemy is in the way.
    fn smart_step(&mut self, dir: Dir6) -> CommandResult {
        let player = self.world.player().ok_or(())?;
        let loc = self.world.location(player).ok_or(())?;
        let destination = loc + dir;

        if let Some(mob) = self.world.mob_at(destination) {
            if self.world.is_hostile_to(player, mob) {
                // Fight on!
                self.world.melee(dir)
            } else {
                // Do we want to do something smarter than walk into friendlies?
                // The world might treat this as a displace action so keep it like this for now.
                self.world.step(dir)
            }
        } else {
            self.world.step(dir)
        }
    }

    fn game_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.state = State::Console;
                Ok(Vec::new())
            }
            Q => self.smart_step(Dir6::Northwest),
            W => self.smart_step(Dir6::North),
            E => self.smart_step(Dir6::Northeast),
            A => self.smart_step(Dir6::Southwest),
            S => self.smart_step(Dir6::South),
            D => self.smart_step(Dir6::Southeast),
            I => {
                self.state = State::Inventory(InventoryAction::Equip);
                Ok(Vec::new())
            }
            // XXX: Key mnemonics, bit awkward when D is taken by movement.
            B => {
                self.state = State::Inventory(InventoryAction::Drop);
                Ok(Vec::new())
            }
            U => {
                self.state = State::Inventory(InventoryAction::Use);
                Ok(Vec::new())
            }
            G => self.world.take(),
            F5 => {
                self.world
                    .save(&mut File::create("save.gam").unwrap())
                    .unwrap();
                Ok(Vec::new())
            }
            F9 => {
                let mut savefile = File::open("save.gam").unwrap();
                self.world = World::load(&mut savefile).unwrap();
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn aim_input(&mut self, slot: Slot, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Q => self.world.zap_item(slot, Dir6::Northwest),
            W => self.world.zap_item(slot, Dir6::North),
            E => self.world.zap_item(slot, Dir6::Northeast),
            A => self.world.zap_item(slot, Dir6::Southwest),
            S => self.world.zap_item(slot, Dir6::South),
            D => self.world.zap_item(slot, Dir6::Southeast),
            Escape => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn inventory_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        for slot in SLOT_DATA.iter() {
            if scancode == slot.code {
                if let State::Inventory(action) = self.state {
                    let ret = self.inventory_action(slot.slot, action);
                    if ret.is_ok() {
                        return ret;
                    }
                }
            }
        }

        match scancode {
            Escape => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn inventory_action(&mut self, slot: Slot, action: InventoryAction) -> CommandResult {
        match action {
            InventoryAction::Drop => {
                let ret = self.world.drop(slot);
                // After succesful drop, go back to main state.
                if ret.is_ok() {
                    self.state = State::Main;
                }
                ret
            }
            // Can equip multiple items in one go, wait for ESC to return to main state.
            InventoryAction::Equip => self.world.equip(slot),
            InventoryAction::Use => {
                let player = self.world.player().ok_or(())?;

                if let Some(item) = self.world.entity_equipped(player, slot) {
                    match self.world.item_type(item) {
                        Some(ItemType::UntargetedUsable(_)) => {
                            return self.world.use_item(slot);
                        }
                        Some(ItemType::TargetedUsable(_)) => {
                            // If we need to aim, switch to aim state before calling world.
                            self.state = State::Aim(AimAction::Zap(slot));
                            return Ok(Vec::new());
                        }
                        _ => {}
                    }
                }
                return Err(());
            }
        }
    }

    fn console_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            Enter | PadEnter => {
                let input = self.console.get_input();
                let _ = writeln!(&mut self.console, "{}", input);
                if let Err(e) = self.parse(&input) {
                    let _ = writeln!(&mut self.console, "{}", e);
                }
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn dump(&mut self) { dump_map(&self.world); }

    /// Generate a new random cave map.
    fn cave(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::caves(
            &mut self.world,
            &mut rand::thread_rng(),
            Location::new(0, 0, 0),
            300,
        );
    }

    /// Generate a new random maze map.
    fn maze(&mut self, sparseness: usize) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::maze(&mut self.world, &mut rand::thread_rng(), sparseness);
    }

    /// Generate a new random rooms and corridors
    fn rooms(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::rooms(&mut self.world, &mut rand::thread_rng());
    }

    command_parser!{
        fn cave(&mut self);
        fn maze(&mut self, sparseness: usize);
        fn rooms(&mut self);

        fn dump(&mut self);
    }

    fn draw_inventory(&mut self, c: &mut display::Backend) -> Result<(), ()> {
        let player = self.world.player().ok_or(())?;

        // Start with hardcoded invetory data to test the UI logic.
        c.fill_rect(
            FracRect::new(FracPoint2D::new(0.0, 0.0), FracSize2D::new(1.0, 1.0)),
            [0.0, 0.0, 0.0, 0.99],
        );

        let mut letter_pos = Point2D::new(0.0, 0.0);
        let mut slot_name_pos = Point2D::new(20.0, 0.0);
        let mut item_name_pos = Point2D::new(80.0, 0.0);
        let text_color = [1.0, 1.0, 1.0, 1.0];

        for slot in SLOT_DATA.iter() {
            // TODO: Bounding box for these is a button...
            letter_pos = c.draw_text(
                letter_pos,
                Align::Left,
                text_color,
                &format!("{})", slot.key),
            );
            slot_name_pos = c.draw_text(slot_name_pos, Align::Left, text_color, slot.name);
            let item_name = if let Some(item) = self.world.entity_equipped(player, slot.slot) {
                self.world.entity_name(item)
            } else {
                "".to_string()
            };

            item_name_pos = c.draw_text(item_name_pos, Align::Left, text_color, &item_name);
        }

        Ok(())
    }

    pub fn draw(&mut self, context: &mut display::Backend, screen_area: &Rect<f32>) {
        let camera_loc = Location::new(0, 0, 0);
        let mut view = display::WorldView::new(camera_loc, *screen_area);
        view.show_cursor = true;

        view.draw(&self.world, context);

        match self.state {
            State::Inventory(_) => {
                let _ = self.draw_inventory(context);
            }
            State::Console => {
                let mut console_area = *screen_area;
                console_area.size.height /= 2.0;
                self.console.draw_large(context, &console_area);
            }
            _ => {
                let mut console_area = *screen_area;
                console_area.size.height /= 8.0;
                self.console.draw_small(context, &console_area);
            }
        }

        if let Some(scancode) = context.poll_key().and_then(|k| Scancode::new(k.scancode)) {
            let ret = match self.state {
                State::Inventory(_) => self.inventory_input(scancode),
                State::Console => self.console_input(scancode),
                State::Aim(AimAction::Zap(slot)) => self.aim_input(slot, scancode),
                _ => self.game_input(scancode),
            };

            if let Ok(events) = ret {
                // Input event caused a successful world step and we got an event sequence out.
                // Convert events into UI display effects.
                for e in events {
                    match e {
                        Event::Msg(text) => {
                            let _ = writeln!(&mut self.console, "{}", text);
                        }
                    }
                }
            }
        }
    }
}

/// Print the world map as ASCII.
fn dump_map(world: &World) {
    for y in -21..21 {
        for x in -39..41 {
            if (x + y) % 2 != 0 {
                print!(" ");
                continue;
            }
            let pos = vec2((x + y) / 2, y);
            if on_screen(pos) {
                let t = world.terrain(Location::new(0, 0, 0) + pos);
                if t.is_open() {
                    print!(".");
                } else if t.is_door() {
                    print!("+");
                } else if t.is_wall() {
                    print!("#");
                } else {
                    print!("*");
                }
            } else {
                print!(" ");
            }
        }
        println!("");
    }
}

struct SlotData {
    key: char,
    code: Scancode,
    slot: Slot,
    name: &'static str,
}

#[cfg_attr(rustfmt, rustfmt_skip)]
static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', code: Scancode::Num1, slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', code: Scancode::Num2, slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', code: Scancode::Num3, slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', code: Scancode::Num4, slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', code: Scancode::Num5, slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', code: Scancode::Num6, slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', code: Scancode::Num7, slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', code: Scancode::Num8, slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', code: Scancode::A,    slot: Slot::Melee,      name: "Weapon" },
    SlotData { key: 'b', code: Scancode::B,    slot: Slot::Ranged,     name: "Ranged" },
    SlotData { key: 'c', code: Scancode::C,    slot: Slot::Head,       name: "Head" },
    SlotData { key: 'd', code: Scancode::D,    slot: Slot::Body,       name: "Body" },
    SlotData { key: 'e', code: Scancode::E,    slot: Slot::Feet,       name: "Feet" },
    SlotData { key: 'f', code: Scancode::F,    slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', code: Scancode::G,    slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', code: Scancode::H,    slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', code: Scancode::I,    slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', code: Scancode::J,    slot: Slot::InventoryJ, name: "" },
    SlotData { key: 'k', code: Scancode::K,    slot: Slot::InventoryK, name: "" },
    SlotData { key: 'l', code: Scancode::L,    slot: Slot::InventoryL, name: "" },
    SlotData { key: 'm', code: Scancode::M,    slot: Slot::InventoryM, name: "" },
    SlotData { key: 'n', code: Scancode::N,    slot: Slot::InventoryN, name: "" },
    SlotData { key: 'o', code: Scancode::O,    slot: Slot::InventoryO, name: "" },
    SlotData { key: 'p', code: Scancode::P,    slot: Slot::InventoryP, name: "" },
    SlotData { key: 'q', code: Scancode::Q,    slot: Slot::InventoryQ, name: "" },
    SlotData { key: 'r', code: Scancode::R,    slot: Slot::InventoryR, name: "" },
    SlotData { key: 's', code: Scancode::S,    slot: Slot::InventoryS, name: "" },
    SlotData { key: 't', code: Scancode::T,    slot: Slot::InventoryT, name: "" },
    SlotData { key: 'u', code: Scancode::U,    slot: Slot::InventoryU, name: "" },
    SlotData { key: 'v', code: Scancode::V,    slot: Slot::InventoryV, name: "" },
    SlotData { key: 'w', code: Scancode::W,    slot: Slot::InventoryW, name: "" },
    SlotData { key: 'x', code: Scancode::X,    slot: Slot::InventoryX, name: "" },
    SlotData { key: 'y', code: Scancode::Y,    slot: Slot::InventoryY, name: "" },
    SlotData { key: 'z', code: Scancode::Z,    slot: Slot::InventoryZ, name: "" },
];
<|endoftext|>


<commit_before>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) whitequark <whitequark@whitequark.org>
// See the LICENSE file included in this distribution.

//! Generators.
//!
//! Generators allow repeatedly suspending the execution of a function,
//! returning a value to the caller, and resuming the suspended function
//! afterwards.

use core::marker::PhantomData;
use core::iter::Iterator;
use core::{ptr, mem};

use stack;
use context;

#[derive(Debug, Clone, Copy)]
pub enum State {
  /// Generator can be resumed. This is the initial state.
  Runnable,
  /// Generator cannot be resumed. This is the state of the generator after
  /// the generator function has returned or panicked.
  Unavailable
}

/// Generator wraps a function and allows suspending its execution more than
/// once, return a value each time.
///
/// It implements the Iterator trait. The first time `next()` is called,
/// the function is called as `f(yielder)`; every time `next()` is called afterwards,
/// the function is resumed. In either case, it runs until it suspends its execution
/// through `yielder.generate(val)`), in which case `next()` returns `Some(val)`, or
/// returns, in which case `next()` returns `None`. `next()` will return `None`
/// every time after that.
///
/// After the generator function returns, it is safe to reclaim the generator
/// stack using `unwrap()`.
///
/// `state()` can be used to determine whether the generator function has returned;
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
/// once the generator function returns or panics.
///
/// # Example
///
/// ```
/// use fringe::{OsStack, Generator};
///
/// let stack = OsStack::new(0).unwrap();
/// let mut gen = Generator::new(stack, move |yielder| {
///   for i in 1..4 {
///     yielder.generate(i);
///   }
/// });
/// println!("{:?}", gen.next()); // prints Some(1)
/// println!("{:?}", gen.next()); // prints Some(2)
/// println!("{:?}", gen.next()); // prints Some(3)
/// println!("{:?}", gen.next()); // prints None
/// ```
#[derive(Debug)]
pub struct Generator<Item: Send, Stack: stack::Stack> {
  state:   State,
  context: context::Context<Stack>,
  phantom: PhantomData<Item>
}

impl<Item, Stack> Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  /// Creates a new generator.
  pub fn new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where Stack: stack::GuardedStack, F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe { Generator::unsafe_new(stack, f) }
  }

  /// Same as `new`, but does not require `stack` to have a guard page.
  ///
  /// This function is unsafe because the generator function can easily violate
  /// memory safety by overflowing the stack. It is useful in environments where
  /// guarded stacks do not exist, e.g. in absence of an MMU.
  pub unsafe fn unsafe_new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe extern "C" fn generator_wrapper<Item, Stack, F>(info: usize) -> !
        where Item: Send, Stack: stack::Stack, F: FnOnce(&mut Yielder<Item, Stack>) {
      // Retrieve our environment from the callee and return control to it.
      let (mut yielder, f) = ptr::read(info as *mut (Yielder<Item, Stack>, F));
      let new_context = context::Context::swap(yielder.context, yielder.context, 0);
      // See Yielder::return_.
      yielder.context = new_context as *mut context::Context<Stack>;
      // Run the body of the generator.
      f(&mut yielder);
      // Past this point, the generator has dropped everything it has held.
      loop { yielder.return_(None) }
    }

    let mut generator = Generator {
      state:   State::Runnable,
      context: context::Context::new(stack, generator_wrapper::<Item, Stack, F>),
      phantom: PhantomData
    };

    // Transfer environment to the callee.
    let mut data = (Yielder::new(&mut generator.context), f);
    context::Context::swap(&mut generator.context, &generator.context,
                           &mut data as *mut (Yielder<Item, Stack>, F) as usize);
    mem::forget(data);

    generator
  }

  /// Returns the state of the generator.
  #[inline]
  pub fn state(&self) -> State { self.state }

  /// Extracts the stack from a generator when the generator function has returned.
  /// If the generator function has not returned
  /// (i.e. `self.state() == State::Runnable`), panics.
  pub fn unwrap(self) -> Stack {
    match self.state {
      State::Runnable    => panic!("Argh! Bastard! Don't touch that!"),
      State::Unavailable => unsafe { self.context.unwrap() }
    }
  }
}

/// Yielder is an interface provided to every generator through which it
/// returns a value.
#[derive(Debug)]
pub struct Yielder<Item: Send, Stack: stack::Stack> {
  context: *mut context::Context<Stack>,
  phantom: PhantomData<Item>
}

impl<Item, Stack> Yielder<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  fn new(context: *mut context::Context<Stack>) -> Yielder<Item, Stack> {
    Yielder {
      context: context,
      phantom: PhantomData
    }
  }

  #[inline(always)]
  fn return_(&mut self, mut val: Option<Item>) {
    unsafe {
      let new_context = context::Context::swap(self.context, self.context,
                                               &mut val as *mut Option<Item> as usize);
      // The generator can be moved (and with it, the context).
      // This changes the address of the context.
      // Thus, we update it after each swap.
      self.context = new_context as *mut context::Context<Stack>;
      // However, between this point and the next time we enter return_
      // the generator cannot be moved, as a &mut Generator is necessary
      // to resume the generator function.
    }
  }

  /// Suspends the generator and returns `Some(item)` from the `next()`
  /// invocation that resumed the generator.
  #[inline(always)]
  pub fn generate(&mut self, item: Item) {
    self.return_(Some(item))
  }
}

impl<Item, Stack> Iterator for Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  type Item = Item;

  /// Resumes the generator and return the next value it yields.
  /// If the generator function has returned, returns `None`.
  #[inline]
  fn next(&mut self) -> Option<Self::Item> {
    match self.state {
      State::Runnable => {
        // Set the state to Unavailable. Since we have exclusive access to the generator,
        // the only case where this matters is the generator function panics, after which
        // it must not be invocable again.
        self.state = State::Unavailable;

        // Switch to the generator function.
        let new_context = &mut self.context as *mut context::Context<Stack> as usize;
        let val = unsafe {
          ptr::read(context::Context::swap(&mut self.context, &self.context, new_context)
                    as *mut Option<Item>)
        };

        // Unless the generator function has returned, it can be switched to again, so
        // set the state to Runnable.
        if val.is_some() { self.state = State::Runnable }

        val
      }
      State::Unavailable => None
    }
  }
}
<commit_msg>Use a more semantically correct PhantomData field in Generator.<commit_after>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) whitequark <whitequark@whitequark.org>
// See the LICENSE file included in this distribution.

//! Generators.
//!
//! Generators allow repeatedly suspending the execution of a function,
//! returning a value to the caller, and resuming the suspended function
//! afterwards.

use core::marker::PhantomData;
use core::iter::Iterator;
use core::{ptr, mem};

use stack;
use context;

#[derive(Debug, Clone, Copy)]
pub enum State {
  /// Generator can be resumed. This is the initial state.
  Runnable,
  /// Generator cannot be resumed. This is the state of the generator after
  /// the generator function has returned or panicked.
  Unavailable
}

/// Generator wraps a function and allows suspending its execution more than
/// once, return a value each time.
///
/// It implements the Iterator trait. The first time `next()` is called,
/// the function is called as `f(yielder)`; every time `next()` is called afterwards,
/// the function is resumed. In either case, it runs until it suspends its execution
/// through `yielder.generate(val)`), in which case `next()` returns `Some(val)`, or
/// returns, in which case `next()` returns `None`. `next()` will return `None`
/// every time after that.
///
/// After the generator function returns, it is safe to reclaim the generator
/// stack using `unwrap()`.
///
/// `state()` can be used to determine whether the generator function has returned;
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
/// once the generator function returns or panics.
///
/// # Example
///
/// ```
/// use fringe::{OsStack, Generator};
///
/// let stack = OsStack::new(0).unwrap();
/// let mut gen = Generator::new(stack, move |yielder| {
///   for i in 1..4 {
///     yielder.generate(i);
///   }
/// });
/// println!("{:?}", gen.next()); // prints Some(1)
/// println!("{:?}", gen.next()); // prints Some(2)
/// println!("{:?}", gen.next()); // prints Some(3)
/// println!("{:?}", gen.next()); // prints None
/// ```
#[derive(Debug)]
pub struct Generator<Item: Send, Stack: stack::Stack> {
  state:   State,
  context: context::Context<Stack>,
  phantom: PhantomData<*const Item>
}

impl<Item, Stack> Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  /// Creates a new generator.
  pub fn new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where Stack: stack::GuardedStack, F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe { Generator::unsafe_new(stack, f) }
  }

  /// Same as `new`, but does not require `stack` to have a guard page.
  ///
  /// This function is unsafe because the generator function can easily violate
  /// memory safety by overflowing the stack. It is useful in environments where
  /// guarded stacks do not exist, e.g. in absence of an MMU.
  pub unsafe fn unsafe_new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe extern "C" fn generator_wrapper<Item, Stack, F>(info: usize) -> !
        where Item: Send, Stack: stack::Stack, F: FnOnce(&mut Yielder<Item, Stack>) {
      // Retrieve our environment from the callee and return control to it.
      let (mut yielder, f) = ptr::read(info as *mut (Yielder<Item, Stack>, F));
      let new_context = context::Context::swap(yielder.context, yielder.context, 0);
      // See Yielder::return_.
      yielder.context = new_context as *mut context::Context<Stack>;
      // Run the body of the generator.
      f(&mut yielder);
      // Past this point, the generator has dropped everything it has held.
      loop { yielder.return_(None) }
    }

    let mut generator = Generator {
      state:   State::Runnable,
      context: context::Context::new(stack, generator_wrapper::<Item, Stack, F>),
      phantom: PhantomData
    };

    // Transfer environment to the callee.
    let mut data = (Yielder::new(&mut generator.context), f);
    context::Context::swap(&mut generator.context, &generator.context,
                           &mut data as *mut (Yielder<Item, Stack>, F) as usize);
    mem::forget(data);

    generator
  }

  /// Returns the state of the generator.
  #[inline]
  pub fn state(&self) -> State { self.state }

  /// Extracts the stack from a generator when the generator function has returned.
  /// If the generator function has not returned
  /// (i.e. `self.state() == State::Runnable`), panics.
  pub fn unwrap(self) -> Stack {
    match self.state {
      State::Runnable    => panic!("Argh! Bastard! Don't touch that!"),
      State::Unavailable => unsafe { self.context.unwrap() }
    }
  }
}

/// Yielder is an interface provided to every generator through which it
/// returns a value.
#[derive(Debug)]
pub struct Yielder<Item: Send, Stack: stack::Stack> {
  context: *mut context::Context<Stack>,
  phantom: PhantomData<Item>
}

impl<Item, Stack> Yielder<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  fn new(context: *mut context::Context<Stack>) -> Yielder<Item, Stack> {
    Yielder {
      context: context,
      phantom: PhantomData
    }
  }

  #[inline(always)]
  fn return_(&mut self, mut val: Option<Item>) {
    unsafe {
      let new_context = context::Context::swap(self.context, self.context,
                                               &mut val as *mut Option<Item> as usize);
      // The generator can be moved (and with it, the context).
      // This changes the address of the context.
      // Thus, we update it after each swap.
      self.context = new_context as *mut context::Context<Stack>;
      // However, between this point and the next time we enter return_
      // the generator cannot be moved, as a &mut Generator is necessary
      // to resume the generator function.
    }
  }

  /// Suspends the generator and returns `Some(item)` from the `next()`
  /// invocation that resumed the generator.
  #[inline(always)]
  pub fn generate(&mut self, item: Item) {
    self.return_(Some(item))
  }
}

impl<Item, Stack> Iterator for Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  type Item = Item;

  /// Resumes the generator and return the next value it yields.
  /// If the generator function has returned, returns `None`.
  #[inline]
  fn next(&mut self) -> Option<Self::Item> {
    match self.state {
      State::Runnable => {
        // Set the state to Unavailable. Since we have exclusive access to the generator,
        // the only case where this matters is the generator function panics, after which
        // it must not be invocable again.
        self.state = State::Unavailable;

        // Switch to the generator function.
        let new_context = &mut self.context as *mut context::Context<Stack> as usize;
        let val = unsafe {
          ptr::read(context::Context::swap(&mut self.context, &self.context, new_context)
                    as *mut Option<Item>)
        };

        // Unless the generator function has returned, it can be switched to again, so
        // set the state to Runnable.
        if val.is_some() { self.state = State::Runnable }

        val
      }
      State::Unavailable => None
    }
  }
}
<|endoftext|>


<commit_before>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) whitequark <whitequark@whitequark.org>
// See the LICENSE file included in this distribution.

//! Generators.
//!
//! Generators allow repeatedly suspending the execution of a function,
//! returning a value to the caller, and resuming the suspended function
//! afterwards.

use core::marker::PhantomData;
use core::iter::Iterator;
use core::{ptr, mem};

use stack;
use context;

#[derive(Debug, Clone, Copy)]
pub enum State {
  /// Generator can be resumed. This is the initial state.
  Suspended,
  /// Generator cannot be resumed. This is the state of the generator after
  /// the generator function has returned.
  Finished
}

/// Generator wraps a function and allows suspending its execution more than
/// once, return a value each time.
///
/// It implements the Iterator trait. The first time `next()` is called,
/// the function is called as `f(yielder)`; every time `next()` is called afterwards,
/// the function is resumed. In either case, it runs until it suspends its execution
/// through `yielder.generate(val)`), in which case `next()` returns `Some(val)`, or
/// returns, in which case `next()` returns `None`. `next()` will return `None`
/// every time after that.
///
/// After the generator function returns, it is safe to reclaim the generator
/// stack using `unwrap()`.
///
/// `state()` can be used to determine whether the generator function has returned;
/// the state is `State::Suspended` after creation and suspension, and `State::Finished`
/// once the generator function returns.
///
/// # Example
///
/// ```
/// use fringe::{OsStack, Generator};
///
/// let stack = OsStack::new(0).unwrap();
/// let mut gen = Generator::new(stack, move |yielder| {
///   for i in 1..4 {
///     yielder.generate(i);
///   }
/// });
/// println!("{:?}", gen.next()); // prints Some(1)
/// println!("{:?}", gen.next()); // prints Some(2)
/// println!("{:?}", gen.next()); // prints Some(3)
/// println!("{:?}", gen.next()); // prints None
/// ```
#[derive(Debug)]
pub struct Generator<Item: Send, Stack: stack::Stack> {
  state:   State,
  context: context::Context<Stack>,
  phantom: PhantomData<Item>
}

impl<Item, Stack> Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  /// Creates a new generator.
  pub fn new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where Stack: stack::GuardedStack, F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe { Generator::unsafe_new(stack, f) }
  }

  /// Same as `new`, but does not require `stack` to have a guard page.
  ///
  /// This function is unsafe because the generator function can easily violate
  /// memory safety by overflowing the stack. It is useful in environments where
  /// guarded stacks do not exist, e.g. in absence of an MMU.
  pub unsafe fn unsafe_new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe extern "C" fn generator_wrapper<Item, Stack, F>(info: usize) -> !
        where Item: Send, Stack: stack::Stack, F: FnOnce(&mut Yielder<Item, Stack>) {
      // Retrieve our environment from the callee and return control to it.
      let (mut yielder, f) = ptr::read(info as *mut (Yielder<Item, Stack>, F));
      let new_context = context::Context::swap(yielder.context, yielder.context, 0);
      // See Yielder::return_.
      yielder.context = new_context as *mut context::Context<Stack>;
      // Run the body of the generator.
      f(&mut yielder);
      // Past this point, the generator has dropped everything it has held.
      loop { yielder.return_(None) }
    }

    let mut generator = Generator {
      state:   State::Suspended,
      context: context::Context::new(stack, generator_wrapper::<Item, Stack, F>),
      phantom: PhantomData
    };

    // Transfer environment to the callee.
    let mut data = (Yielder::new(&mut generator.context), f);
    context::Context::swap(&mut generator.context, &generator.context,
                           &mut data as *mut (Yielder<Item, Stack>, F) as usize);
    mem::forget(data);

    generator
  }

  /// Returns the state of the generator.
  pub fn state(&self) -> State { self.state }

  /// Extracts the stack from a generator when the generator function has returned.
  /// If the generator function has not returned
  /// (i.e. `self.state() == State::Suspended`), panics.
  pub fn unwrap(self) -> Stack {
    match self.state {
      State::Suspended => panic!("Argh! Bastard! Don't touch that!"),
      State::Finished  => unsafe { self.context.unwrap() }
    }
  }
}

/// Yielder is an interface provided to every generator through which it
/// returns a value.
#[derive(Debug)]
pub struct Yielder<Item: Send, Stack: stack::Stack> {
  context: *mut context::Context<Stack>,
  phantom: PhantomData<Item>
}

impl<Item, Stack> Yielder<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  fn new(context: *mut context::Context<Stack>) -> Yielder<Item, Stack> {
    Yielder {
      context: context,
      phantom: PhantomData
    }
  }

  #[inline(always)]
  fn return_(&mut self, mut val: Option<Item>) {
    unsafe {
      let new_context = context::Context::swap(self.context, self.context,
                                               &mut val as *mut Option<Item> as usize);
      // The generator can be moved (and with it, the context).
      // This changes the address of the context.
      // Thus, we update it after each swap.
      self.context = new_context as *mut context::Context<Stack>;
      // However, between this point and the next time we enter return_
      // the generator cannot be moved, as a &mut Generator is necessary
      // to resume the generator function.
    }
  }

  /// Suspends the generator and returns `Some(item)` from the `next()`
  /// invocation that resumed the generator.
  #[inline(always)]
  pub fn generate(&mut self, item: Item) {
    self.return_(Some(item))
  }
}

impl<Item, Stack> Iterator for Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  type Item = Item;

  /// Resumes the generator and return the next value it yields.
  /// If the generator function has returned, returns `None`.
  fn next(&mut self) -> Option<Self::Item> {
    match self.state {
      State::Suspended => {
        let new_context = &mut self.context as *mut context::Context<Stack> as usize;
        let val = unsafe {
          ptr::read(context::Context::swap(&mut self.context, &self.context, new_context)
                    as *mut Option<Item>)
        };
        if let None = val { self.state = State::Finished }
        val
      }
      State::Finished => None
    }
  }
}
<commit_msg>Add an inline hint on Generator::next().<commit_after>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) whitequark <whitequark@whitequark.org>
// See the LICENSE file included in this distribution.

//! Generators.
//!
//! Generators allow repeatedly suspending the execution of a function,
//! returning a value to the caller, and resuming the suspended function
//! afterwards.

use core::marker::PhantomData;
use core::iter::Iterator;
use core::{ptr, mem};

use stack;
use context;

#[derive(Debug, Clone, Copy)]
pub enum State {
  /// Generator can be resumed. This is the initial state.
  Suspended,
  /// Generator cannot be resumed. This is the state of the generator after
  /// the generator function has returned.
  Finished
}

/// Generator wraps a function and allows suspending its execution more than
/// once, return a value each time.
///
/// It implements the Iterator trait. The first time `next()` is called,
/// the function is called as `f(yielder)`; every time `next()` is called afterwards,
/// the function is resumed. In either case, it runs until it suspends its execution
/// through `yielder.generate(val)`), in which case `next()` returns `Some(val)`, or
/// returns, in which case `next()` returns `None`. `next()` will return `None`
/// every time after that.
///
/// After the generator function returns, it is safe to reclaim the generator
/// stack using `unwrap()`.
///
/// `state()` can be used to determine whether the generator function has returned;
/// the state is `State::Suspended` after creation and suspension, and `State::Finished`
/// once the generator function returns.
///
/// # Example
///
/// ```
/// use fringe::{OsStack, Generator};
///
/// let stack = OsStack::new(0).unwrap();
/// let mut gen = Generator::new(stack, move |yielder| {
///   for i in 1..4 {
///     yielder.generate(i);
///   }
/// });
/// println!("{:?}", gen.next()); // prints Some(1)
/// println!("{:?}", gen.next()); // prints Some(2)
/// println!("{:?}", gen.next()); // prints Some(3)
/// println!("{:?}", gen.next()); // prints None
/// ```
#[derive(Debug)]
pub struct Generator<Item: Send, Stack: stack::Stack> {
  state:   State,
  context: context::Context<Stack>,
  phantom: PhantomData<Item>
}

impl<Item, Stack> Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  /// Creates a new generator.
  pub fn new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where Stack: stack::GuardedStack, F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe { Generator::unsafe_new(stack, f) }
  }

  /// Same as `new`, but does not require `stack` to have a guard page.
  ///
  /// This function is unsafe because the generator function can easily violate
  /// memory safety by overflowing the stack. It is useful in environments where
  /// guarded stacks do not exist, e.g. in absence of an MMU.
  pub unsafe fn unsafe_new<F>(stack: Stack, f: F) -> Generator<Item, Stack>
      where F: FnOnce(&mut Yielder<Item, Stack>) + Send {
    unsafe extern "C" fn generator_wrapper<Item, Stack, F>(info: usize) -> !
        where Item: Send, Stack: stack::Stack, F: FnOnce(&mut Yielder<Item, Stack>) {
      // Retrieve our environment from the callee and return control to it.
      let (mut yielder, f) = ptr::read(info as *mut (Yielder<Item, Stack>, F));
      let new_context = context::Context::swap(yielder.context, yielder.context, 0);
      // See Yielder::return_.
      yielder.context = new_context as *mut context::Context<Stack>;
      // Run the body of the generator.
      f(&mut yielder);
      // Past this point, the generator has dropped everything it has held.
      loop { yielder.return_(None) }
    }

    let mut generator = Generator {
      state:   State::Suspended,
      context: context::Context::new(stack, generator_wrapper::<Item, Stack, F>),
      phantom: PhantomData
    };

    // Transfer environment to the callee.
    let mut data = (Yielder::new(&mut generator.context), f);
    context::Context::swap(&mut generator.context, &generator.context,
                           &mut data as *mut (Yielder<Item, Stack>, F) as usize);
    mem::forget(data);

    generator
  }

  /// Returns the state of the generator.
  pub fn state(&self) -> State { self.state }

  /// Extracts the stack from a generator when the generator function has returned.
  /// If the generator function has not returned
  /// (i.e. `self.state() == State::Suspended`), panics.
  pub fn unwrap(self) -> Stack {
    match self.state {
      State::Suspended => panic!("Argh! Bastard! Don't touch that!"),
      State::Finished  => unsafe { self.context.unwrap() }
    }
  }
}

/// Yielder is an interface provided to every generator through which it
/// returns a value.
#[derive(Debug)]
pub struct Yielder<Item: Send, Stack: stack::Stack> {
  context: *mut context::Context<Stack>,
  phantom: PhantomData<Item>
}

impl<Item, Stack> Yielder<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  fn new(context: *mut context::Context<Stack>) -> Yielder<Item, Stack> {
    Yielder {
      context: context,
      phantom: PhantomData
    }
  }

  #[inline(always)]
  fn return_(&mut self, mut val: Option<Item>) {
    unsafe {
      let new_context = context::Context::swap(self.context, self.context,
                                               &mut val as *mut Option<Item> as usize);
      // The generator can be moved (and with it, the context).
      // This changes the address of the context.
      // Thus, we update it after each swap.
      self.context = new_context as *mut context::Context<Stack>;
      // However, between this point and the next time we enter return_
      // the generator cannot be moved, as a &mut Generator is necessary
      // to resume the generator function.
    }
  }

  /// Suspends the generator and returns `Some(item)` from the `next()`
  /// invocation that resumed the generator.
  #[inline(always)]
  pub fn generate(&mut self, item: Item) {
    self.return_(Some(item))
  }
}

impl<Item, Stack> Iterator for Generator<Item, Stack>
    where Item: Send, Stack: stack::Stack {
  type Item = Item;

  /// Resumes the generator and return the next value it yields.
  /// If the generator function has returned, returns `None`.
  #[inline]
  fn next(&mut self) -> Option<Self::Item> {
    match self.state {
      State::Suspended => {
        let new_context = &mut self.context as *mut context::Context<Stack> as usize;
        let val = unsafe {
          ptr::read(context::Context::swap(&mut self.context, &self.context, new_context)
                    as *mut Option<Item>)
        };
        if let None = val { self.state = State::Finished }
        val
      }
      State::Finished => None
    }
  }
}
<|endoftext|>