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

extern crate phosphorus;
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;

use gfx::traits::*;
use phosphorus::widget::*;

//static HELLO_MARKUP: &'static str = include_str!("assets/hello-markup.jade");

fn main() {
    // Set up our window
    let (mut stream, mut device, mut factory) = {
        let window = glutin::WindowBuilder::new()
            .with_vsync()
            .with_dimensions(600, 500)
            .with_title(String::from("Phosphorus Widgets"))
            .build_strict().unwrap();
        gfx_window_glutin::init(window)
    };

    // Set up our Phosphorus UI
    let root = phosphorus::widget::LayoutBuilder::new()
        .with_background_color([21, 23, 24])
        .with_widget(phosphorus::widget::TextBuilder::new()
            .with_text("Hello World!")
            .build_boxed())
        .build();
    let mut gui = phosphorus::Gui::new(&mut device, root, |d: &mut gfx_device_gl::Device| d.spawn_factory());

    // Run our actual UI loop
    'main: loop {
        // Quit when the window is closed
        for event in stream.out.window.poll_events() {
            match event {
                glutin::Event::Closed => break 'main,
                glutin::Event::MouseMoved((x, y)) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseMoved([x, y])),
                glutin::Event::MouseInput(glutin::ElementState::Released, _) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseClick),
                _ => (),
            }
        }

        stream.clear(gfx::ClearData {color: [1.0, 1.0, 1.0, 1.0], depth: 1.0, stencil: 0});

        // Render our actual GUI
        gui.render(&mut factory, &mut stream);

        // Show the rendered to buffer on the screen
        //stream.present(&mut device); ICE!
        stream.flush(&mut device);
        stream.out.window.swap_buffers();
        device.cleanup();
    }
}
<commit_msg>Work in progress commit<commit_after>// Copyright 2015 The Athena Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate phosphorus;
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;

use std::env;
use std::cell::RefCell;
use std::rc::Rc;
use gfx::traits::*;
use phosphorus::Gui;
use phosphorus::widget::{ButtonBuilder, Layout, LayoutBuilder, TextBuilder};

struct CancelationToken {
    canceled: bool
}

fn main() {
    // This is all a bunch of placeholder code that needs to be refactored into something coherent

    // Get the target path to open the editor at
    let path = match env::args().nth(1) {
        Some(v) => v,
        None => {
            display_error("No path given!");
            return;
        }
    };

    // Set up our Phosphorus UI
    let token = Rc::new(RefCell::new(CancelationToken { canceled: false }));
    let layout = LayoutBuilder::<gfx_device_gl::Resources>::new()
        .with_background_color([21, 23, 24])
        .with_widget(TextBuilder::new()
            .with_text(&format!("Opened: {}!", path))
            .build_boxed())
        .build();

    display_gui(layout, token);
}

fn display_error(text: &str) {
    let token = Rc::new(RefCell::new(CancelationToken { canceled: false }));
    let token_clone = token.clone();
    let layout = LayoutBuilder::<gfx_device_gl::Resources>::new()
        .with_background_color([21, 23, 24])
        .with_widget(TextBuilder::new()
            .with_text(text)
            .build_boxed())
        .with_widget(ButtonBuilder::new()
            .with_text("Ok")
            .with_callback(Box::new(move || token_clone.borrow_mut().canceled = true))
            .build_boxed())
        .build();

    display_gui(layout, token);
}

fn display_gui(layout: Layout<gfx_device_gl::Resources>, token: Rc<RefCell<CancelationToken>>)
{
    // Set up our window
    let (mut stream, mut device, mut factory) = {
        let window = glutin::WindowBuilder::new()
            .with_vsync()
            .with_dimensions(600, 500)
            .with_title(String::from("Phosphorus Widgets"))
            .build_strict().unwrap();
        gfx_window_glutin::init(window)
    };

    let mut gui = Gui::new(&mut device, layout, |d: &mut gfx_device_gl::Device| d.spawn_factory());

    // Run our actual UI loop
    'main: loop {
        if token.borrow().canceled {
            break 'main;
        }

        // Quit when the window is closed
        for event in stream.out.window.poll_events() {
            match event {
                glutin::Event::Closed => break 'main,
                glutin::Event::MouseMoved((x, y)) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseMoved([x, y])),
                glutin::Event::MouseInput(glutin::ElementState::Released, _) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseClick),
                _ => (),
            }
        }

        stream.clear(gfx::ClearData {color: [1.0, 1.0, 1.0, 1.0], depth: 1.0, stencil: 0});

        // Render our actual GUI
        gui.render(&mut factory, &mut stream);

        // Show the rendered to buffer on the screen
        //stream.present(&mut device); ICE!
        stream.flush(&mut device);
        stream.out.window.swap_buffers();
        device.cleanup();
    }
}
<|endoftext|>


<commit_before>#![feature(slice_bytes)]

extern crate libc;
extern crate errno;

use std::io::Write;

const ETH_P_ARP: u16 = 0x0806;
const IFNAMSIZ: usize = 16; // net/if.h
const SIOCGIFINDEX: libc::c_int = 0x8933;

#[allow(non_camel_case_types)]
struct ifreq {
    ifr_name: [u8; IFNAMSIZ],
    ifr_ifindex: libc::c_int
}

fn ifindex_from_ifname(ifname: &str, sock: libc::c_int) -> libc::c_int {
    let mut ifr = ifreq {
        ifr_name: [0; IFNAMSIZ],
        ifr_ifindex: 0
    };
    std::slice::bytes::copy_memory(ifname.as_bytes(), &mut ifr.ifr_name);
    if unsafe { libc::funcs::bsd44::ioctl(sock, SIOCGIFINDEX, &ifr) } == -1 {
        let err = errno::errno();
        panic!("Error getting ifindex: {} ({})", err, err.0);
    }
    return ifr.ifr_ifindex;

}

fn main() {
    let mut args = std::env::args();
    if args.len() != 3 {
        std::io::stderr().write("Usage: arpmasqd LISTEN_ADDR SEND_ADDR\n".as_ref()).unwrap();
        panic!("number of arguments");
    }
    args.next();
    //unwrap is ok because arg count has been checked
    let listen_iface = args.next().unwrap();
    let send_addr = args.next().unwrap();
    let listen_socket = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_RAW, ETH_P_ARP.to_be() as i32) };
    if listen_socket == -1 {
        let err = errno::errno();
        panic!("Error opening socket: {} ({})", err, err.0);
    }
    let listen_sockaddr = libc::sockaddr_ll {
        sll_family: libc::AF_PACKET as u16,
        sll_protocol: ETH_P_ARP.to_be(),
        sll_ifindex: ifindex_from_ifname(&listen_iface, listen_socket),
        sll_hatype: 0,
        sll_pkttype: 0,
        sll_halen: 0,
        sll_addr: [0; 8]
    };
    let bind_result = unsafe { libc::bind(listen_socket, std::mem::transmute(&listen_sockaddr), std::mem::size_of_val(&listen_sockaddr) as u32) };
    if bind_result == -1 {
        let err = errno::errno();
        panic!("Error binding socket: {} ({})", err, err.0);
    }
}
<commit_msg>receive a packet<commit_after>#![feature(slice_bytes)]

extern crate libc;
extern crate errno;

use std::io::Write;

const ETH_P_ARP: u16 = 0x0806;
const IFNAMSIZ: usize = 16; // net/if.h
const SIOCGIFINDEX: libc::c_int = 0x8933;
const RECV_BUF_LEN: usize = 1500;

#[allow(non_camel_case_types)]
struct ifreq {
    ifr_name: [u8; IFNAMSIZ],
    ifr_ifindex: libc::c_int
}

fn ifindex_from_ifname(ifname: &str, sock: libc::c_int) -> libc::c_int {
    let mut ifr = ifreq {
        ifr_name: [0; IFNAMSIZ],
        ifr_ifindex: 0
    };
    std::slice::bytes::copy_memory(ifname.as_bytes(), &mut ifr.ifr_name);
    if unsafe { libc::funcs::bsd44::ioctl(sock, SIOCGIFINDEX, &ifr) } == -1 {
        let err = errno::errno();
        panic!("Error getting ifindex: {} ({})", err, err.0);
    }
    return ifr.ifr_ifindex;

}

