<commit_before>#![feature(rust_2018_preview)]
#![feature(rust_2018_idioms)]
#![feature(nll)]
#![feature(euclidean_division)]
#![feature(try_trait)]

mod aabb;
mod benchmark;
mod bvh;
mod camera;
mod color;
mod gl_renderer;
mod input;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod triangle;
mod vertex;

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use cgmath::Vector3;

use glium::Surface;
use glium::backend::Facade;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use crate::camera::Camera;
use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::PTRenderer;
use crate::scene::{Scene, GPUScene};

fn load_scene<F: Facade>(path: &Path, facade: &F) -> (Arc<Scene>, GPUScene, Camera) {
    let scene = Scene::new(path);
    let mut camera = Camera::new(scene.center() + scene.size() * Vector3::new(0.0, 0.0, 1.0f32),
                                 Vector3::new(0.0, 0.0, -1.0f32));
    camera.set_scale(scene.size());
    let gpu_scene = scene.upload_data(facade);
    (Arc::new(scene), gpu_scene, camera)
}

fn main() {
    match std::env::args().nth(1).as_ref().map(|s| s.as_str()) {
        Some("all") => benchmark::benchmark_all(),
        Some("bvh") => benchmark::benchmark_bvh_build(),
        Some("render") => benchmark::benchmark_render(),
        Some(unknown) => println!("Unknown benchmark {}", unknown),
        None => default_render(),
    }
}

fn default_render() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    // TODO: Enable use of arbitrary scene
    let scenes: HashMap<VirtualKeyCode, PathBuf> =
        [(VirtualKeyCode::Key1, root_path.join("scenes/plane.obj")),
         (VirtualKeyCode::Key2, root_path.join("scenes/cornell/cornell_chesterfield.obj")),
         (VirtualKeyCode::Key3, root_path.join("scenes/cornell-box/CornellBox-Original.obj")),
         (VirtualKeyCode::Key4, root_path.join("scenes/cornell-box/CornellBox-Glossy.obj")),
         (VirtualKeyCode::Key5, root_path.join("scenes/cornell-box/CornellBox-Water.obj")),
         (VirtualKeyCode::Key6, root_path.join("scenes/indirect-test/indirect-test_tex.obj")),
         (VirtualKeyCode::Key7, root_path.join("scenes/conference-new/conference.obj")),
         (VirtualKeyCode::Key8, root_path.join("scenes/nanosuit/nanosuit.obj")),
         (VirtualKeyCode::Key9, root_path.join("scenes/sibenik/sibenik.obj")),
         (VirtualKeyCode::Key0, root_path.join("scenes/crytek-sponza/sponza.obj")),
        ]
        .iter().cloned().collect();

    let (mut scene, mut gpu_scene, mut camera) =
        load_scene(&scenes[&VirtualKeyCode::Key1], &display);
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer = PTRenderer::new();

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        camera.update_viewport(target.get_dimensions());
        // Don't draw if the window is minimized
        if camera.width != 0 && camera.height != 0 {
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&mut target, &gpu_scene, &camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.online_render(&display, &scene, &camera);
                            } else {
                                pt_renderer.stop_threads();
                            }
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(ref keycode), ..} => {
                            if let Some(scene_to_load) = scenes.get(keycode) {
                                let res = load_scene(scene_to_load, &display);
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            pt_renderer.stop_threads();
            return;
        }
    }
}
<commit_msg>Make scene paths fully separator agnostic<commit_after>#![feature(rust_2018_preview)]
#![feature(rust_2018_idioms)]
#![feature(nll)]
#![feature(euclidean_division)]
#![feature(try_trait)]

mod aabb;
mod benchmark;
mod bvh;
mod camera;
mod color;
mod gl_renderer;
mod input;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod triangle;
mod vertex;

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use cgmath::Vector3;

use glium::Surface;
use glium::backend::Facade;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use crate::camera::Camera;
use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::PTRenderer;
use crate::scene::{Scene, GPUScene};

fn load_scene<F: Facade>(path: &Path, facade: &F) -> (Arc<Scene>, GPUScene, Camera) {
    let scene = Scene::new(path);
    let mut camera = Camera::new(scene.center() + scene.size() * Vector3::new(0.0, 0.0, 1.0f32),
                                 Vector3::new(0.0, 0.0, -1.0f32));
    camera.set_scale(scene.size());
    let gpu_scene = scene.upload_data(facade);
    (Arc::new(scene), gpu_scene, camera)
}

fn main() {
    match std::env::args().nth(1).as_ref().map(|s| s.as_str()) {
        Some("all") => benchmark::benchmark_all(),
        Some("bvh") => benchmark::benchmark_bvh_build(),
        Some("render") => benchmark::benchmark_render(),
        Some(unknown) => println!("Unknown benchmark {}", unknown),
        None => default_render(),
    }
}

fn default_render() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let scene_dir = root_path.join("scenes");
    // TODO: Enable use of arbitrary scene
    let scenes: HashMap<VirtualKeyCode, PathBuf> =
        [(VirtualKeyCode::Key1, scene_dir.join("plane.obj")),
         (VirtualKeyCode::Key2, scene_dir.join("cornell").join("cornell_chesterfield.obj")),
         (VirtualKeyCode::Key3, scene_dir.join("cornell-box").join("CornellBox-Original.obj")),
         (VirtualKeyCode::Key4, scene_dir.join("cornell-box").join("CornellBox-Glossy.obj")),
         (VirtualKeyCode::Key5, scene_dir.join("cornell-box").join("CornellBox-Water.obj")),
         (VirtualKeyCode::Key6, scene_dir.join("indirect-test").join("indirect-test_tex.obj")),
         (VirtualKeyCode::Key7, scene_dir.join("conference-new").join("conference.obj")),
         (VirtualKeyCode::Key8, scene_dir.join("nanosuit").join("nanosuit.obj")),
         (VirtualKeyCode::Key9, scene_dir.join("sibenik").join("sibenik.obj")),
         (VirtualKeyCode::Key0, scene_dir.join("crytek-sponza").join("sponza.obj")),
        ]
        .iter().cloned().collect();

    let (mut scene, mut gpu_scene, mut camera) =
        load_scene(&scenes[&VirtualKeyCode::Key1], &display);
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer = PTRenderer::new();

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        camera.update_viewport(target.get_dimensions());
        // Don't draw if the window is minimized
        if camera.width != 0 && camera.height != 0 {
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&mut target, &gpu_scene, &camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.online_render(&display, &scene, &camera);
                            } else {
                                pt_renderer.stop_threads();
                            }
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(ref keycode), ..} => {
                            if let Some(scene_to_load) = scenes.get(keycode) {
                                let res = load_scene(scene_to_load, &display);
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            pt_renderer.stop_threads();
            return;
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::io::prelude::*;
use std::io;
use std::fs::File;

extern crate ansi_term;
use ansi_term::Style;
use ansi_term::Colour::Red;

extern crate rustyline;
use rustyline::error::ReadlineError;
use rustyline::Editor;

// include output of rust-peg given grammar.rustpeg
mod parser {
    include!(concat!(env!("OUT_DIR"), "/grammar.rs"));
}

mod ast;
mod interpreter;
mod value;
mod operations;

#[derive(Debug)]
enum ProcessingError {
    ParseError(parser::ParseError),
    IoError(io::Error)
}

impl From<io::Error> for ProcessingError {
    fn from(from: io::Error) -> Self {
        ProcessingError::IoError(from)
    }
}

impl From<parser::ParseError> for ProcessingError {
    fn from(from: parser::ParseError) -> Self {
        ProcessingError::ParseError(from)
    }
}

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

    if args.len() == 1 {
        repl();
        return;
    }

    if args.len() < 3 || (args[1] != "run" && args[1] != "parse") {
        println!("usage: balloon run|parse FILE");
        return;
    }
    let file_name = &args[2];
    let result = parse_file(file_name).and_then(|ast| {
        if args[1] == "parse" {
            println!("{:#?}", ast);
            Ok(())
        } else {
            interpreter::interpret_program(&ast);
            Ok(())
        }
    });
    if let Err(err) = result {
        match err {
            ProcessingError::ParseError(parse_error) => {
                let mut parse_error = parse_error;

                let mut buf_reader = io::BufReader::new(File::open(file_name).unwrap());
                let mut line = buf_reader.by_ref().lines().nth(parse_error.line - 1);

                let line_content;

                // error was in last line which was empty
                if let None = line {
                    parse_error.line -= 1;
                    buf_reader.seek(io::SeekFrom::Start(0)).unwrap();
                    // more helpful to point to end of previous line
                    line = buf_reader.lines().nth(parse_error.line - 1);
                    line_content = line.unwrap().unwrap();
                    parse_error.column = line_content.len() + 1;
                } else {
                    line_content = line.unwrap().unwrap();
                }
                print_parse_error(file_name.to_string(), line_content, parse_error);
            },
            ProcessingError::IoError(io_error) => {
                match io_error.kind() {
                    io::ErrorKind::NotFound => println!("{}", io_error),
                    e => println!("An error occurred.\n{:?}", e),
                };
            },
        };
    }
}

fn parse_file(name: &String) -> Result<Vec<ast::Statement>, ProcessingError> {
    let mut input_file = File::open(name)?;
    let mut input = String::new();
    input_file.read_to_string(&mut input)?;
    if !input.ends_with("\n") {
        input.push('\n');
    }
    let x = parser::program(&input);
    Ok(x?)
}

fn print_parse_error(file_name: String, line_content: String, parse_error: parser::ParseError) {
    println!("{}: {}: line {}, col {}: expected one of {:?}",
        Style::new().bold().paint(file_name.clone()),
        Red.bold().paint("parse error"),
        parse_error.line,
        parse_error.column,
        parse_error.expected
    );
    println!("{}", line_content);
    let mut pointer_string = String::from_utf8(vec![b' '; parse_error.column - 1]).unwrap();
    pointer_string.push('^');
    println!("{}", Style::new().bold().paint(pointer_string));
}

fn repl() {
    println!("Balloon REPL");
    let mut rl = Editor::<()>::new();
    let mut repl = interpreter::Repl::new();
    repl.start();
    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let orig_input = String::from(line.trim());
                let mut input = orig_input.clone();
                if !input.ends_with(";") {
                    input.push(';');
                }
                match parser::program(&input) {
                    Err(parse_error) => {
                        print_parse_error("repl".to_string(), orig_input, parse_error);
                    },
                    Ok(ast) => repl.execute(&ast),
                }
            },
            Err(ReadlineError::Interrupted) => {
                repl.end();
                println!("CTRL-C");
                break
            },
            Err(ReadlineError::Eof) => {
                repl.end();
                println!("CTRL-D");
                break
            },
            Err(err) => {
                repl.end();
                println!("Error: {:?}", err);
                break
            }
        }
    }
}
<commit_msg>Move repl.end() out of REP `loop`<commit_after>use std::env;
use std::io::prelude::*;
use std::io;
use std::fs::File;

