<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::Arc;
use std::thread::{self, JoinHandle};

use model::{Board, Move, Outcome};

const INFINITY: i16 = 0x7FFF;
const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
const DRAW: i16 = 0;

pub enum AI {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> AI {
        AI::Idle
    }

    pub fn is_idle(&self) -> bool {
        match self {
            AI::Idle => true,
            AI::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let AI::Thinking { stop_signal, .. } = self {
            stop_signal.store(true, Ordering::Relaxed);
            *self = AI::Idle;
        }
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        match self {
            AI::Idle => None,
            AI::Thinking { move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => {
                    *self = AI::Idle;
                    Some(mv)
                }
                Err(Empty) => None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }
    }

    pub fn think(&mut self, board: Board, depth: u8) {
        assert_ne!(depth, 0);

        let prev_ai = mem::replace(self, AI::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let handle = thread::spawn(move || {
            if let AI::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_ai
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // 2-ply iterative deepening
            let mut moves: Vec<(Move, i16)> = board
                .generate_moves()
                .into_iter()
                .map(|mv| {
                    let mut new_board = board;
                    new_board.apply_move(&mv);

                    let score = -alphabeta_negamax(&new_board, NEG_INFINITY, INFINITY, 1);
                    (mv, score)
                })
                .collect();

            moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));

            let mut max_score = NEG_INFINITY;
            let mut best_move = None;
            for (mv, _) in moves {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }

                let mut new_board = board;
                new_board.apply_move(&mv);

                let score = -alphabeta_negamax(&new_board, NEG_INFINITY, -max_score, depth - 1);
                if score > max_score {
                    max_score = score;
                    best_move = Some(mv);
                }
            }

            match best_move {
                Some(mv) => move_sender.send(mv).expect("AI failed to send Move"),
                None => panic!("AI failed to find a move"),
            }
        });

        *self = AI::Thinking {
            move_recv,
            stop_signal,
            handle,
        }
    }
}

fn alphabeta_negamax(board: &Board, mut alpha: i16, beta: i16, depth: u8) -> i16 {
    match board.outcome() {
        Outcome::Draw => return DRAW,
        Outcome::Win(color) => {
            assert_ne!(color, board.turn());
            // TODO: weight by depth to encourage shorter wins
            return LOSE;
        }
        Outcome::InProgress => {}
    }

    if depth == 0 {
        evaluate(board)
    } else {
        let moves = board.generate_moves();
        for mv in moves {
            let mut new_board = *board;
            new_board.apply_move(&mv);

            let score = -alphabeta_negamax(&new_board, -beta, -alpha, depth - 1);
            if score >= beta {
                return beta;
            } else if score > alpha {
                alpha = score;
            }
        }
        alpha
    }
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn() {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<commit_msg>Add MoveList<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::Arc;
use std::thread::{self, JoinHandle};

use model::{Board, Move, Outcome};

const INFINITY: i16 = 0x7FFF;
const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
const DRAW: i16 = 0;

struct MoveList<'a> {
    mv: &'a Move,
    prev: Option<&'a MoveList<'a>>,
}

pub enum AI {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> AI {
        AI::Idle
    }

    pub fn is_idle(&self) -> bool {
        match self {
            AI::Idle => true,
            AI::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let AI::Thinking { stop_signal, .. } = self {
            stop_signal.store(true, Ordering::Relaxed);
            *self = AI::Idle;
        }
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        match self {
            AI::Idle => None,
            AI::Thinking { move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => {
                    *self = AI::Idle;
                    Some(mv)
                }
                Err(Empty) => None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }
    }

    pub fn think(&mut self, board: Board, depth: u8) {
        assert_ne!(depth, 0);

        let prev_ai = mem::replace(self, AI::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let handle = thread::spawn(move || {
            if let AI::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_ai
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // 2-ply iterative deepening
            let mut moves: Vec<(Move, i16)> = board
                .generate_moves()
                .into_iter()
                .map(|mv| {
                    let mut new_board = board;
                    new_board.apply_move(&mv);

                    let move_list = MoveList {
                        mv: &mv,
                        prev: None,
                    };

                    let score =
                        -alphabeta_negamax(&new_board, &move_list, NEG_INFINITY, INFINITY, 1);
                    (mv, score)
                })
                .collect();

            moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));

            let mut max_score = NEG_INFINITY;
            let mut best_move = None;
            for (mv, _) in moves {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }

                let mut new_board = board;
                new_board.apply_move(&mv);

                let move_list = MoveList {
                    mv: &mv,
                    prev: None,
                };

                let score =
                    -alphabeta_negamax(&new_board, &move_list, NEG_INFINITY, -max_score, depth - 1);
                if score > max_score {
                    max_score = score;
                    best_move = Some(mv);
                }
            }

            match best_move {
                Some(mv) => move_sender.send(mv).expect("AI failed to send Move"),
                None => panic!("AI failed to find a move"),
            }
        });

        *self = AI::Thinking {
            move_recv,
            stop_signal,
            handle,
        }
    }
}

fn alphabeta_negamax(
    board: &Board,
    move_list: &MoveList,
    mut alpha: i16,
    beta: i16,
    depth: u8,
) -> i16 {
    match board.outcome() {
        Outcome::Draw => return DRAW,
        Outcome::Win(color) => {
            assert_ne!(color, board.turn());
            // TODO: weight by depth to encourage shorter wins
            return LOSE;
        }
        Outcome::InProgress => {}
    }

    if depth == 0 {
        evaluate(board)
    } else {
        let moves = board.generate_moves();
        for mv in moves {
            let mut new_board = *board;
            new_board.apply_move(&mv);

            let next_move_list = MoveList {
                mv: &mv,
                prev: Some(move_list),
            };

            let score = -alphabeta_negamax(&new_board, &next_move_list, -beta, -alpha, depth - 1);
            if score >= beta {
                return beta;
            } else if score > alpha {
                alpha = score;
            }
        }
        alpha
    }
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn() {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<|endoftext|>


<commit_before>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use types::{Color, Viewport, Capability, GLError};
use program::ProgramAttrib;
use shader::ContextShaderExt;
use to_ref::{ToRef, ToMut};

pub mod buffer_context;
pub mod framebuffer_context;
pub mod program_context;
pub mod renderbuffer_context;
pub mod texture_context;
pub mod texture_units;

pub use self::buffer_context::*;
pub use self::framebuffer_context::*;
pub use self::program_context::*;
pub use self::renderbuffer_context::*;
pub use self::texture_context::*;
pub use self::texture_units::*;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn swap_buffers<NB>(self, new_buffer: NB)
        -> (B, ContextOf<NB, F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: new_buffer,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_framebuffer<NF>(self, new_framebuffer: NF)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: new_framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_program<NP>(self, new_program: NP)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: new_program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_renderbuffer<NR>(self, new_renderbuffer: NR)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: new_renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_tex_units<NT>(self, new_tex_units: NT)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: new_tex_units
            }
        )
    }
}

impl<'a, B, F, P, R, T> ToRef<'a> for ContextOf
    where B: 'a + ToRef<'a>,
          F: 'a + ToRef<'a>,
          P: 'a + ToRef<'a>,
          R: 'a + ToRef<'a>,
          T: 'a + ToRef<'a>
{
    type Ref = ContextOf;

    fn to_ref(&'a self) -> Self::Ref {
        ContextOf {
            buffers: self.buffers.to_ref(),
            framebuffer: self.framebuffer.to_ref(),
            program: self.program.to_ref(),
            renderbuffer: self.renderbuffer.to_ref(),
            tex_units: self.tex_units.to_ref()
        }
    }
}

impl<'a, B, F, P, R, T> ToMut<'a> for ContextOf
    where B: 'a + ToMut<'a>,
          F: 'a + ToMut<'a>,
          P: 'a + ToMut<'a>,
          R: 'a + ToMut<'a>,
          T: 'a + ToMut<'a>
{
    type Mut = ContextOf;

    fn to_mut(&'a mut self) -> Self::Mut {
        ContextOf {
            buffers: self.buffers.to_mut(),
            framebuffer: self.framebuffer.to_mut(),
            program: self.program.to_mut(),
            renderbuffer: self.renderbuffer.to_mut(),
            tex_units: self.tex_units.to_mut()
        }
    }
}

