<commit_before>#![crate_name = "rboy"]

extern crate sdl2;
extern crate rboy;

use rboy::device::Device;
use std::sync::{Arc,RwLock};
use std::sync::mpsc::{Sender,Receiver};
use std::sync::mpsc::TryRecvError::{Disconnected,Empty};
use std::sync::atomic;
use std::sync::atomic::Ordering::Relaxed;

const SCALE : usize = 2;
const EXITCODE_SUCCESS : isize = 0;
const EXITCODE_INCORRECTOPTIONS : isize = 1;
const EXITCODE_CPULOADFAILS : isize = 2;

static EXITCODE : atomic::AtomicIsize = atomic::ATOMIC_ISIZE_INIT;

fn main() {
    set_exit_status(EXITCODE_SUCCESS);
    real_main();
    std::process::exit(EXITCODE.load(Relaxed) as i32);
}

fn set_exit_status(status: isize) {
    EXITCODE.store(status, Relaxed);
}

fn print_usage() {
    println!("usage: rboy <filename> [-s|--serial] [-c|--classic]");
    set_exit_status(EXITCODE_INCORRECTOPTIONS);
}

fn real_main() {
    let args: Vec<_> = std::env::args().collect();

    match args.len() {
        0 ... 1 => { print_usage(); return }
        _ => {}
    };

    let mut opt_serial = false;
    let mut opt_classic = false;
    let mut file = None;
    for arg in &args[1..] {
        let arg = &arg[..];
        if arg == "-s" || arg == "--serial" { opt_serial = true; }
        else if arg == "-c" || arg == "--classic" { opt_classic = true; }
        else { file = Some(arg); }
    }

    let filename = if let Some(ref f) = file {
        f.clone()
    } else {
        print_usage();
        return;
    };

    let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap();
    let window = match sdl2::video::Window::new("RBoy - A gameboy in Rust",
                                                sdl2::video::WindowPos::PosUndefined,
                                                sdl2::video::WindowPos::PosUndefined,
                                                160*SCALE as i32,
                                                144*SCALE as i32,
                                                sdl2::video::WindowFlags::empty()) {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let renderer = match sdl2::render::Renderer::from_window(window, sdl2::render::RenderDriverIndex::Auto, sdl2::render::ACCELERATED) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };

    let (sdl_tx, cpu_rx) = std::sync::mpsc::channel();
    let (cpu_tx, sdl_rx) = std::sync::mpsc::channel();
    let rawscreen = ::std::iter::repeat(0u8).take(160*144*3).collect();
    let arc = Arc::new(RwLock::new(rawscreen));
    let arc2 = arc.clone();

    let cpuloop_thread = std::thread::scoped(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, &filename, opt_classic, opt_serial));

    let periodic = timer_periodic(8);

    let mut event_queue = sdl_context.event_pump();
    'main : loop {
        let _ = periodic.recv();
        match sdl_rx.try_recv() {
            Err(Disconnected) => { break 'main },
            Ok(_) => recalculate_screen(&renderer, &arc),
            Err(Empty) => {},
        }
        for event in event_queue.poll_iter() {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::Escape, .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) => { let _ = sdl_tx.send(GBEvent::KeyUp(key)); },
                        None => {},
                    }
                },
                _ => {}
            }
        }
    }

    drop(sdl_tx); // Disconnect such that the cpuloop will exit
    let _ = cpuloop_thread.join();
}

fn sdl_to_keypad(key: sdl2::keycode::KeyCode) -> Option<rboy::KeypadKey> {
    match key {
        sdl2::keycode::KeyCode::Z => Some(rboy::KeypadKey::A),
        sdl2::keycode::KeyCode::X => Some(rboy::KeypadKey::B),
        sdl2::keycode::KeyCode::Up => Some(rboy::KeypadKey::Up),
        sdl2::keycode::KeyCode::Down => Some(rboy::KeypadKey::Down),
        sdl2::keycode::KeyCode::Left => Some(rboy::KeypadKey::Left),
        sdl2::keycode::KeyCode::Right => Some(rboy::KeypadKey::Right),
        sdl2::keycode::KeyCode::Space => Some(rboy::KeypadKey::Select),
        sdl2::keycode::KeyCode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(screen: &sdl2::render::Renderer, arc: &Arc<RwLock<Vec<u8>>>) {
    let mut drawer = screen.drawer();
    drawer.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 0xFF, 0xFF));
    drawer.clear();

    let data =  arc.read().unwrap().clone();
    for y in 0..144 {
        for x in 0..160 {
            drawer.set_draw_color(sdl2::pixels::Color::RGB(data[y*160*3 + x*3 + 0],
                                                            data[y*160*3 + x*3 + 1],
                                                            data[y*160*3 + x*3 + 2]));
            drawer.draw_rect(sdl2::rect::Rect::new((x*SCALE) as i32, (y*SCALE) as i32, SCALE as i32, SCALE as i32));
        }
    }
    drawer.present();
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SlowDown,
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn cpuloop(cpu_tx: &Sender<u32>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RwLock<Vec<u8>>>, filename: &str, need_c: bool, need_s: bool) {
    let opt_c = match need_c {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); set_exit_status(EXITCODE_CPULOADFAILS); return; },
    };
    c.set_stdout(need_s);

    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194.304f32 * 4.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                let mut data = arc.write().unwrap();
                let gpu_data = c.get_gpu_data();
                for i in 0..data.len() { data[i] = gpu_data[i]; }
                if cpu_tx.send(0).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        match cpu_rx.try_recv() {
            Ok(event) => match event {
                GBEvent::KeyUp(key) => c.keyup(key),
                GBEvent::KeyDown(key) => c.keydown(key),
                GBEvent::SpeedUp => limit_speed = false,
                GBEvent::SlowDown => limit_speed = true,
            },
            Err(Empty) => {},
            Err(Disconnected) => { break },
        };
    }
}

fn timer_periodic(ms: u32) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep_ms(ms);
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}
<commit_msg>Use thread::spawn instead of scoped<commit_after>#![crate_name = "rboy"]

extern crate sdl2;
extern crate rboy;

use rboy::device::Device;
use std::sync::{Arc,RwLock};
use std::sync::mpsc::{Sender,Receiver};
use std::sync::mpsc::TryRecvError::{Disconnected,Empty};
use std::sync::atomic;
use std::sync::atomic::Ordering::Relaxed;

const SCALE : usize = 2;
const EXITCODE_SUCCESS : isize = 0;
const EXITCODE_INCORRECTOPTIONS : isize = 1;
const EXITCODE_CPULOADFAILS : isize = 2;

static EXITCODE : atomic::AtomicIsize = atomic::ATOMIC_ISIZE_INIT;

fn main() {
    set_exit_status(EXITCODE_SUCCESS);
    real_main();
    std::process::exit(EXITCODE.load(Relaxed) as i32);
}

fn set_exit_status(status: isize) {
    EXITCODE.store(status, Relaxed);
}

fn print_usage() {
    println!("usage: rboy <filename> [-s|--serial] [-c|--classic]");
    set_exit_status(EXITCODE_INCORRECTOPTIONS);
}