extern crate ansi_term;
use ansi_term::Style;
use ansi_term::Colour::Red;

extern crate rustyline;
use rustyline::error::ReadlineError;
use rustyline::Editor;

// include output of rust-peg given grammar.rustpeg
mod parser {
    include!(concat!(env!("OUT_DIR"), "/grammar.rs"));
}

mod ast;
mod interpreter;
mod value;
mod operations;

#[derive(Debug)]
enum ProcessingError {
    ParseError(parser::ParseError),
    IoError(io::Error)
}

impl From<io::Error> for ProcessingError {
    fn from(from: io::Error) -> Self {
        ProcessingError::IoError(from)
    }
}

impl From<parser::ParseError> for ProcessingError {
    fn from(from: parser::ParseError) -> Self {
        ProcessingError::ParseError(from)
    }
}

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

    if args.len() == 1 {
        repl();
        return;
    }

    if args.len() < 3 || (args[1] != "run" && args[1] != "parse") {
        println!("usage: balloon run|parse FILE");
        return;
    }
    let file_name = &args[2];
    let result = parse_file(file_name).and_then(|ast| {
        if args[1] == "parse" {
            println!("{:#?}", ast);
            Ok(())
        } else {
            interpreter::interpret_program(&ast);
            Ok(())
        }
    });
    if let Err(err) = result {
        match err {
            ProcessingError::ParseError(parse_error) => {
                let mut parse_error = parse_error;

                let mut buf_reader = io::BufReader::new(File::open(file_name).unwrap());
                let mut line = buf_reader.by_ref().lines().nth(parse_error.line - 1);

                let line_content;

                // error was in last line which was empty
                if let None = line {
                    parse_error.line -= 1;
                    buf_reader.seek(io::SeekFrom::Start(0)).unwrap();
                    // more helpful to point to end of previous line
                    line = buf_reader.lines().nth(parse_error.line - 1);
                    line_content = line.unwrap().unwrap();
                    parse_error.column = line_content.len() + 1;
                } else {
                    line_content = line.unwrap().unwrap();
                }
                print_parse_error(file_name.to_string(), line_content, parse_error);
            },
            ProcessingError::IoError(io_error) => {
                match io_error.kind() {
                    io::ErrorKind::NotFound => println!("{}", io_error),
                    e => println!("An error occurred.\n{:?}", e),
                };
            },
        };
    }
}

fn parse_file(name: &String) -> Result<Vec<ast::Statement>, ProcessingError> {
    let mut input_file = File::open(name)?;
    let mut input = String::new();
    input_file.read_to_string(&mut input)?;
    if !input.ends_with("\n") {
        input.push('\n');
    }
    let x = parser::program(&input);
    Ok(x?)
}

fn print_parse_error(file_name: String, line_content: String, parse_error: parser::ParseError) {
    println!("{}: {}: line {}, col {}: expected one of {:?}",
        Style::new().bold().paint(file_name.clone()),
        Red.bold().paint("parse error"),
        parse_error.line,
        parse_error.column,
        parse_error.expected
    );
    println!("{}", line_content);
    let mut pointer_string = String::from_utf8(vec![b' '; parse_error.column - 1]).unwrap();
    pointer_string.push('^');
    println!("{}", Style::new().bold().paint(pointer_string));
}

fn repl() {
    println!("Balloon REPL");
    let mut rl = Editor::<()>::new();
    let mut repl = interpreter::Repl::new();
    repl.start();
    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let orig_input = String::from(line.trim());
                let mut input = orig_input.clone();
                if !input.ends_with(";") {
                    input.push(';');
                }
                match parser::program(&input) {
                    Err(parse_error) => {
                        print_parse_error("repl".to_string(), orig_input, parse_error);
                    },
                    Ok(ast) => repl.execute(&ast),
                }
            },
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break
            },
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);
                break
            }
        }
    }
    repl.end();
}
<|endoftext|>


<commit_before>extern crate boxx;

use boxx::parser::{parse};
use boxx::expr::{eval};

use std::io::{Write, stdout, stdin};

fn main() {
  loop {
    print!("boxx> ");
    let _ = stdout().flush();

    let mut input = String::new();
    match stdin().read_line(&mut input) {
      Ok(_) => {
        if input == "exit\n".to_string() {
          break;
        }
        println!("{:?}", eval(parse(&input)))
      },
      Err(e) => print!("error: {}", e)
    }
    let _ = stdout().flush();
  }
}

#[cfg(test)]
mod tests {
  extern crate boxx;
  use boxx::parser::parse;
  use boxx::expr::{Expr, eval};
  extern crate env_logger;

