<commit_before>//! A module process address spaces

use super::super::super::interrupts::{off, on};
use super::super::super::machine::{invlpg, vmm_on};
use super::super::super::process::proc_table::PROCESS_TABLE;
use super::super::super::process::{State, CURRENT_PROCESS};
use super::super::super::sync::{Barrier, Event, StaticSemaphore};
use super::super::physmem::Frame;
use super::structs::{PagingEntry, VMTable};
use super::{KMAP_ADDRESS, NUM_SHARED, PD_ADDRESS, SHARED_PDES, USER_ADDRESS, VMM_ON};

/// The address space of a single process
pub struct AddressSpace {
    /// paddr of this addr space's PD
    page_dir: usize,

    /// the index of the first un-kmapped page
    kmap_index: u8,

    /// a lock for the address space
    ///
    /// NOTE: to prevent deadlocks, *ALWAYS* acquire
    /// this lock before locks on frames.
    lock: StaticSemaphore,

    /// The PID of the process the current share request is going to
    req_pid: usize,

    /// The frame the current share request is for
    req_paddr: usize,

    /// A barrier for page-sharing
    req_bar: Barrier,

    /// A waiting list for other processes who are expecting this
    /// process to share with them
    req_wait: Event,
}

impl AddressSpace {
    /// Create a new address space and set up the PD.
    pub fn new() -> AddressSpace {
        // allocate a frame
        let (pd, pd_paddr) = VMTable::new();

        // copy the first entries of the pd to direct map
        unsafe {
            let mut i = 0;
            for pde in &SHARED_PDES {
                pd[i] = pde.clone();
                i += 1;
            }
        }

        // point the third PDE at the PD to map all page tables
        // so PD is at vaddr PD_ADDRESS
        unsafe {
            pd[NUM_SHARED].set_present(true); // present
            pd[NUM_SHARED].set_read_write(true); // read/write
            pd[NUM_SHARED].set_privelege_level(false); // kernel only
            pd[NUM_SHARED].set_caching(true); // write-through
            pd[NUM_SHARED].set_address(pd_paddr); // PD paddr
        }

        let a = AddressSpace {
            page_dir: pd_paddr,
            kmap_index: 0,
            lock: StaticSemaphore::new(1),
            req_pid: 0,
            req_paddr: 0,
            req_bar: Barrier::new(2),
            req_wait: Event::new(),
        };

        a
    }

    /// Map `virt` to `phys` in the address space. The method first
    /// tries to acquire the address space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space
    /// because it assumes that the PD is at PD_ADDRESS
    pub fn map(&mut self, phys: usize, virt: usize, lock: bool) {
        // unsafe {
        //    printf!("{:?} [map {:x} -> {:x}]\n", *CURRENT_PROCESS, virt, phys);
        // }

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        if lock {
            self.lock.down();
        }

        let pd = unsafe { &mut *PD_ADDRESS };
        let pde = &mut pd[pde_index];

        // invalidate TLB entry
        unsafe { invlpg(virt) };

        // if pd entry is not already there
        if !pde.is_flag(0) {
            // present bit
            // no pd entry yet => create one

            off();

            // set pde
            pde.set_read_write(true); // read/write
            pde.set_privelege_level(virt >= 0xD00000); // kernel only if vaddr < 0xD00000
            pde.set_caching(false); // write-back
            pde.set_address(Frame::alloc()); // alloc a new frame
            pde.set_present(true); // present

            // clear the pt
            let pt = unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };
            for p in 0..1024 {
                pt[p] = PagingEntry::new();
                unsafe { invlpg((pde_index << 22) | (p << 12)) };
            }

            on();
        }

        // follow pde to get pt
        let pt = unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };
        let pte = &mut pt[pte_index];

        // if pt entry is not already there
        if !pte.is_flag(0) {
            // present bit
            // no pt entry yet -> create one

            off();

            // set pte
            pte.set_read_write(true); // read/write
            pte.set_privelege_level(virt >= 0xD00000); // kernel only if vaddr < 0xD00000
            pte.set_caching(false); // write-back
            pte.set_address(phys); // point to frame
            pte.set_present(true); // present

            on();
        }

        if lock {
            self.lock.up();
        }
    }

    /// Map the given `paddr` for temporary use by the kernel and return a mut reference to the
    /// frame.
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn kmap(&mut self, paddr: usize) -> &mut Frame {
        // get next unmapped address
        let next = self.kmap_index;

        // increment
        if next == 255 {
            // unmap all if we have run out
            for i in 0..256 {
                self.unmap(unsafe { KMAP_ADDRESS } + i * 0x1000, true);
            }
            self.kmap_index = 0;
        } else {
            self.kmap_index += 1;
        }

        // get associated address
        let vaddr = unsafe { KMAP_ADDRESS } + (next as usize) * 0x1000;

        // map the frame
        self.map(paddr, vaddr, true);

        unsafe { &mut *(vaddr as *mut Frame) }
    }

    /// Remove any mapping for this virtual address. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn unmap(&mut self, virt: usize, lock: bool) {
        // unsafe {
        //    printf!("{:?} [unmap {:x}]\n", *CURRENT_PROCESS, virt);
        // }

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe { &mut *PD_ADDRESS };

        if lock {
            self.lock.down();
        }

        if pd[pde_index].is_flag(0) {
            // present bit
            off();

            let pt = unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };

            // unmap and deallocate frame
            pt[pte_index].free(virt >= unsafe { USER_ADDRESS });

            // invalidate TLB entry
            unsafe { invlpg(virt) };

            on();

            // if page table is now empty,
            // unmap and deallocate it
            if (0..1024).all(|i| !pt[i].is_flag(0)) {
                pd[pde_index].free(virt >= unsafe { KMAP_ADDRESS });
            }
        }

        if lock {
            self.lock.up();
        }

        // unsafe {
        //    printf!("{:?} [unmaped {:x}]\n", *CURRENT_PROCESS, virt);
        // }
    }

    /// Returns the current physical address mapped to the given virtual address
    /// or None if it is not mapped. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn v_to_p(&mut self, virt: usize, lock: bool) -> Option<usize> {
        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe { &mut *PD_ADDRESS };

        if lock {
            self.lock.down();
        }

        let ret = if pd[pde_index].is_flag(0) {
            // present bit
            let pt = unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };
            let pte = &pt[pte_index];

            if pte.is_flag(0) {
                Some(pt[pte_index].get_address())
            } else {
                None
            }
        } else {
            None
        };

        if lock {
            self.lock.up();
        }

        ret
    }

    /// Activate the current address space and turn on VM if needed
    pub fn activate(&mut self) {
        off();
        unsafe {
            vmm_on(self.page_dir);
            VMM_ON = true;
        }
        on();
    }

    /// Send a page-share request to the process with PID `pid` for
    /// the frame mapped to `vaddr` in this address space.
    ///
    /// Returns true if the request succeeded, and false if there was
    /// an error (e.g. the process has already died).
    ///
    /// Blocks until the request is accepted.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn request_share(&mut self, pid: usize, vaddr: usize) -> bool {
        // mark the frame and create the request
        self.lock.down();

        unsafe {
            self.req_pid = if let Some(p) = PROCESS_TABLE.get(pid) {
                if (*p).get_state() == State::TERMINATED {
                    return false;
                } else {
                    pid
                }
            } else {
                return false;
            };
        }
        self.req_paddr = if let Some(paddr) = self.v_to_p(vaddr, false) {
            paddr
        } else {
            return false;
        };

        Frame::share(
            unsafe { (*CURRENT_PROCESS).get_pid() },
            vaddr,
            self.req_paddr,
        );

        self.req_wait.notify();

        self.req_wait.reset();

        self.lock.up();

        self.req_bar.reach();

        true
    }

    /// Creates a mapping in this process's address space for the
    /// first page-share request from the process with the given PID
    /// to the given page.
    ///
    /// Returns true if success; false otherwise.
    ///
    /// Blocks until the request is completed.
    ///
    /// NOTE: This should run inside this processes address space
    pub fn accept_share(&mut self, pid: usize, vaddr: usize) -> bool {
        let other = unsafe {
            if let Some(p) = PROCESS_TABLE.get(pid) {
                p
            } else {
                return false;
            }
        };
        let addr_space = unsafe { &mut (*other).addr_space };
        let my_pid = unsafe { (*CURRENT_PROCESS).get_pid() };

        // wait until the other process creates a req for this pid
        loop {
            addr_space.lock.down();

            if addr_space.req_pid == my_pid {
                break;
            } else {
                off();
                addr_space.lock.up();
                addr_space.req_wait.wait();
                on();
            }
        }

        self.lock.down();

        // create a mapping
        Frame::share(my_pid, vaddr, addr_space.req_paddr);
        self.map(addr_space.req_paddr, vaddr, false);

        self.lock.up();

        addr_space.req_pid = 0;
        addr_space.req_paddr = 0;

        addr_space.lock.up();

        addr_space.req_bar.reach();

        true
    }

    /// Remove all non-kernel mappings in this address space.
    /// NOTE: must run while this address space is active
    pub fn clear(&mut self) {
        let pd = unsafe { &mut *PD_ADDRESS };

        self.lock.down();

        // for each present PDE, remove all
        // mappings associated with it
        for pde_index in (unsafe { NUM_SHARED } + 1)..1024 {
            if pd[pde_index].is_flag(0) {
                // present bit
                for pte_index in 0..1024 {
                    self.unmap((pde_index << 22) | (pte_index << 12), false);
                }
            }
        }

        self.lock.up();
    }
}