fn real_main() {
    let args: Vec<_> = std::env::args().collect();

    match args.len() {
        0 ... 1 => { print_usage(); return }
        _ => {}
    };

    let mut opt_serial = false;
    let mut opt_classic = false;
    let mut file = None;
    for argument in &args[1..] {
        let arg = &argument[..];
        if arg == "-s" || arg == "--serial" { opt_serial = true; }
        else if arg == "-c" || arg == "--classic" { opt_classic = true; }
        else { file = Some(argument.clone()); }
    }

    let filename = if let Some(ref f) = file {
        f.clone()
    } else {
        print_usage();
        return;
    };

    let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap();
    let window = match sdl2::video::Window::new("RBoy - A gameboy in Rust",
                                                sdl2::video::WindowPos::PosUndefined,
                                                sdl2::video::WindowPos::PosUndefined,
                                                160*SCALE as i32,
                                                144*SCALE as i32,
                                                sdl2::video::WindowFlags::empty()) {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let renderer = match sdl2::render::Renderer::from_window(window, sdl2::render::RenderDriverIndex::Auto, sdl2::render::ACCELERATED) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };

    let (sdl_tx, cpu_rx) = std::sync::mpsc::channel();
    let (cpu_tx, sdl_rx) = std::sync::mpsc::channel();
    let rawscreen = ::std::iter::repeat(0u8).take(160*144*3).collect();
    let arc = Arc::new(RwLock::new(rawscreen));
    let arc2 = arc.clone();

    std::thread::spawn(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, &filename, opt_classic, opt_serial));

    let periodic = timer_periodic(8);

    let mut event_queue = sdl_context.event_pump();
    'main : loop {
        let _ = periodic.recv();
        match sdl_rx.try_recv() {
            Err(Disconnected) => { break 'main },
            Ok(_) => recalculate_screen(&renderer, &arc),
            Err(Empty) => {},
        }
        for event in event_queue.poll_iter() {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::Escape, .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) => { let _ = sdl_tx.send(GBEvent::KeyUp(key)); },
                        None => {},
                    }
                },
                _ => {}
            }
        }
    }

    drop(sdl_tx); // Disconnect such that the cpuloop will exit
    while let Ok(..) = sdl_rx.recv() {} // Wait for the cpuloop to disconnect
}

fn sdl_to_keypad(key: sdl2::keycode::KeyCode) -> Option<rboy::KeypadKey> {
    match key {
        sdl2::keycode::KeyCode::Z => Some(rboy::KeypadKey::A),
        sdl2::keycode::KeyCode::X => Some(rboy::KeypadKey::B),
        sdl2::keycode::KeyCode::Up => Some(rboy::KeypadKey::Up),
        sdl2::keycode::KeyCode::Down => Some(rboy::KeypadKey::Down),
        sdl2::keycode::KeyCode::Left => Some(rboy::KeypadKey::Left),
        sdl2::keycode::KeyCode::Right => Some(rboy::KeypadKey::Right),
        sdl2::keycode::KeyCode::Space => Some(rboy::KeypadKey::Select),
        sdl2::keycode::KeyCode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(screen: &sdl2::render::Renderer, arc: &Arc<RwLock<Vec<u8>>>) {
    let mut drawer = screen.drawer();
    drawer.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 0xFF, 0xFF));
    drawer.clear();

    let data =  arc.read().unwrap().clone();
    for y in 0..144 {
        for x in 0..160 {
            drawer.set_draw_color(sdl2::pixels::Color::RGB(data[y*160*3 + x*3 + 0],
                                                            data[y*160*3 + x*3 + 1],
                                                            data[y*160*3 + x*3 + 2]));
            drawer.draw_rect(sdl2::rect::Rect::new((x*SCALE) as i32, (y*SCALE) as i32, SCALE as i32, SCALE as i32));
        }
    }
    drawer.present();
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SlowDown,
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn cpuloop(cpu_tx: &Sender<u32>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RwLock<Vec<u8>>>, filename: &str, need_c: bool, need_s: bool) {
    let opt_c = match need_c {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); set_exit_status(EXITCODE_CPULOADFAILS); return; },
    };
    c.set_stdout(need_s);

    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194.304f32 * 4.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                let mut data = arc.write().unwrap();
                let gpu_data = c.get_gpu_data();
                for i in 0..data.len() { data[i] = gpu_data[i]; }
                if cpu_tx.send(0).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        match cpu_rx.try_recv() {
            Ok(event) => match event {
                GBEvent::KeyUp(key) => c.keyup(key),
                GBEvent::KeyDown(key) => c.keydown(key),
                GBEvent::SpeedUp => limit_speed = false,
                GBEvent::SlowDown => limit_speed = true,
            },
            Err(Empty) => {},
            Err(Disconnected) => { break },
        };
    }
}

fn timer_periodic(ms: u32) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep_ms(ms);
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}
<|endoftext|>


<commit_before>#[crate_id = "calx"];
#[desc = "Shared gamelib"];
#[license = "GPLv3"];
#[feature(globs)];
#[crate_type = "rlib"];

//extern mod sdl2;
extern mod extra;

pub mod text;
//pub mod app;
<commit_msg>Fix crate license<commit_after>#[crate_id = "calx"];
#[desc = "Shared gamelib"];
#[license = "MIT"];
#[feature(globs)];
#[crate_type = "rlib"];

//extern mod sdl2;
extern mod extra;

pub mod text;
//pub mod app;
<|endoftext|>


<commit_before>use asm_ops::*;

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::process::Command;

use std::fmt;
impl fmt::Display for Register{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }

}
impl fmt::Display for AsmOperand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AsmOperand::RegisterOperand(ref dest) =>  write!(f, "{}", dest),
            AsmOperand::Value(ref dest) => write!(f, "{}", dest),
            AsmOperand::Memory(ref mem) => write!(f, "[{}]", mem)
        }
    }
}

pub fn compile(ops: &Vec<AsmOp>) -> Vec<u8>{
    let mut str = "use64\n".to_string();
    for op in ops{
        let temp_str = match *op {
            AsmOp::Add(ref dest, ref operand) => format!("add {}, {}\n", dest, operand),
            AsmOp::Sub(ref dest, ref operand) => format!("sub {}, {}\n", dest, operand),

            AsmOp::Mul(ref dest, ref operand) => format!("imul {}, {}\n", dest, operand),
            AsmOp::Div(_, _) => panic!(),
            AsmOp::Mod(_, _) => panic!(),

            AsmOp::Pop(ref dest) => format!("popq {}\n", dest),
            AsmOp::Push(ref dest) => format!("pushq {}\n", dest),
            AsmOp::Mov(ref dest, ref operand) => format!("mov {}, {}\n", dest, operand),
            AsmOp::Out => "ret".to_string() ,

            AsmOp::Label(ref name) => format!("{}:\n", name),
            AsmOp::Loop(ref name) => format!("loopq {}\n", name)
            // _ => {}
        };
        str = str + &temp_str;
    }
    println!("\nOutput assembly is:\n{}\n",str);

    write_asm(&str);
    assemble();
    read_bytes()
}

fn write_asm(str: &String) {
    let path = Path::new("target/temp.asm");
    let display = path.display();

    let mut file = match File::create(&path) {
        Err(why) => panic!("couldn't create {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    match file.write_all(str.as_bytes()) {
        Err(why) => {
            panic!("couldn't write to {}: {}", display, Error::description(&why))
        },
        Ok(_) => println!("Successfully wrote to {}", display),
    }
}

fn assemble() {
    let output = Command::new("fasm").arg("target/temp.asm").output().unwrap_or_else(|e| {
        panic!("failed to execute process: {}", e)
    });

    if !output.status.success() {
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
        panic!("Can't assemble!");
    } else {
        println!("Successfully assembled to temp.bin");
    }
}

fn read_bytes() -> Vec<u8>{
    let path = Path::new("target/temp.bin");
    let display = path.display();

    let mut file = match File::open(&path) {
       Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
       Ok(file) => file,
   };

    let mut contents: Vec<u8> = Vec::new();
    // Returns amount of bytes read and append the result to the buffer
    let result = file.read_to_end(&mut contents).unwrap();
    println!("Program consists of {} bytes", result);
    contents
}
<commit_msg>Added assembly identation<commit_after>use asm_ops::*;

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::process::Command;

use std::fmt;
impl fmt::Display for Register{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }

}
impl fmt::Display for AsmOperand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AsmOperand::RegisterOperand(ref dest) =>  write!(f, "{}", dest),
            AsmOperand::Value(ref dest) => write!(f, "{}", dest),
            AsmOperand::Memory(ref mem) => write!(f, "[{}]", mem)
        }
    }
}