  #[test]
  pub fn test_func() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(2), eval(parse("let foo = fn() { 1 + 1 }; foo()")));
    assert_eq!(Expr::Int(7), eval(parse("let foo = fn() { 1 + 3 }; foo() + 3")));
    assert_eq!(Expr::Int(9), eval(parse("let foo = fn() { 1 + 3 }; let bar = fn() { foo() + 1}; 4 + bar()")));
  }

  #[test]
  pub fn test_const_decl() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(3), eval(parse("let x = 1 + 2; x")));
    assert_eq!(Expr::Int(1), eval(parse("let x = 1; x")));
    assert_eq!(Expr::Int(8), eval(parse("let x = 5; let y = 3; let z = x + y; z")));
  }

  #[test]
  pub fn test_ternary() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(1), eval(parse("true ? 1 : 0")));
    assert_eq!(Expr::Int(0), eval(parse("false ? 1 : 0")));
    assert_eq!(Expr::Int(3), eval(parse("(false ? 1 : 0); 1 + 2")));
    assert_eq!(Expr::Int(3), eval(parse("false ? 1 : 0; 1 + 2")));
    assert_eq!(Expr::Int(0), eval(parse("((1 + 1) > 3) ? 1 : 0")));
    assert_eq!(Expr::Int(14), eval(parse("((1 + 1) > 3) ? true && false : 12 + 2")));
    assert_eq!(Expr::Int(14), eval(parse("1 + 1 > 3 ? true && false : 12 + 2")));
    assert_eq!(
      Expr::Int(10),
      eval(parse(
          "(false || true) ? ((1 + 2 > 12) ? 9 : 10) : ((1 + 2 < 12) ? 6 : 7)"
       ))
    );

    assert_eq!(Expr::Bool(true), eval(parse("1 + 2 > (1 == 0 ? 5 : 1)")));

    assert_eq!(Expr::Int(-1), eval(parse("true;false ? 1;2 : 0;-1")));
  }

  #[test]
  pub fn test_seq() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(5), eval(parse("3;5")));
    //assert_eq!(Expr::Int(4), eval(parse("let x = 3;let y = 1;x + y")));
  }

  #[test]
  pub fn test_mod() {
    assert_eq!(Expr::Int(0), eval(parse("1 % 1")));
    assert_eq!(Expr::Int(2), eval(parse("7 % 5")));
    assert_eq!(Expr::Int(3), eval(parse("-7 % 5")));
    assert_eq!(Expr::Int(-2), eval(parse("-7 % -5")));
  }

  #[test]
  pub fn test_or_and_and() {
    assert_eq!(Expr::Bool(true), eval(parse("true && true")));
    assert_eq!(Expr::Bool(false), eval(parse("false && false")));
    assert_eq!(Expr::Bool(false), eval(parse("true && false")));
    assert_eq!(Expr::Bool(false), eval(parse("false && true")));

    assert_eq!(Expr::Bool(true), eval(parse("true || true")));
    assert_eq!(Expr::Bool(false), eval(parse("false || false")));
    assert_eq!(Expr::Bool(true), eval(parse("true || false")));
    assert_eq!(Expr::Bool(true), eval(parse("false || true")));
  }

  #[test]
  pub fn test_not_and_neg() {
    assert_eq!(Expr::Bool(true), eval(parse("!false")));

    assert_eq!(Expr::Bool(true), eval(parse("!(true == false)")));
    assert_eq!(Expr::Bool(true), eval(parse("!((1 == 1) == (3 <= 2))")));
    assert_eq!(Expr::Bool(false), eval(parse("!((1 == 1) == !(3 <= 2))")));
    assert_eq!(Expr::Bool(true), eval(parse("!!(!(!(true)))")));

    assert_eq!(Expr::Int(-1), eval(parse("-1")));
    assert_eq!(Expr::Int(-100), eval(parse("-(20 * 5)")));
    assert_eq!(Expr::Int(-100), eval(parse("-(-20 * -5)")));
    assert_eq!(Expr::Int(-100), eval(parse("(20 * -5)")));
    assert_eq!(Expr::Int(-100), eval(parse("(-20 * 5)")));
    assert_eq!(Expr::Int(100), eval(parse("(-20 * -5)")));
    assert_eq!(Expr::Int(100), eval(parse("-(20 * -5)")));
    assert_eq!(Expr::Int(100), eval(parse("-(-20 * 5)")));
    assert_eq!(Expr::Int(0), eval(parse("1 + -1")));
    assert_eq!(Expr::Int(2), eval(parse("1 - -1")));
    assert_eq!(Expr::Int(0), eval(parse("-1 - -1")));
    assert_eq!(Expr::Int(-2), eval(parse("-1 - 1")));
    assert_eq!(Expr::Int(-2), eval(parse("-1 * 2")));
    assert_eq!(Expr::Int(-2), eval(parse("2 * -1")));
    assert_eq!(Expr::Int(-2), eval(parse("-2 * 1")));
    assert_eq!(Expr::Int(-1), eval(parse("-(2 * 1) + 1")));
    assert_eq!(Expr::Int(1), eval(parse("(2 * 1) + -1")));
  }

  #[test]
  pub fn test_comparison_operators() {
    assert_eq!(Expr::Bool(true), eval(parse("1 == 1")));
    assert_eq!(Expr::Bool(false), eval(parse("1 == 2")));
    assert_eq!(Expr::Bool(false), eval(parse("(1 == 1) == (1 == 2)")));
    assert_eq!(Expr::Bool(true), eval(parse("(5 == 2) == (1 == 2)")));
    assert_eq!(Expr::Bool(true), eval(parse("(6 == 6) == true")));
    assert_eq!(Expr::Bool(false), eval(parse("1 == true")));
    assert_eq!(Expr::Bool(true), eval(parse("false == false")));

    assert_eq!(Expr::Bool(true), eval(parse("1 > 0")));
    assert_eq!(Expr::Bool(false), eval(parse("1 < 0")));

    assert_eq!(Expr::Bool(true), eval(parse("88 > 34")));
    assert_eq!(Expr::Bool(false), eval(parse("1 < 1")));
    assert_eq!(Expr::Bool(false), eval(parse("1 > 1")));

    assert_eq!(Expr::Bool(true), eval(parse("88 != 34")));
    assert_eq!(Expr::Bool(false), eval(parse("88 != 88")));
    assert_eq!(Expr::Bool(true), eval(parse("88 <= 88")));
    assert_eq!(Expr::Bool(true), eval(parse("88 >= 88")));
    assert_eq!(Expr::Bool(true), eval(parse("1 >= 0")));
    assert_eq!(Expr::Bool(false), eval(parse("1 >= 12")));

    assert_eq!(Expr::Bool(false), eval(parse("true != true")));
    assert_eq!(Expr::Bool(true), eval(parse("true != false")));
  }

  #[test]
  pub fn test_spaces() {
    assert_eq!(Expr::Int(2), eval(parse("1 + 1")));
    assert_eq!(Expr::Int(12), eval(parse(" (3+   3)* 2      ")));
    assert_eq!(Expr::Int(7), eval(parse("1 + 3*(3 + (1 - 2))")));
  }

  #[test]
  pub fn test_eval_mult() {
    assert_eq!(Expr::Int(12), eval(parse("6*2")));
    assert_eq!(Expr::Int(12), eval(parse("(3+3)*2")));
    assert_eq!(Expr::Int(0), eval(parse("(3+3)*0")));
  }

  #[test]
  pub fn test_eval_div() {
    assert_eq!(Expr::Int(6), eval(parse("12/2")));
    //assert_eq!(Expr::Float(1.5), eval(parse("3/2")));
  }

  #[test]
  pub fn test_eval_addition() {
    assert_eq!(Expr::Int(3), eval(parse("1+2")));
    assert_eq!(Expr::Int(16), eval(parse("5+7+4")));
    assert_eq!(Expr::Int(-1), eval(parse("1-2")));
    assert_eq!(Expr::Int(-100), eval(parse("32-132")));
    assert_eq!(Expr::Int(-120), eval(parse("32-132-20")));

    assert_eq!(Expr::Int(-80), eval(parse("32-(132-20)")));

    assert_eq!(Expr::Int(-6), eval(parse("4-(7+3)")));
    assert_eq!(Expr::Int(0), eval(parse("4-(7-3)")));
    assert_eq!(Expr::Int(8), eval(parse("4+(7-3)")));
    assert_eq!(Expr::Int(8), eval(parse("(4+7)-3)")));
    assert_eq!(Expr::Int(0), eval(parse("(4-7)+3)")));
    assert_eq!(Expr::Int(14), eval(parse("(4+7)+3)")));

    assert_eq!(Expr::Int(2), eval(parse("(1-1)+(2-2)+(3-3)+((1+2)-((3-2)+1)+1)")));
    assert_eq!(Expr::Int(0), eval(parse("((((((((((1-1)))+1))))-1)))")));
  }
}
<commit_msg>test ternary without parens<commit_after>extern crate boxx;

use boxx::parser::{parse};
use boxx::expr::{eval};

use std::io::{Write, stdout, stdin};

fn main() {
  loop {
    print!("boxx> ");
    let _ = stdout().flush();

    let mut input = String::new();
    match stdin().read_line(&mut input) {
      Ok(_) => {
        if input == "exit\n".to_string() {
          break;
        }
        println!("{:?}", eval(parse(&input)))
      },
      Err(e) => print!("error: {}", e)
    }
    let _ = stdout().flush();
  }
}

#[cfg(test)]
mod tests {
  extern crate boxx;
  use boxx::parser::parse;
  use boxx::expr::{Expr, eval};
  extern crate env_logger;