pub unsafe trait ContextExt {
    fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }
}

unsafe impl ContextExt for ContextOf {

}

unsafe impl<'a, B, F, P, R, T> ContextExt for &'a mut ContextOf {

}

pub mod ext {
    pub use ContextExt;
    pub use ContextBufferExt;
    pub use ContextFramebufferExt;
    pub use ContextProgramExt;
    pub use ContextRenderbufferExt;
    pub use ContextShaderExt;
    pub use ContextTextureExt;
}

pub trait AContext: ContextExt +
                    ContextBufferExt +
                    ContextFramebufferExt +
                    ContextProgramExt +
                    ContextRenderbufferExt +
                    ContextShaderExt +
                    ContextTextureExt
{

}

impl AContext for ContextOf {

}

impl<'a, B, F, P, R, T> AContext for &'a mut ContextOf {

}
<commit_msg>Add `ContextOf::load_with()`<commit_after>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use types::{Color, Viewport, Capability, GLError};
use program::ProgramAttrib;
use shader::ContextShaderExt;
use to_ref::{ToRef, ToMut};

pub mod buffer_context;
pub mod framebuffer_context;
pub mod program_context;
pub mod renderbuffer_context;
pub mod texture_context;
pub mod texture_units;

pub use self::buffer_context::*;
pub use self::framebuffer_context::*;
pub use self::program_context::*;
pub use self::renderbuffer_context::*;
pub use self::texture_context::*;
pub use self::texture_units::*;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn load_with<L>(load_fn: L)
        where L: FnMut(&str) -> *const GLvoid
    {
        gl::load_with(load_fn);
    }

    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn swap_buffers<NB>(self, new_buffer: NB)
        -> (B, ContextOf<NB, F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: new_buffer,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_framebuffer<NF>(self, new_framebuffer: NF)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: new_framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_program<NP>(self, new_program: NP)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: new_program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_renderbuffer<NR>(self, new_renderbuffer: NR)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: new_renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn swap_tex_units<NT>(self, new_tex_units: NT)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: new_tex_units
            }
        )
    }
}

impl<'a, B, F, P, R, T> ToRef<'a> for ContextOf
    where B: 'a + ToRef<'a>,
          F: 'a + ToRef<'a>,
          P: 'a + ToRef<'a>,
          R: 'a + ToRef<'a>,
          T: 'a + ToRef<'a>
{
    type Ref = ContextOf;

    fn to_ref(&'a self) -> Self::Ref {
        ContextOf {
            buffers: self.buffers.to_ref(),
            framebuffer: self.framebuffer.to_ref(),
            program: self.program.to_ref(),
            renderbuffer: self.renderbuffer.to_ref(),
            tex_units: self.tex_units.to_ref()
        }
    }
}

impl<'a, B, F, P, R, T> ToMut<'a> for ContextOf
    where B: 'a + ToMut<'a>,
          F: 'a + ToMut<'a>,
          P: 'a + ToMut<'a>,
          R: 'a + ToMut<'a>,
          T: 'a + ToMut<'a>
{
    type Mut = ContextOf;

    fn to_mut(&'a mut self) -> Self::Mut {
        ContextOf {
            buffers: self.buffers.to_mut(),
            framebuffer: self.framebuffer.to_mut(),
            program: self.program.to_mut(),
            renderbuffer: self.renderbuffer.to_mut(),
            tex_units: self.tex_units.to_mut()
        }
    }
}

pub unsafe trait ContextExt {
    fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }
}

unsafe impl ContextExt for ContextOf {

}

unsafe impl<'a, B, F, P, R, T> ContextExt for &'a mut ContextOf {

}

pub mod ext {
    pub use ContextExt;
    pub use ContextBufferExt;
    pub use ContextFramebufferExt;
    pub use ContextProgramExt;
    pub use ContextRenderbufferExt;
    pub use ContextShaderExt;
    pub use ContextTextureExt;
}

pub trait AContext: ContextExt +
                    ContextBufferExt +
                    ContextFramebufferExt +
                    ContextProgramExt +
                    ContextRenderbufferExt +
                    ContextShaderExt +
                    ContextTextureExt
{

}

impl AContext for ContextOf {

}

impl<'a, B, F, P, R, T> AContext for &'a mut ContextOf {

}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;

use commander::CommanderEvent;
use reader::ReaderEvent;
use searcher::{SearcherInput, SearcherReply};
use state::{StateInput, StateReply};

const POLLING_INTERVAL_MS: u32 = 10;

enum LoopCond {
    Break,
    Continue,
}

pub struct Coordinator {
    commander_rx: Receiver<CommanderEvent>,
    reader_rx: Receiver<ReaderEvent>,
    searcher_input_tx: Sender<SearcherInput>,
    searcher_reply_rx: Receiver<SearcherReply>,
    state_input_tx: Sender<StateInput>,
    state_reply_rx: Receiver<StateReply>,
}

impl Coordinator {
    pub fn new(
        commander_rx: Receiver<CommanderEvent>,
        reader_rx: Receiver<ReaderEvent>,
        searcher_input_tx: Sender<SearcherInput>,
        searcher_reply_rx: Receiver<SearcherReply>,
        state_input_tx: Sender<StateInput>,
        state_reply_rx: Receiver<StateReply>,
    ) -> Self
    {
        Coordinator {
            commander_rx: commander_rx,
            reader_rx: reader_rx,
            searcher_input_tx: searcher_input_tx,
            searcher_reply_rx: searcher_reply_rx,
            state_input_tx: state_input_tx,
            state_reply_rx: state_reply_rx,
        }
    }

    pub fn start(self) -> Option<Vec<Arc<String>>> {
        use std::sync::mpsc::TryRecvError::Empty;
        'EVENT_LOOP: loop {
            loop {
                match self.commander_rx.try_recv() {
                    Ok(ev) => {
                        match self.process_commander_event(ev) {
                            LoopCond::Continue => (),
                            LoopCond::Break    => break 'EVENT_LOOP,
                        }
                    }
                    Err(Empty) => break,
                    Err(_)     => panic!("commander terminated unexpectedly"),
                };
            }
            loop {
                match self.reader_rx.try_recv() {
                    Ok(ev)     => self.process_reader_event(ev),
                    Err(Empty) => break,
                    Err(_)     => panic!("reader terminated unexpectedly"),
                }
            }
            loop {
                match self.searcher_reply_rx.try_recv() {
                    Ok(reply)  => self.process_searcher_reply(reply),
                    Err(Empty) => break,
                    Err(_)     => panic!("searcher terminated unexpectedly"),
                }
            }
            loop {
                use state::StateReply::*;
                match self.state_reply_rx.try_recv() {
                    Ok(Complete(lines)) => { return lines; }
                    Ok(reply)           => self.process_state_reply(reply),
                    Err(Empty)          => break,
                    Err(_)              => panic!("state terminated unexpectedly"),
                }
            }
            thread::sleep_ms(POLLING_INTERVAL_MS);
        }
        Default::default()
    }

    fn process_commander_event(&self, event: CommanderEvent) -> LoopCond {
        use commander::CommanderEvent::*;
        use state::StateInput;
        use self::LoopCond::{Break, Continue};
        match event {
            KeyDown(key) => {
                self.state_input_tx.send(StateInput::PutKey(key)).is_ok() || return Break;
            }
        }
        Continue
    }

    fn process_reader_event(&self, event: ReaderEvent) {
        use reader::ReaderEvent::*;
        use state::StateInput;
        match event {
            DidReadChunk => {
                let _dont_care = self.state_input_tx.send(StateInput::UpdateScreen).is_ok();
            }
        }
    }

    fn process_searcher_reply(&self, reply: SearcherReply) {
        use searcher::SearcherReply::*;
        match reply {
            DidSearch(line_indices) => {
                let _dont_care = self.state_input_tx.send(StateInput::PutSearchResult(line_indices)).is_ok();
             }
        }
    }

    fn process_state_reply(&self, reply: StateReply) {
        use state::StateReply::*;
        use searcher::SearcherInput;
        match reply {
            Complete(_) => unreachable!(),
            RequestSearch(query) => {
                let _dont_care = self.searcher_input_tx.send(SearcherInput::Search(query)).is_ok();
            }
        }
    }
}
<commit_msg>s/ev/event/<commit_after>use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;