fn main() {
    let mut args = std::env::args();
    if args.len() != 3 {
        std::io::stderr().write("Usage: arpmasqd LISTEN_ADDR SEND_ADDR\n".as_ref()).unwrap();
        panic!("number of arguments");
    }
    args.next();
    //unwrap is ok because arg count has been checked
    let listen_iface = args.next().unwrap();
    let send_addr = args.next().unwrap();
    let listen_socket = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_RAW, ETH_P_ARP.to_be() as i32) };
    if listen_socket == -1 {
        let err = errno::errno();
        panic!("Error opening socket: {} ({})", err, err.0);
    }
    let listen_sockaddr = libc::sockaddr_ll {
        sll_family: libc::AF_PACKET as u16,
        sll_protocol: ETH_P_ARP.to_be(),
        sll_ifindex: ifindex_from_ifname(&listen_iface, listen_socket),
        sll_hatype: 0,
        sll_pkttype: 0,
        sll_halen: 0,
        sll_addr: [0; 8]
    };
    let bind_result = unsafe { libc::bind(listen_socket, std::mem::transmute(&listen_sockaddr), std::mem::size_of_val(&listen_sockaddr) as u32) };
    if bind_result == -1 {
        let err = errno::errno();
        panic!("Error binding socket: {} ({})", err, err.0);
    }

    let buf = [0u8; RECV_BUF_LEN];
    let recv_sockaddr = unsafe { std::mem::zeroed::<libc::sockaddr_ll>() };
    let mut recv_sockaddr_len: u32 = std::mem::size_of_val(&recv_sockaddr) as u32;
    let recv_result = unsafe { libc::recvfrom(listen_socket, std::mem::transmute(&buf), RECV_BUF_LEN as u64, 0, std::mem::transmute(&recv_sockaddr), &mut recv_sockaddr_len) };
    if recv_result == -1 {
        let err = errno::errno();
        panic!("Error in recvfrom: {} ({})", err, err.0);
    }
}
<|endoftext|>


<commit_before>// main.rs
// Copyright 2016 Alexander Ronald Altman.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate hyper;

fn main() {
    println!("Hello, world!");
}
<commit_msg>Macros<commit_after>// main.rs
// Copyright 2016 Alexander Ronald Altman.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[macro_use]
extern crate hyper;

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


<commit_before>#![feature(convert, collections, unicode)]
#![allow(unused_variables)]

use std::collections::{HashMap, VecDeque};
use std::hash::{Hash, Hasher};
use std::io;
use std::io::prelude::*;
use std::rc::Rc;
use std::fmt;

extern crate rustc_unicode;
use rustc_unicode::str::UnicodeStr;

type Int = i32;
type Double = f64;

type Dictionary = HashMap<String, Rc<Entry>>;
type Statement = VecDeque<Op>;
type Stack = Vec<Int>;
type NativeFn = Box<Fn(&mut Interpreter, &mut Stack)>;

#[derive(Clone, Debug)]
enum Op {
	Number(Int),
	Word(Rc<Entry>),
}

enum Code {
	Native(NativeFn),
	Forth(Statement),
}

#[derive(Clone)]
struct Entry {
	name: String,
	code: Rc<Code>,
}

impl PartialEq for Entry {
	fn eq(&self, other: &Self) -> bool { self.name == other.name }
}

impl Eq for Entry {}

impl Hash for Entry {
	fn hash<H>(&self, state: &mut H) where H: Hasher {
		self.name.hash(state)
	}
}

impl fmt::Debug for Entry {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
		self.name.fmt(fmt)
	}
}

impl Entry {
	fn new(name: &str, code: Code) -> Entry {
		Entry {
			name: String::from_str(name),
			code: Rc::new(code),
		}
	}

	fn from_statement(name: &str, stmt: Statement) -> Entry {
		Entry {
			name: name.to_string(),
			code: Rc::new(Code::Forth(stmt)),
		}
	}
}

impl Code {
	fn run(&self, interp: &mut Interpreter, stack: &mut Stack) {
		match self {
			&Code::Native(ref f) => {
				f(interp, stack);
			},
			&Code::Forth(ref statement) => {
				for ref op in statement {
					match *op {
						&Op::Number(ref n) => { stack.push(*n) }
						&Op::Word(ref w) => {
							(*w).code.run(interp, stack);
						},
					}
				}
			},
		}
	}
}

trait StatementExt {
	fn run(&self, &mut Interpreter);
}

impl StatementExt for Statement {
	fn run(&self, interp: &mut Interpreter) {
		let mut stack = Stack::new();
		for word in self {
			match word {
				&Op::Number(n) => {
					stack.push(n);
				},
				&Op::Word(ref w) => {
					let code = interp.dictionary[w.name.as_str()].code.clone();
					code.run(interp, &mut stack);
				},
			};
		};
	}
}

trait DictionaryExt {
	fn insert_entry(&mut self, Entry);
}

impl DictionaryExt for Dictionary {
	fn insert_entry(&mut self, entry: Entry) {
		self.insert(entry.name.clone(), Rc::new(entry));
	}
}

struct Interpreter {
    dictionary: Dictionary,
}

macro_rules! binary_entry {
	($name:expr, $o:expr) => {
		Entry::new($name,
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let b = stack.pop().unwrap();
					let a = stack.pop().unwrap();
					stack.push($o(a, b));
				})),
			)
	}
}

macro_rules! unary_entry {
	($name:expr, $o:expr) => {
		Entry::new($name,
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let a = stack.pop().unwrap();
					stack.push($o(a));
				})),
			)
	}
}

macro_rules! nonary_entry {
	($name:expr, $o:expr) => {
		Entry::new($name,
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					Op::Number($o);
				})),
			)
	}
}

impl Interpreter {
	fn new() -> Interpreter {
		let mut dict = Dictionary::new();

		dict.insert_entry(binary_entry!("/", ::std::ops::Div::div));
		dict.insert_entry(binary_entry!("*", ::std::ops::Mul::mul));
		dict.insert_entry(binary_entry!("+", ::std::ops::Add::add));
		dict.insert_entry(binary_entry!("-", ::std::ops::Sub::sub));
		dict.insert_entry(binary_entry!("mod", ::std::ops::Rem::rem));
		dict.insert_entry(binary_entry!("and", ::std::ops::BitAnd::bitand));
		dict.insert_entry(binary_entry!("or", ::std::ops::BitOr::bitor));
		dict.insert_entry(binary_entry!("xor", ::std::ops::BitXor::bitxor));
		dict.insert_entry(binary_entry!("rshift", ::std::ops::Shr::shr));
		dict.insert_entry(binary_entry!("lshift", ::std::ops::Shl::shl));

		dict.insert_entry(unary_entry!("negate", ::std::ops::Neg::neg));
		dict.insert_entry(unary_entry!("not", ::std::ops::Not::not));

		dict.insert_entry(nonary_entry!("bye", ::std::process::exit(0)));

		dict.insert_entry(Entry::new("<",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let y = stack.pop().unwrap();
					let x = stack.pop().unwrap();
					stack.push(if x < y { 1 } else { 0 });
				})),
			));

		dict.insert_entry(Entry::new(">",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let y = stack.pop().unwrap();
					let x = stack.pop().unwrap();
					stack.push(if x > y { 1 } else { 0 });
				})),
			));

		dict.insert_entry(Entry::new("=",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let y = stack.pop().unwrap();
					let x = stack.pop().unwrap();
					stack.push(if x == y { 1 } else { 0 });
				})),
			));

		dict.insert_entry(Entry::new("0<",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(if x < 0 { 1 } else { 0 });
				})),
			));

		dict.insert_entry(Entry::new("0=",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(if x == 0 { 1 } else { 0 });
				})),
			));

		dict.insert_entry(Entry::new("0>",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(if x > 0 { 1 } else { 0 });
				})),
			));

		dict.insert_entry(Entry::new("1+",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(x + 1);
				})),
			));

		dict.insert_entry(Entry::new("1-",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(x - 1);
				})),
			));

		dict.insert_entry(Entry::new("2+",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(x + 2);
				})),
			));

		dict.insert_entry(Entry::new("2-",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(x - 2);
				})),
			));

		dict.insert_entry(Entry::new("2/",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(x >> 1); // Per fst83 standard
				})),
			));

		dict.insert_entry(Entry::new("dup",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					stack.push(x.clone());
					stack.push(x);
				})),
			));

		dict.insert_entry(Entry::new("?dup",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					if x != 0 {
						stack.push(x.clone());
						stack.push(x);
					} else {
						stack.push(x);
					}
				})),
			));

		dict.insert_entry(Entry::new("over",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					let y = stack.pop().unwrap();
					stack.push(x.clone());
					stack.push(y);
					stack.push(x);
				})),
			));

		dict.insert_entry(Entry::new("swap",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					let y = stack.pop().unwrap();
					stack.push(x);
					stack.push(y);
				})),
			));

		dict.insert_entry(Entry::new("rot",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					let x = stack.pop().unwrap();
					let y = stack.pop().unwrap();
					let z = stack.pop().unwrap();
					stack.push(y);
					stack.push(x);
					stack.push(z);
				})),
			));

		dict.insert_entry(Entry::new("dump",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					println!("ds =  {:?} ", stack);
				})),
			));

		dict.insert_entry(Entry::new("cr",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					println!("");
				})),
			));

		dict.insert_entry(Entry::new(".",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| {
					println!("{}", stack.pop().unwrap());
				})),
			));

		let square = {
			let mut s = Statement::new();
			s.push_back(Op::Word(dict.get("dup").unwrap().clone()));
			s.push_back(Op::Word(dict.get("*").unwrap().clone()));
			Code::Forth(s)
		};
		dict.insert_entry(Entry::new("square", square));

		Interpreter {
			dictionary: dict,
		}
	}

	fn exec(&mut self, statement: &str) {
		let mut words = statement.split(|c: char| c.is_whitespace());
		let mut stmt = Statement::new();
		while let Some(word) = words.next() { match word {
			"(" => while let Some(w) = words.next() { if w == ")" { break; } },
			"\\" => while let Some(w) = words.next() { if w == "\n" { break; } },
			":" => {
				let mut comp = Statement::new();
				let name = words.next().unwrap();
				while let Some(w) = words.next() {
					if w == ";" { break; }
					match w.parse::<i32>() {
						Ok(n) => comp.push_back(Op::Number(n)),
						Err(_) => comp.push_back(Op::Word(self.dictionary.get(w).unwrap().clone())),
					}
				}
				self.dictionary.insert_entry(Entry::from_statement(name, comp));
			},
			_ => match word.parse::<i32>() {
				Ok(n) => {
					stmt.push_back(Op::Number(n));
				},
				Err(e) => {
					if let Some(elem) = self.dictionary.get(word) {
						stmt.push_back(Op::Word(elem.clone()));
					} else {
						println!("Invalid word: {}", word);
						return;
					}
				},
			},

		}}

		stmt.run(self);
	}
}