  #[test]
  pub fn test_func() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(2), eval(parse("let foo = fn() { 1 + 1 }; foo()")));
    assert_eq!(Expr::Int(7), eval(parse("let foo = fn() { 1 + 3 }; foo() + 3")));
    assert_eq!(Expr::Int(9), eval(parse("let foo = fn() { 1 + 3 }; let bar = fn() { foo() + 1}; 4 + bar()")));
  }

  #[test]
  pub fn test_const_decl() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(3), eval(parse("let x = 1 + 2; x")));
    assert_eq!(Expr::Int(1), eval(parse("let x = 1; x")));
    assert_eq!(Expr::Int(8), eval(parse("let x = 5; let y = 3; let z = x + y; z")));
  }

  #[test]
  pub fn test_ternary() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(1), eval(parse("true ? 1 : 0")));
    assert_eq!(Expr::Int(0), eval(parse("false ? 1 : 0")));
    assert_eq!(Expr::Int(3), eval(parse("(false ? 1 : 0); 1 + 2")));
    assert_eq!(Expr::Int(3), eval(parse("false ? 1 : 0; 1 + 2")));
    assert_eq!(Expr::Int(0), eval(parse("((1 + 1) > 3) ? 1 : 0")));
    assert_eq!(Expr::Int(14), eval(parse("((1 + 1) > 3) ? true && false : 12 + 2")));
    assert_eq!(Expr::Int(14), eval(parse("1 + 1 > 3 ? true && false : 12 + 2")));
    assert_eq!(
      Expr::Int(10),
      eval(parse(
          "(false || true) ? ((1 + 2 > 12) ? 9 : 10) : ((1 + 2 < 12) ? 6 : 7)"
       ))
    );
    // same as above but without parens
    assert_eq!(
      Expr::Int(10),
      eval(parse(
          "false || true ? 1 + 2 > 12 ? 9 : 10 : 1 + 2 < 12 ? 6 : 7"
       ))
    );

    assert_eq!(Expr::Bool(true), eval(parse("1 + 2 > (1 == 0 ? 5 : 1)")));

    assert_eq!(Expr::Int(-1), eval(parse("true;false ? 1;2 : 0;-1")));
  }

  #[test]
  pub fn test_seq() {
    let _ = env_logger::init();
    assert_eq!(Expr::Int(5), eval(parse("3;5")));
    //assert_eq!(Expr::Int(4), eval(parse("let x = 3;let y = 1;x + y")));
  }

  #[test]
  pub fn test_mod() {
    assert_eq!(Expr::Int(0), eval(parse("1 % 1")));
    assert_eq!(Expr::Int(2), eval(parse("7 % 5")));
    assert_eq!(Expr::Int(3), eval(parse("-7 % 5")));
    assert_eq!(Expr::Int(-2), eval(parse("-7 % -5")));
  }

  #[test]
  pub fn test_or_and_and() {
    assert_eq!(Expr::Bool(true), eval(parse("true && true")));
    assert_eq!(Expr::Bool(false), eval(parse("false && false")));
    assert_eq!(Expr::Bool(false), eval(parse("true && false")));
    assert_eq!(Expr::Bool(false), eval(parse("false && true")));

    assert_eq!(Expr::Bool(true), eval(parse("true || true")));
    assert_eq!(Expr::Bool(false), eval(parse("false || false")));
    assert_eq!(Expr::Bool(true), eval(parse("true || false")));
    assert_eq!(Expr::Bool(true), eval(parse("false || true")));
  }

  #[test]
  pub fn test_not_and_neg() {
    assert_eq!(Expr::Bool(true), eval(parse("!false")));

    assert_eq!(Expr::Bool(true), eval(parse("!(true == false)")));
    assert_eq!(Expr::Bool(true), eval(parse("!((1 == 1) == (3 <= 2))")));
    assert_eq!(Expr::Bool(false), eval(parse("!((1 == 1) == !(3 <= 2))")));
    assert_eq!(Expr::Bool(true), eval(parse("!!(!(!(true)))")));

    assert_eq!(Expr::Int(-1), eval(parse("-1")));
    assert_eq!(Expr::Int(-100), eval(parse("-(20 * 5)")));
    assert_eq!(Expr::Int(-100), eval(parse("-(-20 * -5)")));
    assert_eq!(Expr::Int(-100), eval(parse("(20 * -5)")));
    assert_eq!(Expr::Int(-100), eval(parse("(-20 * 5)")));
    assert_eq!(Expr::Int(100), eval(parse("(-20 * -5)")));
    assert_eq!(Expr::Int(100), eval(parse("-(20 * -5)")));
    assert_eq!(Expr::Int(100), eval(parse("-(-20 * 5)")));
    assert_eq!(Expr::Int(0), eval(parse("1 + -1")));
    assert_eq!(Expr::Int(2), eval(parse("1 - -1")));
    assert_eq!(Expr::Int(0), eval(parse("-1 - -1")));
    assert_eq!(Expr::Int(-2), eval(parse("-1 - 1")));
    assert_eq!(Expr::Int(-2), eval(parse("-1 * 2")));
    assert_eq!(Expr::Int(-2), eval(parse("2 * -1")));
    assert_eq!(Expr::Int(-2), eval(parse("-2 * 1")));
    assert_eq!(Expr::Int(-1), eval(parse("-(2 * 1) + 1")));
    assert_eq!(Expr::Int(1), eval(parse("(2 * 1) + -1")));
  }

  #[test]
  pub fn test_comparison_operators() {
    assert_eq!(Expr::Bool(true), eval(parse("1 == 1")));
    assert_eq!(Expr::Bool(false), eval(parse("1 == 2")));
    assert_eq!(Expr::Bool(false), eval(parse("(1 == 1) == (1 == 2)")));
    assert_eq!(Expr::Bool(true), eval(parse("(5 == 2) == (1 == 2)")));
    assert_eq!(Expr::Bool(true), eval(parse("(6 == 6) == true")));
    assert_eq!(Expr::Bool(false), eval(parse("1 == true")));
    assert_eq!(Expr::Bool(true), eval(parse("false == false")));

    assert_eq!(Expr::Bool(true), eval(parse("1 > 0")));
    assert_eq!(Expr::Bool(false), eval(parse("1 < 0")));

    assert_eq!(Expr::Bool(true), eval(parse("88 > 34")));
    assert_eq!(Expr::Bool(false), eval(parse("1 < 1")));
    assert_eq!(Expr::Bool(false), eval(parse("1 > 1")));

    assert_eq!(Expr::Bool(true), eval(parse("88 != 34")));
    assert_eq!(Expr::Bool(false), eval(parse("88 != 88")));
    assert_eq!(Expr::Bool(true), eval(parse("88 <= 88")));
    assert_eq!(Expr::Bool(true), eval(parse("88 >= 88")));
    assert_eq!(Expr::Bool(true), eval(parse("1 >= 0")));
    assert_eq!(Expr::Bool(false), eval(parse("1 >= 12")));

    assert_eq!(Expr::Bool(false), eval(parse("true != true")));
    assert_eq!(Expr::Bool(true), eval(parse("true != false")));
  }

  #[test]
  pub fn test_spaces() {
    assert_eq!(Expr::Int(2), eval(parse("1 + 1")));
    assert_eq!(Expr::Int(12), eval(parse(" (3+   3)* 2      ")));
    assert_eq!(Expr::Int(7), eval(parse("1 + 3*(3 + (1 - 2))")));
  }

  #[test]
  pub fn test_eval_mult() {
    assert_eq!(Expr::Int(12), eval(parse("6*2")));
    assert_eq!(Expr::Int(12), eval(parse("(3+3)*2")));
    assert_eq!(Expr::Int(0), eval(parse("(3+3)*0")));
  }

  #[test]
  pub fn test_eval_div() {
    assert_eq!(Expr::Int(6), eval(parse("12/2")));
    //assert_eq!(Expr::Float(1.5), eval(parse("3/2")));
  }

  #[test]
  pub fn test_eval_addition() {
    assert_eq!(Expr::Int(3), eval(parse("1+2")));
    assert_eq!(Expr::Int(16), eval(parse("5+7+4")));
    assert_eq!(Expr::Int(-1), eval(parse("1-2")));
    assert_eq!(Expr::Int(-100), eval(parse("32-132")));
    assert_eq!(Expr::Int(-120), eval(parse("32-132-20")));

    assert_eq!(Expr::Int(-80), eval(parse("32-(132-20)")));

    assert_eq!(Expr::Int(-6), eval(parse("4-(7+3)")));
    assert_eq!(Expr::Int(0), eval(parse("4-(7-3)")));
    assert_eq!(Expr::Int(8), eval(parse("4+(7-3)")));
    assert_eq!(Expr::Int(8), eval(parse("(4+7)-3)")));
    assert_eq!(Expr::Int(0), eval(parse("(4-7)+3)")));
    assert_eq!(Expr::Int(14), eval(parse("(4+7)+3)")));

    assert_eq!(Expr::Int(2), eval(parse("(1-1)+(2-2)+(3-3)+((1+2)-((3-2)+1)+1)")));
    assert_eq!(Expr::Int(0), eval(parse("((((((((((1-1)))+1))))-1)))")));
  }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(oak)]

extern crate oak_runtime;
use oak_runtime::*;

use std::io::prelude::*;
use std::fs::File;
use std::io::BufReader;