use commander::CommanderEvent;
use reader::ReaderEvent;
use searcher::{SearcherInput, SearcherReply};
use state::{StateInput, StateReply};

const POLLING_INTERVAL_MS: u32 = 10;

enum LoopCond {
    Break,
    Continue,
}

pub struct Coordinator {
    commander_rx: Receiver<CommanderEvent>,
    reader_rx: Receiver<ReaderEvent>,
    searcher_input_tx: Sender<SearcherInput>,
    searcher_reply_rx: Receiver<SearcherReply>,
    state_input_tx: Sender<StateInput>,
    state_reply_rx: Receiver<StateReply>,
}

impl Coordinator {
    pub fn new(
        commander_rx: Receiver<CommanderEvent>,
        reader_rx: Receiver<ReaderEvent>,
        searcher_input_tx: Sender<SearcherInput>,
        searcher_reply_rx: Receiver<SearcherReply>,
        state_input_tx: Sender<StateInput>,
        state_reply_rx: Receiver<StateReply>,
    ) -> Self
    {
        Coordinator {
            commander_rx: commander_rx,
            reader_rx: reader_rx,
            searcher_input_tx: searcher_input_tx,
            searcher_reply_rx: searcher_reply_rx,
            state_input_tx: state_input_tx,
            state_reply_rx: state_reply_rx,
        }
    }