impl Drop for AddressSpace {
    /// Deallocate the page directory.
    /// NOTE: cannot run while the address space is active
    fn drop(&mut self) {
        // free the page directory
        Frame::free(self.page_dir >> 12);
    }
}

/// The Rust-side code of the page fault handler.
#[no_mangle]
pub unsafe extern "C" fn vmm_page_fault(
    // context: *mut KContext,
    fault_addr: usize
) {
    // segfault! should be very rare with rust
    // first 13MiB are reserved by kernel
    if fault_addr < 0xD00000 {
        // TODO: only kill that process
        panic!(
            "{:?} [segmentation violation @ 0x{:X}]",
            *CURRENT_PROCESS, fault_addr
        );
    }

    // printf!("page fault {:X}\n", fault_addr);

    if !CURRENT_PROCESS.is_null() {
        (*CURRENT_PROCESS)
            .addr_space
            .map(Frame::alloc(), fault_addr, true);
    } else {
        panic!("Page fault @ 0x{:X} with no current process", fault_addr);
    }

    // memclr an alloced frame
    let page = &mut *((fault_addr & 0xFFFF_F000) as *mut Frame);
    for i in 0..1024 {
        page[i] = 0;
    }

    // printf!("page fault done {:X}\n", fault_addr);
}
<commit_msg>remove more off/on<commit_after>//! A module process address spaces

use interrupts::no_interrupts;
use machine::{invlpg, vmm_on};
use process::proc_table::PROCESS_TABLE;
use process::{State, CURRENT_PROCESS};
use sync::{Barrier, Event, StaticSemaphore};

use super::super::physmem::Frame;
use super::structs::{PagingEntry, VMTable};
use super::{KMAP_ADDRESS, NUM_SHARED, PD_ADDRESS, SHARED_PDES, USER_ADDRESS, VMM_ON};

/// The address space of a single process
pub struct AddressSpace {
    /// paddr of this addr space's PD
    page_dir: usize,

    /// the index of the first un-kmapped page
    kmap_index: u8,

    /// a lock for the address space
    ///
    /// NOTE: to prevent deadlocks, *ALWAYS* acquire
    /// this lock before locks on frames.
    lock: StaticSemaphore,

    /// The PID of the process the current share request is going to
    req_pid: usize,

    /// The frame the current share request is for
    req_paddr: usize,

    /// A barrier for page-sharing
    req_bar: Barrier,

    /// A waiting list for other processes who are expecting this
    /// process to share with them
    req_wait: Event,
}

impl AddressSpace {
    /// Create a new address space and set up the PD.
    pub fn new() -> AddressSpace {
        // allocate a frame
        let (pd, pd_paddr) = VMTable::new();

        // copy the first entries of the pd to direct map
        unsafe {
            let mut i = 0;
            for pde in &SHARED_PDES {
                pd[i] = pde.clone();
                i += 1;
            }
        }

        // point the third PDE at the PD to map all page tables
        // so PD is at vaddr PD_ADDRESS
        unsafe {
            pd[NUM_SHARED].set_present(true); // present
            pd[NUM_SHARED].set_read_write(true); // read/write
            pd[NUM_SHARED].set_privelege_level(false); // kernel only
            pd[NUM_SHARED].set_caching(true); // write-through
            pd[NUM_SHARED].set_address(pd_paddr); // PD paddr
        }

        let a = AddressSpace {
            page_dir: pd_paddr,
            kmap_index: 0,
            lock: StaticSemaphore::new(1),
            req_pid: 0,
            req_paddr: 0,
            req_bar: Barrier::new(2),
            req_wait: Event::new(),
        };

        a
    }

    /// Map `virt` to `phys` in the address space. The method first
    /// tries to acquire the address space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space
    /// because it assumes that the PD is at PD_ADDRESS
    pub fn map(&mut self, phys: usize, virt: usize, lock: bool) {
        // unsafe {
        //    printf!("{:?} [map {:x} -> {:x}]\n", *CURRENT_PROCESS, virt, phys);
        // }

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        if lock {
            self.lock.down();
        }

        let pd = unsafe { &mut *PD_ADDRESS };
        let pde = &mut pd[pde_index];

        // invalidate TLB entry
        unsafe { invlpg(virt) };

        // if pd entry is not already there
        if !pde.is_flag(0) {
            // present bit
            // no pd entry yet => create one

            no_interrupts(|| {
                // set pde
                pde.set_read_write(true); // read/write
                pde.set_privelege_level(virt >= 0xD00000); // kernel only if vaddr < 0xD00000
                pde.set_caching(false); // write-back
                pde.set_address(Frame::alloc()); // alloc a new frame
                pde.set_present(true); // present

                // clear the pt
                let pt =
                    unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };
                for p in 0..1024 {
                    pt[p] = PagingEntry::new();
                    unsafe { invlpg((pde_index << 22) | (p << 12)) };
                }
            })
        }