fn main() {
	println!("forth-rs version 0.1.0");
	println!("Type 'bye' to exit");
    let mut interp = Interpreter::new();
    let mut stdin = io::stdin();
    loop {
    	print!("Forth> ");
    	io::stdout().flush().ok().expect("Could not flush stdout");
    	let mut line = String::new();
    	stdin.read_line(&mut line).ok().expect("Unable to read from stdin");
    	let result = interp.exec(line.trim());
    }
}
<commit_msg>Add initial error handling.<commit_after>#![feature(convert, collections, unicode)]
#![allow(unused_variables)]

use std::collections::{HashMap, VecDeque};
use std::hash::{Hash, Hasher};
use std::io;
use std::io::prelude::*;
use std::rc::Rc;
use std::fmt;

extern crate rustc_unicode;
use rustc_unicode::str::UnicodeStr;

type Int = i32;
type Double = f64;

#[derive(Debug, Clone)]
enum ForthError {
    EmptyStack,
    UnterminatedComment,
    WordNotFound,
    UnterminatedWordDefinition,
    WordNameNotFound,
}

type ForthResult<T> = std::result::Result<T, ForthError>;

type Dictionary = HashMap<String, Rc<Entry>>;
type Statement = VecDeque<Op>;
type Stack = Vec<Int>;
type NativeFn = Box<Fn(&mut Interpreter, &mut Stack) -> ForthResult<()>>;

#[derive(Clone, Debug)]
enum Op {
	Number(Int),
	Word(Rc<Entry>),
}

enum Code {
	Native(NativeFn),
	Forth(Statement),
}

#[derive(Clone)]
struct Entry {
	name: String,
	code: Rc<Code>,
}

impl PartialEq for Entry {
	fn eq(&self, other: &Self) -> bool { self.name == other.name }
}

impl Eq for Entry {}

impl Hash for Entry {
	fn hash<H>(&self, state: &mut H) where H: Hasher {
		self.name.hash(state)
	}
}

impl fmt::Debug for Entry {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
		self.name.fmt(fmt)
	}
}

impl Entry {
	fn new(name: &str, code: Code) -> Entry {
		Entry {
			name: String::from_str(name),
			code: Rc::new(code),
		}
	}

	fn from_statement(name: &str, stmt: Statement) -> Entry {
		Entry {
			name: name.to_string(),
			code: Rc::new(Code::Forth(stmt)),
		}
	}
}

impl Code {
	fn run(&self, interp: &mut Interpreter, stack: &mut Stack) -> ForthResult<()> {
		match self {
			&Code::Native(ref f) => {
				return f(interp, stack);
			},
			&Code::Forth(ref statement) => {
				for ref op in statement {
					match *op {
						&Op::Number(ref n) => { stack.push(*n) }
						&Op::Word(ref w) => {
							try!((*w).code.run(interp, stack));
						},
					}
				}
			},
		}
		Ok(())
	}
}

trait StatementExt {
	fn run(&self, &mut Interpreter) -> ForthResult<Stack>;
}

impl StatementExt for Statement {
	fn run(&self, interp: &mut Interpreter) -> ForthResult<Stack> {
		let mut stack = Stack::new();
		for word in self {
			match word {
				&Op::Number(n) => {
					stack.push(n);
				},
				&Op::Word(ref w) => {
					let code = interp.dictionary[w.name.as_str()].code.clone();
					try!(code.run(interp, &mut stack));
				},
			};
		};
		Ok(stack)
	}
}

trait DictionaryExt {
	fn insert_entry(&mut self, Entry);
}

impl DictionaryExt for Dictionary {
	fn insert_entry(&mut self, entry: Entry) {
		self.insert(entry.name.clone(), Rc::new(entry));
	}
}

struct Interpreter {
    dictionary: Dictionary,
    last_result: Option<Stack>,
}

macro_rules! try_stack {
	($x:expr) => {
		match $x {
			Some(i) => i,
			None => return Err(ForthError::EmptyStack),
		}
	}
}

macro_rules! binary_entry {
	($name:expr, $o:expr) => {
		Entry::new($name,
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let b = try_stack!(stack.pop());
					let a = try_stack!(stack.pop());
					stack.push($o(a, b));
					Ok(())
				})),
			)
	}
}

macro_rules! unary_entry {
	($name:expr, $o:expr) => {
		Entry::new($name,
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let a = try_stack!(stack.pop());
					stack.push($o(a));
					Ok(())
				})),
			)
	}
}

macro_rules! nonary_entry {
	($name:expr, $o:expr) => {
		Entry::new($name,
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					Op::Number($o);
					Ok(())
				})),
			)
	}
}