grammar! awesome {
    program
        = ((expr / decl / stmt) terminator?)*

    decl
        = class_decl > class_decl
        / method_decl > method_decl

    class_decl
        = kw_class constant class_body

    class_body
        = lbracket method_decl* rbracket

    method_decl
        = kw_def identifier lparen param_list? rparen block

    block
        = lbracket ((stmt / expr) terminator?)* rbracket

    stmt
        = if_stmt > if_stmt
        / while_stmt > while_stmt

    if_stmt
        = kw_if expr block (kw_else block)?

    while_stmt
        = kw_while expr block

    call
        = (receiver_expr ".")? identifier lparen arg_list? rparen

    assign_expr
        = identifier bind_op expr > assign_expr

    expr
        = spacing cond_expr

    cond_expr
        = add_expr (cond_expr_op add_expr)* > fold_left

    add_expr
        = mult_expr (add_expr_op mult_expr)* > fold_left

    mult_expr
        = primary_expr (mult_expr_op primary_expr)* > fold_left

    primary_expr
        = call > call_expr
          / receiver_expr

    receiver_expr
        = assign_expr
        / literal
        / constant
        / identifier
        / lparen expr rparen

    arg_list
        = (expr ("," spacing expr)*)

    param_list
        = (identifier ("," spacing identifier)*)

    digit = ["0-9"]
    identifier_char = ["A-Za-z0-9_"]

    identifier = !digit !keyword identifier_char+ spacing > identifier
    constant = !keyword !digit ["A-Z"]+ identifier_char+ spacing > constant

    literal
        = lit_string
        / lit_float
        / lit_int

    lit_int = digit+ spacing > lit_int
    lit_float = digit+ "." digit+ spacing > lit_float
    lit_string = (dbl_quot_lit_string / sng_quot_lit_string) spacing > lit_string

    dbl_quot_lit_string = dbl_quot (spacing_char / !dbl_quot .)* dbl_quot
    sng_quot_lit_string = sng_quot (spacing_char / !sng_quot .)* sng_quot

    spacing_char = [" \n\r\t"]
    spacing = spacing_char* -> ()

    kw_class = "class" spacing
    kw_def = "def" spacing
    kw_else = "else" spacing
    kw_false = "false" spacing
    kw_if = "if" spacing
    kw_nil = "nil" spacing
    kw_true = "true" spacing
    kw_while = "while" spacing

    keyword
        = kw_def
        / kw_if
        / kw_else
        / kw_class
        / kw_true
        / kw_false
        / kw_nil

    add_expr_op
        = add_op > add_bin_op
        / sub_op > sub_bin_op

    mult_expr_op
        = mul_op > mul_bin_op
        / div_op > div_bin_op

    cond_expr_op
        = lt_op > lt_bin_op /
        lte_op > lte_bin_op /
        gt_op > gt_bin_op /
        gte_op > gte_bin_op /
        ne_op > ne_bin_op /
        eq_op > eq_bin_op /
        andand_op > and_bin_op /
        oror_op > or_bin_op

    bind_op = "=" spacing
    add_op = "+" spacing
    sub_op = "-" spacing
    mul_op = "*" spacing
    div_op = "/" spacing
    lt_op = "<" spacing
    lte_op = "<=" spacing
    gt_op = ">" spacing
    gte_op = ">=" spacing
    ne_op = "!=" spacing
    eq_op = "==" spacing
    andand_op  = "&&" spacing
    oror_op = "||" spacing

    lparen = "(" spacing
    rparen = ")" spacing
    lbracket = "{" spacing
    rbracket = "}" spacing
    dbl_quot = "\"" spacing
    sng_quot = "'" spacing
    terminator = ";" spacing

    fn lit_int(raw_text: Vec<char>) -> PExpr {
        Box::new(IntegerLiteral(to_number(raw_text)))
    }

    // FIXME: Please remove this hacky wacky '.' thingy
    fn lit_float(integer: Vec<char>, fractional: Vec<char>) -> PExpr {
        let mut buf = integer;
        buf.push('.');
        Box::new(FloatLiteral(to_float(combine(buf, fractional))))
    }

    fn lit_string(raw_text: Vec<char>) -> PExpr {
        Box::new(StringLiteral(to_string(raw_text)))
    }

    fn constant(first: Vec<char>, rest: Vec<char>) -> PExpr {
        Box::new(Constant(to_string(combine(first, rest))))
    }

    fn identifier(raw_text: Vec<char>) -> PExpr {
        Box::new(Identifier(to_string(raw_text)))
    }

    fn add_bin_op() -> BinOp { Add }
    fn sub_bin_op() -> BinOp { Sub }
    fn mul_bin_op() -> BinOp { Mul }
    fn div_bin_op() -> BinOp { Div }
    fn lt_bin_op() -> BinOp { Lt }
    fn lte_bin_op() -> BinOp { Lte }
    fn gt_bin_op() -> BinOp { Gt }
    fn gte_bin_op() -> BinOp { Gte }
    fn ne_bin_op() -> BinOp { Ne }
    fn eq_bin_op() -> BinOp { Eq }
    fn and_bin_op() -> BinOp { And }
    fn or_bin_op() -> BinOp { Or }

    fn call_expr(receiver: Option<PExpr>, method: PExpr, args: Option<(PExpr, Vec<PExpr>)>) -> PExpr {
        Box::new(Call(receiver, method, args))
    }

    fn assign_expr(identifier: PExpr, expr: PExpr) -> PExpr {
        Box::new(AssingExpr(identifier, expr))
    }

    fn class_decl(class: PExpr, methods: Vec<(PExpr, Option<(PExpr, Vec<PExpr>)>, Vec<(PExpr, Option<()>)>)>) -> PExpr {
        Box::new(ClassDecl(class, methods))
    }

    fn method_decl(identifier: PExpr, params: Option<(PExpr, Vec<PExpr>)>, body: Vec<(PExpr, Option<()>)>) -> PExpr {
        Box::new(MethodDecl(identifier, params, body))
    }

    fn if_stmt(condition: PExpr, block: Vec<(PExpr, Option<()>)>, else_block: Option<Vec<(PExpr, Option<()>)>>) -> PExpr {
        Box::new(IfStatement(condition, block, else_block))
    }

    fn while_stmt(condition: PExpr,  block: Vec<(PExpr, Option<()>)>) -> PExpr {
      Box::new(WhileStatement(condition, block))
    }

    fn to_string(raw_text: Vec<char>) -> String {
        raw_text.into_iter().collect()
    }

    fn to_number(raw_text: Vec<char>) -> u32 {
        u32::from_str(&*to_string(raw_text)).unwrap()
    }

    fn to_float(raw_text: Vec<char>) -> f32 {
        f32::from_str(&*to_string(raw_text)).unwrap()
    }

    fn fold_left(head: PExpr, rest: Vec<(BinOp, PExpr)>) -> PExpr {
        rest.into_iter().fold(head,
          |accu, (op, expr)| Box::new(BinaryExpr(op, accu, expr)))
    }

    fn fold_right(front: Vec<(PExpr, BinOp)>, last: PExpr) -> PExpr {
        front.into_iter().rev().fold(last,
          |accu, (expr, op)| Box::new(BinaryExpr(op, expr, accu)))
    }

    fn combine<T: Clone>(left: Vec<T>, right: Vec<T>) -> Vec<T> {
        let mut result = left.clone();
        result.extend(right.into_iter());
        result
    }

    use std::str::FromStr;
    use self::Expression::*;
    use self::BinOp::*;

    pub type PExpr = Box<Expression>;

    #[derive(Debug)]
    pub enum Expression {
        Identifier(String),
        Constant(String),
        IntegerLiteral(u32),
        FloatLiteral(f32),
        StringLiteral(String),
        BinaryExpr(BinOp, PExpr, PExpr),
        Call(Option<PExpr>, PExpr, Option<(PExpr, Vec<PExpr>)>),
        AssingExpr(PExpr, PExpr),
        MethodDecl(PExpr, Option<(PExpr, Vec<PExpr>)>, Vec<(PExpr, Option<()>)>),
        ClassDecl(PExpr, Vec<(PExpr, Option<(PExpr, Vec<PExpr>)>, Vec<(PExpr, Option<()>)>)>),
        IfStatement(PExpr, Vec<(PExpr, Option<()>)>, Option<Vec<(PExpr, Option<()>)>>),
        WhileStatement(PExpr, Vec<(PExpr, Option<()>)>),
        Block(Vec<PExpr>)
    }

    #[derive(Debug)]
    pub enum BinOp {
        Add, Sub, Mul, Div, Lt, Lte, Gt, Gte, Ne, Eq, And, Or
    }
}

fn main() {
    let f = File::open("./src/example.awm").ok().expect("failed to open file");
    let mut reader = BufReader::new(f);
    let mut contents = String::new();
    reader.read_to_string(&mut contents);
    println!("{:#?}", awesome::parse_program(contents.stream()));
}
<commit_msg>More sorting.<commit_after>#![feature(plugin)]
#![plugin(oak)]

extern crate oak_runtime;
use oak_runtime::*;

use std::io::prelude::*;
use std::fs::File;
use std::io::BufReader;

grammar! awesome {
    program
        = ((expr / decl / stmt) terminator?)*

    decl
        = class_decl > class_decl
        / method_decl > method_decl

    class_decl
        = kw_class constant class_body

    class_body
        = lbracket method_decl* rbracket

    method_decl
        = kw_def identifier lparen param_list? rparen block

    block
        = lbracket ((stmt / expr) terminator?)* rbracket

    stmt
        = if_stmt > if_stmt
        / while_stmt > while_stmt

    if_stmt
        = kw_if expr block (kw_else block)?

    while_stmt
        = kw_while expr block

    call
        = (receiver_expr ".")? identifier lparen arg_list? rparen

    assign_expr
        = identifier bind_op expr > assign_expr

    expr
        = spacing cond_expr

    cond_expr
        = add_expr (cond_expr_op add_expr)* > fold_left

    add_expr
        = mult_expr (add_expr_op mult_expr)* > fold_left

    mult_expr
        = primary_expr (mult_expr_op primary_expr)* > fold_left

    primary_expr
        = call > call_expr
          / receiver_expr

    receiver_expr
        = assign_expr
        / literal
        / constant
        / identifier
        / lparen expr rparen

    arg_list
        = (expr ("," spacing expr)*)

    param_list
        = (identifier ("," spacing identifier)*)

    digit = ["0-9"]
    identifier_char = ["A-Za-z0-9_"]

    identifier = !digit !keyword identifier_char+ spacing > identifier
    constant = !keyword !digit ["A-Z"]+ identifier_char+ spacing > constant

    literal
        = lit_string
        / lit_float
        / lit_int

    lit_int = digit+ spacing > lit_int
    lit_float = digit+ "." digit+ spacing > lit_float
    lit_string = (dbl_quot_lit_string / sng_quot_lit_string) spacing > lit_string

    dbl_quot_lit_string = dbl_quot (spacing_char / !dbl_quot .)* dbl_quot
    sng_quot_lit_string = sng_quot (spacing_char / !sng_quot .)* sng_quot

    spacing_char = [" \n\r\t"]
    spacing = spacing_char* -> ()

    kw_class = "class" spacing
    kw_def = "def" spacing
    kw_else = "else" spacing
    kw_false = "false" spacing
    kw_if = "if" spacing
    kw_nil = "nil" spacing
    kw_true = "true" spacing
    kw_while = "while" spacing

    keyword
        = kw_def
        / kw_if
        / kw_else
        / kw_class
        / kw_true
        / kw_false
        / kw_nil

    add_expr_op
        = add_op > add_bin_op
        / sub_op > sub_bin_op

    mult_expr_op
        = mul_op > mul_bin_op
        / div_op > div_bin_op

    cond_expr_op
        = lt_op > lt_bin_op /
        lte_op > lte_bin_op /
        gt_op > gt_bin_op /
        gte_op > gte_bin_op /
        ne_op > ne_bin_op /
        eq_op > eq_bin_op /
        andand_op > and_bin_op /
        oror_op > or_bin_op

    bind_op = "=" spacing
    add_op = "+" spacing
    sub_op = "-" spacing
    mul_op = "*" spacing
    div_op = "/" spacing
    lt_op = "<" spacing
    lte_op = "<=" spacing
    gt_op = ">" spacing
    gte_op = ">=" spacing
    ne_op = "!=" spacing
    eq_op = "==" spacing
    andand_op  = "&&" spacing
    oror_op = "||" spacing

    lparen = "(" spacing
    rparen = ")" spacing
    lbracket = "{" spacing
    rbracket = "}" spacing
    dbl_quot = "\"" spacing
    sng_quot = "'" spacing
    terminator = ";" spacing

    fn lit_int(raw_text: Vec<char>) -> PExpr {
        Box::new(IntegerLiteral(to_number(raw_text)))
    }

    // FIXME: Please remove this hacky wacky '.' thingy
    fn lit_float(integer: Vec<char>, fractional: Vec<char>) -> PExpr {
        let mut buf = integer;
        buf.push('.');
        Box::new(FloatLiteral(to_float(combine(buf, fractional))))
    }

    fn lit_string(raw_text: Vec<char>) -> PExpr {
        Box::new(StringLiteral(to_string(raw_text)))
    }

    fn constant(first: Vec<char>, rest: Vec<char>) -> PExpr {
        Box::new(Constant(to_string(combine(first, rest))))
    }

    fn identifier(raw_text: Vec<char>) -> PExpr {
        Box::new(Identifier(to_string(raw_text)))
    }

    fn add_bin_op() -> BinOp { Add }
    fn sub_bin_op() -> BinOp { Sub }
    fn mul_bin_op() -> BinOp { Mul }
    fn div_bin_op() -> BinOp { Div }
    fn lt_bin_op() -> BinOp { Lt }
    fn lte_bin_op() -> BinOp { Lte }
    fn gt_bin_op() -> BinOp { Gt }
    fn gte_bin_op() -> BinOp { Gte }
    fn ne_bin_op() -> BinOp { Ne }
    fn eq_bin_op() -> BinOp { Eq }
    fn and_bin_op() -> BinOp { And }
    fn or_bin_op() -> BinOp { Or }

    fn call_expr(receiver: Option<PExpr>, method: PExpr, args: Option<(PExpr, Vec<PExpr>)>) -> PExpr {
        Box::new(Call(receiver, method, args))
    }

    fn assign_expr(identifier: PExpr, expr: PExpr) -> PExpr {
        Box::new(AssingExpr(identifier, expr))
    }

    fn class_decl(class: PExpr, methods: Vec<(PExpr, Option<(PExpr, Vec<PExpr>)>, Vec<(PExpr, Option<()>)>)>) -> PExpr {
        Box::new(ClassDecl(class, methods))
    }

    fn method_decl(identifier: PExpr, params: Option<(PExpr, Vec<PExpr>)>, body: Vec<(PExpr, Option<()>)>) -> PExpr {
        Box::new(MethodDecl(identifier, params, body))
    }

    fn if_stmt(condition: PExpr, block: Vec<(PExpr, Option<()>)>, else_block: Option<Vec<(PExpr, Option<()>)>>) -> PExpr {
        Box::new(IfStatement(condition, block, else_block))
    }

    fn while_stmt(condition: PExpr,  block: Vec<(PExpr, Option<()>)>) -> PExpr {
      Box::new(WhileStatement(condition, block))
    }

    fn to_string(raw_text: Vec<char>) -> String {
        raw_text.into_iter().collect()
    }

    fn to_number(raw_text: Vec<char>) -> u32 {
        u32::from_str(&*to_string(raw_text)).unwrap()
    }

    fn to_float(raw_text: Vec<char>) -> f32 {
        f32::from_str(&*to_string(raw_text)).unwrap()
    }

    fn fold_left(head: PExpr, rest: Vec<(BinOp, PExpr)>) -> PExpr {
        rest.into_iter().fold(head,
          |accu, (op, expr)| Box::new(BinaryExpr(op, accu, expr)))
    }

    fn fold_right(front: Vec<(PExpr, BinOp)>, last: PExpr) -> PExpr {
        front.into_iter().rev().fold(last,
          |accu, (expr, op)| Box::new(BinaryExpr(op, expr, accu)))
    }

    fn combine<T: Clone>(left: Vec<T>, right: Vec<T>) -> Vec<T> {
        let mut result = left.clone();
        result.extend(right.into_iter());
        result
    }

    use std::str::FromStr;
    use self::Expression::*;
    use self::BinOp::*;

    pub type PExpr = Box<Expression>;

    #[derive(Debug)]
    pub enum Expression {
        Identifier(String),
        Constant(String),
        IntegerLiteral(u32),
        FloatLiteral(f32),
        StringLiteral(String),
        BinaryExpr(BinOp, PExpr, PExpr),
        AssingExpr(PExpr, PExpr),
        ClassDecl(PExpr, Vec<(PExpr, Option<(PExpr, Vec<PExpr>)>, Vec<(PExpr, Option<()>)>)>),
        IfStatement(PExpr, Vec<(PExpr, Option<()>)>, Option<Vec<(PExpr, Option<()>)>>),
        MethodDecl(PExpr, Option<(PExpr, Vec<PExpr>)>, Vec<(PExpr, Option<()>)>),
        WhileStatement(PExpr, Vec<(PExpr, Option<()>)>),
        Call(Option<PExpr>, PExpr, Option<(PExpr, Vec<PExpr>)>),
        Block(Vec<PExpr>)
    }

    #[derive(Debug)]
    pub enum BinOp {
        Add, Sub, Mul, Div, Lt, Lte, Gt, Gte, Ne, Eq, And, Or
    }
}