        // follow pde to get pt
        let pt = unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };
        let pte = &mut pt[pte_index];

        // if pt entry is not already there
        if !pte.is_flag(0) {
            // present bit
            // no pt entry yet -> create one

            no_interrupts(|| {
                // set pte
                pte.set_read_write(true); // read/write
                pte.set_privelege_level(virt >= 0xD00000); // kernel only if vaddr < 0xD00000
                pte.set_caching(false); // write-back
                pte.set_address(phys); // point to frame
                pte.set_present(true); // present
            })
        }

        if lock {
            self.lock.up();
        }
    }

    /// Map the given `paddr` for temporary use by the kernel and return a mut reference to the
    /// frame.
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn kmap(&mut self, paddr: usize) -> &mut Frame {
        // get next unmapped address
        let next = self.kmap_index;

        // increment
        if next == 255 {
            // unmap all if we have run out
            for i in 0..256 {
                self.unmap(unsafe { KMAP_ADDRESS } + i * 0x1000, true);
            }
            self.kmap_index = 0;
        } else {
            self.kmap_index += 1;
        }

        // get associated address
        let vaddr = unsafe { KMAP_ADDRESS } + (next as usize) * 0x1000;

        // map the frame
        self.map(paddr, vaddr, true);

        unsafe { &mut *(vaddr as *mut Frame) }
    }

    /// Remove any mapping for this virtual address. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn unmap(&mut self, virt: usize, lock: bool) {
        // unsafe {
        //    printf!("{:?} [unmap {:x}]\n", *CURRENT_PROCESS, virt);
        // }

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe { &mut *PD_ADDRESS };

        if lock {
            self.lock.down();
        }

        if pd[pde_index].is_flag(0) {
            // present bit
            let pt = no_interrupts(|| {
                let pt =
                    unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };

                // unmap and deallocate frame
                pt[pte_index].free(virt >= unsafe { USER_ADDRESS });

                // invalidate TLB entry
                unsafe { invlpg(virt) };

                pt
            });

            // if page table is now empty,
            // unmap and deallocate it
            if (0..1024).all(|i| !pt[i].is_flag(0)) {
                pd[pde_index].free(virt >= unsafe { KMAP_ADDRESS });
            }
        }

        if lock {
            self.lock.up();
        }

        // unsafe {
        //    printf!("{:?} [unmaped {:x}]\n", *CURRENT_PROCESS, virt);
        // }
    }

    /// Returns the current physical address mapped to the given virtual address
    /// or None if it is not mapped. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn v_to_p(&mut self, virt: usize, lock: bool) -> Option<usize> {
        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe { &mut *PD_ADDRESS };

        if lock {
            self.lock.down();
        }

        let ret = if pd[pde_index].is_flag(0) {
            // present bit
            let pt = unsafe { &mut *(((NUM_SHARED << 22) | (pde_index << 12)) as *mut VMTable) };
            let pte = &pt[pte_index];

            if pte.is_flag(0) {
                Some(pt[pte_index].get_address())
            } else {
                None
            }
        } else {
            None
        };

        if lock {
            self.lock.up();
        }

        ret
    }

    /// Activate the current address space and turn on VM if needed
    pub fn activate(&mut self) {
        no_interrupts(|| unsafe {
            vmm_on(self.page_dir);
            VMM_ON = true;
        })
    }

    /// Send a page-share request to the process with PID `pid` for
    /// the frame mapped to `vaddr` in this address space.
    ///
    /// Returns true if the request succeeded, and false if there was
    /// an error (e.g. the process has already died).
    ///
    /// Blocks until the request is accepted.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn request_share(&mut self, pid: usize, vaddr: usize) -> bool {
        // mark the frame and create the request
        self.lock.down();

        unsafe {
            self.req_pid = if let Some(p) = PROCESS_TABLE.get(pid) {
                if (*p).get_state() == State::TERMINATED {
                    return false;
                } else {
                    pid
                }
            } else {
                return false;
            };
        }
        self.req_paddr = if let Some(paddr) = self.v_to_p(vaddr, false) {
            paddr
        } else {
            return false;
        };

        Frame::share(
            unsafe { (*CURRENT_PROCESS).get_pid() },
            vaddr,
            self.req_paddr,
        );

        self.req_wait.notify();

        self.req_wait.reset();

        self.lock.up();

        self.req_bar.reach();

        true
    }

    /// Creates a mapping in this process's address space for the
    /// first page-share request from the process with the given PID
    /// to the given page.
    ///
    /// Returns true if success; false otherwise.
    ///
    /// Blocks until the request is completed.
    ///
    /// NOTE: This should run inside this processes address space
    pub fn accept_share(&mut self, pid: usize, vaddr: usize) -> bool {
        let other = unsafe {
            if let Some(p) = PROCESS_TABLE.get(pid) {
                p
            } else {
                return false;
            }
        };
        let addr_space = unsafe { &mut (*other).addr_space };
        let my_pid = unsafe { (*CURRENT_PROCESS).get_pid() };

        // wait until the other process creates a req for this pid
        loop {
            addr_space.lock.down();

            if addr_space.req_pid == my_pid {
                break;
            } else {
                no_interrupts(|| {
                    addr_space.lock.up();
                    addr_space.req_wait.wait();
                })
            }
        }

        self.lock.down();

        // create a mapping
        Frame::share(my_pid, vaddr, addr_space.req_paddr);
        self.map(addr_space.req_paddr, vaddr, false);

        self.lock.up();

        addr_space.req_pid = 0;
        addr_space.req_paddr = 0;

        addr_space.lock.up();

        addr_space.req_bar.reach();

        true
    }

    /// Remove all non-kernel mappings in this address space.
    /// NOTE: must run while this address space is active
    pub fn clear(&mut self) {
        let pd = unsafe { &mut *PD_ADDRESS };

        self.lock.down();

        // for each present PDE, remove all
        // mappings associated with it
        for pde_index in (unsafe { NUM_SHARED } + 1)..1024 {
            if pd[pde_index].is_flag(0) {
                // present bit
                for pte_index in 0..1024 {
                    self.unmap((pde_index << 22) | (pte_index << 12), false);
                }
            }
        }

        self.lock.up();
    }
}

impl Drop for AddressSpace {
    /// Deallocate the page directory.
    /// NOTE: cannot run while the address space is active
    fn drop(&mut self) {
        // free the page directory
        Frame::free(self.page_dir >> 12);
    }
}

/// The Rust-side code of the page fault handler.
#[no_mangle]
pub unsafe extern "C" fn vmm_page_fault(
    // context: *mut KContext,
    fault_addr: usize
) {
    // segfault! should be very rare with rust
    // first 13MiB are reserved by kernel
    if fault_addr < 0xD00000 {
        // TODO: only kill that process
        panic!(
            "{:?} [segmentation violation @ 0x{:X}]",
            *CURRENT_PROCESS, fault_addr
        );
    }

    // printf!("page fault {:X}\n", fault_addr);

    if !CURRENT_PROCESS.is_null() {
        (*CURRENT_PROCESS)
            .addr_space
            .map(Frame::alloc(), fault_addr, true);
    } else {
        panic!("Page fault @ 0x{:X} with no current process", fault_addr);
    }

    // memclr an alloced frame
    let page = &mut *((fault_addr & 0xFFFF_F000) as *mut Frame);
    for i in 0..1024 {
        page[i] = 0;
    }

    // printf!("page fault done {:X}\n", fault_addr);
}
<|endoftext|>


<commit_before>//
// gash.rs
// by Nathan Typanski
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{run, os};
use std::io::buffered::BufferedReader;
use std::io::{stdin, stdio, File, Truncate, Write};
use std::io::process::ProcessExit;
use std::io::signal::{Listener, Interrupt};
use std::path::posix::Path;
use std::option::Option;
use std::task::try;
use std::run::Process;
use std::run::ProcessOptions;
use std::comm::Port;

use extra::getopts;

use std::libc::types::os::arch::posix88::pid_t;
use std::libc;

extern {
  pub fn kill(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int;
}

// The basic unit for a command that could be run.
struct Cmd {
    program : ~str,
    argv : ~[~str],
}

impl Cmd {
    fn new(cmd_name: &str) -> Option<Cmd> {
        let mut argv: ~[~str] = split_words(cmd_name);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            let argv : ~[~str] = argv;
            Some(Cmd {
                program : program,
                argv : argv,
            })
        }
        else {
            None
        }
    }
}