impl Interpreter {
	fn new() -> Interpreter {
		let mut dict = Dictionary::new();

		dict.insert_entry(binary_entry!("/", ::std::ops::Div::div));
		dict.insert_entry(binary_entry!("*", ::std::ops::Mul::mul));
		dict.insert_entry(binary_entry!("+", ::std::ops::Add::add));
		dict.insert_entry(binary_entry!("-", ::std::ops::Sub::sub));
		dict.insert_entry(binary_entry!("mod", ::std::ops::Rem::rem));
		dict.insert_entry(binary_entry!("and", ::std::ops::BitAnd::bitand));
		dict.insert_entry(binary_entry!("or", ::std::ops::BitOr::bitor));
		dict.insert_entry(binary_entry!("xor", ::std::ops::BitXor::bitxor));
		dict.insert_entry(binary_entry!("rshift", ::std::ops::Shr::shr));
		dict.insert_entry(binary_entry!("lshift", ::std::ops::Shl::shl));

		dict.insert_entry(unary_entry!("negate", ::std::ops::Neg::neg));
		dict.insert_entry(unary_entry!("not", ::std::ops::Not::not));

		dict.insert_entry(nonary_entry!("bye", ::std::process::exit(0)));

		dict.insert_entry(Entry::new("$",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(interp.last_result.clone());
					for v in x {
						stack.push(v);
					}
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("<",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let y = try_stack!(stack.pop());
					let x = try_stack!(stack.pop());
					stack.push(if x < y { 1 } else { 0 });
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new(">",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let y = try_stack!(stack.pop());
					let x = try_stack!(stack.pop());
					stack.push(if x > y { 1 } else { 0 });
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("=",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let y = try_stack!(stack.pop());
					let x = try_stack!(stack.pop());
					stack.push(if x == y { 1 } else { 0 });
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("0<",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(if x < 0 { 1 } else { 0 });
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("0=",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(if x == 0 { 1 } else { 0 });
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("0>",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(if x > 0 { 1 } else { 0 });
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("1+",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(x + 1);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("1-",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(x - 1);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("2+",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(x + 2);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("2-",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(x - 2);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("2/",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(x >> 1); // Per fst83 standard
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("dup",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					stack.push(x.clone());
					stack.push(x);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("?dup",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					if x != 0 {
						stack.push(x.clone());
						stack.push(x);
					} else {
						stack.push(x);
					}
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("over",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					let y = try_stack!(stack.pop());
					stack.push(x.clone());
					stack.push(y);
					stack.push(x);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("swap",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					let y = try_stack!(stack.pop());
					stack.push(x);
					stack.push(y);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("rot",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					let x = try_stack!(stack.pop());
					let y = try_stack!(stack.pop());
					let z = try_stack!(stack.pop());
					stack.push(y);
					stack.push(x);
					stack.push(z);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("dump",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					println!("ds =  {:?} ", stack);
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new("cr",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					println!("");
					Ok(())
				})),
			));

		dict.insert_entry(Entry::new(".",
				Code::Native(Box::new(|interp: &mut Interpreter, stack: &mut Stack| -> ForthResult<()> {
					println!("{}", try_stack!(stack.pop()));
					Ok(())
				})),
			));

		let square = {
			let mut s = Statement::new();
			s.push_back(Op::Word(dict.get("dup").unwrap().clone()));
			s.push_back(Op::Word(dict.get("*").unwrap().clone()));
			s
		};
		dict.insert_entry(Entry::from_statement("square", square));

		Interpreter {
			dictionary: dict,
			last_result: None,
		}
	}

	fn exec(&mut self, statement: &str) -> ForthResult<()> {
		let mut words = statement.split(|c: char| c.is_whitespace());
		let mut stmt = Statement::new();
		while let Some(word) = words.next() { match word {
			"(" => while let Some(w) = words.next() { if w == ")" { break; } },
			"\\" => while let Some(w) = words.next() { if w == "\n" { break; } },
			":" => {
				let mut comp = Statement::new();
				let name = match words.next() {
					Some(w) => w,
					None => return Err(ForthError::WordNameNotFound),
				};
				while let Some(w) = words.next() {
					if w == ";" {
						self.dictionary.insert_entry(Entry::from_statement(name, comp));
						return Ok(());
					}
					match w.parse::<i32>() {
						Ok(n) => comp.push_back(Op::Number(n)),
						Err(_) => comp.push_back(Op::Word(self.dictionary.get(w).unwrap().clone())),
					}
				}
				return Err(ForthError::UnterminatedWordDefinition);
			},
			_ => match word.parse::<i32>() {
				Ok(n) => {
					stmt.push_back(Op::Number(n));
				},
				Err(e) => {
					if let Some(elem) = self.dictionary.get(word) {
						stmt.push_back(Op::Word(elem.clone()));
					} else {
						return Err(ForthError::WordNotFound)
					}
				},
			},

		}}

		match stmt.run(self) {
			Err(e) => {
				self.last_result = None;
				Err(e)
			},
			Ok(s) => {
				self.last_result = Some(s);
				Ok(())
			}
		}
	}
}

fn main() {
	println!("forth-rs version 0.1.0");
	println!("Type 'bye' to exit");
    let mut interp = Interpreter::new();
    let mut stdin = io::stdin();
    loop {
    	print!("Forth> ");
    	io::stdout().flush().ok().expect("Could not flush stdout");
    	let mut line = String::new();
    	stdin.read_line(&mut line).ok().expect("Unable to read from stdin");
    	match interp.exec(line.trim()) {
    		Err(e) => println!("{:?}", e),
    		Ok(_) => {},
    	}
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use regex::Regex;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::str;

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan
const NO_EOF_NEWLINE: &str = "\x1b[2m"; // Faint

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes {
        lines.push(format!("{}{}{}", REMOVE, remove_line, NORMAL));
    }

    for add_line in adds {
        lines.push(format!("{}{}{}", ADD, add_line, NORMAL))
    }

    return lines;
}

/// Joins multiple lines into a single string.
///
/// The first character of each line is skipped, because it is assumed to be
/// either a `+` or a `-` in a diff.
///
/// Between each joined line a `\n` linefeed character is inserted.
#[must_use]
fn join_skip_first(lines: &[String]) -> String {
    let mut joined = String::new();
    for line in lines {
        if !joined.is_empty() {
            joined.push_str("\n")
        }
        joined.push_str(&line[1..]);
    }

    return joined;
}

#[must_use]
fn format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Join inputs by linefeeds into strings
    let adds = join_skip_first(adds);
    let removes = join_skip_first(removes);

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut adds: Vec<String> = Vec::new();
    let mut removes: Vec<String> = Vec::new();
    let input = BufReader::new(input);
    let mut output = BufWriter::new(output);
    for line in input.lines() {
        let line = line.unwrap();

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                output.write(line.as_bytes()).unwrap();
                output.write(b"\n").unwrap();
            }
            adds.clear();
            removes.clear();

            output.write(fixed_highlight.as_bytes()).unwrap();
            output.write(line.as_bytes()).unwrap();
            output.write(NORMAL.as_bytes()).unwrap();
            output.write(b"\n").unwrap();
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push(line);
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push(line);
            continue;
        }

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            output.write(line.as_bytes()).unwrap();
            output.write(b"\n").unwrap();
        }
        adds.clear();
        removes.clear();

        // Print current line
        output.write(line.as_bytes()).unwrap();
        output.write(b"\n").unwrap();
    }
    for line in format_adds_and_removes(&adds, &removes) {
        output.write(line.as_bytes()).unwrap();
        output.write(b"\n").unwrap();
    }
}

fn main() {
    highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_join_skip_first() {
        assert_eq!(
            join_skip_first(&["Xa".to_string(), "Xb".to_string()]),
            "a\nb"
        );
    }

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format_adds_and_removes(&empty, &empty), empty);

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string()], &empty),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string(), "+b".to_string()], &empty),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string()]),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string(), "-b".to_string()]),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&["+[quotes]".to_string()], &["-<quotes>".to_string()]),
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    REMOVE, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    fn remove(text: &str) -> String {
        return format!("{}{}{}", REMOVE, text, NORMAL);
    }

    fn add(text: &str) -> String {
        return format!("{}{}{}", ADD, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            remove(&format!("-hej{}⏎", INVERSE_VIDEO)),
            add("+hej"),
            format!("{}\\ No newline at end of file{}", NO_EOF_NEWLINE, NORMAL)
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }
}
<commit_msg>Refactor for readability<commit_after>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use regex::Regex;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::str;

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan
const NO_EOF_NEWLINE: &str = "\x1b[2m"; // Faint

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes {
        lines.push(format!("{}{}{}", REMOVE, remove_line, NORMAL));
    }

    for add_line in adds {
        lines.push(format!("{}{}{}", ADD, add_line, NORMAL))
    }

    return lines;
}

/// Joins multiple lines into a single string.
///
/// The first character of each line is skipped, because it is assumed to be
/// either a `+` or a `-` in a diff.
///
/// Between each joined line a `\n` linefeed character is inserted.
#[must_use]
fn join_skip_first(lines: &[String]) -> String {
    let mut joined = String::new();
    for line in lines {
        if !joined.is_empty() {
            joined.push_str("\n")
        }
        joined.push_str(&line[1..]);
    }

    return joined;
}

#[must_use]
fn format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Join inputs by linefeeds into strings
    let adds = join_skip_first(adds);
    let removes = join_skip_first(removes);

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn print(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    stream.write(text.as_bytes()).unwrap();
}

fn println(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    print(stream, &text);
    print(stream, "\n");
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut adds: Vec<String> = Vec::new();
    let mut removes: Vec<String> = Vec::new();
    let input = BufReader::new(input);
    let output = &mut BufWriter::new(output);
    for line in input.lines() {
        let line = line.unwrap();

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                println(output, &line);
            }
            adds.clear();
            removes.clear();

            print(output, fixed_highlight);
            print(output, &line);
            println(output, NORMAL);
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push(line);
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push(line);
            continue;
        }

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            println(output, &line);
        }
        adds.clear();
        removes.clear();

        // Print current line
        println(output, &line);
    }
    for line in format_adds_and_removes(&adds, &removes) {
        println(output, &line);
    }
}

fn main() {
    highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_join_skip_first() {
        assert_eq!(
            join_skip_first(&["Xa".to_string(), "Xb".to_string()]),
            "a\nb"
        );
    }

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format_adds_and_removes(&empty, &empty), empty);

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string()], &empty),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string(), "+b".to_string()], &empty),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string()]),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string(), "-b".to_string()]),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&["+[quotes]".to_string()], &["-<quotes>".to_string()]),
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    REMOVE, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    fn remove(text: &str) -> String {
        return format!("{}{}{}", REMOVE, text, NORMAL);
    }

    fn add(text: &str) -> String {
        return format!("{}{}{}", ADD, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            remove(&format!("-hej{}⏎", INVERSE_VIDEO)),
            add("+hej"),
            format!("{}\\ No newline at end of file{}", NO_EOF_NEWLINE, NORMAL)
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }
}
<|endoftext|>


<commit_before>
#[macro_use]
extern crate clap;
use clap::{Arg, App};

use std::path::PathBuf;
use std::ffi::OsString;
use std::error::Error;
use std::env;
use std::collections::HashSet;

use std::process::Command;
use std::fs::File;
use std::io::Write;

extern crate petgraph;
extern crate walkdir;
extern crate itertools;

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

mod path_utils;
mod dot_writer;
mod file_node;
mod hash_graph;

mod find_includes;
use find_includes::find_includes_in_tree;