pub fn compile(ops: &Vec<AsmOp>) -> Vec<u8>{
    let mut str = "use64\n".to_string();
    let mut block_counter = 0;
    for op in ops{
        for _ in 0..block_counter{
            str = str + &"  ";
        }
        let temp_str = match *op {
            AsmOp::Add(ref dest, ref operand) => format!("add {}, {}\n", dest, operand),
            AsmOp::Sub(ref dest, ref operand) => format!("sub {}, {}\n", dest, operand),

            AsmOp::Mul(ref dest, ref operand) => format!("imul {}, {}\n", dest, operand),
            AsmOp::Div(_, _) => panic!(),
            AsmOp::Mod(_, _) => panic!(),

            AsmOp::Pop(ref dest) => format!("popq {}\n", dest),
            AsmOp::Push(ref dest) => format!("pushq {}\n", dest),
            AsmOp::Mov(ref dest, ref operand) => format!("mov {}, {}\n", dest, operand),
            AsmOp::Out => "ret".to_string() ,

            AsmOp::Label(ref name) => {
                block_counter += 1;
                format!("{}:\n", name)
            },
            AsmOp::Loop(ref name) => {
                block_counter -= 1;
                format!("\rloopq {}\n", name)
            }
            // _ => {}
        };
        str = str + &temp_str;
    }
    println!("\nOutput assembly is:\n{}\n",str);

    write_asm(&str);
    assemble();
    read_bytes()
}

fn write_asm(str: &String) {
    let path = Path::new("target/temp.asm");
    let display = path.display();

    let mut file = match File::create(&path) {
        Err(why) => panic!("couldn't create {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    match file.write_all(str.as_bytes()) {
        Err(why) => {
            panic!("couldn't write to {}: {}", display, Error::description(&why))
        },
        Ok(_) => println!("Successfully wrote to {}", display),
    }
}

fn assemble() {
    let output = Command::new("fasm").arg("target/temp.asm").output().unwrap_or_else(|e| {
        panic!("failed to execute process: {}", e)
    });

    if !output.status.success() {
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
        panic!("Can't assemble!");
    } else {
        println!("Successfully assembled to temp.bin");
    }
}

fn read_bytes() -> Vec<u8>{
    let path = Path::new("target/temp.bin");
    let display = path.display();

    let mut file = match File::open(&path) {
       Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
       Ok(file) => file,
   };

    let mut contents: Vec<u8> = Vec::new();
    // Returns amount of bytes read and append the result to the buffer
    let result = file.read_to_end(&mut contents).unwrap();
    println!("Program consists of {} bytes", result);
    contents
}
<|endoftext|>


<commit_before>extern crate find_folder;

use screen::{self, Screen};
use context::{Context};

use piston_window::{self, PistonWindow};

type Screens = Vec<Box<screen::Screen>>;

pub struct Game {
    window: PistonWindow,
    context: Context,
    screens: Screens,
}

impl Game {
    pub fn new() -> Self {
        let window: PistonWindow = piston_window::WindowSettings::new("rrthozopsi", [600, 600])
                                       .build()
                                       .unwrap();
        let mut context = Context {
            assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(),
        };
        let screens: Screens = vec![Box::new(screen::MainMenu::new(&window, &mut context))];
        Game {
            window: window,
            screens: screens,
            context: context,
        }
    }

    pub fn run(&mut self) {
        use piston_window::Event;

        let screens = &mut self.screens;

        for window in &mut self.window {
            match window.event {
                Some(Event::Update(ref args)) => {
                    last(screens).on_update(&args);
                }
                Some(Event::Render(ref args)) => {
                    last(screens).on_draw(&args, &window);
                }
                Some(Event::Input(ref input)) => {
                    handle_input(screens, &input, &window, &mut self.context);
                    if screens.is_empty() {
                        // No screens - exit game.
                        return;
                    }
                }
                _ => {}
            }
        }
    }
}

fn last<'a>(vec: &'a mut Screens) -> &'a mut Box<screen::Screen> {
    vec.last_mut().unwrap()
}

fn handle_input(screens: &mut Screens,
                input: &piston_window::Input,
                window: &PistonWindow,
                context: &mut Context) {
    for action in last(screens).on_input(&input, &window, context) {
        match action {
            screen::InputResult::PushScreen(new_screen) => {
                screens.push(new_screen);
            }
            screen::InputResult::PopScreen => {
                screens.pop();
            }
        }
    }
}
<commit_msg>Game: context constructor fix<commit_after>extern crate find_folder;

use screen::{self, Screen};
use context::{Context, GameState};

use piston_window::{self, PistonWindow};

type Screens = Vec<Box<screen::Screen>>;

pub struct Game {
    window: PistonWindow,
    context: Context,
    screens: Screens,
}

impl Game {
    pub fn new() -> Self {
        let window: PistonWindow = piston_window::WindowSettings::new("rrthozopsi", [600, 600])
                                       .build()
                                       .unwrap();
        let mut context = Context {
            assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(), game_state: GameState::NotStarted,
        };
        let screens: Screens = vec![Box::new(screen::MainMenu::new(&window, &mut context))];
        Game {
            window: window,
            screens: screens,
            context: context,
        }
    }

    pub fn run(&mut self) {
        use piston_window::Event;

        let screens = &mut self.screens;

        for window in &mut self.window {
            match window.event {
                Some(Event::Update(ref args)) => {
                    last(screens).on_update(&args);
                }
                Some(Event::Render(ref args)) => {
                    last(screens).on_draw(&args, &window);
                }
                Some(Event::Input(ref input)) => {
                    handle_input(screens, &input, &window, &mut self.context);
                    if screens.is_empty() {
                        // No screens - exit game.
                        return;
                    }
                }
                _ => {}
            }
        }
    }
}

fn last<'a>(vec: &'a mut Screens) -> &'a mut Box<screen::Screen> {
    vec.last_mut().unwrap()
}