struct FgProcess {
    command     : ~str,
    args        : ~[~str],
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl FgProcess {
    fn new(cmd : Cmd,
           stdin : Option<i32>,
           stdout : Option<i32>)
        -> Option<FgProcess>
    {
        if (cmd_exists(&cmd)) {
            Some(FgProcess {
                command     : cmd.program.to_owned(),
                args        : cmd.argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<Process> {
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        let options = ProcessOptions {
            env    : None,
            dir    : None,
            in_fd  : self.stdin,
            out_fd : self.stdout,
            err_fd : None,
        };
        Process::new(command, args, options)
    }
}

// Background processes are handled differently, but not *that* differently:
// we need to keep track of an ProcessExit port for determining whether a
// process has finished running, as well as a pid for the process (for killing
// it when the shell terminates).
struct BgProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    pid          : Option<i32>,
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl BgProcess {
    fn new(cmd : Cmd) -> Option<BgProcess> {
        if (cmd_exists(&cmd)) {
            Some(BgProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                pid: None,
                stdin: None,
                stdout: None,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Process ports; these don't leave this function and are used for
        // sending the PID out in the return value.
        let (pidport, pidchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
                Some(mut process) => {
                    // Send the pid out for the return value
                    pidchan.try_send_deferred(Some(process.get_id()));
                    chan.try_send_deferred(process.finish());
                }
                None => {
                    pidchan.try_send_deferred(None);
                }
            }
        });
        self.exit_port = Some(port);
        self.pid = pidport.recv();
        self.pid
    }
}

struct Shell {
    cmd_prompt : ~str,
    history    : ~[~str],
    processes  : ~[~BgProcess],
    broken : bool,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            history: ~[],
            processes: ~[],
            broken: false,
        }
    }

    fn run(&mut self) {
        // Setup the interrupt handler. Has to happen here, or it won't
        // retain control over interrupts.
        let mut listener = Listener::new();
        listener.register(Interrupt);
        let port = listener.port;
        spawn(proc() {
            try(proc() {
                loop {
                    match port.recv_opt() {
                        Some(Interrupt) => {
                        }
                        None => {
                            break;
                        }
                        _ => {
                        }
                    }
                }
                return 0
            });
            return ();
        });

        // Standard input reader
        let mut stdin = BufferedReader::new(stdin());
        loop {
            // Show the prompt
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.disown_dead();

            // Push commands onto the history
            match program {
                "" => {}
                _  => {
                    self.push_hist(cmd_line)
                }
            }

            match program {
                "" =>  {
                    continue;
                }
                "exit" =>  {
                    self.kill_all();
                    return;
                }
                "history" => {
                    self.show_hist();
                }
                "jobs" => {
                    self.jobs();
                }
                "cd" =>  {
                    self.chdir(cmd_line);
                }
                _ => {
                    self.run_cmdline(cmd_line);
                }
            }
        }
    }

    // Nice extra feature: list running jobs.
    fn jobs(&mut self) {
        for cmd in self.processes.iter() {
            print!("{:s}", cmd.command);
            match cmd.pid {
                Some(pid) => {
                    println!(" {:i}", pid);
                }
                None => {
                    println("");
                }
            }
        }
    }

    // Remove dead processes from the list of processes.
    fn disown_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            borrowed_maybe(&cmd.exit_port, |port| match port.try_recv() {
                Some(_) => {dead.push(i); Some(i)},
                _ => None,
            });
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.pid {
                Some(pid) => {
                    unsafe {
                        // Kill perhaps isn't the best way to do this, but
                        // without it we have no way of killing our background
                        // processes with only the PID - we'd need the process
                        // object itself, and that can't be moved out of the
                        // spawn() that it's trapped inside.
                        kill(pid, 15);
                    }
                }
                None => {
                }
            }
        }
    }

    // Push a new command onto history.
    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

    fn show_hist(&mut self) {
        println!("{:s}", self.get_hist());
    }

    fn get_hist(&mut self) -> ~str {
        let mut hist = ~"";
        for i in self.history.iter() {
            let s = i.to_owned();
            match hist {
                ~"" => { hist = s; }
                _   => { hist = hist + "\n" + s; }
            }
        }
        return hist
    }

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] = split_words(cmd_line);
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    // Determine the type of the current block, and send it to the right
    // parsing function.
    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

    // Parse a new lone process. Background/foreground it appropriately.
    fn parse_process(&mut self, cmd_line : &str) -> Option<Process>{
        maybe(Cmd::new(cmd_line), |cmd| {
            if (cmd.argv.len() > 0 && cmd.argv.last() == &~"&") {
                let mut argv = cmd.argv.to_owned();
                argv.pop();
                self.make_bg_process(Cmd{
                    program:cmd.program,
                    argv:argv});
                None
            }
            else {
                make_process(cmd, Some(0), Some(1))
            }
        })
    }

    // Parse a process pipeline, and redirect stdin/stdout appropriately.
    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        maybe(Cmd::new(pair[0].trim()), |cmd| {
            if pair.len() > 1 {
                let two = self.parse_pipeline(pair[1].trim());
                self.pipe_input(cmd, two)
            }
            else {
                maybe(make_process(cmd, None, None), |process| Some(~process))
            }
        })
    }

    // Redirect the stdout of Process two into the stdin of `cmd`, and return
    // the process created from `cmd`.
    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
            Some(mut process) => {
                match two {
                    Some(mut p2) => {
                        let output = p2.finish_with_output();
                        if output.status.success() {
                            process.input().write(output.output);
                            process.close_input();
                            process.finish();
                        }
                        Some(~process)
                    }
                    None => {
                        println("No two remain.");
                        Some(~process)
                    }
                }
            }
            None => {
                None
            }
        }
    }

    // Make a new background process, and push it onto our stack of tracked
    // background processes.
    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BgProcess::new(cmd) {
            Some(mut process) => {
                match process.run() {
                    Some(pid) => {
                        println!("{:s} {:i}", name, pid);
                        self.processes.push(~process);
                    }
                    None => {
                    }
                }
            }
            None => {
            }
        }
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
            Some(cmd_str) => {cmd_str.to_owned()},
            None          => {~""}
        };
        return Some(cmd_str);
    } else {
        return None;
    }
}

// Determine whether a command exists using "which".
fn cmd_exists(command : &Cmd) -> bool {
    let ret = run::process_output("which", [command.program.to_owned()]);
    return ret.expect("exit code error.").status.success();
}

fn split_words(word : &str) -> ~[~str] {
    word.split(' ').filter_map(
        |x| if x != "" { Some(x.to_owned()) } else { None }
        ).to_owned_vec()
}

fn make_process(cmd : Cmd,
                stdin: Option<i32>,
                stdout: Option<i32>) -> Option<run::Process> {
    maybe(FgProcess::new(cmd, stdin, stdout),
          |mut cmdprocess| cmdprocess.run())
}

fn parse_l_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('<').collect();
    let filename = pair[0].trim();
    match maybe(Cmd::new(pair[1].trim()), |c| make_process(c, None, Some(1))) {
        Some(mut process) => {
            match File::open_mode(&Path::new(filename),
                                    std::io::Append,
                                    std::io::ReadWrite) {
                Some(file) => {
                    let proc_input = process.input();
                    let mut file_buffer = BufferedReader::new(file);
                    proc_input.write(file_buffer.read_to_end());
                }
                None => {
                }
            }
        }
        None => {
        }
    }
}

fn write_output_to_file(output : std::run::ProcessOutput,
                        filename : &str) {
    if output.status.success() {
        match File::open_mode(&Path::new(filename),
                                std::io::Truncate,
                                std::io::Write)
        {
            Some(mut file) => {
                file.write(output.output);
            }
            None =>{
                println!("Opening {:s} failed!", filename);
            }
        }
    }
    else {
        println!("{:?}", output.error);
    }
}

fn parse_r_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('>').collect();
    let file = pair[0].trim();
    let cmd = Cmd::new(pair[1].trim());
    match maybe(cmd, |c| make_process(c, None, None)) {
        Some(mut process) => {
            write_output_to_file(
                process.finish_with_output(),
                file);
        }
        None => {
        }
    }
}

// Describes a computation that could fail.
// If v is Some(...), then f is called on v and the result is returned.
// Otherwise, None is returned.
fn maybe<A, B>(v : Option<A>, f : |A| -> Option<B>) -> Option<B> {
    match v {
        Some(v) => f(v),
        None    => None,
    }
}

