<commit_before>#![feature(slicing_syntax)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(globs)]

extern crate gl;
extern crate glfw;

use std::os;
use std::path::Path;

use gl::types::*;
use glfw::Context;

use std::mem;
use std::ptr;

use sceneio::read_scene;
use vec4::{Vec4,dot};

// module definition order matters for macro exports!
mod colour;
mod vec4;
mod mat4;
mod scene;
mod sceneio;

mod shaders;

fn make_gl_proj_mat(width: uint, height: uint) -> [GLfloat, ..16] {
  let mut m:[GLfloat, ..16] = [0.0, ..16];
  let w = width as f32;
  let h = height as f32;
  let f = 0.1;
  let n = 0.0;

  // from NDC to world space
  // translate by -1,-1 to put origin in bottom left
  // scale to change square 2,2 to rectangle w,h
  // flip Z because NDC is left handed

  // OpenGL matrix memory layout has transposition vector as the last row like D3D
  m[0] = 2.0 / w; // 2/(r-l)
  m[12] = -1.0;   // direct construction, so -1.0 rather than -(r+l)/(r-l)

  m[5] = 2.0 / h; // 2/(t-b)
  m[13] = -1.0;   // direct construction, so -1.0 rather than -(t+b)/(t-l)

  m[10] = -2.0 / (f - n);
  m[14] = -1.0 * (f + n) / (f - n);

  m[15] = 1.0;

  m
}

fn gl_init_and_render(scene: &scene::Scene) {
  let wx = scene.image_size.val0() as uint;
  let wy = scene.image_size.val1() as uint;

  let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

  // Choose a GL profile that is compatible with OS X 10.7+
  glfw.window_hint(glfw::ContextVersion(3, 2));
  glfw.window_hint(glfw::OpenglForwardCompat(true));
  glfw.window_hint(glfw::OpenglProfile(glfw::OpenGlCoreProfile));
  glfw.window_hint(glfw::Resizable(false));

  let (window, _) = glfw.create_window(wx as u32, wy as u32, "OpenGL", glfw::Windowed)
    .expect("Failed to create GLFW window.");

  // It is essential to make the context current before calling `gl::load_with`.
  window.make_current();

  // Load the OpenGL function pointers
  gl::load_with(|s| window.get_proc_address(s));

  // Create GLSL shaders
  let vs = shaders::compile_shader(shaders::VS_SRC, gl::VERTEX_SHADER);
  let fs = shaders::compile_shader(shaders::FS_SRC, gl::FRAGMENT_SHADER);
  let program = shaders::link_program(vs, fs);
  let mut vao = 0;

  // Add a vertex at every pixel... seems to cause issues at any res other than 800x600
  // TODO try building a texture instead
  let mut vbo = 0;
  let wy2 = wy * 2;
  let vertex_data_vec: Vec<GLfloat> = Vec::from_fn(wx * wy2, |n| {
    let x = n / wy2;
    let y = n % wy2;
    let v = if y % 2 == 0 { x as f32 } else { (y / 2) as f32};
    v + 0.5
  });
  let vertex_data = vertex_data_vec.as_slice();
  println!("{}", vertex_data[wx * wy2 - 4..]);

  // Add a colour for each vertex
  let mut cbo = 0;
  let wy4 = wy2 * 2;
  let mut colour_data_vec: Vec<GLfloat> = Vec::from_fn(wx * wy4, |n| {
    match n % 4 {
      0 => (scene.background.red as f32   / 255.0) as GLfloat,
      1 => (scene.background.green as f32 / 255.0) as GLfloat,
      2 => (scene.background.blue as f32  / 255.0) as GLfloat,
      _ => 1.0
    }
  });
  let colour_data = colour_data_vec.as_mut_slice();
  println!("{}", colour_data[wx * wy4 - 8..]);

  unsafe {
    // Create Vertex Array Object
    gl::GenVertexArrays(1, &mut vao);
    gl::BindVertexArray(vao);

    // Set up vertex buffer object
    gl::GenBuffers(1, &mut vbo);
    gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
    gl::BufferData(gl::ARRAY_BUFFER,
                   (vertex_data.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                   mem::transmute(&vertex_data[0]),
                   gl::STATIC_DRAW);

    // Set up colour buffer object
    gl::GenBuffers(1, &mut cbo);
    gl::BindBuffer(gl::ARRAY_BUFFER, cbo);
    gl::BufferData(gl::ARRAY_BUFFER,
                   (colour_data.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                   mem::transmute(&colour_data[0]),
                   gl::STATIC_DRAW);

    gl::UseProgram(program);
    // Bind fragment shader
    "out_colour".with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));

    // Configure vertex buffer
    let pos_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
    gl::EnableVertexAttribArray(pos_attr as GLuint);
    gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
    gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT,
                            gl::FALSE as GLboolean, 0, ptr::null());

    // Set up the projection manually because we don't have glm or glu
    let proj = make_gl_proj_mat(wx, wy);
    let mvp_uni = "mvp".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));

    // 3rd argument is transpose. GLES does not support this
    gl::UniformMatrix4fv(mvp_uni as GLint, 1, gl::FALSE as GLboolean,
                         mem::transmute(&proj[0]));

    // Configure colour buffer
    let col_attr = "vertex_colour".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
    gl::EnableVertexAttribArray(col_attr as GLuint);
    gl::BindBuffer(gl::ARRAY_BUFFER, cbo);
    gl::VertexAttribPointer(col_attr as GLuint, 4, gl::FLOAT,
                            gl::FALSE as GLboolean, 0, ptr::null());
  }

  let mut progress = 0;
  println!("starting loop");
  while progress < colour_data.len() {
    progress = render(scene, colour_data, progress);
  }
  println!("done");

  // TODO fix crap performance by using streaming, mapping the buffer, or using framebuffers etc
  // need to somehow decouple/desync changing the pixels and pushing to GPU
  while !window.should_close() {
    glfw.poll_events();
    unsafe {

      /*if progress < colour_data.len() {
        progress = render(scene, colour_data, progress);
      }*/

      gl::BufferData(gl::ARRAY_BUFFER,
                     (colour_data.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                     mem::transmute(&colour_data[0]),
                     gl::STATIC_DRAW);

      gl::ClearColor(0.3, 0.3, 0.3, 1.0);
      gl::Clear(gl::COLOR_BUFFER_BIT);
      gl::DrawArrays(gl::POINTS, 0, vertex_data.len() as i32);
    }
    window.swap_buffers();
  }

  unsafe {
    gl::DeleteProgram(program);
    gl::DeleteShader(fs);
    gl::DeleteShader(vs);
    gl::DeleteBuffers(1, &cbo);
    gl::DeleteBuffers(1, &vbo);
    gl::DeleteVertexArrays(1, &vao);
  }
}

//fn trace(scene: &scene::Scene, ) -> Colour {
//
//}

fn intersect_unit_sphere(u: Vec4, v: Vec4) -> (bool, f64, f64) {
  let uu = dot(&u, &u);
  let uv = dot(&u, &v);
  let vv = dot(&v, &v);

  let a = vv;
  let b = 2.0 * uv;
  let c = uu - 1.0;

  let ac4 = a * c * 4.0;
  let bsq =  b * b;

  if bsq <= ac4 {
    // < no real roots; miss
    // = 1  real root ; ray is tangent to sphere; treat as miss
    return (false, 0.0, 0.0)
  }

  let p = (bsq -  ac4).sqrt();

  let t1 = if b > 0.0 { (-b - p) / (2.0 * a) } else { (-b + p) / (2.0 * a) };
  let t2 = c / (a * t1);

  (true, 0.0, 0.0)
}

// scene space => 1 ray per pixel(vertex), right handed system, but with 0,0 in the centre
// so need to translate by w/2 and h/2, don't flip Z because we should already be in an RH system
// and scale back to 2x2 plane
fn render(scene: &scene::Scene, colour_data: &mut [GLfloat], progress: uint) -> uint {
  let wx = scene.image_size.val0() as int;
  let wy = scene.image_size.val1() as int;

  let row = progress as int / (4 * wy);
  let col = (progress as int % (4 * wy)) / 4;

  let hx = (wx / 2);
  let hy = (wy / 2);

  // Let our screen in object space be -1*wx/wy,-1,1 to 1*wx/wy,1,1
  // ray start
  let px = ((row - hx) as f64 + 0.5) / hy as f64;
  let py = ((col - hy) as f64 + 0.5) / hy as f64;
  let pz = 1.0;

  // ray direction
  let dx = 0.0;
  let dy = 0.0;
  let dz = -1.0;
  let d = vector!(dx dy dz);
  let p = point!(px py pz);

  let result = intersect_unit_sphere(p, d);

  if result.val0() {
    colour_data[progress] = 1.0;
    colour_data[progress + 2] = 0.0;
  }

  progress + 4
}