fn main() {
    let f = File::open("./src/example.awm").ok().expect("failed to open file");
    let mut reader = BufReader::new(f);
    let mut contents = String::new();
    reader.read_to_string(&mut contents);
    println!("{:#?}", awesome::parse_program(contents.stream()));
}
<|endoftext|>


<commit_before>
extern crate tiny;

use tiny::*;
use std::path::Path;

struct App {
   font: Font,
}

impl tiny::Application for App {
   fn new(ctx: &tiny::Context) -> App {
      let font_bitmap = Bitmap::load(ctx, Path::new("res/font.png")).unwrap();

      App {
         font: Font::new(font_bitmap, 4, 7)
      }
   }

   fn step(&mut self, ctx: &tiny::Context) -> bool {
      !ctx.key_down(tiny::Key::Escape)
   }

   fn paint(&self, painter: &tiny::Painter) {
      painter.clear(tiny::BLACK);
      painter.text(135, 90, "Hello World!", WHITE, &self.font);
   }
}

fn main() {
   if let Err(err) = tiny::run::<App>("Tiny RTS", 320, 200, 3) {
      println!("Error: {}", err);
   }
}<commit_msg>We don't have to include the tiny namespace<commit_after>
extern crate tiny;

use std::path::Path;

struct App {
   font: tiny::Font,
}

impl tiny::Application for App {
   fn new(ctx: &tiny::Context) -> App {
      let font_bitmap = tiny::Bitmap::load(ctx, Path::new("res/font.png")).unwrap();

      App {
         font: tiny::Font::new(font_bitmap, 4, 7)
      }
   }

   fn step(&mut self, ctx: &tiny::Context) -> bool {
      !ctx.key_down(tiny::Key::Escape)
   }

   fn paint(&self, painter: &tiny::Painter) {
      painter.clear(tiny::BLACK);
      painter.text(136, 96, "Hello World!", tiny::WHITE, &self.font);
   }
}

fn main() {
   if let Err(err) = tiny::run::<App>("Tiny RTS", 320, 200, 3) {
      println!("Error: {}", err);
   }
}<|endoftext|>


<commit_before>#![feature(globs)]
extern crate rgtk;

use rgtk::*;
use std::collections::HashSet;

mod projects;
mod utils;

fn main() {
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.8) as i32;

    // window

    let mut window = gtk::Window::new(gtk::window_type::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::window_position::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        gtk::main_quit();
        true
    }));

    // project pane

    let new_project_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_project_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let column_types = vec![glib::ffi::g_type_string];
    let store = gtk::ListStore::new(column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let column = gtk::TreeViewColumn::new().unwrap();
    project_tree.append_text_column(&column);

    let mut iter = gtk::ffi::C_GtkTreeIter;
    model.get_iter_first(&mut iter);
    store.append(&mut iter);
    store.set_column_text(&mut iter, 0, "Hello, world!");

    let mut project_pane = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&project_tree, true, true, 0);

    // editor pane

    let editor_pane = gtk::TextView::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);

    // build pane

    let build_pane = gtk::TextView::new().unwrap();

    // content

    let mut content = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    // hbox

    let mut hbox = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // state

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model
    };

    // connections

    new_project_button.connect(gtk::signals::Clicked::new(|| {::projects::new_project(&mut state)}));
    import_button.connect(gtk::signals::Clicked::new(|| {::projects::import_project(&mut state)}));
    rename_button.connect(gtk::signals::Clicked::new(|| {::projects::rename_project(&mut state)}));
    remove_button.connect(gtk::signals::Clicked::new(|| {::projects::remove_project(&mut state)}));

    // show window

    window.show_all();
    gtk::main();
}
<commit_msg>Use TreeStore and remove convenience function<commit_after>#![feature(globs)]
extern crate rgtk;

use rgtk::*;
use std::collections::HashSet;
use std::ptr;

mod projects;
mod utils;

fn main() {
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.8) as i32;

    // window

    let mut window = gtk::Window::new(gtk::window_type::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::window_position::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        gtk::main_quit();
        true
    }));

    // project pane

    let new_project_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_project_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let column_types = vec![glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    project_tree.append_column(&column);

    let mut iter = gtk::ffi::C_GtkTreeIter;
    model.get_iter_first(&mut iter);
    store.append(&mut iter, ptr::null_mut());
    let mut child = gtk::ffi::C_GtkTreeIter;
    store.append(&mut child, &mut iter);

    store.set_column_text(&mut iter, 0, "Hello, world!");
    store.set_column_text(&mut child, 0, "Bye, world!");

    let mut project_pane = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&project_tree, true, true, 0);

    // editor pane

    let editor_pane = gtk::TextView::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);

    // build pane

    let build_pane = gtk::TextView::new().unwrap();

    // content

    let mut content = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    // hbox

    let mut hbox = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // state

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model
    };

    // connections

    new_project_button.connect(gtk::signals::Clicked::new(|| {::projects::new_project(&mut state)}));
    import_button.connect(gtk::signals::Clicked::new(|| {::projects::import_project(&mut state)}));
    rename_button.connect(gtk::signals::Clicked::new(|| {::projects::rename_project(&mut state)}));
    remove_button.connect(gtk::signals::Clicked::new(|| {::projects::remove_project(&mut state)}));

    // show window

    window.show_all();
    gtk::main();
}
<|endoftext|>


<commit_before>extern crate regex;

use regex::{Regex,Captures};