// Same as maybe(v, f), but for &v.
fn borrowed_maybe<A, B>(v : &Option<A>, f : |&A| -> Option<B>) -> Option<B> {
    match *v {
        Some(ref v) => f(v),
        None    => None,
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => {
            let mut shell = Shell::new("");
            shell.run_cmdline(cmd_line);
        }
        None => {
            let mut shell = Shell::new("gash > ");
            shell.run();
        }
    }
}
<commit_msg>factor out prompt loop<commit_after>//
// gash.rs
// by Nathan Typanski
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{run, os};
use std::io::buffered::BufferedReader;
use std::io::{stdin, stdio, File, Truncate, Write};
use std::io::process::ProcessExit;
use std::io::signal::{Listener, Interrupt};
use std::path::posix::Path;
use std::option::Option;
use std::task::try;
use std::run::Process;
use std::run::ProcessOptions;
use std::comm::Port;

use extra::getopts;

use std::libc::types::os::arch::posix88::pid_t;
use std::libc;

extern {
  pub fn kill(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int;
}

// The basic unit for a command that could be run.
struct Cmd {
    program : ~str,
    argv : ~[~str],
}

impl Cmd {
    fn new(cmd_name: &str) -> Option<Cmd> {
        let mut argv: ~[~str] = split_words(cmd_name);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            let argv : ~[~str] = argv;
            Some(Cmd {
                program : program,
                argv : argv,
            })
        }
        else {
            None
        }
    }
}

struct FgProcess {
    command     : ~str,
    args        : ~[~str],
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl FgProcess {
    fn new(cmd : Cmd,
           stdin : Option<i32>,
           stdout : Option<i32>)
        -> Option<FgProcess>
    {
        if (cmd_exists(&cmd)) {
            Some(FgProcess {
                command     : cmd.program.to_owned(),
                args        : cmd.argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<Process> {
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        let options = ProcessOptions {
            env    : None,
            dir    : None,
            in_fd  : self.stdin,
            out_fd : self.stdout,
            err_fd : None,
        };
        Process::new(command, args, options)
    }
}

// Background processes are handled differently, but not *that* differently:
// we need to keep track of an ProcessExit port for determining whether a
// process has finished running, as well as a pid for the process (for killing
// it when the shell terminates).
struct BgProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    pid          : Option<i32>,
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl BgProcess {
    fn new(cmd : Cmd) -> Option<BgProcess> {
        if (cmd_exists(&cmd)) {
            Some(BgProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                pid: None,
                stdin: None,
                stdout: None,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Process ports; these don't leave this function and are used for
        // sending the PID out in the return value.
        let (pidport, pidchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
                Some(mut process) => {
                    // Send the pid out for the return value
                    pidchan.try_send_deferred(Some(process.get_id()));
                    chan.try_send_deferred(process.finish());
                }
                None => {
                    pidchan.try_send_deferred(None);
                }
            }
        });
        self.exit_port = Some(port);
        self.pid = pidport.recv();
        self.pid
    }
}

struct Shell {
    cmd_prompt : ~str,
    history    : ~[~str],
    processes  : ~[~BgProcess],
    broken : bool,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            history: ~[],
            processes: ~[],
            broken: false,
        }
    }

    fn run(&mut self) {
        // Setup the interrupt handler. Has to happen here, or it won't
        // retain control over interrupts.
        let mut listener = Listener::new();
        listener.register(Interrupt);
        let port = listener.port;
        spawn(proc() {
            try(proc() {
                loop {
                    match port.recv_opt() {
                        Some(Interrupt) => {
                        }
                        None => {
                            break;
                        }
                        _ => {
                        }
                    }
                }
                return 0
            });
            return ();
        });

        self.display_prompt();
    }

    fn display_prompt(&mut self) {
        // Standard input reader
        let mut stdin = BufferedReader::new(stdin());
        // Show the prompt
        print(self.cmd_prompt);
        stdio::flush();

        let line = stdin.read_line().unwrap();
        let cmd_line = line.trim().to_owned();
        let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
        self.disown_dead();

        // Push commands onto the history
        match program {
            "" => {}
            _  => {
                self.push_hist(cmd_line)
            }
        }

        match program {
            "" =>  {
                self.display_prompt();
            }
            "exit" =>  {
                self.kill_all();
            }
            "history" => {
                self.show_hist();
                self.display_prompt();
            }
            "jobs" => {
                self.jobs();
                self.display_prompt();
            }
            "cd" =>  {
                self.chdir(cmd_line);
                self.display_prompt();
            }
            _ => {
                self.run_cmdline(cmd_line);
                self.display_prompt();
            }
        }
    }

    // Nice extra feature: list running jobs.
    fn jobs(&mut self) {
        for cmd in self.processes.iter() {
            print!("{:s}", cmd.command);
            match cmd.pid {
                Some(pid) => {
                    println!(" {:i}", pid);
                }
                None => {
                    println("");
                }
            }
        }
    }

    // Remove dead processes from the list of processes.
    fn disown_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            borrowed_maybe(&cmd.exit_port, |port| match port.try_recv() {
                Some(_) => {dead.push(i); Some(i)},
                _ => None,
            });
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.pid {
                Some(pid) => {
                    unsafe {
                        // Kill perhaps isn't the best way to do this, but
                        // without it we have no way of killing our background
                        // processes with only the PID - we'd need the process
                        // object itself, and that can't be moved out of the
                        // spawn() that it's trapped inside.
                        kill(pid, 15);
                    }
                }
                None => {
                }
            }
        }
    }

    // Push a new command onto history.
    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

    fn show_hist(&mut self) {
        println!("{:s}", self.get_hist());
    }

    fn get_hist(&mut self) -> ~str {
        let mut hist = ~"";
        for i in self.history.iter() {
            let s = i.to_owned();
            match hist {
                ~"" => { hist = s; }
                _   => { hist = hist + "\n" + s; }
            }
        }
        return hist
    }

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] = split_words(cmd_line);
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    // Determine the type of the current block, and send it to the right
    // parsing function.
    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

    // Parse a new lone process. Background/foreground it appropriately.
    fn parse_process(&mut self, cmd_line : &str) -> Option<Process>{
        maybe(Cmd::new(cmd_line), |cmd| {
            if (cmd.argv.len() > 0 && cmd.argv.last() == &~"&") {
                let mut argv = cmd.argv.to_owned();
                argv.pop();
                self.make_bg_process(Cmd{
                    program:cmd.program,
                    argv:argv});
                None
            }
            else {
                make_process(cmd, Some(0), Some(1))
            }
        })
    }

    // Parse a process pipeline, and redirect stdin/stdout appropriately.
    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        maybe(Cmd::new(pair[0].trim()), |cmd| {
            if pair.len() > 1 {
                let two = self.parse_pipeline(pair[1].trim());
                self.pipe_input(cmd, two)
            }
            else {
                maybe(make_process(cmd, None, None), |process| Some(~process))
            }
        })
    }

    // Redirect the stdout of Process two into the stdin of `cmd`, and return
    // the process created from `cmd`.
    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
            Some(mut process) => {
                match two {
                    Some(mut p2) => {
                        let output = p2.finish_with_output();
                        if output.status.success() {
                            process.input().write(output.output);
                            process.close_input();
                            process.finish();
                        }
                        Some(~process)
                    }
                    None => {
                        println("No two remain.");
                        Some(~process)
                    }
                }
            }
            None => {
                None
            }
        }
    }

    // Make a new background process, and push it onto our stack of tracked
    // background processes.
    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BgProcess::new(cmd) {
            Some(mut process) => {
                match process.run() {
                    Some(pid) => {
                        println!("{:s} {:i}", name, pid);
                        self.processes.push(~process);
                    }
                    None => {
                    }
                }
            }
            None => {
            }
        }
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
            Some(cmd_str) => {cmd_str.to_owned()},
            None          => {~""}
        };
        return Some(cmd_str);
    } else {
        return None;
    }
}

// Determine whether a command exists using "which".
fn cmd_exists(command : &Cmd) -> bool {
    let ret = run::process_output("which", [command.program.to_owned()]);
    return ret.expect("exit code error.").status.success();
}