fn handle_input(screens: &mut Screens,
                input: &piston_window::Input,
                window: &PistonWindow,
                context: &mut Context) {
    for action in last(screens).on_input(&input, &window, context) {
        match action {
            screen::InputResult::PushScreen(new_screen) => {
                screens.push(new_screen);
            }
            screen::InputResult::PopScreen => {
                screens.pop();
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;
use std::mem;
use std::ops::Deref;

use gc::{GcPtr, Gc, DataDef, Traverseable};

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct InternedStr(GcPtr<[u8]>);

unsafe impl Sync for InternedStr { }

impl Deref for InternedStr {
    type Target = str;
    fn deref(&self) -> &str {
        unsafe { ::std::str::from_utf8_unchecked(&*self.0) }
    }
}

pub struct Interner {
    //For this map and this map only we can't use InternedStr as keys since the hash should
    //not be expected to be the same as ordinary strings, we use a transmute to &'static str to
    //have the keys as strings without any unsafety as the keys do not escape the interner and they
    //live as long as their values
    indexes: HashMap<&'static str, InternedStr>
}

struct StrDef<'a>(&'a str);

impl <'a> DataDef for StrDef<'a> {
    type Value = [u8];
    fn size(&self) -> usize {
        self.0.len()
    }
    fn initialize(self, ptr: *mut [u8]) {
        let ptr: &mut [u8] = unsafe { &mut *ptr };
        assert_eq!(self.0.len(), ptr.len());
        ::std::slice::bytes::copy_memory(ptr, self.0.as_bytes());
    }
    fn make_ptr(&self, ptr: *mut ()) -> *mut [u8] {
        unsafe {
            use std::raw::Slice;
            let x = Slice { data: ptr as *mut u8, len: self.0.len() };
            mem::transmute(x)
        }
    }
}

impl Traverseable for Interner {
    fn traverse(&self, gc: &mut Gc) {
        for (_, v) in self.indexes.iter() {
            v.0.traverse(gc);
        }
    }
}

impl Interner {

    pub fn new() -> Interner {
        Interner { indexes: HashMap::new() }
    }

    pub fn intern(&mut self, gc: &mut Gc, s: &str) -> InternedStr {
        match self.indexes.get(s) {
            Some(interned_str) => {
                return *interned_str
            }
            None => ()
        }
        let gc_str = InternedStr(gc.alloc(StrDef(s)));
        //The key will live as long as the value it refers to and the static str never escapes
        //outside interner so this is safe
        let key: &'static str = unsafe { ::std::mem::transmute::<&str, &'static str>(&gc_str) };
        self.indexes.insert(key, gc_str);
        gc_str
    }
}

impl Str for InternedStr {
    fn as_slice<'a>(&'a self) -> &'a str {
        &**self
    }
}

impl fmt::Debug for InternedStr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.as_slice())
    }
}
impl fmt::Display for InternedStr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_slice())
    }
}

#[cfg(test)]
pub mod tests {
    use std::rc::Rc;
    use std::cell::RefCell;
    use super::*;
    use gc::Gc;

///Returns a reference to the interner stored in TLD
pub fn get_local_interner() -> Rc<RefCell<(Interner, Gc)>> {
    thread_local!(static INTERNER: Rc<RefCell<(Interner, Gc)>> = Rc::new(RefCell::new((Interner::new(), Gc::new()))));
    INTERNER.with(|interner| interner.clone())
}

pub fn intern(s: &str) -> InternedStr {
    let i = get_local_interner();
    let mut i = i.borrow_mut();
    let &mut(ref mut i, ref mut gc) = &mut *i;
    i.intern(gc, s)
}

}
<commit_msg>Implement more efficient hashing and equality for InternedStr<commit_after>use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Deref;

use gc::{GcPtr, Gc, DataDef, Traverseable};

#[derive(Copy, Clone, Eq)]
pub struct InternedStr(GcPtr<[u8]>);

impl PartialEq<InternedStr> for InternedStr {
    fn eq(&self, other: &InternedStr) -> bool {
        self.as_ptr() == other.as_ptr()
    }
}

impl Hash for InternedStr {
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
        self.as_ptr().hash(hasher)
    }
}

unsafe impl Sync for InternedStr { }

impl Deref for InternedStr {
    type Target = str;
    fn deref(&self) -> &str {
        unsafe { ::std::str::from_utf8_unchecked(&*self.0) }
    }
}

pub struct Interner {
    //For this map and this map only we can't use InternedStr as keys since the hash should
    //not be expected to be the same as ordinary strings, we use a transmute to &'static str to
    //have the keys as strings without any unsafety as the keys do not escape the interner and they
    //live as long as their values
    indexes: HashMap<&'static str, InternedStr>
}

struct StrDef<'a>(&'a str);

impl <'a> DataDef for StrDef<'a> {
    type Value = [u8];
    fn size(&self) -> usize {
        self.0.len()
    }
    fn initialize(self, ptr: *mut [u8]) {
        let ptr: &mut [u8] = unsafe { &mut *ptr };
        assert_eq!(self.0.len(), ptr.len());
        ::std::slice::bytes::copy_memory(ptr, self.0.as_bytes());
    }
    fn make_ptr(&self, ptr: *mut ()) -> *mut [u8] {
        unsafe {
            use std::raw::Slice;
            let x = Slice { data: ptr as *mut u8, len: self.0.len() };
            mem::transmute(x)
        }
    }
}

impl Traverseable for Interner {
    fn traverse(&self, gc: &mut Gc) {
        for (_, v) in self.indexes.iter() {
            v.0.traverse(gc);
        }
    }
}

impl Interner {

    pub fn new() -> Interner {
        Interner { indexes: HashMap::new() }
    }

    pub fn intern(&mut self, gc: &mut Gc, s: &str) -> InternedStr {
        match self.indexes.get(s) {
            Some(interned_str) => {
                return *interned_str
            }
            None => ()
        }
        let gc_str = InternedStr(gc.alloc(StrDef(s)));
        //The key will live as long as the value it refers to and the static str never escapes
        //outside interner so this is safe
        let key: &'static str = unsafe { ::std::mem::transmute::<&str, &'static str>(&gc_str) };
        self.indexes.insert(key, gc_str);
        gc_str
    }
}

impl Str for InternedStr {
    fn as_slice<'a>(&'a self) -> &'a str {
        &**self
    }
}

impl fmt::Debug for InternedStr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.as_slice())
    }
}
impl fmt::Display for InternedStr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_slice())
    }
}

#[cfg(test)]
pub mod tests {
    use std::rc::Rc;
    use std::cell::RefCell;
    use super::*;
    use gc::Gc;

///Returns a reference to the interner stored in TLD
pub fn get_local_interner() -> Rc<RefCell<(Interner, Gc)>> {
    thread_local!(static INTERNER: Rc<RefCell<(Interner, Gc)>> = Rc::new(RefCell::new((Interner::new(), Gc::new()))));
    INTERNER.with(|interner| interner.clone())
}

pub fn intern(s: &str) -> InternedStr {
    let i = get_local_interner();
    let mut i = i.borrow_mut();
    let &mut(ref mut i, ref mut gc) = &mut *i;
    i.intern(gc, s)
}

}
<|endoftext|>


<commit_before>

fn id<@T>(x: T) -> T { ret x; }

/* FIXME (issue #141):  See test/run-pass/constrained-type.rs.  Uncomment
 * the constraint once fixed. */
type rational = {num: int, den: int};

// : int::positive(*.den);
fn rational_leq(x: rational, y: rational) -> bool {
    // NB: Uses the fact that rationals have positive denominators WLOG:

    ret x.num * y.den <= y.num * x.den;
}

fn orb(a: bool, b: bool) -> bool { ret a || b; }
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Add unreachable() fn, also nitpicking<commit_after>
pure fn id<@T>(x: T) -> T { x }

fn unreachable() -> ! {
    fail "Internal error: entered unreachable code";
}

/* FIXME (issue #141):  See test/run-pass/constrained-type.rs.  Uncomment
 * the constraint once fixed. */
type rational = {num: int, den: int}; // : int::positive(*.den);

// : int::positive(*.den);
pure fn rational_leq(x: rational, y: rational) -> bool {
    // NB: Uses the fact that rationals have positive denominators WLOG:

    x.num * y.den <= y.num * x.den
}