fn load_scene_or_fail(filename: &Path) -> scene::Scene {
  let scene = match read_scene(filename) {
    Ok(scene) => {
      println!("\nParsed {}: {}", filename.display(), scene);
      scene
    },
    Err(desc) => {
      panic!("\nParsing {} failed\n\t {}", filename.display(), desc);
    }
  };

  for s in scene.spheres.iter() {
    mat4::matrix_print(&s.transform, "Transform");
    mat4::matrix_print(&s.inverse_t, "Inverse T");
  }

  scene
}

fn main() {
  let args = os::args();
  let print_usage = args.len() != 2u;

  if print_usage {
    println!("Usage:\n\t {} scene_file.txt", args[0]);
    return
  }

  let filename = Path::new(args[1].clone());
  let scene = load_scene_or_fail(&filename);

  gl_init_and_render(&scene);
}
<commit_msg>tidied up magic numbers for flat arrays<commit_after>#![feature(slicing_syntax)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(globs)]

extern crate gl;
extern crate glfw;

use std::os;
use std::path::Path;

use gl::types::*;
use glfw::Context;

use std::mem;
use std::ptr;

use sceneio::read_scene;
use vec4::{Vec4,dot};

// module definition order matters for macro exports!
mod colour;
mod vec4;
mod mat4;
mod scene;
mod sceneio;

mod shaders;

static COLOUR_WIDTH:uint = 4;
static VERTEX_WIDTH:uint = 2;

fn make_gl_proj_mat(width: uint, height: uint) -> [GLfloat, ..16] {
  let mut m:[GLfloat, ..16] = [0.0, ..16];
  let w = width as f32;
  let h = height as f32;
  let f = 0.1;
  let n = 0.0;

  // from NDC to world space
  // translate by -1,-1 to put origin in bottom left
  // scale to change square 2,2 to rectangle w,h
  // flip Z because NDC is left handed

  // OpenGL matrix memory layout has transposition vector as the last row like D3D
  m[0] = 2.0 / w; // 2/(r-l)
  m[12] = -1.0;   // direct construction, so -1.0 rather than -(r+l)/(r-l)

  m[5] = 2.0 / h; // 2/(t-b)
  m[13] = -1.0;   // direct construction, so -1.0 rather than -(t+b)/(t-l)

  m[10] = -2.0 / (f - n);
  m[14] = -1.0 * (f + n) / (f - n);

  m[15] = 1.0;

  m
}

fn gl_init_and_render(scene: &scene::Scene) {
  let wx = scene.image_size.val0() as uint;
  let wy = scene.image_size.val1() as uint;

  let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

  // Choose a GL profile that is compatible with OS X 10.7+
  glfw.window_hint(glfw::ContextVersion(3, 2));
  glfw.window_hint(glfw::OpenglForwardCompat(true));
  glfw.window_hint(glfw::OpenglProfile(glfw::OpenGlCoreProfile));
  glfw.window_hint(glfw::Resizable(false));

  let (window, _) = glfw.create_window(wx as u32, wy as u32, "OpenGL", glfw::Windowed)
    .expect("Failed to create GLFW window.");

  // It is essential to make the context current before calling `gl::load_with`.
  window.make_current();

  // Load the OpenGL function pointers
  gl::load_with(|s| window.get_proc_address(s));

  // Create GLSL shaders
  let vs = shaders::compile_shader(shaders::VS_SRC, gl::VERTEX_SHADER);
  let fs = shaders::compile_shader(shaders::FS_SRC, gl::FRAGMENT_SHADER);
  let program = shaders::link_program(vs, fs);
  let mut vao = 0;

  // Add a vertex at every pixel... seems to cause issues at any res other than 800x600
  // TODO try building a texture instead
  let mut vbo = 0;
  let wy2 = wy * VERTEX_WIDTH;
  let vertex_data_vec: Vec<GLfloat> = Vec::from_fn(wx * wy2, |n| {
    let x = n / wy2;
    let y = n % wy2;
    let v = if y % 2 == 0 { x as f32 } else { (y / VERTEX_WIDTH) as f32};
    v + 0.5
  });
  let vertex_data = vertex_data_vec.as_slice();
  println!("{}", vertex_data[wx * wy2 - 4..]);

  // Add a colour for each vertex
  let mut cbo = 0;
  let wy4 = wy * COLOUR_WIDTH;
  let mut colour_data_vec: Vec<GLfloat> = Vec::from_fn(wx * wy4, |n| {
    match n % COLOUR_WIDTH {
      0 => (scene.background.red as f32   / 255.0) as GLfloat,
      1 => (scene.background.green as f32 / 255.0) as GLfloat,
      2 => (scene.background.blue as f32  / 255.0) as GLfloat,
      _ => 1.0
    }
  });
  let colour_data = colour_data_vec.as_mut_slice();

  unsafe {
    // Create Vertex Array Object
    gl::GenVertexArrays(1, &mut vao);
    gl::BindVertexArray(vao);

    // Set up vertex buffer object
    gl::GenBuffers(1, &mut vbo);
    gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
    gl::BufferData(gl::ARRAY_BUFFER,
                   (vertex_data.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                   mem::transmute(&vertex_data[0]),
                   gl::STATIC_DRAW);

    // Set up colour buffer object
    gl::GenBuffers(1, &mut cbo);
    gl::BindBuffer(gl::ARRAY_BUFFER, cbo);
    gl::BufferData(gl::ARRAY_BUFFER,
                   (colour_data.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                   mem::transmute(&colour_data[0]),
                   gl::STATIC_DRAW);

    gl::UseProgram(program);
    // Bind fragment shader
    "out_colour".with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));

    // Configure vertex buffer
    let pos_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
    gl::EnableVertexAttribArray(pos_attr as GLuint);
    gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
    gl::VertexAttribPointer(pos_attr as GLuint, VERTEX_WIDTH as i32, gl::FLOAT,
                            gl::FALSE as GLboolean, 0, ptr::null());

    // Set up the projection manually because we don't have glm or glu
    let proj = make_gl_proj_mat(wx, wy);
    let mvp_uni = "mvp".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));

    // 3rd argument is transpose. GLES does not support this
    gl::UniformMatrix4fv(mvp_uni as GLint, 1, gl::FALSE as GLboolean,
                         mem::transmute(&proj[0]));

    // Configure colour buffer
    let col_attr = "vertex_colour".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
    gl::EnableVertexAttribArray(col_attr as GLuint);
    gl::BindBuffer(gl::ARRAY_BUFFER, cbo);
    gl::VertexAttribPointer(col_attr as GLuint, COLOUR_WIDTH as i32, gl::FLOAT,
                            gl::FALSE as GLboolean, 0, ptr::null());
  }

  let mut progress = 0;
  println!("starting loop");
  while progress < colour_data.len() {
    progress = render(scene, colour_data, progress);
  }
  println!("done");

  // TODO fix crap performance by using streaming, mapping the buffer, or using framebuffers etc
  // need to somehow decouple/desync changing the pixels and pushing to GPU
  while !window.should_close() {
    glfw.poll_events();
    unsafe {

      /*if progress < colour_data.len() {
        progress = render(scene, colour_data, progress);
      }*/

      gl::BufferData(gl::ARRAY_BUFFER,
                     (colour_data.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                     mem::transmute(&colour_data[0]),
                     gl::STATIC_DRAW);

      gl::ClearColor(0.3, 0.3, 0.3, 1.0);
      gl::Clear(gl::COLOR_BUFFER_BIT);
      gl::DrawArrays(gl::POINTS, 0, vertex_data.len() as i32);
    }
    window.swap_buffers();
  }

  unsafe {
    gl::DeleteProgram(program);
    gl::DeleteShader(fs);
    gl::DeleteShader(vs);
    gl::DeleteBuffers(1, &cbo);
    gl::DeleteBuffers(1, &vbo);
    gl::DeleteVertexArrays(1, &vao);
  }
}