fn split_words(word : &str) -> ~[~str] {
    word.split(' ').filter_map(
        |x| if x != "" { Some(x.to_owned()) } else { None }
        ).to_owned_vec()
}

fn make_process(cmd : Cmd,
                stdin: Option<i32>,
                stdout: Option<i32>) -> Option<run::Process> {
    maybe(FgProcess::new(cmd, stdin, stdout),
          |mut cmdprocess| cmdprocess.run())
}

fn parse_l_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('<').collect();
    let filename = pair[0].trim();
    match maybe(Cmd::new(pair[1].trim()), |c| make_process(c, None, Some(1))) {
        Some(mut process) => {
            match File::open_mode(&Path::new(filename),
                                    std::io::Append,
                                    std::io::ReadWrite) {
                Some(file) => {
                    let proc_input = process.input();
                    let mut file_buffer = BufferedReader::new(file);
                    proc_input.write(file_buffer.read_to_end());
                }
                None => {
                }
            }
        }
        None => {
        }
    }
}

fn write_output_to_file(output : std::run::ProcessOutput,
                        filename : &str) {
    if output.status.success() {
        match File::open_mode(&Path::new(filename),
                                std::io::Truncate,
                                std::io::Write)
        {
            Some(mut file) => {
                file.write(output.output);
            }
            None =>{
                println!("Opening {:s} failed!", filename);
            }
        }
    }
    else {
        println!("{:?}", output.error);
    }
}

fn parse_r_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('>').collect();
    let file = pair[0].trim();
    let cmd = Cmd::new(pair[1].trim());
    match maybe(cmd, |c| make_process(c, None, None)) {
        Some(mut process) => {
            write_output_to_file(
                process.finish_with_output(),
                file);
        }
        None => {
        }
    }
}

// Describes a computation that could fail.
// If v is Some(...), then f is called on v and the result is returned.
// Otherwise, None is returned.
fn maybe<A, B>(v : Option<A>, f : |A| -> Option<B>) -> Option<B> {
    match v {
        Some(v) => f(v),
        None    => None,
    }
}

// Same as maybe(v, f), but for &v.
fn borrowed_maybe<A, B>(v : &Option<A>, f : |&A| -> Option<B>) -> Option<B> {
    match *v {
        Some(ref v) => f(v),
        None    => None,
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => {
            let mut shell = Shell::new("");
            shell.run_cmdline(cmd_line);
        }
        None => {
            let mut shell = Shell::new("gash > ");
            shell.run();
        }
    }
}
<|endoftext|>


<commit_before>use std::{io, path};
use std::rand;
use std::rand::Rng;

fn draw(level: uint){
	let mut myfile: ~str = level.to_str();
	myfile = myfile + ".txt";

	let read: Result<@Reader, ~str>;
	read = io::file_reader(~path::Path(myfile));

	if read.is_ok() {
	let file = read.unwrap();
	let text: ~[~str] = file.read_lines();
        for i in range(0, text.len()) {
		println(text[i]);
        }
	}

}

fn load(filename: ~str) -> ~[~str] {
	let read: Result<@Reader, ~str>;
	read = io::file_reader(~path::Path(filename));

	if read.is_ok() {
	let file = read.unwrap();
	return file.read_lines();
	}

	println(fmt!("Error in reading the file: %?", read.unwrap_err()));
	return ~[];
}

fn isDuplicate(guess: char) -> bool{
	for uint::range(0,guesses.length()) |b|{
		let ch = copy(guesses[b]);
		match ch {
			guess => {return true;}
			_ => {}
		}
	}
	return false;
}

//fn searchChar(guess: char) -> bool{

//}

fn main() {
        let mut rng = rand::task_rng();
	let dictionary: ~[~str] = load(~"words.txt");
	let max_size: uint = dictionary.len();
        let n: uint = rng.gen_integer_range(0u, max_size);
	let word: ~str = dictionary[n];
	let word_len: uint = word.char_len();

	//Stores the guessed word
	let mut i = 0;
	//let mut guessedWord: ~[~str] = ~[~""];
	let mut guessedWord: ~[char] = ~[];
	while (i < word_len) {
        	guessedWord = guessedWord + ~['_'];
		i += 1;
        }
        println(guessedWord.to_str());
	println(word);

	let mut guesses: ~[char] = ~[];

	let mut j = 0;
        let max_guess : uint = 6;
	while (j < max_guess){

		let line = io::stdin().read_line();
		let guess_char: char = line.char_at(0);
		println(fmt!("%? guessed: ", guess_char));
		guesses = guesses + ~['a', 'b'];
		println(isDuplicate(guess_char).to_str());
		let has_char: bool = word.contains_char(guess_char);
	        draw(j+1);
		if (has_char){
       			let mut i = 0;
			while (i < word_len) {
				if(word.char_at(i) != guess_char){
        	 			print("_ ");
                		}else{
		 			print(fmt!("%c", guess_char));
		 			print(" ");
				}
				i += 1;
       			 }
			println("");
		}
		else{
			println("nope.");
			j += 1;
		}
	}
	println("Out of guesses - you lose.");

}
<commit_msg>isDuplicate works, has not been called yet<commit_after>use std::{io, path, libc, os};
use std::rand;
use std::rand::Rng;

fn draw(level: uint){
	let mut myfile: ~str = level.to_str();
	myfile = myfile + ".txt";

	let read: Result<@Reader, ~str>;
	read = io::file_reader(~path::Path(myfile));

	if read.is_ok() {
	let file = read.unwrap();
	let text: ~[~str] = file.read_lines();
        for i in range(0, text.len()) {
		println(text[i]);
        }
	}

}

fn load(filename: ~str) -> ~[~str] {
	let read: Result<@Reader, ~str>;
	read = io::file_reader(~path::Path(filename));

	if read.is_ok() {
	let file = read.unwrap();
	return file.read_lines();
	}

	println(fmt!("Error in reading the file: %?", read.unwrap_err()));
	return ~[];
}

fn isDuplicate(guess: char, guesses: ~[~str]) -> bool{

	for i in range(0,guesses.len()) {
		if (guess.to_str() == guesses[i]){
			return true;
		}
	}
	return false;

}

//fn searchChar(guess: char) -> bool{

//}

fn main() {
        let mut rng = rand::task_rng();
	let dictionary: ~[~str] = load(~"words.txt");
	let max_size: uint = dictionary.len();
        let n: uint = rng.gen_integer_range(0u, max_size);
	let word: ~str = dictionary[n];
	let word_len: uint = word.char_len();

	//Stores the guessed word
	let mut i = 0;
	let mut guessedWord: ~[~str] = ~[];
	while (i < word_len) {
        	guessedWord = guessedWord + ~[~""];
		i += 1;
        }
        println(guessedWord.to_str());
	println(word);

	let mut guesses: ~[~str] = ~[];

	let mut j = 0;
        let max_guess : uint = 6;
	while (j < max_guess){

		let mut line = io::stdin().read_line();
		let mut guess_char: char = line.char_at(0);
		println(fmt!("%? guessed: ", guess_char));

		let has_char: bool = word.contains_char(guess_char);
	        draw(j+1);
		if (has_char){
       			let mut i = 0;
			while (i < word_len) {
				if(word.char_at(i) != guess_char){
        	 			print("_ ");
                		}else{
		 			print(fmt!("%c", guess_char));
		 			print(" ");
				}
				i += 1;
       			 }
			println("");
		}
		else{
			println("nope.");
			j += 1;
		}
	}
	println("Out of guesses - you lose.");

}
<|endoftext|>


<commit_before>// Copyright (c) 2015 by Stacy Prowell.  All rights reserved.
//
// Licensed under the BSD 2-Clause license.  See the file LICENSE
// that is part of this distribution.  This file may not be copied,
// modified, or distributed except according to those terms.extern

//! Provide basic definitions of terms.