    pub fn start(self) -> Option<Vec<Arc<String>>> {
        use std::sync::mpsc::TryRecvError::Empty;
        'EVENT_LOOP: loop {
            loop {
                match self.commander_rx.try_recv() {
                    Ok(event) => {
                        match self.process_commander_event(event) {
                            LoopCond::Continue => (),
                            LoopCond::Break    => break 'EVENT_LOOP,
                        }
                    }
                    Err(Empty) => break,
                    Err(_)     => panic!("commander terminated unexpectedly"),
                };
            }
            loop {
                match self.reader_rx.try_recv() {
                    Ok(event)  => self.process_reader_event(event),
                    Err(Empty) => break,
                    Err(_)     => panic!("reader terminated unexpectedly"),
                }
            }
            loop {
                match self.searcher_reply_rx.try_recv() {
                    Ok(reply)  => self.process_searcher_reply(reply),
                    Err(Empty) => break,
                    Err(_)     => panic!("searcher terminated unexpectedly"),
                }
            }
            loop {
                use state::StateReply::*;
                match self.state_reply_rx.try_recv() {
                    Ok(Complete(lines)) => { return lines; }
                    Ok(reply)           => self.process_state_reply(reply),
                    Err(Empty)          => break,
                    Err(_)              => panic!("state terminated unexpectedly"),
                }
            }
            thread::sleep_ms(POLLING_INTERVAL_MS);
        }
        Default::default()
    }

    fn process_commander_event(&self, event: CommanderEvent) -> LoopCond {
        use commander::CommanderEvent::*;
        use state::StateInput;
        use self::LoopCond::{Break, Continue};
        match event {
            KeyDown(key) => {
                self.state_input_tx.send(StateInput::PutKey(key)).is_ok() || return Break;
            }
        }
        Continue
    }

    fn process_reader_event(&self, event: ReaderEvent) {
        use reader::ReaderEvent::*;
        use state::StateInput;
        match event {
            DidReadChunk => {
                let _dont_care = self.state_input_tx.send(StateInput::UpdateScreen).is_ok();
            }
        }
    }

    fn process_searcher_reply(&self, reply: SearcherReply) {
        use searcher::SearcherReply::*;
        match reply {
            DidSearch(line_indices) => {
                let _dont_care = self.state_input_tx.send(StateInput::PutSearchResult(line_indices)).is_ok();
             }
        }
    }

    fn process_state_reply(&self, reply: StateReply) {
        use state::StateReply::*;
        use searcher::SearcherInput;
        match reply {
            Complete(_) => unreachable!(),
            RequestSearch(query) => {
                let _dont_care = self.searcher_input_tx.send(SearcherInput::Search(query)).is_ok();
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod compiler_test;

compiler_tests! {
    add0_0: "0+0" => type(0),
    add0_1: "0+1" => type(1),
    add1_0: "1+0" => type(1),
    add1_1: "1+1" => type(2),
    add123_123: "123+123" => type(246),
    add1_2_3: "1+2+3" => type(6),

    sub0_0: "0-0" => type(0),
    sub1_1: "1-1" => type(0),
    sub2_1: "2-1" => type(1),
    sub1_2: "1-2" => type(-1),
    sub1_0: "1-0" => type(1),
    sub369_123: "369-123" => type(246),
    sub6_2_1: "6-2-1" => type(3),

    mul0_0: "0*0" => type(0),
    mul0_1: "0*1" => type(0),
    mul1_0: "1*0" => type(0),
    mul1_1: "1*1" => type(1),
    mul11_11: "11*11" => type(121),
    mul2_3_4: "2*3*4" => type(24),

    div0_1: "0/1" => type(0),
    div12_1: "12/1" => type(12),
    div12_3: "12/3" => type(4),
    div11_11: "11/11" => type(1),
    div24_3_4: "24/3/4" => type(2),
    div1_2: "1/2" => type(BigRational::new(1.into(), 2.into())),
    div15_7: "15/7" => type(BigRational::new(15.into(), 7.into())),
    div45_3_7: "45/3/7" => type(BigRational::new(15.into(), 7.into())),

    div0_0: "0/0" => error(DivideByZero@1) type(error),
    div1_0: "1/0" => error(DivideByZero@1) type(error),

    muladd2_3_4: "2*3+4" => type(10),
    muladd2_3_4_neg: "-2*3+4" => type(-2),
    muladd2_3_4_pos: "+2*3+4" => type(10),

    addmul2_3_4: "2+3*4" => type(14),
    addmul2_3_4_neg: "-2+3*4" => type(10),
    addmul2_3_4_pos: "+2+3*4" => type(14),

    divadd2_3_4: "30/2*3" => type(45),
    divadd2_3_4_neg: "-30/2*3" => type(-45),
    divadd2_3_4_pos: "+30/2*3" => type(45),

    adddiv3_8_2: "3+8/2" => type(7),
    adddiv3_8_2_neg: "-3+8/2" => type(1),
    adddiv3_8_2_pos: "+3+8/2" => type(7),

    addsub0_0_0: "0+0-0" => type(0),
    addsub0_0_0_neg: "-0+0-0" => type(0),
    addsub0_0_0_pos: "+0+0-0" => type(0),
    addsub1_2_3: "1+2-3" => type(0),
    addsub1_2_3_neg: "-1+2-3" => type(-2),
    addsub1_2_3_pos: "+1+2-3" => type(0),

    subadd0_0_0: "0-0+0" => type(0),
    subadd0_0_0_neg: "-0-0+0" => type(0),
    subadd0_0_0_pos: "+0-0+0" => type(0),
    subadd1_2_3: "1-2+3" => type(2),
    subadd1_2_3_neg: "-1-2+3" => type(0),
    subadd1_2_3_pos: "+1-2+3" => type(2),

    neg_0: "-0" => type(0),
    neg_1: "-1" => type(-1),
    pos_0: "+0" => type(0),
    pos_1: "+1" => type(1),

    trailing_neg: "0-" => error(UnrecognizedOperator@1) type(error),
    trailing_pos: "0+" => error(UnrecognizedOperator@1) type(error),
    neg_only:      "-" => error(MissingRightOperand@0) type(error),
    pos_only:      "+" => error(MissingRightOperand@0) type(error),
    plus_minus: "1+-2" => error(UnrecognizedOperator@[1-2]) type(error),
}
<commit_msg>Add parentheses tests. It woooorks!<commit_after>#[macro_use]
pub mod compiler_test;

compiler_tests! {
    add0_0: "0+0" => type(0),
    add0_1: "0+1" => type(1),
    add1_0: "1+0" => type(1),
    add1_1: "1+1" => type(2),
    add123_123: "123+123" => type(246),
    add1_2_3: "1+2+3" => type(6),

    sub0_0: "0-0" => type(0),
    sub1_1: "1-1" => type(0),
    sub2_1: "2-1" => type(1),
    sub1_2: "1-2" => type(-1),
    sub1_0: "1-0" => type(1),
    sub369_123: "369-123" => type(246),
    sub6_2_1: "6-2-1" => type(3),

    mul0_0: "0*0" => type(0),
    mul0_1: "0*1" => type(0),
    mul1_0: "1*0" => type(0),
    mul1_1: "1*1" => type(1),
    mul11_11: "11*11" => type(121),
    mul2_3_4: "2*3*4" => type(24),

    div0_1: "0/1" => type(0),
    div12_1: "12/1" => type(12),
    div12_3: "12/3" => type(4),
    div11_11: "11/11" => type(1),
    div24_3_4: "24/3/4" => type(2),
    div1_2: "1/2" => type(BigRational::new(1.into(), 2.into())),
    div15_7: "15/7" => type(BigRational::new(15.into(), 7.into())),
    div45_3_7: "45/3/7" => type(BigRational::new(15.into(), 7.into())),

    div0_0: "0/0" => error(DivideByZero@1) type(error),
    div1_0: "1/0" => error(DivideByZero@1) type(error),

    muladd2_3_4: "2*3+4" => type(10),
    muladd2_3_4_neg: "-2*3+4" => type(-2),
    muladd2_3_4_pos: "+2*3+4" => type(10),

    addmul2_3_4: "2+3*4" => type(14),
    addmul2_3_4_neg: "-2+3*4" => type(10),
    addmul2_3_4_pos: "+2+3*4" => type(14),

    divadd2_3_4: "30/2*3" => type(45),
    divadd2_3_4_neg: "-30/2*3" => type(-45),
    divadd2_3_4_pos: "+30/2*3" => type(45),

    adddiv3_8_2: "3+8/2" => type(7),
    adddiv3_8_2_neg: "-3+8/2" => type(1),
    adddiv3_8_2_pos: "+3+8/2" => type(7),

    addsub0_0_0: "0+0-0" => type(0),
    addsub0_0_0_neg: "-0+0-0" => type(0),
    addsub0_0_0_pos: "+0+0-0" => type(0),
    addsub1_2_3: "1+2-3" => type(0),
    addsub1_2_3_neg: "-1+2-3" => type(-2),
    addsub1_2_3_pos: "+1+2-3" => type(0),

    subadd0_0_0: "0-0+0" => type(0),
    subadd0_0_0_neg: "-0-0+0" => type(0),
    subadd0_0_0_pos: "+0-0+0" => type(0),
    subadd1_2_3: "1-2+3" => type(2),
    subadd1_2_3_neg: "-1-2+3" => type(0),
    subadd1_2_3_pos: "+1-2+3" => type(2),

    neg_0: "-0" => type(0),
    neg_1: "-1" => type(-1),
    pos_0: "+0" => type(0),
    pos_1: "+1" => type(1),

    parens: "(1+2*3)*3" => type(21),
    parens_neg: "-(1+2*3)*3" => type(-21),
    parens_neg_neg: "-(-1+2*3)*3" => type(-15),

    trailing_neg: "0-" => error(UnrecognizedOperator@1) type(error),
    trailing_pos: "0+" => error(UnrecognizedOperator@1) type(error),
    neg_only:      "-" => error(MissingRightOperand@0) type(error),
    pos_only:      "+" => error(MissingRightOperand@0) type(error),
    plus_minus: "1+-2" => error(UnrecognizedOperator@[1-2]) type(error),
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io;
use std::string::ToString;

use buffoon::{OutputStream, Serialize};
use serde_json::builder::ObjectBuilder;

use adapter::Adapter;
use block::Block;
use entry::{self, Entry};
use error::{Error, Result};
use metadata::Metadata;
use object::{Class, Object};
use objecthash::{self, ObjectHash, ObjectHasher};
use path::{Path, PathBuf};
use proto::ToProto;

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Type {
    Add,
}

impl ObjectHash for Type {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        self.to_string().objecthash(hasher);
    }
}

impl Serialize for Type {
    fn serialize<O: OutputStream>(&self, _: &mut O) -> io::Result<()> {
        unimplemented!();
    }

    fn serialize_nested<O: OutputStream>(&self, field: u32, out: &mut O) -> io::Result<()> {
        out.write_varint(field, *self as u32 + 1)
    }
}

impl ToString for Type {
    fn to_string(&self) -> String {
        match *self {
            Type::Add => "ADD".to_string(),
        }
    }
}

pub struct Op {
    pub optype: Type,
    pub path: PathBuf,
    pub object: Object,
}

impl Op {
    pub fn new(optype: Type, path: PathBuf, objectclass: Object) -> Op {
        Op {
            optype: optype,
            path: path,
            object: objectclass,
        }
    }

    pub fn apply<'a, 'b, A: Adapter<'a>>(&'b self,
                                         adapter: &A,
                                         txn: &mut A::W,
                                         state: &mut State<'b>,
                                         block: &Block)
                                         -> Result<()> {
        match self.optype {
            Type::Add => self.add(adapter, txn, state, block),
        }
    }

    pub fn build_json(&self, builder: ObjectBuilder) -> ObjectBuilder {
        builder.insert("optype", self.optype.to_string())
            .insert("path", self.path.as_path().to_string())
            .insert_object("objectclass", |b| self.object.build_json(b))
    }

    fn add<'a, 'b, A: Adapter<'a>>(&'b self,
                                   adapter: &A,
                                   txn: &mut A::W,
                                   state: &mut State<'b>,
                                   block: &Block)
                                   -> Result<()> {
        let entry_id = state.get_next_entry_id();

        let parent_path = self.path.as_path().parent();
        let parent_id = match parent_path {
            Some(parent) => {
                match state.new_entries.get(parent) {
                    Some(&(id, parent_class)) => {
                        if !parent_class.allows_child(&self.object) {
                            return Err(Error::ObjectNestingError);
                        }
                        id
                    }
                    _ => {
                        let id = try!(adapter.find_direntry(txn, parent)).id;
                        let parent_entry = try!(adapter.find_entry(txn, &id));

                        if !parent_entry.class.allows_child(&self.object) {
                            return Err(Error::ObjectNestingError);
                        }

                        id
                    }
                }
            }
            None => entry::Id::root(),
        };

        let name = try!(self.path.as_path().entry_name().ok_or(Error::PathInvalid));
        let metadata = Metadata::new(block.id, block.timestamp);
        let proto = try!(self.object.to_proto());
        let entry = Entry {
            id: entry_id,
            class: self.object.class(),
            data: &proto,
        };

        // NOTE: The underlying adapter must handle Error::EntryAlreadyExists
        try!(adapter.add_entry(txn, &entry, &name, parent_id, &metadata));
        state.new_entries.insert(self.path.as_path(), (entry_id, self.object.class()));

        Ok(())
    }
}

impl Serialize for Op {
    fn serialize<O: OutputStream>(&self, out: &mut O) -> io::Result<()> {
        try!(out.write(1, &self.optype));
        try!(out.write(2, &self.path.as_path().to_string()));
        try!(out.write(3, &self.object));

        Ok(())
    }
}

impl ObjectHash for Op {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        objecthash_struct!(
            hasher,
            "optype" => self.optype,
            "path" => self.path,
            "object" => self.object
        )
    }
}

pub struct State<'a> {
    next_entry_id: entry::Id,
    new_entries: HashMap<&'a Path, (entry::Id, Class)>,
}

impl<'a> State<'a> {
    pub fn new(next_entry_id: entry::Id) -> State<'a> {
        State {
            next_entry_id: next_entry_id,
            new_entries: HashMap::new(),
        }
    }

    pub fn get_next_entry_id(&mut self) -> entry::Id {
        let id = self.next_entry_id;
        self.next_entry_id = id.next();
        id
    }
}
<commit_msg>Extract StateEntry type<commit_after>use std::collections::HashMap;
use std::io;
use std::string::ToString;

use buffoon::{OutputStream, Serialize};
use serde_json::builder::ObjectBuilder;