pure fn orb(a: bool, b: bool) -> bool { a || b }
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>// Copyright 2016 Vincent Vigneron. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at.your option.
// This file may not be copied, modified, or distributed
// except according to those terms.

extern crate itertools;

use std::collections::{HashSet,HashMap};
use std::fmt;                          // Formatter, format!, Display, Debug, write!
use std::error;
use std::result;

/// The `NFAError` type.
#[derive(Debug)]
pub enum NFAError {
    /// The transition from state `usize` with symbol `char` is defined twice.
    DuplicatedTransition(char,usize),
    /// No final state is specified.
    MissingFinalStates,
    /// No starting state is specified.
    MissingStartingState,
}

impl fmt::Display for NFAError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            NFAError::DuplicatedTransition(symb,state) => write!(f, "Duplicated transition ('{}',{}).", symb, state),
            NFAError::MissingFinalStates => write!(f, "Missing final states."),
            NFAError::MissingStartingState => write!(f, "Missing starting state."),
        }
    }
}

impl error::Error for NFAError {
    fn description(&self) -> &str {
        match *self {
            NFAError::DuplicatedTransition(_,_) => "Duplicated transition.",
            NFAError::MissingFinalStates => "Missing final states.",
            NFAError::MissingStartingState => "Missing starting state.",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

/// The type `NFA` represents a NonDeterministic Finite Automaton. The transitions
/// of the automatan are stored in a hashtable.
#[derive(Debug)]
pub struct NFA {
    transitions : HashMap<(char,usize),HashSet<usize>>,
    start       : usize,
    finals      : HashSet<usize>,
}

/// The `NFABuilder` follows the builder pattern and allows to create a Deterministic
/// Finite Automaton. The builder is moved at each call so it is necessary to bind
/// to a new variable the return value for each function of the builder.
///
/// # Errors
///
/// Return an error if the starting state is not specified.
///
/// Return an error if the final states are not specified.
///
/// # Examples
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     // (abc)*
///     let nfa = NFABuilder::new()
///         .add_start(0)
///         .add_final(3)
///         .add_final(0)
///         .add_transition('a', 0, 1)
///         .add_transition('b', 1, 2)
///         .add_transition('c', 2, 3)
///         .add_transition('a', 3, 1)
///         .finalize();
/// }
/// ```
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let nfa = NFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match nfa {
///         Err(NFAError::MissingFinalStates) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let nfa = NFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .add_transition('t', 0, 2)
///         .finalize();
///     match nfa {
///         Err(NFAError::DuplicatedTransition(symb,src)) => assert!((symb,src)==('t',0)),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let nfa = NFABuilder::new()
///         .add_final(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match nfa {
///         Err(NFAError::MissingStartingState) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
#[derive(Debug)]
pub struct NFABuilder {
    transitions : HashMap<(char,usize),HashSet<usize>>,
    start       : Option<usize>,
    finals      : HashSet<usize>,
}

/// Alias for result::Result<T,NFAError>.
pub type Result<T> = result::Result<T,NFAError>;

/// NFABuilding is the trait assiociated to the NFABuilder type. Each NFABuilder
/// should implement NFABuilding trait.
///
/// NFABuilder can generate some errors during the building stage. For instance,
/// one could try to insert a transition with two different destination states.
///
/// #Errors
///
/// If self contains a NFAerror then each function should transfer this error.
pub trait NFABuilding {
    /// Add a starting state to the NFA.
    ///
    /// # Errors
    ///
    /// In the futur will return a NFAError::DuplicatedStartingState if
    /// two starting states are added.
    fn add_start(mut self, state: usize) -> Result<NFABuilder>;

    /// Add a final state to the NFA.
    fn add_final(mut self, state: usize) -> Result<NFABuilder>;

    /// Add a transition to the NFA.
    ///
    /// # Errors
    ///
    /// Return a NFAError::DuplicatedTransition(symb,src) if a transtion
    /// with the same symb and src has already been inserted, even if
    /// the destination state is the same.
    fn add_transition(mut self, symb: char, src: usize, dest: usize) -> Result<NFABuilder>;

    /// Finalize the building of the NFA.
    ///
    /// # Errors
    ///
    /// Return a NFAError::MissingStartingState if no starting state is specified.
    ///
    /// Return a NFAError::MissingFinalStates if no final state is specified.
    fn finalize(self) -> Result<NFA>;
}

impl NFABuilder {
    /// Creates a new NFABuilder.
    pub fn new() -> Result<NFABuilder> {
        Ok(NFABuilder{transitions: HashMap::new(), start: None, finals: HashSet::new()})
    }
}

impl NFABuilding for NFABuilder {
    fn add_start(self, state: usize) -> Result<NFABuilder> {
        Ok(self).add_start(state)
    }

    fn add_final(self, state: usize) -> Result<NFABuilder> {
        Ok(self).add_final(state)
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<NFABuilder> {
        Ok(self).add_transition(symb,src,dest)
    }

    fn finalize(self) -> Result<NFA> {
        Ok(self).finalize()
    }
}

/// Implementing NFABuilding trait for Result<NFABuilder> allows
/// to chain the return value of the NFABuilder instead of unwrapping them
/// at each stage of the building process.
impl NFABuilding for Result<NFABuilder> {
    fn add_start(self, state: usize) -> Result<NFABuilder> {
        self.map(|mut nfa| {
            nfa.start = Some(state);
            nfa
        })
    }

    fn add_final(self, state: usize) -> Result<NFABuilder> {
        self.map(|mut nfa| {
            nfa.finals.insert(state);
            nfa
        })
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<NFABuilder> {
        self.map(|mut nfa| {
            {
                // A block is mandatory here because states borrow a value inside nfa.
                // Ok(nfa) moves nfa but if states is in the same block it will has the
                // same lifetime and it's not possible to move a borrowed value.
                let states = nfa.transitions.entry((symb,src)).or_insert(HashSet::new());
                (*states).insert(dest);
            }
            nfa
        })
    }

    fn finalize(self) -> Result<NFA> {
        self.and_then(|nfa| {
            if nfa.start.is_none() {
                Err(NFAError::MissingStartingState)
            } else if nfa.finals.is_empty() {
                Err(NFAError::MissingFinalStates)
            } else {
                Ok(NFA{transitions: nfa.transitions, start: nfa.start.unwrap(), finals: nfa.finals})
            }
        })
    }
}

impl NFA {
    /// Test if an input string is a word of the language defined by the NFA.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate automata;
    ///
    /// use automata::nfa::core::*;
    /// use std::error::Error;
    ///
    /// fn main() {
    ///     // (abc)*
    ///     let nfa = NFABuilder::new()
    ///         .add_start(0)
    ///         .add_final(3)
    ///         .add_final(0)
    ///         .add_transition('a', 0, 1)
    ///         .add_transition('b', 1, 2)
    ///         .add_transition('c', 2, 3)
    ///         .add_transition('a', 3, 1)
    ///         .finalize();
    ///     match nfa {
    ///         Ok(nfa) => {
    ///            assert!(nfa.test("abc"));
    ///            assert!(nfa.test(""));
    ///            assert!(!nfa.test("a"));
    ///            assert!(!nfa.test("ab"));
    ///            assert!(!nfa.test("abca"));
    ///            assert!(!nfa.test("abcab"));
    ///            assert!(nfa.test("abcabcabc"));
    ///         },
    ///         Err(e) => println!("{}", e),
    ///     }
    /// }
    /// ```
    pub fn test(&self, input: &str) -> bool {
        let start : HashSet<_> = [self.start].iter().cloned().collect();
        input
            .chars()
            .fold(Some(start), |states,c| {
                states.and_then(|states| {
                    states.iter().fold(Some(HashSet::new()), |acc, state| {
                        acc.and_then(|acc| {
                            self.transitions
                                .get(&(c,*state))
                                .map(|trans| acc.union(trans).cloned().collect())
                        })
                    })
                })
            })
            .unwrap_or(HashSet::new())
            .intersection(&self.finals)
            .next().is_some()
    }
}

impl fmt::Display for NFA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(writeln!(f, "START: {}", self.start));
        try!(writeln!(f, "FINALS:"));
        for fi in self.finals.iter() {
            try!(writeln!(f,"  {}", fi));
        }
        try!(writeln!(f, "TRANSITIONS:"));
        for (tr,d) in self.transitions.iter() {
            let (c,s) = *tr;
            try!(writeln!(f, "  ({},{}) => {:?}", c, s, d));
        }
        write!(f, "")
    }
}

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

    #[test]
    fn test_nfa() {
        let nfa = NFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
        let samples =
            vec![("ababac", false),
                 ("ababc", true),
                 ("", false),
                 ("abc", true),
                 ("c", true),
                 ("ac", false),
                 ("ababababababababababababababababababababc", true),];

        for (input,expected_result) in samples {
            assert!(nfa.test(input) == expected_result, "input false for: \"{}\"", input);
        }
    }

    #[test]
    fn test_nfa_builder() {
        let _nfa = NFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
    }

    #[test]
    fn test_nfa_builder_missing_start() {
        let nfa = NFABuilder::new()
            .add_final(3)
            .add_transition('a', 0, 1)
            .finalize();
        match nfa {
            Err(NFAError::MissingStartingState) => assert!(true),
            _ => assert!(false, "MissingStartingState expected."),
        }
    }

    #[test]
    fn test_nfa_builder_missing_finals() {
        let nfa = NFABuilder::new()
            .add_start(0)
            .add_transition('a', 0, 1)
            .finalize();
        match nfa {
            Err(NFAError::MissingFinalStates) => assert!(true),
            _ => assert!(false, "MissingFinalStates expected."),
        }
    }
}
<commit_msg>Comment<commit_after>// Copyright 2016 Vincent Vigneron. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at.your option.
// This file may not be copied, modified, or distributed
// except according to those terms.

extern crate itertools;

use std::collections::{HashSet,HashMap};
use std::fmt;                          // Formatter, format!, Display, Debug, write!
use std::error;
use std::result;

/// The `NFAError` type.
#[derive(Debug)]
pub enum NFAError {
    /// The transition from state `usize` with symbol `char` is defined twice.
    DuplicatedTransition(char,usize),
    /// No final state is specified.
    MissingFinalStates,
    /// No starting state is specified.
    MissingStartingState,
}

impl fmt::Display for NFAError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            NFAError::DuplicatedTransition(symb,state) => write!(f, "Duplicated transition ('{}',{}).", symb, state),
            NFAError::MissingFinalStates => write!(f, "Missing final states."),
            NFAError::MissingStartingState => write!(f, "Missing starting state."),
        }
    }
}