use num::BigInt;
use std::rc::Rc;

/// Export the kinds of terms.
pub use self::Term::{Root, /*Symbol, String,*/ Integer, Float, BitString, Boolean};

/// Represent an instance of a term.
#[derive(Copy, Debug)]
pub enum Term {
	/// The special root type.
	Root,
	/// Integer literals.
	Integer(Rc<Term>, BigInt),
	/// Float literals.
	Float(Rc<Term>, BigInt, BigInt, u32),
	/// Bit string literals.
	BitString(Rc<Term>, BigInt, BigInt),
	/// Boolean literals.
	Boolean(Rc<Term>, bool),
	// /// Symbol literals.
	// Symbol(Rc<Term>, String),
	// /// String literals.
	// String(Rc<Term>, String),
}

impl Term {
	pub fn get_typ(&self) -> Term {
		match *self {
			Root => Root,
			// Symbol(typ, _) => typ,
			// String(typ, _) => typ,
			Integer(typ, _) => typ,
			Float(typ, _, _, _) => typ,
			BitString(typ, _, _) => typ,
			Boolean(typ, _) => typ
		}
	}
}
<commit_msg>Removed the old derive.<commit_after>// Copyright (c) 2015 by Stacy Prowell.  All rights reserved.
//
// Licensed under the BSD 2-Clause license.  See the file LICENSE
// that is part of this distribution.  This file may not be copied,
// modified, or distributed except according to those terms.extern

//! Provide basic definitions of terms.

use num::BigInt;
use std::rc::Rc;

/// Export the kinds of terms.
pub use self::Term::{Root, SymbolLiteral, StringLiteral, IntegerLiteral,
	FloatLiteral, BitStringLiteral, BooleanLiteral};

/// Represent an instance of a term.
pub enum Term {
	/// The special root type.
	Root,
	/// Integer literals.
	IntegerLiteral(Rc<Term>, BigInt),
	/// Float literals.
	FloatLiteral(Rc<Term>, BigInt, BigInt, u32),
	/// Bit string literals.
	BitStringLiteral(Rc<Term>, BigInt, BigInt),
	/// Boolean literals.
	BooleanLiteral(Rc<Term>, bool),
	/// Symbol literals.
	SymbolLiteral(Rc<Term>, String),
	/// String literals.
	StringLiteral(Rc<Term>, String),
}
<|endoftext|>


<commit_before>use token::{Lexeme, Token};
use error::{Result, SassError, ErrorKind};

use std::str::CharIndices;
use std::iter::Peekable;

pub struct Tokenizer<'a> {
    text: &'a str,
    chars: Peekable<CharIndices<'a>>,
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Lexeme>;

    fn next(&mut self) -> Option<Result<Lexeme>> {
        match self.parse() {
            Ok(Some(t)) => Some(Ok(t)),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

impl<'a> Tokenizer<'a> {
    pub fn new(text: &str) -> Tokenizer {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    pub fn parse(&mut self) -> Result<Option<Lexeme>> {
        let mut value = String::new();
        let mut start;

        while let Some((char_offset, curr_char)) = self.chars.next() {
            // Skip leading whitespace
            if curr_char.is_whitespace() {
                continue;
            } else {
                let single_char_token = Token::from_char(curr_char);
                if value.is_empty() && single_char_token.is_some() {
                    if curr_char == '-' {
                        value.push(curr_char);
                        start = char_offset;

                        if let Some(&(_, peek_char)) = self.chars.peek() {
                            if peek_char.is_whitespace() {
                                return Ok(Some(Lexeme { token: Token::Minus, offset: Some(start) }))
                            } else if peek_char.is_numeric() {
                                while let Some(&(_, next_char)) = self.chars.peek() {
                                    // Stop when we reach a non-numeric char
                                    if !next_char.is_numeric() && next_char != '.' {
                                        break;
                                    } else {
                                        value.push(next_char);
                                        self.chars.next();
                                    }
                                }

                                let value = match value.parse() {
                                    Ok(v) => v,
                                    Err(_) => return Err(SassError {
                                        offset: start,
                                        kind: ErrorKind::TokenizerError,
                                        message: format!(
                                            "Tried to parse `{}` into a f32 but failed.",
                                            value,
                                        ),
                                    })
                                };
                                return Ok(Some(Lexeme { token: Token::Number(value), offset: Some(start) }))

                            } else {
                                while let Some(&(_, next_char)) = self.chars.peek() {
                                    // Stop when we reach a non-ident char (hyphens are special)
                                    if next_char.is_whitespace() || (
                                        is_single_char_token(next_char) && next_char != '-'
                                    ) {
                                        break;
                                    } else {
                                        value.push(next_char);
                                        self.chars.next();
                                    }
                                }
                                return Ok(Some(Lexeme { token: Token::Ident(value), offset: Some(start) }))
                            }
                        }
                    } else {
                        // We already tested that single_char_token was Some.
                        return Ok(Some(Lexeme {
                            token: single_char_token.unwrap(),
                            offset: Some(char_offset),
                        }))
                    }
                } else {

                    if curr_char.is_numeric() {
                        // Start of a multi-char numeric token
                        value.push(curr_char);
                        start = char_offset;

                        while let Some(&(_, next_char)) = self.chars.peek() {
                            // Stop when we reach a non-numeric char
                            if !next_char.is_numeric() && next_char != '.' {
                                break;
                            } else {
                                value.push(next_char);
                                self.chars.next();
                            }
                        }
                        let value = match value.parse() {
                            Ok(v) => v,
                            Err(_) => return Err(SassError {
                                offset: start,
                                kind: ErrorKind::TokenizerError,
                                message: format!(
                                    "Tried to parse `{}` into a f32 but failed.",
                                    value,
                                ),
                            })
                        };
                        return Ok(Some(Lexeme { token: Token::Number(value), offset: Some(start) }))
                    } else {
                        // Start of a multi-char non-numeric token
                        value.push(curr_char);
                        start = char_offset;
                        while let Some(&(_, next_char)) = self.chars.peek() {
                            // Stop when we reach a non-ident char (hyphens are special)
                            if next_char.is_whitespace() || (
                                is_single_char_token(next_char) && next_char != '-'
                            ) {
                                break;
                            } else {
                                value.push(next_char);
                                self.chars.next();
                            }
                        }
                        return Ok(Some(Lexeme { token: Token::Ident(value), offset: Some(start) }))
                    }
                }
            }
        }
        return Ok(None)
    }
}

fn is_single_char_token(ch: char) -> bool {
    Token::from_char(ch).is_some()
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use error::Result;

    fn expected_lexeme(expected_token: Token, expected_offset: usize) -> Option<Result<Lexeme>> {
        Some(Ok(Lexeme {
            token: expected_token,
            offset: Some(expected_offset),
        }))
    }

    fn expected_ident(expected_value: &str, expected_offset: usize) -> Option<Result<Lexeme>> {
        expected_lexeme(Token::Ident(expected_value.into()), expected_offset)
    }

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_none_for_only_whitespace() {
        let mut tokenizer = Tokenizer::new("     ");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_words() {
        // Without regard to Sass word validity
        let mut tokenizer = Tokenizer::new(" \n  div   aoeu  ");
        assert_eq!(tokenizer.next(), expected_ident("div", 4));
        assert_eq!(tokenizer.next(), expected_ident("aoeu", 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_curly_braces() {
        // Without regard to matching
        let mut tokenizer = Tokenizer::new("{}}a{ blah}");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 2));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 4));
        assert_eq!(tokenizer.next(), expected_ident("blah", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_colon() {
        let mut tokenizer = Tokenizer::new(":invalid: property::");
        // This might be wrong. I have a note somewhere else that colons can start idents.
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 0));
        assert_eq!(tokenizer.next(), expected_ident("invalid", 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 8));
        assert_eq!(tokenizer.next(), expected_ident("property", 10));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 19));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_semicolon() {
        let mut tokenizer = Tokenizer::new(";;\na;\nb\n;");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 1));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 4));
        assert_eq!(tokenizer.next(), expected_ident("b", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 8));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_numbers() {
        let mut tokenizer = Tokenizer::new("border: 0px 1.5 11em;");
        assert_eq!(tokenizer.next(), expected_ident("border", 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(0.0), 8));
        assert_eq!(tokenizer.next(), expected_ident("px", 9));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(1.5), 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(11.0), 16));
        assert_eq!(tokenizer.next(), expected_ident("em", 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 20));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_hyphen() {
        let mut tokenizer = Tokenizer::new("font-weight -webkit -3 - 4-5 a-1");
        assert_eq!(tokenizer.next(), expected_ident("font-weight", 0));
        assert_eq!(tokenizer.next(), expected_ident("-webkit", 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-3.0), 20));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Minus, 23));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 25));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-5.0), 26));
        assert_eq!(tokenizer.next(), expected_ident("a-1", 29));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_parens() {
        let mut tokenizer = Tokenizer::new("() rgb)()(");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 1));
        assert_eq!(tokenizer.next(), expected_ident("rgb", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 7));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 9));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_slash() {
        let mut tokenizer = Tokenizer::new("/ / 3/4 / 8");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 2));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(3.0), 4));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 5));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(8.0), 10));
        assert_eq!(tokenizer.next(), None);
    }
}
<commit_msg>Extract functions out of Tokenize.parse<commit_after>use token::{Lexeme, Token};
use error::{Result, SassError, ErrorKind};