//fn trace(scene: &scene::Scene, ) -> Colour {
//
//}

fn intersect_unit_sphere(u: Vec4, v: Vec4) -> (bool, f64, f64) {
  let uu = dot(&u, &u);
  let uv = dot(&u, &v);
  let vv = dot(&v, &v);

  let a = vv;
  let b = 2.0 * uv;
  let c = uu - 1.0;

  let ac4 = a * c * 4.0;
  let bsq =  b * b;

  if bsq <= ac4 {
    // < no real roots; miss
    // = 1  real root ; ray is tangent to sphere; treat as miss
    return (false, 0.0, 0.0)
  }

  let p = (bsq -  ac4).sqrt();

  let t1 = if b > 0.0 { (-b - p) / (2.0 * a) } else { (-b + p) / (2.0 * a) };
  let t2 = c / (a * t1);

  (true, t1, t2)
}

// scene space => 1 ray per pixel(vertex), right handed system, but with 0,0 in the centre
// so need to translate by w/2 and h/2, don't flip Z because we should already be in an RH system
// and scale back to 2x2 plane
fn render(scene: &scene::Scene, colour_data: &mut [GLfloat], progress: uint) -> uint {
  let wx = scene.image_size.val0() as int;
  let wy = scene.image_size.val1() as int;

  let row = progress as int / (COLOUR_WIDTH as int * wy);
  let col = (progress as int % (COLOUR_WIDTH as int * wy)) / COLOUR_WIDTH as int;

  let hx = wx / 2;
  let hy = wy / 2;

  // Let our screen in object space be -1*wx/wy,-1,1 to 1*wx/wy,1,1
  // ray start
  let px = ((row - hx) as f64 + 0.5) / hy as f64;
  let py = ((col - hy) as f64 + 0.5) / hy as f64;
  let pz = 1.0;

  // ray direction
  let dx = 0.0;
  let dy = 0.0;
  let dz = -1.0;
  let d = vector!(dx dy dz);
  let p = point!(px py pz);

  let result = intersect_unit_sphere(p, d);

  if result.val0() {
    colour_data[progress] = 1.0;
    colour_data[progress + 2] = 0.0;
  }

  progress + 4
}

fn load_scene_or_fail(filename: &Path) -> scene::Scene {
  let scene = match read_scene(filename) {
    Ok(scene) => {
      println!("\nParsed {}: {}", filename.display(), scene);
      scene
    },
    Err(desc) => {
      panic!("\nParsing {} failed\n\t {}", filename.display(), desc);
    }
  };

  for s in scene.spheres.iter() {
    mat4::matrix_print(&s.transform, "Transform");
    mat4::matrix_print(&s.inverse_t, "Inverse T");
  }

  scene
}

fn main() {
  let args = os::args();
  let print_usage = args.len() != 2u;

  if print_usage {
    println!("Usage:\n\t {} scene_file.txt", args[0]);
    return
  }

  let filename = Path::new(args[1].clone());
  let scene = load_scene_or_fail(&filename);

  gl_init_and_render(&scene);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate libc;
extern crate x11;

use std::ffi::CString;
use std::os::raw::{c_int, c_char};
use std::ptr;
use std::convert::From;

use x11::xlib;
use x11::xlib::{BadAccess, Display, Window, XErrorEvent, XOpenDisplay, XDefaultRootWindow,
                XSetErrorHandler};

#[derive(Debug)]
struct Win {
    display: *mut Display,
    xwindow: Window,
}

impl Win {
    // Does this really need to be &mut self? It feels light it ought to be as it is actually
    // modifying the underlying window, even if we're not actually modifying the value as far as
    // Rust is concerned.
    fn position(&mut self, x: i32, y: i32, width: i32, height: i32) -> Result<(), ()> {
        let mut changes = xlib::XWindowChanges {
            x: x,
            y: y,
            width: width,
            height: height,

            // Ignored:
            border_width: 0,
            sibling: 0,
            stack_mode: 0,
        };
        let flags = (xlib::CWX | xlib::CWY | xlib::CWWidth | xlib::CWHeight) as u32;

        unsafe {
            xlib::XConfigureWindow(self.display, self.xwindow, flags, &mut changes);
        };

        Ok(())
    }

    fn grab_keys(&mut self, keys: &[Key]) {
        for key in keys.iter() {
            unsafe {
                let keycode = xlib::XKeysymToKeycode(self.display, key.keysym as u64) as i32;
                xlib::XGrabKey(self.display,
                               keycode,
                               key.mod_mask,
                               self.xwindow,
                               0,
                               xlib::GrabModeAsync,
                               xlib::GrabModeAsync);
            }
        }
    }
}

trait Layout {
    fn layout(&self, width: i32, height: i32, stack: &mut [Win]) -> Result<(), ()>;
}

struct TiledLayout;

impl Layout for TiledLayout {
    fn layout(&self, width: i32, height: i32, stack: &mut [Win]) -> Result<(), ()> {
        if stack.len() == 0 {
            return Ok(());
        }

        let tile_height = height / stack.len() as i32;

        for (i, window) in stack.iter_mut().enumerate() {
            window.position(0, i as i32 * tile_height, width, tile_height)?;
        }

        Ok(())
    }
}

// Error handler used during setup, which simply checks for the BadAccess error
// which indicates that another WM is already running.
unsafe extern "C" fn error_handler_init(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    if (*err).error_code == BadAccess {
        panic!("Another WM is already running");
    }
    0
}

// Actual error handler used during normal operation.
unsafe extern "C" fn error_handler(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    let buffer_size: usize = 1024;
    let mut buffer = Vec::<u8>::with_capacity(buffer_size);
    // XXX the docs say this returns the error text in the 'current locale'. We're
    // being extremely naughty and assuming this is UTF-8.
    // We're also assuming the return value of XGetErrorText is the actual length
    // of the string.
    let len: c_int = xlib::XGetErrorText(disp,
                                         (*err).error_code as i32,
                                         buffer.as_mut_ptr() as *mut c_char,
                                         buffer_size as i32);
    buffer.truncate(len as usize);
    let error_text = CString::new(buffer).unwrap().into_string().unwrap();
    error!("Received X error: request={}, error_code=({}, {}), resource_id={}",
           (*err).request_code,
           (*err).error_code,
           error_text,
           (*err).resourceid);
    0
}

fn xevent_to_str(event: &xlib::XEvent) -> &str {
    match event.get_type() {
        2 => "KeyPress",
        3 => "KeyRelease",
        4 => "ButtonPress",
        5 => "ButtonRelease",
        6 => "MotionNotify",
        7 => "EnterNotify",
        8 => "LeaveNotify",
        9 => "FocusIn",
        10 => "FocusOut",
        11 => "KeymapNotify",
        12 => "Expose",
        13 => "GraphicsExpose",
        14 => "NoExpose",
        15 => "VisibilityNotify",
        16 => "CreateNotify",
        17 => "DestroyNotify",
        18 => "UnmapNotify",
        19 => "MapNotify",
        20 => "MapRequest",
        21 => "ReparentNotify",
        22 => "ConfigureNotify",
        23 => "ConfigureRequest",
        24 => "GravityNotify",
        25 => "ResizeRequest",
        26 => "CirculateNotify",
        27 => "CirculateRequest",
        28 => "PropertyNotify",
        29 => "SelectionClear",
        30 => "SelectionRequest",
        31 => "SelectionNotify",
        32 => "ColormapNotify",
        33 => "ClientMessage",
        34 => "MappingNotify",
        35 => "GenericEvent",
        36 => "LASTEvent",
        _ => {
            error!("Unknown XEvent type: {}", event.get_type());
            "Unknown"
        }
    }
}

fn key_handler() {
    info!("Key press!");
}

struct Key {
    mod_mask: std::os::raw::c_uint,
    keysym: std::os::raw::c_uint,
    handler: Box<Fn()>,
}

impl std::fmt::Display for Key {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f,
               "Key {{ mod_mask: {}, keysym: {} }}",
               self.mod_mask,
               self.keysym)
    }
}