use adapter::Adapter;
use block::Block;
use entry::{self, Entry};
use error::{Error, Result};
use metadata::Metadata;
use object::{Class, Object};
use objecthash::{self, ObjectHash, ObjectHasher};
use path::{Path, PathBuf};
use proto::ToProto;

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Type {
    Add,
}

impl ObjectHash for Type {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        self.to_string().objecthash(hasher);
    }
}

impl Serialize for Type {
    fn serialize<O: OutputStream>(&self, _: &mut O) -> io::Result<()> {
        unimplemented!();
    }

    fn serialize_nested<O: OutputStream>(&self, field: u32, out: &mut O) -> io::Result<()> {
        out.write_varint(field, *self as u32 + 1)
    }
}

impl ToString for Type {
    fn to_string(&self) -> String {
        match *self {
            Type::Add => "ADD".to_string(),
        }
    }
}

pub struct Op {
    pub optype: Type,
    pub path: PathBuf,
    pub object: Object,
}

impl Op {
    pub fn new(optype: Type, path: PathBuf, objectclass: Object) -> Op {
        Op {
            optype: optype,
            path: path,
            object: objectclass,
        }
    }

    pub fn apply<'a, 'b, A: Adapter<'a>>(&'b self,
                                         adapter: &A,
                                         txn: &mut A::W,
                                         state: &mut State<'b>,
                                         block: &Block)
                                         -> Result<()> {
        match self.optype {
            Type::Add => self.add(adapter, txn, state, block),
        }
    }

    pub fn build_json(&self, builder: ObjectBuilder) -> ObjectBuilder {
        builder.insert("optype", self.optype.to_string())
            .insert("path", self.path.as_path().to_string())
            .insert_object("objectclass", |b| self.object.build_json(b))
    }

    fn add<'a, 'b, A: Adapter<'a>>(&'b self,
                                   adapter: &A,
                                   txn: &mut A::W,
                                   state: &mut State<'b>,
                                   block: &Block)
                                   -> Result<()> {
        let entry_id = state.get_next_entry_id();

        let parent_path = self.path.as_path().parent();
        let parent_id = match parent_path {
            Some(parent) => {
                let (id, class) = match state.new_entries.get(parent) {
                    Some(ref parent_entry) => (parent_entry.id, parent_entry.class),
                    _ => {
                        let id = try!(adapter.find_direntry(txn, parent)).id;
                        let class = try!(adapter.find_entry(txn, &id)).class;
                        (id, class)
                    }
                };

                if !class.allows_child(&self.object) {
                    return Err(Error::ObjectNestingError);
                }

                id

            }
            None => entry::Id::root(),
        };

        let name = try!(self.path.as_path().entry_name().ok_or(Error::PathInvalid));
        let metadata = Metadata::new(block.id, block.timestamp);
        let proto = try!(self.object.to_proto());
        let entry = Entry {
            id: entry_id,
            class: self.object.class(),
            data: &proto,
        };

        // NOTE: The underlying adapter must handle Error::EntryAlreadyExists
        try!(adapter.add_entry(txn, &entry, &name, parent_id, &metadata));

        let new_entry = StateEntry {
            id: entry_id,
            class: self.object.class(),
        };
        state.new_entries.insert(self.path.as_path(), new_entry);

        Ok(())
    }
}

impl Serialize for Op {
    fn serialize<O: OutputStream>(&self, out: &mut O) -> io::Result<()> {
        try!(out.write(1, &self.optype));
        try!(out.write(2, &self.path.as_path().to_string()));
        try!(out.write(3, &self.object));

        Ok(())
    }
}

impl ObjectHash for Op {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        objecthash_struct!(
            hasher,
            "optype" => self.optype,
            "path" => self.path,
            "object" => self.object
        )
    }
}

pub struct State<'a> {
    next_entry_id: entry::Id,
    new_entries: HashMap<&'a Path, StateEntry>,
}

impl<'a> State<'a> {
    pub fn new(next_entry_id: entry::Id) -> State<'a> {
        State {
            next_entry_id: next_entry_id,
            new_entries: HashMap::new(),
        }
    }

    pub fn get_next_entry_id(&mut self) -> entry::Id {
        let id = self.next_entry_id;
        self.next_entry_id = id.next();
        id
    }
}

struct StateEntry {
    id: entry::Id,
    class: Class,
}
<|endoftext|>


<commit_before>// Copyright 2015 Jerome Rasky <jerome@rasky.co>
//
// Licensed under the Apache License, version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     <http://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. See the
// License for the specific language concerning governing permissions and
// limitations under the License.
use unicode_width::*;
use term::terminfo::TermInfo;

use std::collections::HashMap;
use std::borrow::Borrow;
use std::fmt::Write;
use std::iter::FromIterator;
use std::sync::Arc;
use std::sync::mpsc::Sender;

use std::cmp;

use bis_c::*;
use constants::*;
use error::*;
use types::*;

#[derive(PartialEq, Clone, Debug)]
pub enum TermStack {
    // here for correctness
    #[allow(dead_code)]
    Str(String),
    Int(isize),
    // here for correctness
    #[allow(dead_code)]
    Bool(bool)
}

pub struct UI {
    rows: u16,
    cols: u16,
    strings: HashMap<String, String>,
    control: HashMap<String, Option<String>>,
    emit: Sender<Event>
}

impl UI {
    pub fn create(emit: Sender<Event>) -> StrResult<UI> {
        let info = match TermInfo::from_env() {
            Ok(info) => info,
            Err(e) => return errs!(e, "Failed to get TermInfo")
        };

        let mut strings = HashMap::default();
        let mut control = HashMap::default();

        for (name, value) in info.strings.into_iter() {
            let strvalue = match String::from_utf8(value) {
                Ok(s) => s,
                Err(e) => return errs!(e, "failed to convert value to String")
            };

            strings.insert(name, strvalue);
        }

        let (rows, cols) = match get_terminal_size() {
            Ok(size) => size,
            Err(e) => return errs!(e, "Failed to get terminal size")
        };

        // assume an ANSI terminal for input sequences
        control.insert(format!("["), None);
        control.insert(format!("[A"), Some(format!("cuu1")));
        control.insert(format!("[B"), Some(format!("cud1")));

        Ok(UI {
            rows: rows,
            cols: cols,
            strings: strings,
            control: control,
            emit: emit
        })
    }

    fn get_string<T: Borrow<String>>(&self, name: T, params: Vec<TermStack>) -> Option<String> {
        // only implement what we're actually using in the UI
        let sequence = match self.strings.get(name.borrow()) {
            None => {
                trace!("No match for string: {:?}", name.borrow());
                return None;
            },
            Some(s) => {
                trace!("Matched string: {:?}", s);
                s.clone()
            }
        };

        let mut escaped = false;
        let mut stack: Vec<TermStack> = vec![];
        let mut result = String::default();
        let mut escape = String::default();

        // only implement the sequences we care about

        for c in sequence.chars() {
            if !escaped {
                if c == '%' {
                    escaped = true;
                } else {
                    result.push(c);
                }
            } else if escape.is_empty() {
                if c == 'd' {
                    match stack.pop() {
                        Some(TermStack::Int(c)) => {
                            result.push_str(format!("{}", c).as_ref());
                        },
                        Some(o) => {
                            error!("Numeric print on non-numeric type: {:?}", o);
                        },
                        None => {
                            error!("Stack was empty on print");
                        }
                    }
                    escaped = false;
                } else if c == 'p' {
                    escape.push('p');
                } else {
                    error!("Unknown escape character: {:?}", c);
                    escaped = false;
                }
            } else {
                if escape == "p" {
                    match c.to_digit(10) {
                        Some(idx) => {
                            if idx != 0 {
                                match params.get(idx as usize - 1) {
                                    Some(item) => {
                                        stack.push(item.clone())
                                    },
                                    None => {
                                        error!("There was no parameter {}", idx);
                                    }
                                }
                            } else {
                                error!("Tried to print 0th paramater");
                            }
                        },
                        None => {
                            error!("Paramater number was not a digit");
                        }
                    }

                    escape.clear();
                    escaped = false;
                } else {
                    error!("Unknown escape sequence: {:?}", escape);
                    escape.clear();
                    escaped = false;
                }
            }
        }

        trace!("Returning result: {:?}", result);

        // return result
        Some(result)
    }