impl error::Error for NFAError {
    fn description(&self) -> &str {
        match *self {
            NFAError::DuplicatedTransition(_,_) => "Duplicated transition.",
            NFAError::MissingFinalStates => "Missing final states.",
            NFAError::MissingStartingState => "Missing starting state.",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

/// The type `NFA` represents a NonDeterministic Finite Automaton. The transitions
/// of the automatan are stored in a hashtable.
#[derive(Debug)]
pub struct NFA {
    transitions : HashMap<(char,usize),HashSet<usize>>,
    start       : usize,
    finals      : HashSet<usize>,
}

/// The `NFABuilder` follows the builder pattern and allows to create a Deterministic
/// Finite Automaton. The builder is moved at each call so it is necessary to bind
/// to a new variable the return value for each function of the builder.
///
/// # Errors
///
/// Return an error if the starting state is not specified.
///
/// Return an error if the final states are not specified.
///
/// # Examples
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     // (abc)*
///     let nfa = NFABuilder::new()
///         .add_start(0)
///         .add_final(3)
///         .add_final(0)
///         .add_transition('a', 0, 1)
///         .add_transition('b', 1, 2)
///         .add_transition('c', 2, 3)
///         .add_transition('a', 3, 1)
///         .finalize();
/// }
/// ```
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let nfa = NFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match nfa {
///         Err(NFAError::MissingFinalStates) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let nfa = NFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .add_transition('t', 0, 2)
///         .finalize();
///     match nfa {
///         Err(NFAError::DuplicatedTransition(symb,src)) => assert!((symb,src)==('t',0)),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automata;
///
/// use automata::nfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let nfa = NFABuilder::new()
///         .add_final(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match nfa {
///         Err(NFAError::MissingStartingState) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
#[derive(Debug)]
pub struct NFABuilder {
    transitions : HashMap<(char,usize),HashSet<usize>>,
    start       : Option<usize>,
    finals      : HashSet<usize>,
}

/// Alias for result::Result<T,NFAError>.
pub type Result<T> = result::Result<T,NFAError>;

/// NFABuilding is the trait assiociated to the NFABuilder type. Each NFABuilder
/// should implement NFABuilding trait.
///
/// NFABuilder can generate some errors during the building stage. For instance,
/// one could try to insert a transition with two different destination states.
///
/// #Errors
///
/// If self contains a NFAerror then each function should transfer this error.
pub trait NFABuilding {
    /// Add a starting state to the NFA.
    ///
    /// # Errors
    ///
    /// In the futur will return a NFAError::DuplicatedStartingState if
    /// two starting states are added.
    fn add_start(mut self, state: usize) -> Result<NFABuilder>;

    /// Add a final state to the NFA.
    fn add_final(mut self, state: usize) -> Result<NFABuilder>;

    /// Add a transition to the NFA.
    ///
    /// # Errors
    ///
    /// Return a NFAError::DuplicatedTransition(symb,src) if a transtion
    /// with the same symb and src has already been inserted, even if
    /// the destination state is the same.
    fn add_transition(mut self, symb: char, src: usize, dest: usize) -> Result<NFABuilder>;

    /// Finalize the building of the NFA.
    ///
    /// # Errors
    ///
    /// Return a NFAError::MissingStartingState if no starting state is specified.
    ///
    /// Return a NFAError::MissingFinalStates if no final state is specified.
    fn finalize(self) -> Result<NFA>;
}

impl NFABuilder {
    /// Creates a new NFABuilder.
    pub fn new() -> Result<NFABuilder> {
        Ok(NFABuilder{transitions: HashMap::new(), start: None, finals: HashSet::new()})
    }
}

impl NFABuilding for NFABuilder {
    fn add_start(self, state: usize) -> Result<NFABuilder> {
        Ok(self).add_start(state)
    }

    fn add_final(self, state: usize) -> Result<NFABuilder> {
        Ok(self).add_final(state)
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<NFABuilder> {
        Ok(self).add_transition(symb,src,dest)
    }

    fn finalize(self) -> Result<NFA> {
        Ok(self).finalize()
    }
}

/// Implementing NFABuilding trait for Result<NFABuilder> allows
/// to chain the return value of the NFABuilder instead of unwrapping them
/// at each stage of the building process.
impl NFABuilding for Result<NFABuilder> {
    fn add_start(self, state: usize) -> Result<NFABuilder> {
        self.map(|mut nfa| {
            nfa.start = Some(state);
            nfa
        })
    }

    fn add_final(self, state: usize) -> Result<NFABuilder> {
        self.map(|mut nfa| {
            nfa.finals.insert(state);
            nfa
        })
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<NFABuilder> {
        self.map(|mut nfa| {
            {
                // `states` is a mutable reference to a value inside `transitions` (see or_insert).
                // It 's not possible to return nfa if states is in the same scope, because the
                // return statement will try to move nfa, including the `transitions` field while
                // states will still have a mutable refrence of `transitions`.
                let states = nfa.transitions.entry((symb,src)).or_insert(HashSet::new());
                (*states).insert(dest);
            }
            nfa
        })
    }

    fn finalize(self) -> Result<NFA> {
        self.and_then(|nfa| {
            if nfa.start.is_none() {
                Err(NFAError::MissingStartingState)
            } else if nfa.finals.is_empty() {
                Err(NFAError::MissingFinalStates)
            } else {
                Ok(NFA{transitions: nfa.transitions, start: nfa.start.unwrap(), finals: nfa.finals})
            }
        })
    }
}

impl NFA {
    /// Test if an input string is a word of the language defined by the NFA.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate automata;
    ///
    /// use automata::nfa::core::*;
    /// use std::error::Error;
    ///
    /// fn main() {
    ///     // (abc)*
    ///     let nfa = NFABuilder::new()
    ///         .add_start(0)
    ///         .add_final(3)
    ///         .add_final(0)
    ///         .add_transition('a', 0, 1)
    ///         .add_transition('b', 1, 2)
    ///         .add_transition('c', 2, 3)
    ///         .add_transition('a', 3, 1)
    ///         .finalize();
    ///     match nfa {
    ///         Ok(nfa) => {
    ///            assert!(nfa.test("abc"));
    ///            assert!(nfa.test(""));
    ///            assert!(!nfa.test("a"));
    ///            assert!(!nfa.test("ab"));
    ///            assert!(!nfa.test("abca"));
    ///            assert!(!nfa.test("abcab"));
    ///            assert!(nfa.test("abcabcabc"));
    ///         },
    ///         Err(e) => println!("{}", e),
    ///     }
    /// }
    /// ```
    pub fn test(&self, input: &str) -> bool {
        let start : HashSet<_> = [self.start].iter().cloned().collect();
        input
            .chars()
            .fold(Some(start), |states,c| {
                states.and_then(|states| {
                    states.iter().fold(Some(HashSet::new()), |acc, state| {
                        acc.and_then(|acc| {
                            self.transitions
                                .get(&(c,*state))
                                .map(|trans| acc.union(trans).cloned().collect())
                        })
                    })
                })
            })
            .unwrap_or(HashSet::new())
            .intersection(&self.finals)
            .next().is_some()
    }
}

impl fmt::Display for NFA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(writeln!(f, "START: {}", self.start));
        try!(writeln!(f, "FINALS:"));
        for fi in self.finals.iter() {
            try!(writeln!(f,"  {}", fi));
        }
        try!(writeln!(f, "TRANSITIONS:"));
        for (tr,d) in self.transitions.iter() {
            let (c,s) = *tr;
            try!(writeln!(f, "  ({},{}) => {:?}", c, s, d));
        }
        write!(f, "")
    }
}

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

    #[test]
    fn test_nfa() {
        let nfa = NFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
        let samples =
            vec![("ababac", false),
                 ("ababc", true),
                 ("", false),
                 ("abc", true),
                 ("c", true),
                 ("ac", false),
                 ("ababababababababababababababababababababc", true),];

        for (input,expected_result) in samples {
            assert!(nfa.test(input) == expected_result, "input false for: \"{}\"", input);
        }
    }

    #[test]
    fn test_nfa_builder() {
        let _nfa = NFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
    }

    #[test]
    fn test_nfa_builder_missing_start() {
        let nfa = NFABuilder::new()
            .add_final(3)
            .add_transition('a', 0, 1)
            .finalize();
        match nfa {
            Err(NFAError::MissingStartingState) => assert!(true),
            _ => assert!(false, "MissingStartingState expected."),
        }
    }

    #[test]
    fn test_nfa_builder_missing_finals() {
        let nfa = NFABuilder::new()
            .add_start(0)
            .add_transition('a', 0, 1)
            .finalize();
        match nfa {
            Err(NFAError::MissingFinalStates) => assert!(true),
            _ => assert!(false, "MissingFinalStates expected."),
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry::*;

use util::*;

/// Information about the position of a single term within a document
#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, RustcDecodable, RustcEncodable)]
pub struct Position {
    /// Pair of byte indexes into the document at the beginning (inclusive) and end (exclusive) of
    /// the term.
    pub offsets: (usize, usize),
    /// The token position of the term, i.e., the number of tokens that occur before it in the doc.
    /// For example, for the sentence "I have to go to the store",
    /// the term "to" has positions [2, 4].
    pub position: usize,
}

impl Position {
    /// Creates a new Position struct with the given offsets and position.
    pub fn new(offsets: (usize, usize), position: usize) -> Position {
        Position {
            offsets: offsets,
            position: position,
        }
    }
}

impl Merge for Position {
    fn merge(self, other: Position) -> Option<Position> {
        if self.position == other.position {
            self.offsets.merge(other.offsets).map(|offsets| Position::new(offsets, self.position))
        } else {
            None
        }
    }
}

/// Each document is assigned a unique string id.
pub type DocId = String;

/// A postings map (doc id => positions) for a single term.
/// Records which Documents contain the term, and at which locations in the documents.
pub type PostingsMap = BTreeMap<DocId, Vec<Position>>;

/// An extension trait for iterables over `PostingsMap`s
/// that enables computing their union.
pub trait PostingsMerge {
    /// Computes the map containing the union of the maps in self
    fn merge_postings(self) -> PostingsMap;
}

impl<'a, Iter> PostingsMerge for Iter
    where Iter: IntoIterator,
          Iter::Item: Borrow<PostingsMap> {
    fn merge_postings(self) -> PostingsMap {
        let mut map = PostingsMap::new();
        for tree in self {
            for (doc_id, positions) in tree.borrow() {
                match map.entry(doc_id.clone()) {
                    Vacant(entry) => {
                        entry.insert(positions.clone());
                    }
                    Occupied(mut entry) =>
                        entry.get_mut().merge_coalesce(positions.iter().cloned()),
                }
            }
        }
        map
    }
}

/// An extension trait for slices of `PostingsMap`s
/// that enables computing their intersection.
pub trait PostingsIntersect {
    /// Computes the map containing the intersection of the the maps in self
    fn intersect_postings(self) -> PostingsMap;
}

impl<'a> PostingsIntersect for &'a [PostingsMap] {
    fn intersect_postings(self) -> PostingsMap {
        match self {
            [] => PostingsMap::new(),
            [ref posting] => posting.clone(),
            [ref posting0, rest..] => {
                self.intersection()
                    .map(|doc_id| {
                        let mut positions = posting0[doc_id].clone();
                        for posting in rest {
                            positions.merge_coalesce(posting[doc_id].iter().cloned());
                        }
                        (doc_id.clone(), positions)
                    })
                    .collect()
            }
        }
    }
}

/// An extension trait for PostingsMaps which is useful in phrase query intersections.
pub trait PositionalIntersect {
    /// Intersect two postings maps positionally, returning a PostingsMap containing postings lists
    /// whose terms are present at position X in self's postings list for document D
    /// and position X + 1 in the input's postings list for document D.
    fn intersect_positionally(&self, &Self) -> PostingsMap;
}

impl PositionalIntersect for PostingsMap {
    fn intersect_positionally(&self, other: &Self) -> PostingsMap {
        fn positional_intersect(left: &[Position], right: &[Position]) -> Vec<Position> {
            let mut intersection = vec![];
            let mut left = left.iter().cloned();
            let mut right = right.iter().cloned();
            let mut lval = left.next();
            let mut rval = right.next();
            loop {
                if let (Some(l), Some(r)) = (lval, rval) {
                    match l.position.cmp(&r.position) {
                        Ordering::Less => {
                            if l.position + 1 == r.position {
                                if !intersection.is_empty() {
                                    if intersection[intersection.len() - 1] != l {
                                        intersection.push(l);
                                    }
                                } else {
                                    intersection.push(l);
                                }
                                intersection.push(r);
                                rval = right.next();
                            }
                            lval = left.next();
                        }
                        Ordering::Greater | Ordering::Equal => rval = right.next(),
                    }
                } else {
                    return intersection;
                }
            }
        }

        let maps = &[self, other];
        maps.intersection()
            .map(|doc_id| {
                (doc_id.clone(),
                 positional_intersect(&self[doc_id], &other[doc_id]))
            })
            .collect()
    }
}

#[cfg(test)]
mod test {
    use std::iter;
    use super::super::Position;
    use super::PostingsMerge;

    #[test]
    fn test_merge() {
        let postings = [iter::once(("1".into(),
                                    vec![Position::new((0, 1), 0), Position::new((2, 3), 1)]))
                            .collect(),
                        iter::once(("1".into(),
                                    vec![Position::new((4, 5), 2), Position::new((6, 7), 3)]))
                            .collect()];
        assert_eq!(postings.iter().merge_postings(),
                   iter::once(("1".into(),
                               vec![Position::new((0, 1), 0),
                                    Position::new((2, 3), 1),
                                    Position::new((4, 5), 2),
                                    Position::new((6, 7), 3)]))
                       .collect());
    }
}
<commit_msg>Make &[Position] impl PositionalIntersect<commit_after>use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry::*;

use util::*;

/// Information about the position of a single term within a document
#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, RustcDecodable, RustcEncodable)]
pub struct Position {
    /// Pair of byte indexes into the document at the beginning (inclusive) and end (exclusive) of
    /// the term.
    pub offsets: (usize, usize),
    /// The token position of the term, i.e., the number of tokens that occur before it in the doc.
    /// For example, for the sentence "I have to go to the store",
    /// the term "to" has positions [2, 4].
    pub position: usize,
}

impl Position {
    /// Creates a new Position struct with the given offsets and position.
    pub fn new(offsets: (usize, usize), position: usize) -> Position {
        Position {
            offsets: offsets,
            position: position,
        }
    }
}

impl Merge for Position {
    fn merge(self, other: Position) -> Option<Position> {
        if self.position == other.position {
            self.offsets.merge(other.offsets).map(|offsets| Position::new(offsets, self.position))
        } else {
            None
        }
    }
}

/// Each document is assigned a unique string id.
pub type DocId = String;

/// A postings map (doc id => positions) for a single term.
/// Records which Documents contain the term, and at which locations in the documents.
pub type PostingsMap = BTreeMap<DocId, Vec<Position>>;

/// An extension trait for iterables over `PostingsMap`s
/// that enables computing their union.
pub trait PostingsMerge {
    /// Computes the map containing the union of the maps in self
    fn merge_postings(self) -> PostingsMap;
}

impl<'a, Iter> PostingsMerge for Iter
    where Iter: IntoIterator,
          Iter::Item: Borrow<PostingsMap> {
    fn merge_postings(self) -> PostingsMap {
        let mut map = PostingsMap::new();
        for tree in self {
            for (doc_id, positions) in tree.borrow() {
                match map.entry(doc_id.clone()) {
                    Vacant(entry) => {
                        entry.insert(positions.clone());
                    }
                    Occupied(mut entry) =>
                        entry.get_mut().merge_coalesce(positions.iter().cloned()),
                }
            }
        }
        map
    }
}

/// An extension trait for slices of `PostingsMap`s
/// that enables computing their intersection.
pub trait PostingsIntersect {
    /// Computes the map containing the intersection of the the maps in self
    fn intersect_postings(self) -> PostingsMap;
}

impl<'a> PostingsIntersect for &'a [PostingsMap] {
    fn intersect_postings(self) -> PostingsMap {
        match self {
            [] => PostingsMap::new(),
            [ref posting] => posting.clone(),
            [ref posting0, rest..] => {
                self.intersection()
                    .map(|doc_id| {
                        let mut positions = posting0[doc_id].clone();
                        for posting in rest {
                            positions.merge_coalesce(posting[doc_id].iter().cloned());
                        }
                        (doc_id.clone(), positions)
                    })
                    .collect()
            }
        }
    }
}

/// An extension trait for positionally intersecting two types. A positional intersection is
/// broadly defined as an intersection in which each element returned is close to an element
/// not in its own set.
pub trait PositionalIntersect {
    /// The return type of the positional intersection. Typically this will be the same type
    /// as the inputs, but for some cases it needs to be different, e.g. when the inputs are slices
    /// then the output will be an owned vec.
    type Intersection;

    /// Intersect positionally, returning an Intersection
    /// whose terms are present at position X in self's postings list for document D
    /// and position X + delta (for some delta) in the input's postings list for document D.
    fn intersect_positionally(&self, &Self) -> Self::Intersection;
}

impl PositionalIntersect for [Position] {
    type Intersection = Vec<Position>;

    fn intersect_positionally(&self, other: &[Position]) -> Vec<Position> {
        let mut intersection = vec![];
        let mut this = self.iter().cloned();
        let mut other = other.iter().cloned();
        let mut lval = this.next();
        let mut rval = other.next();
        loop {
            if let (Some(l), Some(r)) = (lval, rval) {
                match l.position.cmp(&r.position) {
                    Ordering::Less => {
                        if l.position + 1 == r.position {
                            if !intersection.is_empty() {
                                if intersection[intersection.len() - 1] != l {
                                    intersection.push(l);
                                }
                            } else {
                                intersection.push(l);
                            }
                            intersection.push(r);
                            rval = other.next();
                        }
                        lval = this.next();
                    }
                    Ordering::Greater | Ordering::Equal => rval = other.next(),
                }
            } else {
                return intersection;
            }
        }
    }
}

impl PositionalIntersect for PostingsMap {
    type Intersection = PostingsMap;
    fn intersect_positionally(&self, other: &Self) -> PostingsMap {
        let maps = &[self, other];
        maps.intersection()
            .map(|doc_id| {
                (doc_id.clone(),
                 self[doc_id].intersect_positionally(&other[doc_id]))
            })
            .collect()
    }
}

#[cfg(test)]
mod test {
    use std::iter;
    use super::super::Position;
    use super::PostingsMerge;

    #[test]
    fn test_merge() {
        let postings = [iter::once(("1".into(),
                                    vec![Position::new((0, 1), 0), Position::new((2, 3), 1)]))
                            .collect(),
                        iter::once(("1".into(),
                                    vec![Position::new((4, 5), 2), Position::new((6, 7), 3)]))
                            .collect()];
        assert_eq!(postings.iter().merge_postings(),
                   iter::once(("1".into(),
                               vec![Position::new((0, 1), 0),
                                    Position::new((2, 3), 1),
                                    Position::new((4, 5), 2),
                                    Position::new((6, 7), 3)]))
                       .collect());
    }
}
<|endoftext|>