use std::str::CharIndices;
use std::iter::Peekable;

pub struct Tokenizer<'a> {
    text: &'a str,
    chars: Peekable<CharIndices<'a>>,
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<Lexeme>;

    fn next(&mut self) -> Option<Result<Lexeme>> {
        match self.parse() {
            Ok(Some(t)) => Some(Ok(t)),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

impl<'a> Tokenizer<'a> {
    pub fn new(text: &str) -> Tokenizer {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    pub fn parse(&mut self) -> Result<Option<Lexeme>> {
        while let Some((char_offset, curr_char)) = self.chars.next() {
            // Skip leading whitespace
            if curr_char.is_whitespace() {
                continue;
            } else {
                let single_char_token = Token::from_char(curr_char);
                if single_char_token.is_some() {
                    if curr_char == '-' {
                        if let Some(&(_, peek_char)) = self.chars.peek() {
                            if peek_char.is_whitespace() {
                                return Ok(Some(Lexeme {
                                    token: Token::Minus,
                                    offset: Some(char_offset)
                                }))
                            } else if peek_char.is_numeric() {
                                return self.number(curr_char, char_offset)
                            } else {
                                return self.ident(curr_char, char_offset)
                            }
                        }
                    } else {
                        // We already tested that single_char_token was Some.
                        return Ok(Some(Lexeme {
                            token: single_char_token.unwrap(),
                            offset: Some(char_offset),
                        }))
                    }
                } else {
                    if curr_char.is_numeric() {
                        return self.number(curr_char, char_offset)
                    } else {
                        return self.ident(curr_char, char_offset)
                    }
                }
            }
        }
        return Ok(None)
    }

    fn peek_char(&mut self) -> Option<char> {
        match self.chars.peek() {
            Some(&(_, peek_char)) => Some(peek_char),
            None => None,
        }
    }

    fn ident(&mut self, curr_char: char, start: usize) -> Result<Option<Lexeme>> {
        let mut value = String::new();
        value.push(curr_char);

        while let Some(peek_char) = self.peek_char() {
            // Stop when we reach a non-ident char (hyphens are special)
            if peek_char.is_whitespace() || (
                is_single_char_token(peek_char) && peek_char != '-'
            ) {
                break;
            } else {
                value.push(peek_char);
                self.chars.next();
            }
        }
        Ok(Some(Lexeme { token: Token::Ident(value), offset: Some(start) }))
    }

    fn number(&mut self, curr_char: char, start: usize) -> Result<Option<Lexeme>> {
        let mut value = String::new();
        value.push(curr_char);

        while let Some(peek_char) = self.peek_char() {
            // Stop when we reach a non-numeric char
            if !peek_char.is_numeric() && peek_char != '.' {
                break;
            } else {
                value.push(peek_char);
                self.chars.next();
            }
        }

        let value = match value.parse() {
            Ok(v) => v,
            Err(_) => return Err(SassError {
                offset: start,
                kind: ErrorKind::TokenizerError,
                message: format!(
                    "Tried to parse `{}` into a f32 but failed.",
                    value,
                ),
            })
        };
        Ok(Some(Lexeme { token: Token::Number(value), offset: Some(start) }))
    }
}

fn is_single_char_token(ch: char) -> bool {
    Token::from_char(ch).is_some()
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use error::Result;

    fn expected_lexeme(expected_token: Token, expected_offset: usize) -> Option<Result<Lexeme>> {
        Some(Ok(Lexeme {
            token: expected_token,
            offset: Some(expected_offset),
        }))
    }

    fn expected_ident(expected_value: &str, expected_offset: usize) -> Option<Result<Lexeme>> {
        expected_lexeme(Token::Ident(expected_value.into()), expected_offset)
    }

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_none_for_only_whitespace() {
        let mut tokenizer = Tokenizer::new("     ");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_returns_words() {
        // Without regard to Sass word validity
        let mut tokenizer = Tokenizer::new(" \n  div   aoeu  ");
        assert_eq!(tokenizer.next(), expected_ident("div", 4));
        assert_eq!(tokenizer.next(), expected_ident("aoeu", 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_curly_braces() {
        // Without regard to matching
        let mut tokenizer = Tokenizer::new("{}}a{ blah}");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 2));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftCurlyBrace, 4));
        assert_eq!(tokenizer.next(), expected_ident("blah", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightCurlyBrace, 10));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_colon() {
        let mut tokenizer = Tokenizer::new(":invalid: property::");
        // This might be wrong. I have a note somewhere else that colons can start idents.
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 0));
        assert_eq!(tokenizer.next(), expected_ident("invalid", 1));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 8));
        assert_eq!(tokenizer.next(), expected_ident("property", 10));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 19));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_semicolon() {
        let mut tokenizer = Tokenizer::new(";;\na;\nb\n;");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 1));
        assert_eq!(tokenizer.next(), expected_ident("a", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 4));
        assert_eq!(tokenizer.next(), expected_ident("b", 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 8));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_numbers() {
        let mut tokenizer = Tokenizer::new("border: 0px 1.5 11em;");
        assert_eq!(tokenizer.next(), expected_ident("border", 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Colon, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(0.0), 8));
        assert_eq!(tokenizer.next(), expected_ident("px", 9));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(1.5), 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(11.0), 16));
        assert_eq!(tokenizer.next(), expected_ident("em", 18));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Semicolon, 20));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_hyphen() {
        let mut tokenizer = Tokenizer::new("font-weight -webkit -3 - 4-5 a-1");
        assert_eq!(tokenizer.next(), expected_ident("font-weight", 0));
        assert_eq!(tokenizer.next(), expected_ident("-webkit", 12));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-3.0), 20));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Minus, 23));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 25));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(-5.0), 26));
        assert_eq!(tokenizer.next(), expected_ident("a-1", 29));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_parens() {
        let mut tokenizer = Tokenizer::new("() rgb)()(");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 1));
        assert_eq!(tokenizer.next(), expected_ident("rgb", 3));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 7));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::RightParen, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::LeftParen, 9));
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn it_separates_slash() {
        let mut tokenizer = Tokenizer::new("/ / 3/4 / 8");
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 0));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 2));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(3.0), 4));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 5));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(4.0), 6));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Slash, 8));
        assert_eq!(tokenizer.next(), expected_lexeme(Token::Number(8.0), 10));
        assert_eq!(tokenizer.next(), None);
    }
}
<|endoftext|>