fn main() {
    env_logger::init().unwrap();

    let keys = vec![Key {
                        mod_mask: xlib::Mod4Mask,
                        keysym: x11::keysym::XK_T,
                        handler: Box::new(key_handler),
                    }];

    unsafe {
        let disp: *mut Display = XOpenDisplay(ptr::null_mut());
        assert!(!disp.is_null());
        let root: Window = XDefaultRootWindow(disp);
        assert!(root != 0);

        XSetErrorHandler(Some(error_handler_init));
        xlib::XSelectInput(disp,
                           root,
                           xlib::SubstructureNotifyMask | xlib::SubstructureRedirectMask);
        xlib::XSync(disp, 0);

        // If we get this far, then our panicing error handler didn't complain!
        info!("We are now the WM");

        XSetErrorHandler(Some(error_handler));

        let mut attrs: xlib::XWindowAttributes = std::mem::zeroed();
        xlib::XGetWindowAttributes(disp, root, &mut attrs);
        info!("Root window has geometry: {}x{}", attrs.width, attrs.height);

        let mut stack: Vec<Win> = Vec::new();

        let layout = TiledLayout {};

        loop {
            info!("Getting event...");
            let mut event = xlib::XEvent { pad: [0; 24] };
            xlib::XNextEvent(disp, &mut event);
            info!("Received event: {}", xevent_to_str(&event));

            match event.get_type() {
                xlib::MapRequest => {
                    let event = xlib::XMapRequestEvent::from(event);
                    let mut window = Win {
                        display: disp,
                        xwindow: event.window,
                    };
                    window.grab_keys(&keys);

                    stack.push(window);

                    layout.layout(attrs.width, attrs.height, stack.as_mut());

                    // TODO: Make this into a method on Win?
                    xlib::XMapWindow(disp, event.window);

                }

                xlib::DestroyNotify => {
                    let event = xlib::XDestroyWindowEvent::from(event);

                    stack.iter().position(|ref w| w.xwindow == event.window).map(|index| stack.remove(index));

                    layout.layout(attrs.width, attrs.height, stack.as_mut());
                }

                xlib::KeyPress => {
                    let event = xlib::XKeyEvent::from(event);

                    for key in keys.iter() {
                        let keycode = xlib::XKeysymToKeycode(disp, key.keysym as u64) as u32;
                        debug!("KeyPress: state={}, keycode={}", event.state, event.keycode);

                        // TODO: Allow extra mod keys to be pressed at the same time. (Add test!)
                        if (event.state & key.mod_mask != 0) && event.keycode == keycode {
                            info!("KeyPress matches key: {}", key);
                            (key.handler)();
                            break;
                        }
                    }

                }

                _ => {}
            }

            println!("Stack: {:?}", stack);
        }

    };
    println!("Hello, world!");
}
<commit_msg>Track focus (poorly!)<commit_after>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate libc;
extern crate x11;

use std::ffi::CString;
use std::os::raw::{c_int, c_char};
use std::ptr;
use std::convert::From;

use x11::xlib;
use x11::xlib::{BadAccess, Display, Window, XErrorEvent, XOpenDisplay, XDefaultRootWindow,
                XSetErrorHandler};

#[derive(Debug)]
struct Win {
    display: *mut Display,
    xwindow: Window,
}

impl Win {
    // Does this really need to be &mut self? It feels light it ought to be as it is actually
    // modifying the underlying window, even if we're not actually modifying the value as far as
    // Rust is concerned.
    fn position(&mut self, x: i32, y: i32, width: i32, height: i32) -> Result<(), ()> {
        let mut changes = xlib::XWindowChanges {
            x: x,
            y: y,
            width: width,
            height: height,

            // Ignored:
            border_width: 0,
            sibling: 0,
            stack_mode: 0,
        };
        let flags = (xlib::CWX | xlib::CWY | xlib::CWWidth | xlib::CWHeight) as u32;

        unsafe {
            xlib::XConfigureWindow(self.display, self.xwindow, flags, &mut changes);
        };

        Ok(())
    }

    fn grab_keys(&mut self, keys: &[Key]) {
        for key in keys.iter() {
            unsafe {
                let keycode = xlib::XKeysymToKeycode(self.display, key.keysym as u64) as i32;
                xlib::XGrabKey(self.display,
                               keycode,
                               key.mod_mask,
                               self.xwindow,
                               0,
                               xlib::GrabModeAsync,
                               xlib::GrabModeAsync);
            }
        }
    }
}

trait Layout {
    fn layout(&self, width: i32, height: i32, stack: &mut [Win]) -> Result<(), ()>;
}

struct TiledLayout;

impl Layout for TiledLayout {
    fn layout(&self, width: i32, height: i32, stack: &mut [Win]) -> Result<(), ()> {
        if stack.len() == 0 {
            return Ok(());
        }

        let tile_height = height / stack.len() as i32;

        for (i, window) in stack.iter_mut().enumerate() {
            window.position(0, i as i32 * tile_height, width, tile_height)?;
        }

        Ok(())
    }
}

// Error handler used during setup, which simply checks for the BadAccess error
// which indicates that another WM is already running.
unsafe extern "C" fn error_handler_init(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    if (*err).error_code == BadAccess {
        panic!("Another WM is already running");
    }
    0
}

// Actual error handler used during normal operation.
unsafe extern "C" fn error_handler(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    let buffer_size: usize = 1024;
    let mut buffer = Vec::<u8>::with_capacity(buffer_size);
    // XXX the docs say this returns the error text in the 'current locale'. We're
    // being extremely naughty and assuming this is UTF-8.
    // We're also assuming the return value of XGetErrorText is the actual length
    // of the string.
    let len: c_int = xlib::XGetErrorText(disp,
                                         (*err).error_code as i32,
                                         buffer.as_mut_ptr() as *mut c_char,
                                         buffer_size as i32);
    buffer.truncate(len as usize);
    let error_text = CString::new(buffer).unwrap().into_string().unwrap();
    error!("Received X error: request={}, error_code=({}, {}), resource_id={}",
           (*err).request_code,
           (*err).error_code,
           error_text,
           (*err).resourceid);
    0
}

fn xevent_to_str(event: &xlib::XEvent) -> &str {
    match event.get_type() {
        2 => "KeyPress",
        3 => "KeyRelease",
        4 => "ButtonPress",
        5 => "ButtonRelease",
        6 => "MotionNotify",
        7 => "EnterNotify",
        8 => "LeaveNotify",
        9 => "FocusIn",
        10 => "FocusOut",
        11 => "KeymapNotify",
        12 => "Expose",
        13 => "GraphicsExpose",
        14 => "NoExpose",
        15 => "VisibilityNotify",
        16 => "CreateNotify",
        17 => "DestroyNotify",
        18 => "UnmapNotify",
        19 => "MapNotify",
        20 => "MapRequest",
        21 => "ReparentNotify",
        22 => "ConfigureNotify",
        23 => "ConfigureRequest",
        24 => "GravityNotify",
        25 => "ResizeRequest",
        26 => "CirculateNotify",
        27 => "CirculateRequest",
        28 => "PropertyNotify",
        29 => "SelectionClear",
        30 => "SelectionRequest",
        31 => "SelectionNotify",
        32 => "ColormapNotify",
        33 => "ClientMessage",
        34 => "MappingNotify",
        35 => "GenericEvent",
        36 => "LASTEvent",
        _ => {
            error!("Unknown XEvent type: {}", event.get_type());
            "Unknown"
        }
    }
}

fn key_handler() {
    info!("Key press!");
}

struct Key {
    mod_mask: std::os::raw::c_uint,
    keysym: std::os::raw::c_uint,
    handler: Box<Fn()>,
}

impl std::fmt::Display for Key {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f,
               "Key {{ mod_mask: {}, keysym: {} }}",
               self.mod_mask,
               self.keysym)
    }
}