/// Calculates the result of the given calculation.
///
/// The calculation may only contain one type of arithmetic operations, either
/// `[+-]` or `[*/]` and may not contain braces.
/// The result will be calculated by replacing the first arithmetic operator and
/// the first two numbers with the result of this calculation.
fn calc(equation:&str)->String
{
	let mut equ=equation.to_string();
	let split=Regex::new(
		r"(?P<n1>(\d*\.)?\d+)(?P<char>[-+*/])(?P<n2>(\d*\.)?\d+)").unwrap();

	while split.is_match(equ.as_ref())
	{
		equ=split.replace(equ.as_ref(),|cap:&Captures|
		{
			let n1=cap.name("n1").unwrap().parse::<f64>().unwrap();
			let n2=cap.name("n2").unwrap().parse::<f64>().unwrap();
			//TODO: write back different for more accuracy
			match cap.name("char").unwrap()
			{
				"+" => n1+n2,
				"-" => n1-n2,
				"*" => n1*n2,
				"/" => n1/n2,
				_ => unreachable!(),
			}.to_string()
		});
	}
	equ
}

/// Parses a calculation including the four basic arithmetic operations and
/// braces.
///
/// This function is responsible for dealing with the order of the operations.
/// It firstly recursively disassembles the passed string  based on the braces
/// contained.
/// These parts are being calculated and merged into the former string, which
/// then will not contain braces anymore.
/// Secondly it passes the multiplications and divisions to `calc` and lastly
/// resolves additions and subtractions using, again, `calc`.
fn parse(equation:String)->String
{
	let mut equ=equation;
	let braces=Regex::new(r"\((?P<res>([^()]*|(.*?\(.*?\))+.*?))\)").unwrap();
	let dot=Regex::new(r"((\d*\.)?\d+)([*/]((\d*\.)?\d+))+").unwrap();
	let line=Regex::new(r"((\d*\.)?\d+)([+-]((\d*\.)?\d+))+").unwrap();

	equ=braces.replace_all(equ.as_ref(),
		|cap:&Captures|parse(cap.name("res").unwrap().to_string()));
	equ=dot.replace_all(equ.as_ref(),
		|cap:&Captures|calc(cap.at(0).unwrap()));
	equ=line.replace_all(equ.as_ref(),
		|cap:&Captures|calc(cap.at(0).unwrap()));
	equ
}

/// Prepares the commandline arguments for usage with `parse` which then will be
/// called.
///
/// The format of `parse`s argument is the following:
///
/// - no spaces
fn main()
{
	let args:Vec<String>=std::env::args().skip(1).collect();
	let s=args.connect("").replace(" ","");
	println!("{}",parse(s));
}

<commit_msg>use join instead of connect<commit_after>extern crate regex;

use regex::{Regex,Captures};

/// Calculates the result of the given calculation.
///
/// The calculation may only contain one type of arithmetic operations, either
/// `[+-]` or `[*/]` and may not contain braces.
/// The result will be calculated by replacing the first arithmetic operator and
/// the first two numbers with the result of this calculation.
fn calc(equation:&str)->String
{
	let mut equ=equation.to_string();
	let split=Regex::new(
		r"(?P<n1>(\d*\.)?\d+)(?P<char>[-+*/])(?P<n2>(\d*\.)?\d+)").unwrap();

	while split.is_match(equ.as_ref())
	{
		equ=split.replace(equ.as_ref(),|cap:&Captures|
		{
			let n1=cap.name("n1").unwrap().parse::<f64>().unwrap();
			let n2=cap.name("n2").unwrap().parse::<f64>().unwrap();
			//TODO: write back different for more accuracy
			match cap.name("char").unwrap()
			{
				"+" => n1+n2,
				"-" => n1-n2,
				"*" => n1*n2,
				"/" => n1/n2,
				_ => unreachable!(),
			}.to_string()
		});
	}
	equ
}

/// Parses a calculation including the four basic arithmetic operations and
/// braces.
///
/// This function is responsible for dealing with the order of the operations.
/// It firstly recursively disassembles the passed string  based on the braces
/// contained.
/// These parts are being calculated and merged into the former string, which
/// then will not contain braces anymore.
/// Secondly it passes the multiplications and divisions to `calc` and lastly
/// resolves additions and subtractions using, again, `calc`.
fn parse(equation:String)->String
{
	let mut equ=equation;
	let braces=Regex::new(r"\((?P<res>([^()]*|(.*?\(.*?\))+.*?))\)").unwrap();
	let dot=Regex::new(r"((\d*\.)?\d+)([*/]((\d*\.)?\d+))+").unwrap();
	let line=Regex::new(r"((\d*\.)?\d+)([+-]((\d*\.)?\d+))+").unwrap();

	equ=braces.replace_all(equ.as_ref(),
		|cap:&Captures|parse(cap.name("res").unwrap().to_string()));
	equ=dot.replace_all(equ.as_ref(),
		|cap:&Captures|calc(cap.at(0).unwrap()));
	equ=line.replace_all(equ.as_ref(),
		|cap:&Captures|calc(cap.at(0).unwrap()));
	equ
}

/// Prepares the commandline arguments for usage with `parse` which then will be
/// called.
///
/// The format of `parse`s argument is the following:
///
/// - no spaces
fn main()
{
	let args:Vec<String>=std::env::args().skip(1).collect();
	let s=args.join("").replace(" ","");
	println!("{}",parse(s));
}

<|endoftext|>


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

extern crate clap;
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::error::Error;

const EXITCODE_SUCCESS : i32 = 0;
const EXITCODE_CPULOADFAILS : i32 = 2;

fn main() {
    let exit_status = real_main();
    if exit_status != EXITCODE_SUCCESS {
        std::process::exit(exit_status);
    }
}

fn real_main() -> i32 {
    let matches = clap::App::new("rboy")
        .version("0.1")
        .author("Mathijs van de Nes")
        .about("A Gameboy Colour emulator written in Rust")
        .arg(clap::Arg::with_name("filename")
             .help("Sets the ROM file to load")
             .required(true))
        .arg(clap::Arg::with_name("serial")
             .help("Prints the data from the serial port to stdout")
             .short("s")
             .long("serial"))
        .arg(clap::Arg::with_name("classic")
             .help("Forces the emulator to run in classic Gameboy mode")
             .short("c")
             .long("classic"))
        .arg(clap::Arg::with_name("scale")
             .help("Sets the scale of the interface. Default: 2")
             .short("x")
             .long("scale")
             .validator(|s|
                 match s.parse::<u32>() {
                     Err(e) => Err(format!("Could not parse scale: {}", e.description())),
                     Ok(s) if s < 1 => Err("Scale must be at least 1".to_owned()),
                     Ok(s) if s > 8 => Err("Scale may be at most 8".to_owned()),
                     Ok(..) => Ok(()),
                 })
             .takes_value(true))
        .get_matches();

    let opt_serial = matches.is_present("serial");
    let opt_classic = matches.is_present("classic");
    let filename = matches.value_of("filename").unwrap();
    let scale = matches.value_of("scale").unwrap_or("2").parse::<u32>().unwrap();

    let cpu = construct_cpu(filename, opt_classic, opt_serial);
    if cpu.is_none() { return EXITCODE_CPULOADFAILS; }
    let cpu = cpu.unwrap();

    let sdl_context = sdl2::init().unwrap();
    let sdl_video = sdl_context.video().ok().expect("Could not open video");
    let window = match sdl2::video::WindowBuilder::new(
            &sdl_video,
            "RBoy - A gameboy in Rust",
            160 * scale,
            144 * scale)
        .resizable()
        .build()
    {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let mut renderer = match sdl2::render::RendererBuilder::new(window).software().build() {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };
    let mut texture = renderer.create_texture_streaming(sdl2::pixels::PixelFormatEnum::RGB24, (160, 144)).unwrap();

    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::spawn(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, cpu));

    let mut event_queue = sdl_context.event_pump().unwrap();
    'main : loop {
        let mut refreshed = false;
        'rx : loop {
            match sdl_rx.try_recv() {
                Err(Disconnected) => break 'main,
                Ok(_) => { if !refreshed { recalculate_screen(&mut renderer, &mut texture, &arc); refreshed = true } },
                Err(Empty) => break 'rx,
            }
        }
        for event in event_queue.wait_timeout_iter(2) {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Num1), .. }
                    => renderer.window_mut().unwrap().set_size(160, 144),
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::R), .. }
                    => renderer.window_mut().unwrap().set_size(160*scale, 144*scale),
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: Some(sdlkey), .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: Some(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();

    EXITCODE_SUCCESS
}

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

fn recalculate_screen(renderer: &mut sdl2::render::Renderer, texture: &mut sdl2::render::Texture, arc: &Arc<RwLock<Vec<u8>>>) {
    use std::io::Write;

    texture.with_lock(None, |ref mut buffer, _pitch| {
        buffer.write(&*arc.read().unwrap()).unwrap();
    }).unwrap();

    renderer.copy(&texture, None, None);
    renderer.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 construct_cpu(filename: &str, classic_mode: bool, output_serial: bool) -> Option<Device> {
    let opt_c = match classic_mode {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); return None; },
    };
    c.set_stdout(output_serial);
    Some(c)
}

fn cpuloop(cpu_tx: &Sender<()>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RwLock<Vec<u8>>>, cpu: Device) {
    let mut c = cpu;
    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194304f64 / 1000.0 * 8.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                *arc.write().unwrap() = c.get_gpu_data().to_vec();
                if cpu_tx.send(()).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        'rx : loop {
            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) => break 'rx,
                Err(Disconnected) => break 'cpuloop,
            }
        }
    }
}

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>Switch to Mutex and use texture.update<commit_after>#![crate_name = "rboy"]

extern crate clap;
extern crate sdl2;
extern crate rboy;

use rboy::device::Device;
use std::sync::{Arc,Mutex};
use std::sync::mpsc::{Sender,Receiver};
use std::sync::mpsc::TryRecvError::{Disconnected,Empty};
use std::error::Error;