    pub fn render_selection(&self, maybe_matches: Option<&[Arc<String>]>, number: usize, old: usize) -> String {
        if number == old {
            return format!("");
        }

        let matches;
        match maybe_matches {
            Some(m) => {
                matches = m;
            },
            None => {
                return format!("");
            }
        }

        let mut result = format!("\n");

        if old > 0 {
            trysp!(write!(result, "{}",
                          self.get_string(format!("cud"),
                                          vec![TermStack::Int(old as isize)]).unwrap_or(format!(""))),
                   "Failed to write to result");
        }

        trysp!(write!(result, "{}{}",
                      self.get_string(format!("clr_eol"), vec![]).unwrap_or(format!("")),
                      matches[old]),
               "Failed to write to result");

        if number > old + 1 {
            trysp!(write!(result, "{}",
                          self.get_string(format!("cud"),
                                          vec![TermStack::Int((number - old - 1) as isize)])
                          .unwrap_or(format!(""))),
                   "Foiled to write to result");
        } else if old > number {
            trysp!(write!(result, "{}",
                          self.get_string(format!("cuu"),
                                          vec![TermStack::Int((old - number + 1) as isize)])
                          .unwrap_or(format!(""))),
                   "Failed to write to result");
        } else {
            // do nothing
        }

        format!("{}\n{}{}{}{}", result,
                self.get_string(format!("clr_eol"), vec![]).unwrap_or(format!("")),
                MATCH_SELECT, self.truncate_string(matches[number].clone()),
                self.get_string(format!("rc"), vec![]).unwrap_or(format!("")))
    }

    fn truncate_string(&self, item: Arc<String>) -> Arc<String> {
        if UnicodeWidthStr::width(item.as_str()) > self.cols as usize {
            let mut owned = (*item).clone();
            while UnicodeWidthStr::width(owned.as_str()) > self.cols as usize {
                owned.pop();
            }
            Arc::new(owned)
        } else {
            item
        }
    }

    pub fn render_matches(&self, maybe_matches: Option<&[Arc<String>]>, number: usize) -> String {
        let matches;
        match maybe_matches {
            Some(m) => {
                matches = m;
            },
            None => {
                return format!("");
            }
        }

        let mut result = format!("");
        let match_number = cmp::min(MATCH_NUMBER, self.rows as usize - 1);

        for (idx, item) in matches.into_iter().enumerate() {
            if idx >= match_number {
                // don't render past the end of the screen
                break;
            }

            // write the pre
            if idx == number {
                write!(result, "{}{}",
                       MATCH_PRE, MATCH_SELECT).expect("Failed to write pre to result");
            } else {
                write!(result, "{}",
                       MATCH_PRE).expect("Failed to write pre to result");
            }

            // draw the item
            write!(result, "{}", self.truncate_string(item.clone())).expect("Writes to strings should not fail");
        }

        // restore the cursor
        write!(result, "{}", self.get_string(format!("rc"), vec![]).unwrap_or(format!("")))
            .expect("Writes to strings should not fail");

        result
    }

    pub fn render_best_match<T: AsRef<str>>(&self, query: T) -> String {
        format!("{}{}\n{}", FINISH, query.as_ref(),
                self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!("")))
    }

    pub fn render_prompt(&self) -> String {
        let number = cmp::min(MATCH_NUMBER, self.rows as usize - 1);
        format!("{}{}{}{}", String::from_iter(vec!['\n'; number].into_iter()),
                self.get_string(format!("cuu"), vec![TermStack::Int(number as isize)]).unwrap_or(format!("")),
                PROMPT,
                self.get_string(format!("sc"), vec![]).unwrap_or(format!("")))
    }

    fn input_query<T: AsRef<str>>(&self, query: T, chr: char) -> StrResult<(String, Option<String>)> {
        if chr.is_control() {
            match chr {
                ESC => {
                    // escape sequence
                    Ok((format!(""), Some(format!(""))))
                },
                EOT => {
                    // stop
                    trys!(self.emit.send(Event::Quit(false)), "Failed to send quit signal");
                    Ok((format!(""), None))
                },
                CTRL_U => {
                    // create our output
                    let output = format!("{}{}{}",
                                         self.get_string(format!("cub"),
                                                         vec![TermStack::Int(query.as_ref().len() as isize)])
                                         .unwrap_or(format!("")),
                                         self.get_string(format!("sc"), vec![]).unwrap_or(format!("")),
                                         self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!("")));

                    trys!(self.emit.send(Event::Query(format!(""))), "Failed to send query signal");
                    Ok((output, None))
                },
                '\n' => {
                    // exit
                    trys!(self.emit.send(Event::Quit(true)), "Failed to send quit signal");
                    Ok((format!(""), None))
                },
                _ => {
                    // unknown character
                    // \u{7} is BEL
                    Ok((format!("\u{7}"), None))
                }
            }
        } else if UnicodeWidthStr::width(query.as_ref()) + UnicodeWidthStr::width(PROMPT) +
            UnicodeWidthChar::width(chr).unwrap_or(0) >= self.cols as usize
        {
            // don't allow users to type past the end of one line
            Ok((format!("\u{7}"), None))
        } else {
            // output the character and clear the screen
            trys!(self.emit.send(Event::Query(format!("{}{}", query.as_ref(), chr))), "Failed to send query signal");

            Ok((format!("{}{}{}", chr,
                        self.get_string(format!("sc"), vec![]).unwrap_or(format!("")),
                        self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!(""))),
                None))
        }
    }

    fn input_escape<T: AsRef<str>, V: AsRef<str>>(&self, query: T, escape: V, chr: char) -> StrResult<(String, Option<String>)> {
        let esc_query = format!("{}{}", escape.as_ref(), chr);
        debug!("Escape query: {}", esc_query);
        match self.control.get(&esc_query) {
            None => {
                // no possible escape sequence like this
                trys!(self.emit.send(Event::Query(format!("{}{}", query.as_ref(), chr))), "Failed to send query signal");

                // BEL and then print the character
                Ok((format!("{}{}{}{}", BEL, chr,
                            self.get_string(format!("sc"), vec![]).unwrap_or(format!("")),
                            self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!(""))),
                    None))
            },
            Some(&None) => {
                // keep going
                Ok((format!(""), Some(format!("{}{}", escape.as_ref(), chr))))
            },
            Some(&Some(ref name)) => {
                if name == "cuu1" {
                    trys!(self.emit.send(Event::MatchUp), "Failed to send match up signal");
                    Ok((format!(""), None))
                } else if name == "cud1" {
                    trys!(self.emit.send(Event::MatchDown), "Failed to send match down signal");
                    Ok((format!(""), None))
                } else {
                    Err(StrError::new(format!("Unknown escape sequence: {:?}", name), None))
                }
            }
        }
    }

    pub fn input_chr<T: AsRef<str>, V: AsRef<str>>(&self, query: T, escape: Option<V>,
                                                   chr: char) -> StrResult<(String, Option<String>)> {
        match escape {
            Some(esc) => self.input_escape(query, esc, chr),
            None => self.input_query(query, chr)
        }
    }

    pub fn match_down(&self, number: usize, total: Option<usize>) -> StrResult<String> {
        match total {
            Some(total) => {
                if number + 1 >= total {
                    Ok(format!("{}", BEL))
                } else {
                    trys!(self.emit.send(Event::Select(number + 1)), "Failed to send select event");
                    Ok(format!(""))
                }
            },
            None => {
                debug!("Match down without any matches");
                Ok(format!("{}", BEL))
            }
        }
    }

    pub fn match_up(&self, number: usize) -> StrResult<String> {
        if number <= 0 {
            Ok(format!("{}", BEL))
        } else {
            trys!(self.emit.send(Event::Select(number - 1)), "Failed to send select event");
            Ok(format!(""))
        }
    }
}
<commit_msg>Fix string truncation for selected matches<commit_after>// Copyright 2015 Jerome Rasky <jerome@rasky.co>
//
// Licensed under the Apache License, version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     <http://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. See the
// License for the specific language concerning governing permissions and
// limitations under the License.
use unicode_width::*;
use term::terminfo::TermInfo;