fn main() {
    env_logger::init().unwrap();

    let keys = vec![Key {
                        mod_mask: xlib::Mod4Mask,
                        keysym: x11::keysym::XK_T,
                        handler: Box::new(key_handler),
                    }];

    unsafe {
        let disp: *mut Display = XOpenDisplay(ptr::null_mut());
        assert!(!disp.is_null());
        let root: Window = XDefaultRootWindow(disp);
        assert!(root != 0);

        XSetErrorHandler(Some(error_handler_init));
        xlib::XSelectInput(disp,
                           root,
                           xlib::SubstructureNotifyMask | xlib::SubstructureRedirectMask);
        xlib::XSync(disp, 0);

        // If we get this far, then our panicing error handler didn't complain!
        info!("We are now the WM");

        XSetErrorHandler(Some(error_handler));

        let mut attrs: xlib::XWindowAttributes = std::mem::zeroed();
        xlib::XGetWindowAttributes(disp, root, &mut attrs);
        info!("Root window has geometry: {}x{}", attrs.width, attrs.height);

        let mut stack: Vec<Win> = Vec::new();

        // Focus is an index into the stack. We could do better and use the borrow checker to ensure
        // that it doesn't point at the wrong data when an item is added/removed from the stack.
        let mut focus: Option<usize> = None;

        let layout = TiledLayout {};

        loop {
            info!("Getting event...");
            let mut event = xlib::XEvent { pad: [0; 24] };
            xlib::XNextEvent(disp, &mut event);
            info!("Received event: {}", xevent_to_str(&event));

            match event.get_type() {
                xlib::MapRequest => {
                    let event = xlib::XMapRequestEvent::from(event);
                    let mut window = Win {
                        display: disp,
                        xwindow: event.window,
                    };
                    window.grab_keys(&keys);

                    xlib::XSelectInput(disp, window.xwindow, xlib::EnterWindowMask);

                    stack.push(window);

                    layout.layout(attrs.width, attrs.height, stack.as_mut());

                    // TODO: Make this into a method on Win?
                    xlib::XMapWindow(disp, event.window);

                }

                xlib::DestroyNotify => {
                    let event = xlib::XDestroyWindowEvent::from(event);

                    stack.iter().position(|ref w| w.xwindow == event.window).map(|index| stack.remove(index));

                    layout.layout(attrs.width, attrs.height, stack.as_mut());
                }

                xlib::KeyPress => {
                    let event = xlib::XKeyEvent::from(event);

                    for key in keys.iter() {
                        let keycode = xlib::XKeysymToKeycode(disp, key.keysym as u64) as u32;
                        debug!("KeyPress: state={}, keycode={}", event.state, event.keycode);

                        // TODO: Allow extra mod keys to be pressed at the same time. (Add test!)
                        if (event.state & key.mod_mask != 0) && event.keycode == keycode {
                            info!("KeyPress matches key: {}", key);
                            (key.handler)();
                            break;
                        }
                    }

                }

                xlib::EnterNotify => {
                    let event = xlib::XEnterWindowEvent::from(event);

                    focus = stack.iter().position(|ref w| w.xwindow == event.window);
                    debug!("EnterNotify: {}", focus);
                }

                _ => {}
            }

            println!("Stack: {:?}", stack);
        }

    };
    println!("Hello, world!");
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate time;
extern crate regex;
extern crate rustc_serialize;

use getopts::Options;

use std::env;
use std::io::Write;
use std::fs;

#[macro_use]
mod utils;
mod lib;

const VERSION: &'static str = "0.1.1";

fn print_version() {
	println!("Using version {}", VERSION);
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
	let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
	opts.optflag("v", "version", "print the version and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
	if matches.opt_present("v") {
		print_version();
		return;
	}
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    {
        let start = time::now();
        lib::preprocess(&input).ok();
        printerrln!("It took {} milliseconds for the preprocess to run", (time::now() - start).num_milliseconds()).unwrap();
    }
    {
        let start = time::now();
        lib::analyse("tmp", output).ok();
        fs::remove_file("tmp").ok();
        printerrln!("It took {} milliseconds for the analysis to run", (time::now() - start).num_milliseconds()).unwrap();
    }
}
<commit_msg>Added licence<commit_after>extern crate getopts;
extern crate time;
extern crate regex;
extern crate rustc_serialize;

use getopts::Options;

use std::env;
use std::io::Write;
use std::fs;

#[macro_use]
mod utils;
mod lib;

const VERSION: &'static str = "0.1.1";

fn print_version() {
	println!("Using version {}", VERSION);
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
	let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "FILE");
    opts.optflag("h", "help", "print this help menu");
	opts.optflag("v", "version", "print the version and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
	if matches.opt_present("v") {
		print_version();
		return;
	}
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    {
        let start = time::now();
        lib::preprocess(&input).ok();
        printerrln!("It took {} milliseconds for the preprocess to run", (time::now() - start).num_milliseconds()).unwrap();
    }
    {
        let start = time::now();
        lib::analyse("tmp", output).ok();
        fs::remove_file("tmp").ok();
        printerrln!("It took {} milliseconds for the analysis to run", (time::now() - start).num_milliseconds()).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::error::Error;
use std::fs::File;
use std::path::Path;
use std::io::Read;
use std::process;

#[derive(PartialEq)]
enum BfOpKind {
    InvalidOp,
    IncPtr,
    DecPtr,
    IncData,
    DecData,
    ReadStdin,
    WriteStdout,
    LoopSetToZero,
    LoopMovePtr,
    LoopMoveData,
    JumpIfDataZero,
    JumpIfDataNotZero,
}

#[allow(dead_code)]
fn bf_op_kind_name(kind: BfOpKind) -> char {
    match kind {
        BfOpKind::IncPtr => return '>',
        BfOpKind::DecPtr => return '<',
        BfOpKind::IncData => return '+',
        BfOpKind::DecData => return '-',
        BfOpKind::ReadStdin => return ',',
        BfOpKind::WriteStdout => return '.',
        BfOpKind::JumpIfDataZero => return '[',
        BfOpKind::JumpIfDataNotZero => return ']',
        BfOpKind::LoopSetToZero => return 's',
        BfOpKind::LoopMovePtr => return 'm',
        BfOpKind::LoopMoveData => return 'd',
        BfOpKind::InvalidOp => return 'x',
    }
}

struct BfOp {
    kind: BfOpKind,
    argument: i64,
}

fn optimize_loop(ops: &Vec<BfOp>, loop_start: i64) -> Vec<BfOp> {
    let mut new_ops = Vec::new();

    if ops.len() as i64 - loop_start == 2 {
        let repeated_op = &ops[loop_start as usize + 1];
        if repeated_op.kind == BfOpKind::IncData ||
            repeated_op.kind == BfOpKind::DecData {
            new_ops.push(BfOp {kind: BfOpKind::LoopSetToZero, argument: 0});
        } else if repeated_op.kind == BfOpKind::IncPtr ||
            repeated_op.kind == BfOpKind::DecPtr {
            if repeated_op.kind == BfOpKind::IncPtr {
                new_ops.push(
                            BfOp { kind: BfOpKind::LoopMovePtr,
                                argument: repeated_op.argument});
            } else {
                new_ops.push(
                            BfOp { kind: BfOpKind::LoopMovePtr,
                                argument: -repeated_op.argument});
            }
        } else if ops.len() as i64 - loop_start == 5 {
            if ops[loop_start as usize + 1].kind == BfOpKind::DecData &&
                ops[loop_start as usize + 3].kind == BfOpKind::IncData &&
                ops[loop_start as usize + 1].argument == 1 &&
                ops[loop_start as usize + 3].argument == 1 {

                if ops[loop_start as usize + 2].kind == BfOpKind::IncPtr &&
                    ops[loop_start as usize + 4].kind == BfOpKind::DecPtr &&
                    ops[loop_start as usize + 2].argument ==
                    ops[loop_start as usize + 4].argument {
                    new_ops.push(
                        BfOp { kind: BfOpKind::LoopMoveData,
                            argument: ops[loop_start as usize + 4].argument});
                } else if ops[loop_start as usize + 2].kind == BfOpKind::DecPtr &&
                    ops[loop_start as usize + 4].kind == BfOpKind::IncPtr &&
                    ops[loop_start as usize + 2].argument ==
                    ops[loop_start as usize + 4].argument {
                    new_ops.push(
                        BfOp { kind: BfOpKind::LoopMoveData,
                            argument: -ops[loop_start as usize + 2].argument});
                }
            }
        }
    }

    new_ops
}

fn translate_code(code: &Vec<u8>) -> Vec<BfOp> {
    let mut ptr = 0;
    let code_size = code.len();
    let mut ops = Vec::new();
    let mut open_bracket_stack = Vec::new();

    while ptr < code_size {
        let instruction = code[ptr];
        if instruction == '[' as u8 {
            open_bracket_stack.push(ops.len() as i64);
            ops.push(BfOp{ kind: BfOpKind::JumpIfDataZero,
                            argument: 0 });
            ptr += 1;
        } else if instruction == ']' as u8 {
            if open_bracket_stack.is_empty() {
                panic!("Unmatch closing ']': ptr={}", ptr);
            }
            let open_bracket_offset =
                    open_bracket_stack[open_bracket_stack.len()-1];
            open_bracket_stack.pop();

            let mut optimized_loop = optimize_loop(&ops, open_bracket_offset);

            if optimized_loop.is_empty() {
                ops[open_bracket_offset as usize].argument = ops.len() as i64;
                ops.push(BfOp{ kind: BfOpKind::JumpIfDataNotZero,
                                argument: open_bracket_offset});
            } else {
                let ops_len: usize  = ops.len() - 1;
                ops.drain(open_bracket_offset as usize ..ops_len);
                ops.append(&mut optimized_loop);
            }
            ptr += 1;
        } else {
            let start = ptr;
            ptr += 1;
            while ptr < code_size && code[ptr] == instruction {
                ptr += 1;
            }

            let num_repeats = ptr - start;

            let mut kind = BfOpKind::InvalidOp;
            match instruction as char {
                '>' => kind = BfOpKind::IncPtr,
                '<' => kind = BfOpKind::DecPtr,
                '+' => kind = BfOpKind::IncData,
                '-' => kind = BfOpKind::DecData,
                ',' => kind = BfOpKind::ReadStdin,
                '.' => kind = BfOpKind::WriteStdout,
                _ => {}
            }

            ops.push(BfOp{ kind: kind, argument: num_repeats as i64 });
        }
    }

    ops
}

fn input() -> u8 {
    let c = match std::io::stdin().bytes().next()
                .and_then(|result| result.ok()).map(|byte| byte as u8) {
        None => process::exit(0),
        Some(c) => c,
    };
    c
}

fn output(c: u8) {
    print!("{}", c as char);
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let path = Path::new(&args[1]);
    let display = path.display();

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

    let mut s = String::new();
    match file.read_to_string(&mut s) {
        Err(why) => panic!("Could not read {}: {}",
                           display, Error::description(&why)),
        Ok(_) => {},
    }

    let mut code_ptr: usize = 0;
    let mut data_ptr: usize = 0;
    let code = s.into_bytes();
    let mut data = [0 as u8; 30000];
    let ops = translate_code(&code);

    while code_ptr < ops.len() {
        let op = &ops[code_ptr];
        let kind = &op.kind;

        match kind {
            &BfOpKind::IncPtr => data_ptr += op.argument as usize,
            &BfOpKind::DecPtr => data_ptr -= op.argument as usize,
            &BfOpKind::IncData => data[data_ptr] += op.argument as u8,
            &BfOpKind::DecData => data[data_ptr] -= op.argument as u8,
            &BfOpKind::ReadStdin => {
                for _ in 0..op.argument {
                    data[data_ptr] = input();
                }
            },
            &BfOpKind::WriteStdout => {
                for _ in 0..op.argument {
                    output(data[data_ptr]);
                }
            },
            &BfOpKind::LoopSetToZero => data[data_ptr] = 0,
            &BfOpKind::LoopMovePtr => {
                while data[data_ptr] != 0 {
                    data_ptr += op.argument as usize;
                }
            },
            &BfOpKind::LoopMoveData => {
                if data[data_ptr] != 0 {
                    let move_to_ptr = data_ptr + op.argument as usize;
                    data[move_to_ptr] += data[data_ptr];
                    data[data_ptr] = 0;
                }
            },
            &BfOpKind::JumpIfDataZero => {
                if data[data_ptr] == 0 {
                    code_ptr = op.argument as usize;
                }
            },
            &BfOpKind::JumpIfDataNotZero => {
                if data[data_ptr] != 0 {
                    code_ptr = op.argument as usize;
                }
            },
            _ => {},
        }
        code_ptr += 1;
    }
}
<commit_msg>Fix optimize_loop<commit_after>use std::env;
use std::error::Error;
use std::fs::File;
use std::path::Path;
use std::io::Read;
use std::process;

#[derive(PartialEq)]
enum BfOpKind {
    InvalidOp,
    IncPtr,
    DecPtr,
    IncData,
    DecData,
    ReadStdin,
    WriteStdout,
    LoopSetToZero,
    LoopMovePtr,
    LoopMoveData,
    JumpIfDataZero,
    JumpIfDataNotZero,
}

#[allow(dead_code)]
fn bf_op_kind_name(kind: BfOpKind) -> char {
    match kind {
        BfOpKind::IncPtr => return '>',
        BfOpKind::DecPtr => return '<',
        BfOpKind::IncData => return '+',
        BfOpKind::DecData => return '-',
        BfOpKind::ReadStdin => return ',',
        BfOpKind::WriteStdout => return '.',
        BfOpKind::JumpIfDataZero => return '[',
        BfOpKind::JumpIfDataNotZero => return ']',
        BfOpKind::LoopSetToZero => return 's',
        BfOpKind::LoopMovePtr => return 'm',
        BfOpKind::LoopMoveData => return 'd',
        BfOpKind::InvalidOp => return 'x',
    }
}

struct BfOp {
    kind: BfOpKind,
    argument: i64,
}

fn optimize_loop(ops: &Vec<BfOp>, loop_start: i64) -> Vec<BfOp> {
    let mut new_ops = Vec::new();

    if ops.len() as i64 - loop_start == 2 {
        let repeated_op = &ops[loop_start as usize + 1];
        if repeated_op.kind == BfOpKind::IncData ||
            repeated_op.kind == BfOpKind::DecData {
            new_ops.push(BfOp {kind: BfOpKind::LoopSetToZero, argument: 0});
        } else if repeated_op.kind == BfOpKind::IncPtr {
            new_ops.push(
                        BfOp { kind: BfOpKind::LoopMovePtr,
                            argument: repeated_op.argument});
        } else if repeated_op.kind == BfOpKind::DecPtr {
            new_ops.push(
                        BfOp { kind: BfOpKind::LoopMovePtr,
                            argument: -repeated_op.argument});
        } else if ops.len() as i64 - loop_start == 5 {
            if ops[loop_start as usize + 1].kind == BfOpKind::DecData &&
                ops[loop_start as usize + 3].kind == BfOpKind::IncData &&
                ops[loop_start as usize + 1].argument == 1 &&
                ops[loop_start as usize + 3].argument == 1 {
                if ops[loop_start as usize + 2].kind == BfOpKind::IncPtr &&
                    ops[loop_start as usize + 4].kind == BfOpKind::DecPtr &&
                    ops[loop_start as usize + 2].argument ==
                    ops[loop_start as usize + 4].argument {
                    new_ops.push(
                        BfOp { kind: BfOpKind::LoopMoveData,
                            argument: ops[loop_start as usize + 2].argument});
                } else if ops[loop_start as usize + 2].kind == BfOpKind::DecPtr &&
                    ops[loop_start as usize + 4].kind == BfOpKind::IncPtr &&
                    ops[loop_start as usize + 2].argument ==
                    ops[loop_start as usize + 4].argument {
                    new_ops.push(
                        BfOp { kind: BfOpKind::LoopMoveData,
                            argument: -ops[loop_start as usize + 2].argument});
                }
            }
        }
    }

    new_ops
}

fn translate_code(code: &Vec<u8>) -> Vec<BfOp> {
    let mut ptr = 0;
    let code_size = code.len();
    let mut ops = Vec::new();
    let mut open_bracket_stack = Vec::new();

    while ptr < code_size {
        let instruction = code[ptr];
        if instruction == '[' as u8 {
            open_bracket_stack.push(ops.len() as i64);
            ops.push(BfOp{ kind: BfOpKind::JumpIfDataZero,
                            argument: 0 });
            ptr += 1;
        } else if instruction == ']' as u8 {
            if open_bracket_stack.is_empty() {
                panic!("Unmatch closing ']': ptr={}", ptr);
            }
            let open_bracket_offset =
                    open_bracket_stack[open_bracket_stack.len()-1];
            open_bracket_stack.pop();

            let mut optimized_loop = optimize_loop(&ops, open_bracket_offset);

            if optimized_loop.is_empty() {
                ops[open_bracket_offset as usize].argument = ops.len() as i64;
                ops.push(BfOp{ kind: BfOpKind::JumpIfDataNotZero,
                                argument: open_bracket_offset});
            } else {
                let ops_len: usize  = ops.len();
                ops.drain(open_bracket_offset as usize..ops_len);
                ops.append(&mut optimized_loop);
            }
            ptr += 1;
        } else {
            let start = ptr;
            ptr += 1;
            while ptr < code_size && code[ptr] == instruction {
                ptr += 1;
            }

            let num_repeats = ptr - start;

            let mut kind = BfOpKind::InvalidOp;
            match instruction as char {
                '>' => kind = BfOpKind::IncPtr,
                '<' => kind = BfOpKind::DecPtr,
                '+' => kind = BfOpKind::IncData,
                '-' => kind = BfOpKind::DecData,
                ',' => kind = BfOpKind::ReadStdin,
                '.' => kind = BfOpKind::WriteStdout,
                _ => {}
            }

            ops.push(BfOp{ kind: kind, argument: num_repeats as i64 });
        }
    }

    ops
}

fn input() -> u8 {
    let c = match std::io::stdin().bytes().next()
                .and_then(|result| result.ok()).map(|byte| byte as u8) {
        None => process::exit(0),
        Some(c) => c,
    };
    c
}

fn output(c: u8) {
    print!("{}", c as char);
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let path = Path::new(&args[1]);
    let display = path.display();

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

    let mut s = String::new();
    match file.read_to_string(&mut s) {
        Err(why) => panic!("Could not read {}: {}",
                           display, Error::description(&why)),
        Ok(_) => {},
    }

    let mut code_ptr: usize = 0;
    let mut data_ptr: usize = 0;
    let code = s.into_bytes();
    let mut data = [0 as u8; 30000];
    let ops = translate_code(&code);

    while code_ptr < ops.len() {
        let op = &ops[code_ptr];
        let kind = &op.kind;

        match kind {
            &BfOpKind::IncPtr => data_ptr += op.argument as usize,
            &BfOpKind::DecPtr => data_ptr -= op.argument as usize,
            &BfOpKind::IncData => data[data_ptr] += op.argument as u8,
            &BfOpKind::DecData => data[data_ptr] -= op.argument as u8,
            &BfOpKind::ReadStdin => {
                for _ in 0..op.argument {
                    data[data_ptr] = input();
                }
            },
            &BfOpKind::WriteStdout => {
                for _ in 0..op.argument {
                    output(data[data_ptr]);
                }
            },
            &BfOpKind::LoopSetToZero => data[data_ptr] = 0,
            &BfOpKind::LoopMovePtr => {
                while data[data_ptr] != 0 {
                    data_ptr += op.argument as usize;
                }
            },
            &BfOpKind::LoopMoveData => {
                if data[data_ptr] != 0 {
                    let move_to_ptr = data_ptr + op.argument as usize;
                    data[move_to_ptr] += data[data_ptr];
                    data[data_ptr] = 0;
                }
            },
            &BfOpKind::JumpIfDataZero => {
                if data[data_ptr] == 0 {
                    code_ptr = op.argument as usize;
                }
            },
            &BfOpKind::JumpIfDataNotZero => {
                if data[data_ptr] != 0 {
                    code_ptr = op.argument as usize;
                }
            },
            _ => {},
        }
        code_ptr += 1;
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    move_up(&origin.y); // borrowing the value
    move_up(&origin.y); // so that I can change it

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // Here I get the value and store it
    y_cord.set(new_val + 1); // Here I modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

#[test]
fn it_can_move_up() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    assert_eq!(0, origin.y.get());

    move_up(&origin.y);

    assert_eq!(1, origin.y.get());

    move_up(&origin.y);
    move_up(&origin.y);
    move_up(&origin.y);

    assert_eq!(4, origin.y.get());

    for _ in 0..19 {
        move_up(&origin.y);
    }

    assert_eq!(23, origin.y.get());
}
<commit_msg>added some comments<commit_after>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    move_up(&origin.y); // borrowing the value
    move_up(&origin.y); // so that I can change it

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // Here I get the value and store it
    y_cord.set(new_val + 1); // Here I modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

#[test]
fn it_can_move_up() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    assert_eq!(0, origin.y.get());

    move_up(&origin.y);

    assert_eq!(1, origin.y.get());

    move_up(&origin.y);
    move_up(&origin.y);
    move_up(&origin.y);

    assert_eq!(4, origin.y.get());

    for _ in 0..19 { // _ instead of x so that rust does not complain about x not being used
        move_up(&origin.y); // moves up 19 times
    }

    assert_eq!(23, origin.y.get());
}
<|endoftext|>


<commit_before>use structopt::StructOpt;

/// The CaSILE command line interface, publishing automation level wizard.
#[derive(StructOpt)]
#[structopt(version = env!("VERGEN_SEMVER"))]
struct Cli {
    /// Activate debug mode
    #[structopt(short, long, env = "DEBUG")]
    debug: bool,

    /// Outputs verbose feedback where possible
    #[structopt(short, long)]
    verbose: bool,

    #[structopt(subcommand)]
    command: Command,
}

#[derive(StructOpt)]
enum Command {
    /// Executes a make target
    Make {
        target: String
    },

    /// Pass through other commands to shell
    #[structopt(external_subcommand)]
    Other(Vec<String>),
}

fn main() {
    let args = Cli::from_args();
    println!("Insert magic potion!");
}
<commit_msg>chore(cli): Add outline for setup subcommand<commit_after>use structopt::StructOpt;

/// The CaSILE command line interface, publishing automation level wizard.
#[derive(StructOpt)]
#[structopt(version = env!("VERGEN_SEMVER"))]
struct Cli {
    /// Activate debug mode
    #[structopt(short, long, env = "DEBUG")]
    debug: bool,

    /// Outputs verbose feedback where possible
    #[structopt(short, long)]
    verbose: bool,

    #[structopt(subcommand)]
    command: Subcommand,
}

#[derive(StructOpt)]
enum Subcommand {
    /// Executes a make target
    Make {
        /// Target as defined in CaSILE makefile
        target: String
    },

    /// Configure a book repository
    Setup {
        #[structopt(default_value = "./")]
        /// Path to project repository
        path: String
    },

    /// Pass through other commands to shell
    #[structopt(external_subcommand)]
    Other(Vec<String>),
}

fn main() {
    let args = Cli::from_args();
    println!("Insert magic potion!");
}
<|endoftext|>


<commit_before>#![feature(io, slice_patterns, vec_resize)]
use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::thread;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(isize),
    In,
    Out,
    Loop(OpStream),

    // extra optimized ops
    Transfer(u8, Vec<(isize, u8)>)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a + b);
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                    if i > 0 {
                        i -= 1;
                    }
                }
                [Loop(_), ..] => {
                    let mut maybe_new_op = None;

                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                        maybe_new_op = stream.find_alternative();
                    }

                    if let Some(new_op) = maybe_new_op {
                        self.ops[i] = new_op;
                    }

                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn find_alternative(&self) -> Option<Op> {
        let mut map: BTreeMap<isize, u8> = BTreeMap::new();
        let mut rel_index = 0;

        for op in &self.ops[..] {
            match op {
                &Add(x) => {
                    let new_val = map.get(&rel_index).unwrap_or(&0).wrapping_add(x);
                    map.insert(rel_index, new_val);
                }
                &Mov(x) => {
                    rel_index += x;
                }
                _ => {
                    return None;
                }
            }
        }

        if rel_index != 0 {
            return None;
        }

        map.remove(&0).map(|summand| Transfer(summand, map.into_iter().collect()))
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

static ZERO: u8 = 0;

struct State {
    index: usize,
    memory: Vec<u8>
}

impl State {
    fn new() -> State {
        State { index: 0, memory: vec![] }
    }

    fn rel_index(&self, relative: isize) -> usize {
        (self.index as isize + relative) as usize
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                self[0] = self[0].wrapping_add(i);
            }
            &Mov(n) => {
                self.index = self.rel_index(n);
            }
            &In => {
                let mut c = [0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self[0] = c[0];
            }
            &Out => {
                io::stdout().write(&[self[0]]).unwrap();
            }
            &Loop(ref ops) => {
                while self[0] != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
            &Transfer(d, ref map) => {
                if self[0] == 0 {
                    return true
                }

                let mut _v = self[0];
                let mut _n = 0;

                while _v != 0 {
                    _v = _v.wrapping_add(d);
                    if _v == self[0] {
                        // stalled: the current transfer will never complete
                        loop {
                            thread::park()
                        }
                    }
                    _n += 1
                }

                self[0] = 0;
                for &(k, v) in &map[..] {
                    self[k] = self[k].wrapping_add(v.wrapping_mul(_n));
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

impl std::ops::Index<isize> for State {
    type Output = u8;
    fn index(&self, _index: isize) -> &u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            &ZERO
        } else {
            &self.memory[idx]
        }
    }
}

impl std::ops::IndexMut<isize> for State {
    fn index_mut(&mut self, _index: isize) -> &mut u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            self.memory.resize(idx * 2 + 1, 0);
        }
        &mut self.memory[idx]
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars();

                let mut opstream = OpStream::new();
                while let Some(op) = parse(&mut chars, false) {
                    opstream.add(op);
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut io::Chars<T>, in_a_loop: bool) -> Option<Op> {
    loop {
        return match chars.next() {
            Some(Ok(c)) => match c {
                '+' => Some(Add(0x01)),
                '-' => Some(Add(0xff)),
                '<' => Some(Mov(-1)),
                '>' => Some(Mov(1)),
                ',' => Some(In),
                '.' => Some(Out),
                '[' => {
                    let mut childstream = OpStream::new();
                    while let Some(op) = parse(&mut chars, true) {
                        childstream.add(op);
                    }
                    Some(Loop(childstream))
                }
                ']' => {
                    if in_a_loop {
                        None
                    } else {
                        panic!("Stray loop terminator")
                    }
                }
                _ => continue
            },
            Some(e @ Err(_)) => panic!("{:?}", e),
            None => {
                if in_a_loop {
                    panic!("EOF within a loop")
                } else {
                    None
                }
            }
        }
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    fs::File::open(path).map(|f| io::BufReader::new(f))
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use super::{parse, State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_index() {
        let mut state = State { index: 0, memory: vec![23, 0, 0, 0, 0, 42] };
        assert_eq!(23, state[0]);
        state.index = 5;
        assert_eq!(42, state[0]);
    }

    #[test]
    fn test_state_index_mut() {
        let mut state = State::new();
        state[0] = 23;
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state[0] = 42;
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state[0]);
        state.step(&Add(42));
        assert_eq!(65, state[0]);
        state.step(&Add(190));
        assert_eq!(255, state[0]);
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state[0] = 23;
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state[0]);
    }

    #[test]
    fn test_state_step_transfer() {
        let mut state = State::new();
        state[0] = 15;
        state[1] = 7;
        state.step(&Transfer(5, vec![(1, 2)]));
        assert_eq!(0, state[0]);
        assert_eq!(1, state[1]);
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(0x01));
        opstream.add(Add(0xff));
        opstream.add(Add(0xff));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(0xff), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }

    #[test]
    fn test_parse() {
        let input = b"+-[+.,]+";
        let mut chars = io::BufReader::new(&input[..]).chars();
        assert_let!(Some(Add(0x01)), parse(&mut chars, false));
        assert_let!(Some(Add(0xff)), parse(&mut chars, false));
        assert_let!(Some(Loop(loop_op)), parse(&mut chars, false), {
            assert_let!([Add(1), Out, In], &loop_op.ops[..]);
        });
        assert_let!(Some(Add(1)), parse(&mut chars, false));
    }
}
<commit_msg>fix: use Transfer even in edgy cases<commit_after>#![feature(io, slice_patterns, vec_resize)]
use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::thread;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(isize),
    In,
    Out,
    Loop(OpStream),

    // extra optimized ops
    Transfer(u8, Vec<(isize, u8)>)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a + b);
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                    if i > 0 {
                        i -= 1;
                    }
                }
                [Loop(_), ..] => {
                    let mut maybe_new_op = None;

                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                        maybe_new_op = stream.find_alternative();
                    }

                    if let Some(new_op) = maybe_new_op {
                        self.ops[i] = new_op;
                    }

                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn find_alternative(&self) -> Option<Op> {
        let mut map: BTreeMap<isize, u8> = BTreeMap::new();
        let mut rel_index = 0;

        for op in &self.ops[..] {
            match op {
                &Add(x) => {
                    let new_val = map.get(&rel_index).unwrap_or(&0).wrapping_add(x);
                    map.insert(rel_index, new_val);
                }
                &Mov(x) => {
                    rel_index += x;
                }
                _ => {
                    return None;
                }
            }
        }

        if rel_index != 0 {
            return None;
        }

        Some(Transfer(map.remove(&0).unwrap_or(0), map.into_iter().collect()))
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

static ZERO: u8 = 0;

struct State {
    index: usize,
    memory: Vec<u8>
}

impl State {
    fn new() -> State {
        State { index: 0, memory: vec![] }
    }

    fn rel_index(&self, relative: isize) -> usize {
        (self.index as isize + relative) as usize
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                self[0] = self[0].wrapping_add(i);
            }
            &Mov(n) => {
                self.index = self.rel_index(n);
            }
            &In => {
                let mut c = [0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self[0] = c[0];
            }
            &Out => {
                io::stdout().write(&[self[0]]).unwrap();
            }
            &Loop(ref ops) => {
                while self[0] != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
            &Transfer(d, ref map) => {
                if self[0] == 0 {
                    return true
                }

                let mut _v = self[0];
                let mut _n = 0;

                while _v != 0 {
                    _v = _v.wrapping_add(d);
                    if _v == self[0] {
                        // stalled: the current transfer will never complete
                        loop {
                            thread::park()
                        }
                    }
                    _n += 1
                }

                self[0] = 0;
                for &(k, v) in &map[..] {
                    self[k] = self[k].wrapping_add(v.wrapping_mul(_n));
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

impl std::ops::Index<isize> for State {
    type Output = u8;
    fn index(&self, _index: isize) -> &u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            &ZERO
        } else {
            &self.memory[idx]
        }
    }
}

impl std::ops::IndexMut<isize> for State {
    fn index_mut(&mut self, _index: isize) -> &mut u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            self.memory.resize(idx * 2 + 1, 0);
        }
        &mut self.memory[idx]
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars();

                let mut opstream = OpStream::new();
                while let Some(op) = parse(&mut chars, false) {
                    opstream.add(op);
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut io::Chars<T>, in_a_loop: bool) -> Option<Op> {
    loop {
        return match chars.next() {
            Some(Ok(c)) => match c {
                '+' => Some(Add(0x01)),
                '-' => Some(Add(0xff)),
                '<' => Some(Mov(-1)),
                '>' => Some(Mov(1)),
                ',' => Some(In),
                '.' => Some(Out),
                '[' => {
                    let mut childstream = OpStream::new();
                    while let Some(op) = parse(&mut chars, true) {
                        childstream.add(op);
                    }
                    Some(Loop(childstream))
                }
                ']' => {
                    if in_a_loop {
                        None
                    } else {
                        panic!("Stray loop terminator")
                    }
                }
                _ => continue
            },
            Some(e @ Err(_)) => panic!("{:?}", e),
            None => {
                if in_a_loop {
                    panic!("EOF within a loop")
                } else {
                    None
                }
            }
        }
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    fs::File::open(path).map(|f| io::BufReader::new(f))
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use super::{parse, State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_index() {
        let mut state = State { index: 0, memory: vec![23, 0, 0, 0, 0, 42] };
        assert_eq!(23, state[0]);
        state.index = 5;
        assert_eq!(42, state[0]);
    }

    #[test]
    fn test_state_index_mut() {
        let mut state = State::new();
        state[0] = 23;
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state[0] = 42;
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state[0]);
        state.step(&Add(42));
        assert_eq!(65, state[0]);
        state.step(&Add(190));
        assert_eq!(255, state[0]);
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state[0] = 23;
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state[0]);
    }

    #[test]
    fn test_state_step_transfer() {
        let mut state = State::new();
        state[0] = 15;
        state[1] = 7;
        state.step(&Transfer(5, vec![(1, 2)]));
        assert_eq!(0, state[0]);
        assert_eq!(1, state[1]);
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(0x01));
        opstream.add(Add(0xff));
        opstream.add(Add(0xff));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(0xff), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }

    #[test]
    fn test_parse() {
        let input = b"+-[+.,]+";
        let mut chars = io::BufReader::new(&input[..]).chars();
        assert_let!(Some(Add(0x01)), parse(&mut chars, false));
        assert_let!(Some(Add(0xff)), parse(&mut chars, false));
        assert_let!(Some(Loop(loop_op)), parse(&mut chars, false), {
            assert_let!([Add(1), Out, In], &loop_op.ops[..]);
        });
        assert_let!(Some(Add(1)), parse(&mut chars, false));
    }
}
<|endoftext|>