// -----------------------------------------------------------------------------

// TODO: implement the command line arguments from the original 'cinclude2dot' project.
// Output from "cinclude2dot.pl --help":
// --debug       Display various debug info
// --exclude     Specify a regular expression of filenames to ignore
//              For example, ignore your test harnesses.
// --merge       Granularity of the diagram:
//              file - the default, treats each file as separate
//              module - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs
//              directory - merges directories into one node
// --groups      Cluster files or modules into directory groups
// --help        Display this help page.
// --include     Followed by a comma separated list of include search paths.
// --paths       Leaves relative paths in displayed filenames.
// --quotetypes  Select for parsing the files included by strip quotes or angle brackets:
//              both - the default, parse all headers.
//              angle - include only "system" headers included by anglebrackets (<>)
//              quote - include only "user" headers included by strip quotes ("")
// --src         Followed by a path to the source code, defaults to current directory

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum MergeType {
        file,
        module,
        directory
    }
}

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum QuoteTypes {
        both,
        angle,
        quote
    }
}

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("debug")
//            .long("debug")
//            .help("Display extra debug info")
//            .takes_value(true))
        .arg(Arg::with_name("exclude")
            .long("exclude")
            .help("Specify a regular expression of filenames to ignore. \nRust/RE2 \
                   syntax.\n\tExample: --exclude=\"test_|noisyFile\"")
            .takes_value(true))
//        .arg(Arg::with_name("merge")
//            .long("merge")
//            .help("Granularity of the diagram: \nfile - the default, treats each file as \
//                   separate \nmodule - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs \
//                   \ndirectory - merges directories into one node\n")
//            .possible_values(&MergeType::variants())
//            .default_value("file")
//            .takes_value(true))
//        .arg(Arg::with_name("groups")
//            .long("groups")
//            .help("Cluster files or modules into directory groups")
//            .takes_value(true))
        .arg(Arg::with_name("include")
            .long("include")
            .help("Space separated list of include search paths. (e.g. ./*/include)")
            .multiple(true)
            .takes_value(true))
//        .arg(Arg::with_name("paths")
//            .long("paths")
//            .help("Leaves relative paths in displayed filenames.")
//            .takes_value(true))
        .arg(Arg::with_name("quotetypes")
            .long("quotetypes")
            .help("Select which type of includes to parse:\nboth - parse all \
                   includes. \nangle - parse only \"system\" includes (<>) \nquote - parse only \
                   \"user\" includes (\"\")\n")
            .possible_values(&QuoteTypes::variants())
            .default_value("quote")
            .multiple(false)
            .takes_value(true))
        .arg(Arg::with_name("filter")
            .long("filter")
            .help("Specify a filename to filter by. Will only list files above or below in the tree.")
            .takes_value(true))
        .arg(Arg::with_name("src")
            .long("src")
            .help("Path to the source code, defaults to current directory.")
            .multiple(false)
            .takes_value(true))
        .get_matches();

    let root_dir = match args.value_of("src") {
        Some(path) => PathBuf::from(path),
        None => env::current_dir().unwrap(),
    };

    if !root_dir.exists() {
        panic!("Unable to access directory: {}", root_dir.display());
    }
    // println!("Scanning directory: {}", root_dir.display());

    // Collect a list of include paths to search.
    let mut search_paths = Vec::new();
    if let Some(values) = args.values_of("include") {
        for string in values {

            if let Ok(absolute_path) = std::fs::canonicalize(PathBuf::from(string)) {
                println!("Using search path: {}", absolute_path.display());
                search_paths.push(absolute_path);
            } else {
                println!("Unable to find absolute include path from: {}", &string);
            }

        }
    }

    // Add a list of default system include paths.
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Collect the type of includes to scan (<> vs "")
    let quote_types = args.value_of("quotetypes").unwrap_or("both");
    let (parse_user_includes, parse_system_includes) = match quote_types {
        "angle" => (false, true),
        "quote" => (true, false),
        _ => (true, true), // both
    };

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsString::from("c"));
    extensions.insert(OsString::from("cc"));
    extensions.insert(OsString::from("cpp"));
    extensions.insert(OsString::from("cxx"));
    extensions.insert(OsString::from("h"));
    extensions.insert(OsString::from("hpp"));
    extensions.insert(OsString::from("hxx"));

    // Regular expression of files to exclude. Skip if exclude string is empty.
    let exclude_regex = args.value_of("exclude")
        .and_then(|regex_str| {
            Regex::new(regex_str)
                .map_err(|err| panic!("Unable to parse exclude regex: {}", err.description()))
                .ok() // Converts successful result to Some(), discarding errors.
        });

    let mut hash_graph = find_includes_in_tree(&root_dir,
                                               &search_paths,
                                               &extensions,
                                               parse_user_includes,
                                               parse_system_includes,
                                               &exclude_regex);

    // Filter the output if requested
    if let Some(filter_name) = args.value_of("filter") {
        // Find files with the target name. For now, just use the first one.
        let idx_list = hash_graph.find(|n| n.path.file_name().unwrap() == filter_name);
        let root_idx = idx_list[0];

        hash_graph = hash_graph.filter_bidirectional(root_idx);
    }

    // Write the graph to a dot file.
    let _ = dot_writer::write_dot_with_header("./graph.dot", &hash_graph.graph);

    // Print summary stats
    println!("Generated graph with {} nodes and {} edges.",
             &hash_graph.graph.node_count(),
             &hash_graph.graph.edge_count());

    // Create a sub-process to generate a PDF of the graph
    let graphviz_output_result = Command::new("dot")
        .arg("-Tpdf")
        .arg("graph.dot")
        .output();

    if let Ok(graphviz_output) = graphviz_output_result {
        if graphviz_output.status.success() {
            if let Err(err) = File::create("graph.pdf")
                .map(|mut pdf_file| pdf_file.write_all(&graphviz_output.stdout)) {
                println!("Error writing PDF: {}", err);
            }
        }
    } else {
        println!("Unable to find graphviz. Is it installed?");
        println!("Run \"dot -Tpdf graph.dot > graph.pdf\" to render the graph.");
    }

}
<commit_msg>Added hint for glob expansion<commit_after>
#[macro_use]
extern crate clap;
use clap::{Arg, App};

use std::path::PathBuf;
use std::ffi::OsString;
use std::error::Error;
use std::env;
use std::collections::HashSet;

use std::process::Command;
use std::fs::File;
use std::io::Write;

extern crate petgraph;
extern crate walkdir;
extern crate itertools;

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

mod path_utils;
mod dot_writer;
mod file_node;
mod hash_graph;

mod find_includes;
use find_includes::find_includes_in_tree;

// -----------------------------------------------------------------------------

// TODO: implement the command line arguments from the original 'cinclude2dot' project.
// Output from "cinclude2dot.pl --help":
// --debug       Display various debug info
// --exclude     Specify a regular expression of filenames to ignore
//              For example, ignore your test harnesses.
// --merge       Granularity of the diagram:
//              file - the default, treats each file as separate
//              module - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs
//              directory - merges directories into one node
// --groups      Cluster files or modules into directory groups
// --help        Display this help page.
// --include     Followed by a comma separated list of include search paths.
// --paths       Leaves relative paths in displayed filenames.
// --quotetypes  Select for parsing the files included by strip quotes or angle brackets:
//              both - the default, parse all headers.
//              angle - include only "system" headers included by anglebrackets (<>)
//              quote - include only "user" headers included by strip quotes ("")
// --src         Followed by a path to the source code, defaults to current directory

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum MergeType {
        file,
        module,
        directory
    }
}

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum QuoteTypes {
        both,
        angle,
        quote
    }
}

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("debug")
//            .long("debug")
//            .help("Display extra debug info")
//            .takes_value(true))
        .arg(Arg::with_name("exclude")
            .long("exclude")
            .help("Specify a regular expression of filenames to ignore. \nRust/RE2 \
                   syntax.\n\tExample: --exclude=\"test_|noisyFile\"")
            .takes_value(true))
//        .arg(Arg::with_name("merge")
//            .long("merge")
//            .help("Granularity of the diagram: \nfile - the default, treats each file as \
//                   separate \nmodule - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs \
//                   \ndirectory - merges directories into one node\n")
//            .possible_values(&MergeType::variants())
//            .default_value("file")
//            .takes_value(true))
//        .arg(Arg::with_name("groups")
//            .long("groups")
//            .help("Cluster files or modules into directory groups")
//            .takes_value(true))
        .arg(Arg::with_name("include")
            .long("include")
            .help("Space separated list of include search paths. (e.g. --include= ./*/include) \
                    \tInclude a space after the equals sign for glob expansion")
            .multiple(true)
            .takes_value(true))