use std::collections::HashMap;
use std::borrow::Borrow;
use std::fmt::Write;
use std::iter::FromIterator;
use std::sync::Arc;
use std::sync::mpsc::Sender;

use std::cmp;

use bis_c::*;
use constants::*;
use error::*;
use types::*;

#[derive(PartialEq, Clone, Debug)]
pub enum TermStack {
    // here for correctness
    #[allow(dead_code)]
    Str(String),
    Int(isize),
    // here for correctness
    #[allow(dead_code)]
    Bool(bool)
}

pub struct UI {
    rows: u16,
    cols: u16,
    strings: HashMap<String, String>,
    control: HashMap<String, Option<String>>,
    emit: Sender<Event>
}

impl UI {
    pub fn create(emit: Sender<Event>) -> StrResult<UI> {
        let info = match TermInfo::from_env() {
            Ok(info) => info,
            Err(e) => return errs!(e, "Failed to get TermInfo")
        };

        let mut strings = HashMap::default();
        let mut control = HashMap::default();

        for (name, value) in info.strings.into_iter() {
            let strvalue = match String::from_utf8(value) {
                Ok(s) => s,
                Err(e) => return errs!(e, "failed to convert value to String")
            };

            strings.insert(name, strvalue);
        }

        let (rows, cols) = match get_terminal_size() {
            Ok(size) => size,
            Err(e) => return errs!(e, "Failed to get terminal size")
        };

        // assume an ANSI terminal for input sequences
        control.insert(format!("["), None);
        control.insert(format!("[A"), Some(format!("cuu1")));
        control.insert(format!("[B"), Some(format!("cud1")));

        Ok(UI {
            rows: rows,
            cols: cols,
            strings: strings,
            control: control,
            emit: emit
        })
    }

    fn get_string<T: Borrow<String>>(&self, name: T, params: Vec<TermStack>) -> Option<String> {
        // only implement what we're actually using in the UI
        let sequence = match self.strings.get(name.borrow()) {
            None => {
                trace!("No match for string: {:?}", name.borrow());
                return None;
            },
            Some(s) => {
                trace!("Matched string: {:?}", s);
                s.clone()
            }
        };

        let mut escaped = false;
        let mut stack: Vec<TermStack> = vec![];
        let mut result = String::default();
        let mut escape = String::default();

        // only implement the sequences we care about

        for c in sequence.chars() {
            if !escaped {
                if c == '%' {
                    escaped = true;
                } else {
                    result.push(c);
                }
            } else if escape.is_empty() {
                if c == 'd' {
                    match stack.pop() {
                        Some(TermStack::Int(c)) => {
                            result.push_str(format!("{}", c).as_ref());
                        },
                        Some(o) => {
                            error!("Numeric print on non-numeric type: {:?}", o);
                        },
                        None => {
                            error!("Stack was empty on print");
                        }
                    }
                    escaped = false;
                } else if c == 'p' {
                    escape.push('p');
                } else {
                    error!("Unknown escape character: {:?}", c);
                    escaped = false;
                }
            } else {
                if escape == "p" {
                    match c.to_digit(10) {
                        Some(idx) => {
                            if idx != 0 {
                                match params.get(idx as usize - 1) {
                                    Some(item) => {
                                        stack.push(item.clone())
                                    },
                                    None => {
                                        error!("There was no parameter {}", idx);
                                    }
                                }
                            } else {
                                error!("Tried to print 0th paramater");
                            }
                        },
                        None => {
                            error!("Paramater number was not a digit");
                        }
                    }

                    escape.clear();
                    escaped = false;
                } else {
                    error!("Unknown escape sequence: {:?}", escape);
                    escape.clear();
                    escaped = false;
                }
            }
        }

        trace!("Returning result: {:?}", result);

        // return result
        Some(result)
    }

    pub fn render_selection(&self, maybe_matches: Option<&[Arc<String>]>, number: usize, old: usize) -> String {
        if number == old {
            return format!("");
        }

        let matches;
        match maybe_matches {
            Some(m) => {
                matches = m;
            },
            None => {
                return format!("");
            }
        }

        let mut result = format!("\n");

        if old > 0 {
            trysp!(write!(result, "{}",
                          self.get_string(format!("cud"),
                                          vec![TermStack::Int(old as isize)]).unwrap_or(format!(""))),
                   "Failed to write to result");
        }

        trysp!(write!(result, "{}{}",
                      self.get_string(format!("clr_eol"), vec![]).unwrap_or(format!("")),
                      matches[old]),
               "Failed to write to result");

        if number > old + 1 {
            trysp!(write!(result, "{}",
                          self.get_string(format!("cud"),
                                          vec![TermStack::Int((number - old - 1) as isize)])
                          .unwrap_or(format!(""))),
                   "Foiled to write to result");
        } else if old > number {
            trysp!(write!(result, "{}",
                          self.get_string(format!("cuu"),
                                          vec![TermStack::Int((old - number + 1) as isize)])
                          .unwrap_or(format!(""))),
                   "Failed to write to result");
        } else {
            // do nothing
        }

        format!("{}\n{}{}{}", result,
                self.get_string(format!("clr_eol"), vec![]).unwrap_or(format!("")),
                self.truncate_string(Arc::new(format!("{}{}", MATCH_SELECT, matches[number]))),
                self.get_string(format!("rc"), vec![]).unwrap_or(format!("")))
    }

    fn truncate_string(&self, item: Arc<String>) -> Arc<String> {
        if UnicodeWidthStr::width(item.as_str()) > self.cols as usize {
            let mut owned = (*item).clone();
            while UnicodeWidthStr::width(owned.as_str()) > self.cols as usize {
                owned.pop();
            }
            Arc::new(owned)
        } else {
            item
        }
    }

    pub fn render_matches(&self, maybe_matches: Option<&[Arc<String>]>, number: usize) -> String {
        let matches;
        match maybe_matches {
            Some(m) => {
                matches = m;
            },
            None => {
                return format!("");
            }
        }

        let mut result = format!("");
        let match_number = cmp::min(MATCH_NUMBER, self.rows as usize - 1);

        for (idx, item) in matches.into_iter().enumerate() {
            if idx >= match_number {
                // don't render past the end of the screen
                break;
            }

            let item_string;

            write!(result, "{}",
                   MATCH_PRE).expect("Failed to write pre to result");

            // write the pre
            if idx == number {
                item_string = Arc::new(format!("{}{}", MATCH_SELECT, item));
            } else {
                item_string = item.clone();
            }

            // draw the item
            write!(result, "{}", self.truncate_string(item_string)).expect("Writes to strings should not fail");
        }

        // restore the cursor
        write!(result, "{}", self.get_string(format!("rc"), vec![]).unwrap_or(format!("")))
            .expect("Writes to strings should not fail");

        result
    }