const EXITCODE_SUCCESS : i32 = 0;
const EXITCODE_CPULOADFAILS : i32 = 2;

fn main() {
    let exit_status = real_main();
    if exit_status != EXITCODE_SUCCESS {
        std::process::exit(exit_status);
    }
}

fn real_main() -> i32 {
    let matches = clap::App::new("rboy")
        .version("0.1")
        .author("Mathijs van de Nes")
        .about("A Gameboy Colour emulator written in Rust")
        .arg(clap::Arg::with_name("filename")
             .help("Sets the ROM file to load")
             .required(true))
        .arg(clap::Arg::with_name("serial")
             .help("Prints the data from the serial port to stdout")
             .short("s")
             .long("serial"))
        .arg(clap::Arg::with_name("classic")
             .help("Forces the emulator to run in classic Gameboy mode")
             .short("c")
             .long("classic"))
        .arg(clap::Arg::with_name("scale")
             .help("Sets the scale of the interface. Default: 2")
             .short("x")
             .long("scale")
             .validator(|s|
                 match s.parse::<u32>() {
                     Err(e) => Err(format!("Could not parse scale: {}", e.description())),
                     Ok(s) if s < 1 => Err("Scale must be at least 1".to_owned()),
                     Ok(s) if s > 8 => Err("Scale may be at most 8".to_owned()),
                     Ok(..) => Ok(()),
                 })
             .takes_value(true))
        .get_matches();

    let opt_serial = matches.is_present("serial");
    let opt_classic = matches.is_present("classic");
    let filename = matches.value_of("filename").unwrap();
    let scale = matches.value_of("scale").unwrap_or("2").parse::<u32>().unwrap();

    let cpu = construct_cpu(filename, opt_classic, opt_serial);
    if cpu.is_none() { return EXITCODE_CPULOADFAILS; }
    let cpu = cpu.unwrap();

    let sdl_context = sdl2::init().unwrap();
    let sdl_video = sdl_context.video().ok().expect("Could not open video");
    let window = match sdl2::video::WindowBuilder::new(
            &sdl_video,
            "RBoy - A gameboy in Rust",
            160 * scale,
            144 * scale)
        .resizable()
        .build()
    {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let mut renderer = match sdl2::render::RendererBuilder::new(window).software().build() {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };
    let mut texture = renderer.create_texture_streaming(sdl2::pixels::PixelFormatEnum::RGB24, (160, 144)).unwrap();

    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(Mutex::new(rawscreen));
    let arc2 = arc.clone();

    let cpuloop_thread = std::thread::spawn(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, cpu));

    let mut event_queue = sdl_context.event_pump().unwrap();
    'main : loop {
        let mut refreshed = false;
        'rx : loop {
            match sdl_rx.try_recv() {
                Err(Disconnected) => break 'main,
                Ok(_) => { if !refreshed { recalculate_screen(&mut renderer, &mut texture, &arc); refreshed = true } },
                Err(Empty) => break 'rx,
            }
        }
        for event in event_queue.wait_timeout_iter(2) {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Num1), .. }
                    => renderer.window_mut().unwrap().set_size(160, 144),
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::R), .. }
                    => renderer.window_mut().unwrap().set_size(160*scale, 144*scale),
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: Some(sdlkey), .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: Some(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();

    EXITCODE_SUCCESS
}

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

fn recalculate_screen(renderer: &mut sdl2::render::Renderer, texture: &mut sdl2::render::Texture, arc: &Arc<Mutex<Vec<u8>>>) {
    texture.update(None, &*arc.lock().unwrap().clone(), 160*3).unwrap();

    renderer.clear();
    renderer.copy(&texture, None, None);
    renderer.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 construct_cpu(filename: &str, classic_mode: bool, output_serial: bool) -> Option<Device> {
    let opt_c = match classic_mode {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); return None; },
    };
    c.set_stdout(output_serial);
    Some(c)
}

fn cpuloop(cpu_tx: &Sender<()>, cpu_rx: &Receiver<GBEvent>, arc: Arc<Mutex<Vec<u8>>>, cpu: Device) {
    let mut c = cpu;
    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194304f64 / 1000.0 * 8.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                *arc.lock().unwrap() = c.get_gpu_data().to_vec();
                if cpu_tx.send(()).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        'rx : loop {
            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) => break 'rx,
                Err(Disconnected) => break 'cpuloop,
            }
        }
    }
}

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>#![warn(rust_2018_idioms)]

use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};

use futures::FutureExt;
use std::env;
use std::error::Error;

use nix::sys::socket::{sockopt,getsockopt,InetAddr};
use std::os::unix::io::AsRawFd;
use std::net::SocketAddr;

use bytes::BytesMut;

const BUF_SIZE: usize = 1024 * 1024;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let listen_addr = env::args()
        .nth(1)
        .unwrap_or_else(|| "127.0.0.1:1080".to_string());

    println!("Listening on: {}", listen_addr);

    let listener = TcpListener::bind(listen_addr).await?;

    while let Ok((inbound, client_addr)) = listener.accept().await {
        let server_addr = getsockopt(inbound.as_raw_fd(), sockopt::OriginalDst).unwrap();
        let server_addr = InetAddr::V4(server_addr).to_std();
        eprintln!("[INFO]{} -> {}: connection incoming", client_addr, server_addr);
        if inbound.local_addr().unwrap() == server_addr {
            eprintln!("[WARM]{} -> {}: connection from local network", client_addr, server_addr);
            continue;
        }
        let transfer = transfer(inbound, server_addr).map(move |r| {
            if let Err(e) = r {
                println!("[INFO]{} -> {}: connection closed with error; {}", client_addr, server_addr, e);
            }
        });
        tokio::spawn(transfer);
    }

    Ok(())
}

async fn transfer(mut inbound: TcpStream, proxy_addr: SocketAddr) -> Result<(), Box<dyn Error>> {
    let mut outbound = TcpStream::connect(proxy_addr).await?;

    inbound.set_nodelay(true)?;
    outbound.set_nodelay(true)?;

    let client_addr = inbound.peer_addr().unwrap();
    let server_addr = outbound.peer_addr().unwrap();

    let (ri, wi) = inbound.split();
    let (ro, wo) = outbound.split();

    tokio::try_join!(
        copy_one_direction(ro, wi),
        copy_one_direction(ri, wo)
    )?;
    eprintln!("[INFO]{} -> {}: connection closed", client_addr, server_addr);

    Ok(())
}

async fn copy_one_direction<T, U>(mut from: T, mut to: U) -> std::io::Result<()>
    where T: AsyncReadExt + Unpin, U: AsyncWriteExt + Unpin
{
    let mut buf = BytesMut::with_capacity(BUF_SIZE);
    loop {
        let n = from.read_buf(&mut buf).await?;
        if n == 0 { break }
        let n = to.write_buf(&mut buf).await?;
        if n == 0 { break }
    }
    to.shutdown().await
}

<commit_msg>Cargo fmt<commit_after>#![warn(rust_2018_idioms)]

use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};

use futures::FutureExt;
use std::env;
use std::error::Error;

use nix::sys::socket::{getsockopt, sockopt, InetAddr};
use std::net::SocketAddr;
use std::os::unix::io::AsRawFd;

use bytes::BytesMut;

const BUF_SIZE: usize = 1024 * 1024;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let listen_addr = env::args()
        .nth(1)
        .unwrap_or_else(|| "127.0.0.1:1080".to_string());

    println!("Listening on: {}", listen_addr);

    let listener = TcpListener::bind(listen_addr).await?;

    while let Ok((inbound, client_addr)) = listener.accept().await {
        let server_addr = getsockopt(inbound.as_raw_fd(), sockopt::OriginalDst).unwrap();
        let server_addr = InetAddr::V4(server_addr).to_std();
        eprintln!(
            "[INFO]{} -> {}: connection incoming",
            client_addr, server_addr
        );
        if inbound.local_addr().unwrap() == server_addr {
            eprintln!(
                "[WARM]{} -> {}: connection from local network",
                client_addr, server_addr
            );
            continue;
        }
        let transfer = transfer(inbound, server_addr).map(move |r| {
            if let Err(e) = r {
                println!(
                    "[INFO]{} -> {}: connection closed with error; {}",
                    client_addr, server_addr, e
                );
            }
        });
        tokio::spawn(transfer);
    }

    Ok(())
}

async fn transfer(mut inbound: TcpStream, proxy_addr: SocketAddr) -> Result<(), Box<dyn Error>> {
    let mut outbound = TcpStream::connect(proxy_addr).await?;

    inbound.set_nodelay(true)?;
    outbound.set_nodelay(true)?;

    let client_addr = inbound.peer_addr().unwrap();
    let server_addr = outbound.peer_addr().unwrap();

    let (ri, wi) = inbound.split();
    let (ro, wo) = outbound.split();

    tokio::try_join!(copy_one_direction(ro, wi), copy_one_direction(ri, wo))?;
    eprintln!(
        "[INFO]{} -> {}: connection closed",
        client_addr, server_addr
    );

    Ok(())
}

async fn copy_one_direction<T, U>(mut from: T, mut to: U) -> std::io::Result<()>
where
    T: AsyncReadExt + Unpin,
    U: AsyncWriteExt + Unpin,
{
    let mut buf = BytesMut::with_capacity(BUF_SIZE);
    loop {
        let n = from.read_buf(&mut buf).await?;
        if n == 0 {
            break;
        }
        let n = to.write_buf(&mut buf).await?;
        if n == 0 {
            break;
        }
    }
    to.shutdown().await
}
<|endoftext|>