//        .arg(Arg::with_name("paths")
//            .long("paths")
//            .help("Leaves relative paths in displayed filenames.")
//            .takes_value(true))
        .arg(Arg::with_name("quotetypes")
            .long("quotetypes")
            .help("Select which type of includes to parse:\nboth - parse all \
                   includes. \nangle - parse only \"system\" includes (<>) \nquote - parse only \
                   \"user\" includes (\"\")\n")
            .possible_values(&QuoteTypes::variants())
            .default_value("quote")
            .multiple(false)
            .takes_value(true))
        .arg(Arg::with_name("filter")
            .long("filter")
            .help("Specify a filename to filter by. Will only list files above or below in the tree.")
            .takes_value(true))
        .arg(Arg::with_name("src")
            .long("src")
            .help("Path to the source code, defaults to current directory.")
            .multiple(false)
            .takes_value(true))
        .get_matches();

    let root_dir = match args.value_of("src") {
        Some(path) => PathBuf::from(path),
        None => env::current_dir().unwrap(),
    };

    if !root_dir.exists() {
        println!("Unable to access src directory: {}", root_dir.display());
        std::process::exit(1);
    }
    // println!("Scanning directory: {}", root_dir.display());

    // Collect a list of include paths to search.
    let mut search_paths = Vec::new();
    if let Some(values) = args.values_of("include") {
        for string in values {

            if let Ok(absolute_path) = std::fs::canonicalize(PathBuf::from(string)) {
                println!("Using search path: {}", absolute_path.display());
                search_paths.push(absolute_path);
            } else {
                println!("Unable to find absolute include path from: {}", &string);
            }

        }
    }

    // Add a list of default system include paths.
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Collect the type of includes to scan (<> vs "")
    let quote_types = args.value_of("quotetypes").unwrap_or("both");
    let (parse_user_includes, parse_system_includes) = match quote_types {
        "angle" => (false, true),
        "quote" => (true, false),
        _ => (true, true), // both
    };

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsString::from("c"));
    extensions.insert(OsString::from("cc"));
    extensions.insert(OsString::from("cpp"));
    extensions.insert(OsString::from("cxx"));
    extensions.insert(OsString::from("h"));
    extensions.insert(OsString::from("hpp"));
    extensions.insert(OsString::from("hxx"));

    // Regular expression of files to exclude. Skip if exclude string is empty.
    let exclude_regex = args.value_of("exclude")
        .and_then(|regex_str| {
            Regex::new(regex_str)
                .map_err(|err| panic!("Unable to parse exclude regex: {}", err.description()))
                .ok() // Converts successful result to Some(), discarding errors.
        });

    let mut hash_graph = find_includes_in_tree(&root_dir,
                                               &search_paths,
                                               &extensions,
                                               parse_user_includes,
                                               parse_system_includes,
                                               &exclude_regex);

    // Filter the output if requested
    if let Some(filter_name) = args.value_of("filter") {
        // Find files with the target name. For now, just use the first one.
        let idx_list = hash_graph.find(|n| n.path.file_name().unwrap() == filter_name);
        let root_idx = idx_list[0];

        hash_graph = hash_graph.filter_bidirectional(root_idx);
    }

    // Write the graph to a dot file.
    let _ = dot_writer::write_dot_with_header("./graph.dot", &hash_graph.graph);

    // Print summary stats
    println!("Generated graph with {} nodes and {} edges.",
             &hash_graph.graph.node_count(),
             &hash_graph.graph.edge_count());

    // Create a sub-process to generate a PDF of the graph
    let graphviz_output_result = Command::new("dot")
        .arg("-Tpdf")
        .arg("graph.dot")
        .output();

    if let Ok(graphviz_output) = graphviz_output_result {
        if graphviz_output.status.success() {
            if let Err(err) = File::create("graph.pdf")
                .map(|mut pdf_file| pdf_file.write_all(&graphviz_output.stdout)) {
                println!("Error writing PDF: {}", err);
            }
        }
    } else {
        println!("Unable to find graphviz. Is it installed?");
        println!("Run \"dot -Tpdf graph.dot > graph.pdf\" to render the graph.");
    }

}
<|endoftext|>


<commit_before>// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Copyright 2014 Mark McDermott.
//
// Licensed under the the MIT license
// <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

extern crate sdl2;
extern crate gl;
extern crate libc;
extern crate log;
extern crate native;

use gl::types::{GLfloat,GLuint,GLint,GLchar,GLenum,GLboolean,GLsizeiptr};
use std::ptr;
use std::str;
use std::mem;

// Vertex data
static VERTEX_DATA: [GLfloat, ..6] = [
     0.0,  0.5,
     0.5, -0.5,
    -0.5, -0.5
];

// Shader sources
static VS_SRC: &'static str =
   "#version 150\n\
    in vec2 position;\n\
    void main() {\n\
       gl_Position = vec4(position, 0.0, 1.0);\n\
    }";

static FS_SRC: &'static str =
   "#version 150\n\
    out vec4 out_color;\n\
    void main() {\n\
       out_color = vec4(1.0, 1.0, 1.0, 1.0);\n\
    }";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader;
    unsafe {
        shader = gl::CreateShader(ty);
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice()).expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint { unsafe {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice()).expect("ProgramInfoLog not valid utf8"));
        }
    }
    program
}}

// --------------------------------------------------------------------------
// Native entry point
// --------------------------------------------------------------------------
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
    native::start(argc, argv, main)
}

// --------------------------------------------------------------------------
// The "Real" entry point
// --------------------------------------------------------------------------
fn main() {
    println!("Welcome to the the Cosmolark editor!")

    sdl2::init(sdl2::INIT_EVERYTHING);

            sdl2::video::gl_set_attribute(sdl2::video::GLContextMajorVersion,3);
        sdl2::video::gl_set_attribute(sdl2::video::GLContextMinorVersion,0);
        sdl2::video::gl_set_attribute(sdl2::video::GLDepthSize,24);
        sdl2::video::gl_set_attribute(sdl2::video::GLDoubleBuffer, 1);
        sdl2::video::gl_set_attribute(sdl2::video::GLContextProfileMask,sdl2::video::ll::SDL_GL_CONTEXT_PROFILE_CORE as int);

    // open a window
    let window = match sdl2::video::Window::new("Cosmolark Asset Editor",sdl2::video::PosCentered, sdl2::video::PosCentered, 300,300,sdl2::video::OPENGL) {
        Ok(window) => window,
        Err(err)   => panic!("failed to create window: {}", err)
    };

    let context = window.gl_create_context().unwrap();
    sdl2::clear_error();
    gl::load_with(|name| {
        match sdl2::video::gl_get_proc_address(name) {
            Some(glproc) => glproc as *const libc::c_void,
            None         => { println!("missing GL function: {}", name); std::ptr::null() }
        }
    });

    let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER);
    let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER);
    let program = link_program(vs, fs);

    window.show();

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

        // Create a Vertex Buffer Object and copy the vertex data to it
        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);

        // Use shader program
        gl::UseProgram(program);
        "out_color".with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));

        // Specify the layout of the vertex data
        let pos_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
        gl::EnableVertexAttribArray(pos_attr as GLuint);
        gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT,
                               gl::FALSE as GLboolean, 0, ptr::null());
    }

    unsafe {
        gl::ClearColor(0.3,0.3,0.3,1.0);
        gl::Clear(gl::COLOR_BUFFER_BIT);
        gl::DrawArrays(gl::TRIANGLES, 0,3);
    }

    // swap buffer
    window.gl_swap_window();

    'event : loop {
        match sdl2::event::poll_event() {
            sdl2::event::Quit(_) => break 'event,
            sdl2::event::None    => continue,
            //event                => println!("event: {}", event),
            _                    => continue
        }
    }

    unsafe {
        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        gl::DeleteBuffers(1, &vbo);
        gl::DeleteVertexArrays(1, &vao);
    }
    sdl2::quit();
} // end function main()

<commit_msg>vsync off<commit_after>// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Copyright 2014 Mark McDermott.
//
// Licensed under the the MIT license
// <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

extern crate sdl2;
extern crate gl;
extern crate libc;
extern crate log;
extern crate native;

use gl::types::{GLfloat,GLuint,GLint,GLchar,GLenum,GLboolean,GLsizeiptr};
use std::ptr;
use std::str;
use std::mem;

// Vertex data
static VERTEX_DATA: [GLfloat, ..6] = [
     0.0,  0.5,
     0.5, -0.5,
    -0.5, -0.5
];

// Shader sources
static VS_SRC: &'static str =
   "#version 150\n\
    in vec2 position;\n\
    void main() {\n\
       gl_Position = vec4(position, 0.0, 1.0);\n\
    }";

static FS_SRC: &'static str =
   "#version 150\n\
    out vec4 out_color;\n\
    void main() {\n\
       out_color = vec4(1.0, 1.0, 1.0, 1.0);\n\
    }";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader;
    unsafe {
        shader = gl::CreateShader(ty);
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice()).expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint { unsafe {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice()).expect("ProgramInfoLog not valid utf8"));
        }
    }
    program
}}