    pub fn render_best_match<T: AsRef<str>>(&self, query: T) -> String {
        format!("{}{}\n{}", FINISH, query.as_ref(),
                self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!("")))
    }

    pub fn render_prompt(&self) -> String {
        let number = cmp::min(MATCH_NUMBER, self.rows as usize - 1);
        format!("{}{}{}{}", String::from_iter(vec!['\n'; number].into_iter()),
                self.get_string(format!("cuu"), vec![TermStack::Int(number as isize)]).unwrap_or(format!("")),
                PROMPT,
                self.get_string(format!("sc"), vec![]).unwrap_or(format!("")))
    }

    fn input_query<T: AsRef<str>>(&self, query: T, chr: char) -> StrResult<(String, Option<String>)> {
        if chr.is_control() {
            match chr {
                ESC => {
                    // escape sequence
                    Ok((format!(""), Some(format!(""))))
                },
                EOT => {
                    // stop
                    trys!(self.emit.send(Event::Quit(false)), "Failed to send quit signal");
                    Ok((format!(""), None))
                },
                CTRL_U => {
                    // create our output
                    let output = format!("{}{}{}",
                                         self.get_string(format!("cub"),
                                                         vec![TermStack::Int(query.as_ref().len() as isize)])
                                         .unwrap_or(format!("")),
                                         self.get_string(format!("sc"), vec![]).unwrap_or(format!("")),
                                         self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!("")));

                    trys!(self.emit.send(Event::Query(format!(""))), "Failed to send query signal");
                    Ok((output, None))
                },
                '\n' => {
                    // exit
                    trys!(self.emit.send(Event::Quit(true)), "Failed to send quit signal");
                    Ok((format!(""), None))
                },
                _ => {
                    // unknown character
                    // \u{7} is BEL
                    Ok((format!("\u{7}"), None))
                }
            }
        } else if UnicodeWidthStr::width(query.as_ref()) + UnicodeWidthStr::width(PROMPT) +
            UnicodeWidthChar::width(chr).unwrap_or(0) >= self.cols as usize
        {
            // don't allow users to type past the end of one line
            Ok((format!("\u{7}"), None))
        } else {
            // output the character and clear the screen
            trys!(self.emit.send(Event::Query(format!("{}{}", query.as_ref(), chr))), "Failed to send query signal");

            Ok((format!("{}{}{}", chr,
                        self.get_string(format!("sc"), vec![]).unwrap_or(format!("")),
                        self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!(""))),
                None))
        }
    }

    fn input_escape<T: AsRef<str>, V: AsRef<str>>(&self, query: T, escape: V, chr: char) -> StrResult<(String, Option<String>)> {
        let esc_query = format!("{}{}", escape.as_ref(), chr);
        debug!("Escape query: {}", esc_query);
        match self.control.get(&esc_query) {
            None => {
                // no possible escape sequence like this
                trys!(self.emit.send(Event::Query(format!("{}{}", query.as_ref(), chr))), "Failed to send query signal");

                // BEL and then print the character
                Ok((format!("{}{}{}{}", BEL, chr,
                            self.get_string(format!("sc"), vec![]).unwrap_or(format!("")),
                            self.get_string(format!("clr_eos"), vec![]).unwrap_or(format!(""))),
                    None))
            },
            Some(&None) => {
                // keep going
                Ok((format!(""), Some(format!("{}{}", escape.as_ref(), chr))))
            },
            Some(&Some(ref name)) => {
                if name == "cuu1" {
                    trys!(self.emit.send(Event::MatchUp), "Failed to send match up signal");
                    Ok((format!(""), None))
                } else if name == "cud1" {
                    trys!(self.emit.send(Event::MatchDown), "Failed to send match down signal");
                    Ok((format!(""), None))
                } else {
                    Err(StrError::new(format!("Unknown escape sequence: {:?}", name), None))
                }
            }
        }
    }

    pub fn input_chr<T: AsRef<str>, V: AsRef<str>>(&self, query: T, escape: Option<V>,
                                                   chr: char) -> StrResult<(String, Option<String>)> {
        match escape {
            Some(esc) => self.input_escape(query, esc, chr),
            None => self.input_query(query, chr)
        }
    }

    pub fn match_down(&self, number: usize, total: Option<usize>) -> StrResult<String> {
        match total {
            Some(total) => {
                if number + 1 >= total {
                    Ok(format!("{}", BEL))
                } else {
                    trys!(self.emit.send(Event::Select(number + 1)), "Failed to send select event");
                    Ok(format!(""))
                }
            },
            None => {
                debug!("Match down without any matches");
                Ok(format!("{}", BEL))
            }
        }
    }

    pub fn match_up(&self, number: usize) -> StrResult<String> {
        if number <= 0 {
            Ok(format!("{}", BEL))
        } else {
            trys!(self.emit.send(Event::Select(number - 1)), "Failed to send select event");
            Ok(format!(""))
        }
    }
}
<|endoftext|>


<commit_before>use atom::*;
use number::*;
use env::Env;

#[derive (Debug)]
struct Function2 {
    program: Vec<Instruction>,
    env: Env,
    arity: u8
}

#[derive (Debug)]
struct Frame {
    program: Function2,
    env: Env
}

#[derive (Debug)]
pub struct VirtualMachine {
    stack: Vec<StackElement>,
    frames: Vec<Frame>,
    env: Env,
    pc: usize,
    fp: usize,
    sp: usize,
}

#[derive (Debug)]
enum StackElement {
    Atom(Atom),
}

fn compile(node: &[Atom]) -> Vec<Instruction> {
    let mut inst = vec![];

    // inst.push(Instruction::LOADI(1));
    // inst.push(Instruction::LOADI(2));
    // inst.push(Instruction::ADD);
    // inst.push(Instruction::LOADI(3));
    // inst.push(Instruction::ADD);

    inst
}

#[derive (Debug,Clone, Copy, PartialEq)]
enum Instruction {
    CONST,
    LOAD,
    STORE,
    DEFINE,
    FUNCTION,
    JUMP,
    CALL,
}

use self::Instruction::*;

impl VirtualMachine {
    pub fn new() -> VirtualMachine {
        VirtualMachine {
            program: vec![],
            stack: vec![],
            frames: vec![],
            env: Env::new(None),
            pc: 0,
            sp: 0,
            fp: 0
        }
    }

    pub fn run(&mut self) -> AtomResult {
        loop {
            if self.pc >= self.program.len() {
                break;
            }
            match self.program[self.pc] {
                ADD => {
                    let a = self.pop().as_number().unwrap();
                    let b = self.pop().as_number().unwrap();
                    self.push( Atom::Number(a + b));
                    self.pc += 1;
                },
                LOADI(i) => {
                    self.push( Atom::Number(Number::Integer(i)));
                    self.pc += 1;
                },
                PRINT => {
                    println!("{}", self.pop());
                    self.pc += 1;
                },
                JUMP_REL(offset) => {
                    self.pc = ((self.pc as i64) + offset) as usize;
                    assert!( self.pc < self.program.len())
                },
                JUMP(addr) => {
                    self.pc = addr;
                    assert!( self.pc < self.program.len())
                },
                POP => {
                    self.pop();
                },
                LOAD_ENV => {
                    let atom = self.pop();
                    let key = try!(atom.as_symbol());
                    let v = self.env.get(key);
                    self.push(v.unwrap_or(Atom::Nil));
                    self.pc += 1;
                },
                CALL => {
                    let func = self.pop();
                    match try!(func.as_function()) {
                        &Function::Proc(ref p) => {
                            let e = Env::new(Some(p.closures.clone()))
                                .bind(&p.params, &self.stack[self.sp-p.len()..p.len()]);
                            for _ in 1..p.len() {
                                self.pop();
                                self.sp -= 1;
                            }
                        }
                        _ => ()
                    }
                }
                _ => ()
            }
        }
        Ok(self.pop())
    }

    fn push(&mut self, a: Atom)  {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> Atom {
        self.sp -= 1;
        self.stack.pop().unwrap()
    }
}
<commit_msg>things<commit_after>use atom::*;
use number::*;
use env::Env;

#[derive (Debug)]
struct Function2 {
    program: Vec<Instruction>,
    env: Env,
    arity: u8
}

#[derive (Debug)]
struct Frame {
    program: Function2,
    env: Env
}

#[derive (Debug)]
pub struct VirtualMachine {
    stack: Vec<Atom>,
    frames: Vec<Frame>,
    env: Env,
    pc: usize,
    fp: usize,
    sp: usize,
}

fn compile(node: &[Atom]) -> Vec<Instruction> {
    let mut inst = vec![];

    // inst.push(Instruction::LOADI(1));
    // inst.push(Instruction::LOADI(2));
    // inst.push(Instruction::ADD);
    // inst.push(Instruction::LOADI(3));
    // inst.push(Instruction::ADD);

    inst
}

#[derive (Debug,Clone, Copy, PartialEq)]
enum Instruction {
    CONST,
    LOAD,
    STORE,
    DEFINE,
    FUNCTION,
    JUMP,
    CALL,
}

use self::Instruction::*;

impl VirtualMachine {
    pub fn new() -> VirtualMachine {
        VirtualMachine {
            stack: vec![],
            frames: vec![],
            env: Env::new(None),
            pc: 0,
            sp: 0,
            fp: 0
        }
    }

    pub fn run(&mut self) -> AtomResult {
        loop {
        }
        Ok(self.pop())
    }

    fn push(&mut self, a: Atom)  {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> Atom {
        self.sp -= 1;
        self.stack.pop().unwrap()
    }
}
<|endoftext|>