// --------------------------------------------------------------------------
// Native entry point
// --------------------------------------------------------------------------
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
    native::start(argc, argv, main)
}

// --------------------------------------------------------------------------
// The "Real" entry point
// --------------------------------------------------------------------------
fn main() {
    println!("Welcome to the the Cosmolark editor!")

    sdl2::init(sdl2::INIT_EVERYTHING);

            sdl2::video::gl_set_attribute(sdl2::video::GLContextMajorVersion,3);
        sdl2::video::gl_set_attribute(sdl2::video::GLContextMinorVersion,0);
        sdl2::video::gl_set_attribute(sdl2::video::GLDepthSize,24);
        sdl2::video::gl_set_attribute(sdl2::video::GLDoubleBuffer, 1);
        sdl2::video::gl_set_attribute(sdl2::video::GLContextProfileMask,sdl2::video::ll::SDL_GL_CONTEXT_PROFILE_CORE as int);

    // open a window
    let window = match sdl2::video::Window::new("Cosmolark Asset Editor",sdl2::video::PosCentered, sdl2::video::PosCentered, 300,300,sdl2::video::OPENGL) {
        Ok(window) => window,
        Err(err)   => panic!("failed to create window: {}", err)
    };

    let context = window.gl_create_context().unwrap();
    sdl2::clear_error();
    gl::load_with(|name| {
        match sdl2::video::gl_get_proc_address(name) {
            Some(glproc) => glproc as *const libc::c_void,
            None         => { println!("missing GL function: {}", name); std::ptr::null() }
        }
    });

    let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER);
    let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER);
    let program = link_program(vs, fs);

    sdl2::video::gl_set_swap_interval(0);
    window.show();

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

        // Create a Vertex Buffer Object and copy the vertex data to it
        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);

        // Use shader program
        gl::UseProgram(program);
        "out_color".with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));

        // Specify the layout of the vertex data
        let pos_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
        gl::EnableVertexAttribArray(pos_attr as GLuint);
        gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT,
                               gl::FALSE as GLboolean, 0, ptr::null());
    }

    let mut cnt: u64 = 0;
    'event : loop {

        unsafe {
            gl::ClearColor(0.3,0.3,0.3,1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
            gl::DrawArrays(gl::TRIANGLES, 0,3);
        }

        window.gl_swap_window();

        cnt = cnt + 1;

        if cnt % 100 == 0 {
            println!("Cnt: {}",cnt);
        }

        match sdl2::event::poll_event() {
            sdl2::event::Quit(_) => break 'event,
            sdl2::event::None    => continue,
            //event                => println!("event: {}", event),
            _                    => continue
        }
    }

    unsafe {
        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        gl::DeleteBuffers(1, &vbo);
        gl::DeleteVertexArrays(1, &vao);
    }
    sdl2::quit();
} // end function main()

<|endoftext|>


<commit_before>#![feature(lang_items,core_intrinsics,asm,start)]
#![no_std]
#![crate_type="staticlib"]

use core::intrinsics::{volatile_store};

//#[lang="stack_exhausted"] extern fn stack_exhausted() {}
#[lang="eh_personality"] extern fn eh_personality() {}
#[lang="panic_fmt"]
#[no_mangle]
pub extern fn rust_begin_unwind(_fmt: &core::fmt::Arguments, _file_line: &(&'static str, usize)) -> !
{
    loop {}
}

#[no_mangle]
pub extern fn __aeabi_unwind_cpp_pr0() -> ()
{
    loop {}
}

#[no_mangle]
pub extern fn __aeabi_unwind_cpp_pr1() -> ()
{
    loop {}
}

macro_rules! GPIOC_PDOR   {() => (0x400FF080 as *mut u32);} // GPIOC_PDOR - page 1334,1335
macro_rules! WDOG_UNLOCK  {() => (0x4005200E as *mut u16);} // Watchdog Unlock register
macro_rules! WDOG_STCTRLH {() => (0x40052000 as *mut u16);} // Watchdog Status and Control Register High
macro_rules! GPIO_CONFIG  {() => (0x40048038 as *mut u32);}
macro_rules! PORTC_PCR5   {() => (0x4004B014 as *mut u32);} // PORTC_PCR5 - page 223/227
macro_rules! GPIOC_PDDR   {() => (0x400FF094 as *mut u32);} // GPIOC_PDDR - page 1334,1337
macro_rules! GPIOC_PDOR   {() => (0x400FF080 as *mut u32);} // GPIOC_PDOR - page 1334,1335

extern {
    static mut _sflashdata: u32;
    static mut _sdata: u32;
    static mut _edata: u32;
    static mut _sbss: u32;
    static mut _ebss: u32;
    fn _estack();
}

#[link_section=".vectors"]
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static ISRVectors: [Option<unsafe extern fn()>; 16] = [
  Some(_estack),          // Stack pointer
  Some(startup),          // Reset
  Some(isr_nmi),          // NMI
  Some(isr_hardfault),    // Hard Fault
  Some(isr_mmfault),      // CM3 Memory Management Fault
  Some(isr_busfault),     // CM3 Bus Fault
  Some(isr_usagefault),   // CM3 Usage Fault
  Some(isr_reserved_1),   // Reserved - Used as NXP Checksum
  None,                   // Reserved
  None,                   // Reserved
  None,                   // Reserved
  Some(isr_svcall),       // SVCall
  Some(isr_debugmon),     // Reserved for debug
  None,                   // Reserved
  Some(isr_pendsv),       // PendSV
  Some(isr_systick),      // SysTick
];

#[link_section=".flashconfig"]
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static flashconfigbytes: [usize; 4] = [
    0xFFFFFFFF,
    0xFFFFFFFF,
    0xFFFFFFFF,
    0xFFFFFFFE,
];

pub unsafe extern fn startup() {
    let mut src: *mut u32 = &mut _sflashdata;
    let mut dest: *mut u32 = &mut _sdata;

    volatile_store(WDOG_UNLOCK!(), 0xC520);
    volatile_store(WDOG_UNLOCK!(), 0xD928);
    volatile_store(WDOG_STCTRLH!(), 0x01D2);

    while dest < &mut _edata as *mut u32 {
        *dest = *src;
        dest = ((dest as u32) + 4) as *mut u32;
        src = ((src as u32) + 4) as *mut u32;
    }

    dest = &mut _sbss as *mut u32;

    while dest < &mut _edata as *mut u32 {
        *dest = 0;
        dest = ((dest as u32) + 4) as *mut u32;
    }

    // Enable system clock on all GPIO ports - page 254
    *GPIO_CONFIG!() = 0x00043F82; // 0b1000011111110000010
    // Configure the led pin
    *PORTC_PCR5!() = 0x00000143; // Enables GPIO | DSE | PULL_ENABLE | PULL_SELECT - page 227
    // Set the led pin to output
    *GPIOC_PDDR!() = 0x20; // pin 5 on port c

    rust_loop();
}

pub fn led_on() {
    unsafe {
        volatile_store(GPIOC_PDOR!(), 0x20);
    }
}

pub fn led_off() {
    unsafe {
        volatile_store(GPIOC_PDOR!(), 0x0);
    }
}

pub fn delay(ms: i32) {
    for _ in 0..ms*250 {
        unsafe {
            asm!("NOP");
        }
    }
}

pub fn rust_loop() {
    loop {
        led_on();
        delay(1000);
        led_off();
        delay(1000);
    }
}

#[start]
fn lang_start(_: isize, _: *const *const u8) -> isize {
    unsafe {
        startup();
    }
    0
}

pub unsafe extern fn isr_nmi() { loop {} }
pub unsafe extern fn isr_hardfault() { loop {} }
pub unsafe extern fn isr_mmfault() { loop {} }
pub unsafe extern fn isr_busfault() { loop {} }
pub unsafe extern fn isr_usagefault() { loop {} }
pub unsafe extern fn isr_reserved_1() { loop {} }
pub unsafe extern fn isr_svcall() { loop {} }
pub unsafe extern fn isr_debugmon() { loop {} }
pub unsafe extern fn isr_pendsv() { loop {} }
pub unsafe extern fn isr_systick() { loop {} }
<commit_msg>Runs main.src through rustfmt<commit_after>#![feature(lang_items,core_intrinsics,asm,start)]
#![no_std]
#![crate_type="staticlib"]

use core::intrinsics::volatile_store;

#[lang="eh_personality"]
extern "C" fn eh_personality() {}
#[lang="panic_fmt"]
#[no_mangle]
pub extern "C" fn rust_begin_unwind(_fmt: &core::fmt::Arguments,
                                    _file_line: &(&'static str, usize))
                                    -> ! {
    loop {}
}

#[no_mangle]
pub extern "C" fn __aeabi_unwind_cpp_pr0() -> () {
    loop {}
}

#[no_mangle]
pub extern "C" fn __aeabi_unwind_cpp_pr1() -> () {
    loop {}
}

macro_rules! GPIOC_PDOR   {() => (0x400FF080 as *mut u32);} // GPIOC_PDOR - page 1334,1335
macro_rules! WDOG_UNLOCK  {() => (0x4005200E as *mut u16);} // Watchdog Unlock register
macro_rules! WDOG_STCTRLH {() => (0x40052000 as *mut u16);} // Watchdog Status and Control Register High
macro_rules! GPIO_CONFIG  {() => (0x40048038 as *mut u32);}
macro_rules! PORTC_PCR5   {() => (0x4004B014 as *mut u32);} // PORTC_PCR5 - page 223/227
macro_rules! GPIOC_PDDR   {() => (0x400FF094 as *mut u32);} // GPIOC_PDDR - page 1334,1337
macro_rules! GPIOC_PDOR   {() => (0x400FF080 as *mut u32);} // GPIOC_PDOR - page 1334,1335

extern "C" {
    static mut _sflashdata: u32;
    static mut _sdata: u32;
    static mut _edata: u32;
    static mut _sbss: u32;
    static mut _ebss: u32;
    fn _estack();
}

#[link_section=".vectors"]
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static ISRVectors: [Option<unsafe extern "C" fn()>; 16] = [Some(_estack), // Stack pointer
                                                               Some(startup), // Reset
                                                               Some(isr_nmi), // NMI
                                                               Some(isr_hardfault), // Hard Fault
                                                               Some(isr_mmfault), /* CM3 Memory Management Fault */
                                                               Some(isr_busfault), /* CM3 Bus Fault */
                                                               Some(isr_usagefault), /* CM3 Usage Fault */
                                                               Some(isr_reserved_1), /* Reserved - Used as NXP Checksum */
                                                               None, // Reserved
                                                               None, // Reserved
                                                               None, // Reserved
                                                               Some(isr_svcall), // SVCall
                                                               Some(isr_debugmon), /* Reserved for debug */
                                                               None, // Reserved
                                                               Some(isr_pendsv), // PendSV
                                                               Some(isr_systick) /* SysTick */];

#[link_section=".flashconfig"]
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static flashconfigbytes: [usize; 4] = [0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE];

pub unsafe extern "C" fn startup() {
    let mut src: *mut u32 = &mut _sflashdata;
    let mut dest: *mut u32 = &mut _sdata;

    volatile_store(WDOG_UNLOCK!(), 0xC520);
    volatile_store(WDOG_UNLOCK!(), 0xD928);
    volatile_store(WDOG_STCTRLH!(), 0x01D2);

    while dest < &mut _edata as *mut u32 {
        *dest = *src;
        dest = ((dest as u32) + 4) as *mut u32;
        src = ((src as u32) + 4) as *mut u32;
    }

    dest = &mut _sbss as *mut u32;

    while dest < &mut _edata as *mut u32 {
        *dest = 0;
        dest = ((dest as u32) + 4) as *mut u32;
    }

    // Enable system clock on all GPIO ports - page 254
    *GPIO_CONFIG!() = 0x00043F82; // 0b1000011111110000010
    // Configure the led pin
    *PORTC_PCR5!() = 0x00000143; // Enables GPIO | DSE | PULL_ENABLE | PULL_SELECT - page 227
    // Set the led pin to output
    *GPIOC_PDDR!() = 0x20; // pin 5 on port c

    rust_loop();
}

pub fn led_on() {
    unsafe {
        volatile_store(GPIOC_PDOR!(), 0x20);
    }
}

pub fn led_off() {
    unsafe {
        volatile_store(GPIOC_PDOR!(), 0x0);
    }
}

pub fn delay(ms: i32) {
    for _ in 0..ms * 250 {
        unsafe {
            asm!("NOP");
        }
    }
}

pub fn rust_loop() {
    loop {
        led_on();
        delay(1000);
        led_off();
        delay(1000);
    }
}

#[start]
fn lang_start(_: isize, _: *const *const u8) -> isize {
    unsafe {
        startup();
    }
    0
}

pub unsafe extern "C" fn isr_nmi() {
    loop {}
}
pub unsafe extern "C" fn isr_hardfault() {
    loop {}
}
pub unsafe extern "C" fn isr_mmfault() {
    loop {}
}
pub unsafe extern "C" fn isr_busfault() {
    loop {}
}
pub unsafe extern "C" fn isr_usagefault() {
    loop {}
}
pub unsafe extern "C" fn isr_reserved_1() {
    loop {}
}
pub unsafe extern "C" fn isr_svcall() {
    loop {}
}
pub unsafe extern "C" fn isr_debugmon() {
    loop {}
}
pub unsafe extern "C" fn isr_pendsv() {
    loop {}
}
pub unsafe extern "C" fn isr_systick() {
    loop {}
}
<|endoftext|>


<commit_before>use std::io::{self, stdin, stdout, Write};

// TODO(tsion): Use the readline library.
fn prompt(line: &mut String) -> io::Result<usize> {
    let current_dir = try!(std::env::current_dir());
    print!("{}> ", current_dir.display());
    try!(stdout().flush());
    stdin().read_line(line)
}

#[derive(Clone, Debug)]
enum Ast {
    Empty,
    Call { command: String, args: Vec<String> }
}

fn parse(line: &str) -> Ast {
    let mut words = line.split_whitespace();

    match words.next() {
        Some(command) => Ast::Call {
            command: String::from(command),
            args: words.map(String::from).collect()
        },
        None => Ast::Empty
    }
}

fn execute(ast: &Ast) -> io::Result<()> {
    match *ast {
        Ast::Empty => Ok(()),
        Ast::Call { ref command, ref args } => {
            match &**command {
                "cd" => {
                    if args.len() > 1 {
                        return Err(io::Error::new(io::ErrorKind::Other, "cd: too many arguments"));
                    }

                    if let Some(path) = args.get(0) {
                        std::env::set_current_dir(path)
                    } else if let Some(home) = std::env::home_dir() {
                        std::env::set_current_dir(home)
                    } else {
                        Err(io::Error::new(io::ErrorKind::Other, "cd: couldn't find home dir"))
                    }
                },
                _ => std::process::Command::new(command).args(args).status().map(|_| ()),
            }
        },
    }
}

fn main() {
    let mut line = String::new();
    loop {
        prompt(&mut line).unwrap();
        let ast = parse(&line);
        if let Err(e) = execute(&ast) {
            println!("shroom: error: {}", e);
        }
        line.clear();
    }
}
<commit_msg>Add exit built-in command.<commit_after>use std::io::{self, stdin, stdout, Write};

// TODO(tsion): Use the readline library.
fn prompt(line: &mut String) -> io::Result<usize> {
    let current_dir = try!(std::env::current_dir());
    print!("{}> ", current_dir.display());
    try!(stdout().flush());
    stdin().read_line(line)
}

#[derive(Clone, Debug)]
enum Ast {
    Empty,
    Call { command: String, args: Vec<String> }
}

fn parse(line: &str) -> Ast {
    let mut words = line.split_whitespace();

    match words.next() {
        Some(command) => Ast::Call {
            command: String::from(command),
            args: words.map(String::from).collect()
        },
        None => Ast::Empty
    }
}

fn execute(ast: &Ast) -> io::Result<()> {
    match *ast {
        Ast::Empty => Ok(()),
        Ast::Call { ref command, ref args } => {
            match &**command {
                "cd" => {
                    if args.len() > 1 {
                        return Err(io::Error::new(io::ErrorKind::Other, "cd: too many arguments"));
                    }

                    if let Some(path) = args.get(0) {
                        std::env::set_current_dir(path)
                    } else if let Some(home) = std::env::home_dir() {
                        std::env::set_current_dir(home)
                    } else {
                        Err(io::Error::new(io::ErrorKind::Other, "cd: couldn't find home dir"))
                    }
                },

                "exit" => {
                    if args.len() > 1 {
                        return Err(io::Error::new(io::ErrorKind::Other,
                                                  "exit: too many arguments"));
                    }

                    if let Some(exit_code_str) = args.get(0) {
                        if let Ok(exit_code) = exit_code_str.parse() {
                            std::process::exit(exit_code);
                        } else {
                            Err(io::Error::new(io::ErrorKind::Other,
                                               "exit: couldn't parse exit code as integer"))
                        }
                    } else {
                        std::process::exit(0);
                    }
                }

                _ => {
                    std::process::Command::new(command).args(args).status().map(|_| ())
                },
            }
        },
    }
}

fn main() {
    let mut line = String::new();
    loop {
        prompt(&mut line).unwrap();
        let ast = parse(&line);
        if let Err(e) = execute(&ast) {
            println!("shroom: error: {}", e);
        }
        line.clear();
    }
}
<|endoftext|>