<commit_before>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(DATA_READY)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 17],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 17] = [0; 17];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            {
                let mut buffer = self.buffer.lock().unwrap();
                buffer.i = 0;
                match parameters.command {
                    NOP => (),
                    SEL_DSK => {
                        if buffer.bytes[0] < MAX_DISK {
                            parameters.disk = buffer.bytes[0];
                        } else {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    },
                    SEL_TRK => {
                        let track = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                if track < disk.tracks {
                                    parameters.track = track;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    SEL_SEC => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let sector = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                                if sector < disk.spt {
                                    parameters.sector = sector;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    READ => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_slice() };
                                for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                    *byte = bytes[off + i];
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    WRITE => {
                        match disks[parameters.disk as usize] {
                            Some(ref mut disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_mut_slice() };
                                for (i, byte) in buffer.bytes.iter().enumerate() {
                                    bytes[i] = *byte;
                                };
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
            parameters.do_command = false;
            self.status.fetch_or(DATA_READY, Ordering::SeqCst);
        }
    }
}
<commit_msg>Racey racey race-race<commit_after>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(DATA_READY)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 17],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 17] = [0; 17];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            {
                let mut buffer = self.buffer.lock().unwrap();
                buffer.i = 0;
                match parameters.command {
                    NOP => (),
                    SEL_DSK => {
                        if buffer.bytes[0] < MAX_DISK {
                            parameters.disk = buffer.bytes[0];
                        } else {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    },
                    SEL_TRK => {
                        let track = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                if track < disk.tracks {
                                    parameters.track = track;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    SEL_SEC => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let sector = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                                if sector < disk.spt {
                                    parameters.sector = sector;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    READ => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_slice() };
                                for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                    *byte = bytes[off + i];
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    WRITE => {
                        match disks[parameters.disk as usize] {
                            Some(ref mut disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_mut_slice() };
                                for (i, byte) in buffer.bytes.iter().enumerate() {
                                    bytes[i] = *byte;
                                };
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
            parameters.do_command = false;
            self.status.fetch_or(DATA_READY, Ordering::SeqCst);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>require c++11 to run the test suite<commit_after><|endoftext|>


<commit_before>//! Ranges.
//!
//! This module contains the main primitive in zmicro.

/// A range.
///
/// This is the main primitive in the range encoding techniques. Ranges represent some variable
/// size block of the stream, which breaks down to bits.
///
/// Bits are read and written according to some probability. When a bit is written, the range
/// updates to a smaller subrange. The subrange's size is determined according to the probability
/// of the bit being 0. If the bit written is 0, and the probability pridiction was `Pr(0) = p`,
/// then the range's new size is `p·r` with `r` being the old size.
///
/// Ranges has an important invariant: They can never be of zero size. If a range is of size 1,
/// however, the range is said to be _exhausted_, meaning that it cannot store any more
/// information.
///
/// This concept might seem weird as first, but it is incredibly logical: The more correct the
/// prediction is, the less the range narrows, and consequently it is exhausted earlier, meaning
/// that it can store more information.
///
/// As such, the efficiency depends entirely on the predictor, and the only theoretical bound that
/// exists is the state space.
struct Range {
    /// The start of the range.
    start: u32,
    /// The length of the range.
    len: u32,
}

impl Range {
    /// Create a full size range.
    fn full() -> Range {
        Range {
            start: 0,
            len: !0,
        }
    }

    /// Write a bit into the range.
    ///
    /// This pushes `bit` to the stream represented in the range, with the probability `pr_0` of
    /// being 0 (`false`).
    ///
    /// `pr_0` linearly corresponds to the probability, but is represented as an integer for
    /// performance reasons. As such, 0.5 corresponds to 0x7FFFFFFF, 1 corresponds to 0xFFFFFFFF,
    /// and 0 corresponds to 0.
    ///
    /// The returned boolean is true if there is space for more bits.
    fn write(&mut self, bit: bool, pr_0: u32) -> bool {
        // Fetch the length for performance reasons.
        let len = self.len;

        debug_assert!(len > 1, "The current length of the range is too small to contain more \
                      bits, please renormalize/flush.");

        // Calculate the new length of the left subrange (i.e. the length of the range if the bit
        // is 0):
        //
        // - Cast the integers to 64-bit to avoid any overflows.
        // - Multiply the current length by the integer representing a probability. The integer
        //   corresponds linearly to the probability on the unit interval.
        // - Divide by the maximal value of the unnormalized probability, in order to normalize the
        //   result:
        //     - Add half the maximal value to shift the result and flip the results which would be
        //       have decimal parts above .5 to the next number.
        //     - Shift 32 bits downwards (equivalent to floored division by the maximal
        //       unnormalized probability).
        // - Cast back to 32-bit integer truncating only 0s because of the above bit shift.
        let mut len_0 = ((len as u64 * pr_0 as u64 + 0x7FFFFFFF) >> 32) as u32;

        // Normalize len_0 to avoid zero intervals.
        if len_0 == 0 {
            // len_0 rounded down to an empty range. This means that the left subrange for bit 0 is
            // empty, and thus not representable. For this reason, we round up to 1 just to make
            // the subrange representable.
            len_0 = 1;
        } else if len_0 == len {
            // len_0 rounded up to the length of the current range. This means that the right
            // subrange for bit 1 will be empty, and thus not representable. For this reason, we
            // leap to a range which is one unit shorter than the current one, leaving a right
            // subrange of length 1.
            len_0 = len - 1;
        }

        if bit {
            // The bit was 1.

            // Refine the range to the second half:
            //
            //     [            self.len           ]
            //     [  len_0  ][  self.len - len_0  ]
            //                \~~~~~~~~~~~~~~~~~~~~/
            //                 This is the new range
            self.len   -= len_0;
            self.start += len_0;

            // It's not exhausted if the length is stil above one.
            self.len > 1
        } else {
            // The bit was 0.

            // The start of the range is fixed, but update the length of the range.
            self.len = len_0;

            // We do the same as above, but for microoptimization we use len_0 instead as it could
            // have been register allocated.
            len_0 > 1
        }
    }

    /// Read a bit from the range.
    ///
    /// This reads the top bit (first written bit) with probability `pr_0` from the range, and
    /// updates the range such that the second bit is the new first (similar to `pop` but FIFO).
    ///
    /// The probability `pr_0` **must** match the probability given when the bit was written into
    /// the range.
    ///
    /// `None` is returned if the range is exhausted and no more bits are stored in the range.
    fn read(&mut self, pr_0: u32) -> Option<bool> {
        debug_assert!(self.len != 0, "Zero-sized ranges are invalid.");

        if self.len == !0 {
            // The range cannot contain more information, so no more bits can be extracted from
            // this range.
            None
        } else {
            debug_assert!(self.len != 0, "Zero-sized ranges are invalid.");

            // Construct the left half subrange of the full range according to the given
            // probability of 0 occuring.
            let mut left_half = Range::full();
            left_half.write(false, pr_0);

            // Determine if the start false on the left or right half.
            if self.start < left_half.len {
                // The bit is 0.

                // Rescale the length to get the left superrange:
                //
                //     [ full range           ]
                //         [ rescaled range ]
                //         [ left ][  right ]
                //
                // The left range is `left_half`, which is relative to the full range. Now, we
                // divide by the size of this range, but we double the width to get the desired
                // precision.
                self.len = (((self.len as u64) << 32) / left_half.len as u64) as u32;

                // The start of the range is fixed, because the read bit is zer.

                Some(false)
            } else {
                // The bit is 1.

                // Negate the range to get the right half of the range.
                let right_half = !0 - left_half.len;
                // We repeat the same as above conditional.
                self.len = (((self.len as u64) << 32) / right_half as u64) as u32;

                // Subtract the length of the left half to "shift" the range towards the left in
                // order to complete the transformation. This means that the new offset is the
                // start of the right range.
                self.start -= left_half.len;

                Some(true)
            }
        }
    }
}

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

    #[test]
    fn write_read() {
        let mut range = Range::full();

        range.write(true, 5000000);
        range.write(true, 2999);
        range.write(false, 5000000000);
        range.write(false, 50000000);
        assert!(range.write(true, 333333332999));

        assert!( range.read(5000000).unwrap());
        assert!( range.read(2999).unwrap());
        assert!(!range.read(5000000000).unwrap());
        assert!(!range.read(50000000).unwrap());
    }

    #[test]
    fn write_ones() {
        let mut range = Range::full();

        let mut n = 0;
        while range.write(true, 500) {
            n += 1;
        }

        for i in 0..n {
            assert_eq!(range.read(500), Some(true));
        }

        assert_eq!(range.read(500), None);
    }
}
<commit_msg>Fix subtle off-by-one error and add more tests to zmicro.<commit_after>//! Ranges.
//!
//! This module contains the main primitive in zmicro.

/// A range.
///
/// This is the main primitive in the range encoding techniques. Ranges represent some variable
/// size block of the stream, which breaks down to bits.
///
/// Bits are read and written according to some probability. When a bit is written, the range
/// updates to a smaller subrange. The subrange's size is determined according to the probability
/// of the bit being 0. If the bit written is 0, and the probability pridiction was `Pr(0) = p`,
/// then the range's new size is `p·r` with `r` being the old size.
///
/// Ranges has an important invariant: They can never be of zero size. If a range is of size 1,
/// however, the range is said to be _exhausted_, meaning that it cannot store any more
/// information.
///
/// This concept might seem weird as first, but it is incredibly logical: The more correct the
/// prediction is, the less the range narrows, and consequently it is exhausted earlier, meaning
/// that it can store more information.
///
/// As such, the efficiency depends entirely on the predictor, and the only theoretical bound that
/// exists is the state space.
struct Range {
    /// The start of the range.
    start: u32,
    /// The length of the range.
    len: u32,
}

impl Range {
    /// Create a full size range.
    fn full() -> Range {
        Range {
            start: 0,
            len: !0,
        }
    }

    /// Write a bit into the range.
    ///
    /// This pushes `bit` to the stream represented in the range, with the probability `pr_0` of
    /// being 0 (`false`).
    ///
    /// `pr_0` linearly corresponds to the probability, but is represented as an integer for
    /// performance reasons. As such, 0.5 corresponds to 0x7FFFFFFF, 1 corresponds to 0xFFFFFFFF,
    /// and 0 corresponds to 0.
    ///
    /// The returned boolean is true if there is space for more bits.
    fn write(&mut self, bit: bool, pr_0: u32) -> bool {
        // Fetch the length for performance reasons.
        let len = self.len;

        debug_assert!(len > 1, "The current length of the range is too small to contain more \
                      bits, please renormalize/flush.");

        // Calculate the new length of the left subrange (i.e. the length of the range if the bit
        // is 0):
        //
        // - Cast the integers to 64-bit to avoid any overflows.
        // - Multiply the current length by the integer representing a probability. The integer
        //   corresponds linearly to the probability on the unit interval.
        // - Divide by the maximal value of the unnormalized probability, in order to normalize the
        //   result:
        //     - Add half the maximal value to shift the result and flip the results which would be
        //       have decimal parts above .5 to the next number.
        //     - Shift 32 bits downwards (equivalent to floored division by the maximal
        //       unnormalized probability).
        // - Cast back to 32-bit integer truncating only 0s because of the above bit shift.
        let mut len_0 = ((len as u64 * pr_0 as u64 + 0x80000000) >> 32) as u32;

        // Normalize len_0 to avoid zero intervals.
        if len_0 == 0 {
            // len_0 rounded down to an empty range. This means that the left subrange for bit 0 is
            // empty, and thus not representable. For this reason, we round up to 1 just to make
            // the subrange representable.
            len_0 = 1;
        } else if len_0 == len {
            // len_0 rounded up to the length of the current range. This means that the right
            // subrange for bit 1 will be empty, and thus not representable. For this reason, we
            // leap to a range which is one unit shorter than the current one, leaving a right
            // subrange of length 1.
            len_0 = len - 1;
        }

        debug_assert!(len_0 < len, "The length of the left subrange must be smaller than the \
                      current range's length.");

        if bit {
            // The bit was 1.

            // Refine the range to the second half:
            //
            //     [            self.len           ]
            //     [  len_0  ][  self.len - len_0  ]
            //                \~~~~~~~~~~~~~~~~~~~~/
            //                 This is the new range
            self.len   -= len_0;
            self.start += len_0;
        } else {
            // The bit was 0.

            // The start of the range is fixed, but update the length of the range.
            self.len = len_0;
        }

        // It's not exhausted if the length is stil above one.
        self.len > 1
    }

    /// Read a bit from the range.
    ///
    /// This reads the top bit (first written bit) with probability `pr_0` from the range, and
    /// updates the range such that the second bit is the new first (similar to `pop` but FIFO).
    ///
    /// `pr_0` must not be 0xFFFFFFFF or 0.
    ///
    /// The probability `pr_0` **must** match the probability given when the bit was written into
    /// the range.
    ///
    /// `None` is returned if the range is exhausted and no more bits are stored in the range.
    fn read(&mut self, pr_0: u32) -> Option<bool> {
        debug_assert!(self.len != 0, "Zero-sized ranges are invalid.");
        debug_assert!(pr_0 != 0, "Pr(0) can't be 0.");
        debug_assert!(pr_0 != !0, "Pr(0) can't be 1.");

        if self.len == !0 {
            // The range cannot contain more information, so no more bits can be extracted from
            // this range.
            None
        } else {
            // Determine if the start false on the left or right half (simply compare to the
            // probability).
            if self.start < pr_0 {
                // The bit is 0.

                // Rescale the length to get the left superrange:
                //
                //     [ rescaled range ]
                //     [ left ][  right ]
                //
                // The left range is of size `pr_0`. Now, we divide by this number, but we double
                // the width to get the desired precision.
                self.len = (((self.len as u64) << 32) / pr_0 as u64) as u32;

                // The start of the range is fixed, because the read bit is zer.

                Some(false)
            } else {
                // The bit is 1.

                // Negate the probability to get Pr(1).
                let pr_1 = !0 - pr_0;
                // We repeat the same as above conditional, except that we use `pr_1` instead of
                // `pr_0`.
                self.len = (((self.len as u64) << 32) / pr_1 as u64) as u32;

                // Subtract the length of the left half to "shift" the range towards the left in
                // order to complete the transformation. This means that the new offset is the
                // start of the right range.
                self.start -= pr_0;

                Some(true)
            }
        }
    }
}

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

    #[test]
    fn write_read() {
        let mut range = Range::full();

        range.write(true, 5000000);
        range.write(true, 2999);
        range.write(false, 5000000000);
        range.write(false, 50000000);
        range.write(true, 333333332999);

        assert!( range.read(5000000).unwrap());
        assert!( range.read(2999).unwrap());
        assert!(!range.read(5000000000).unwrap());
        assert!(!range.read(50000000).unwrap());
        assert!( range.read(333333332999).unwrap());
    }

    #[test]
    fn write_ones() {
        let mut range = Range::full();

        let mut n = 0;
        while range.write(true, 500) {
            n += 1;
        }

        for i in 0..n {
            assert_eq!(range.read(500), Some(true));
        }

        assert_eq!(range.read(500), None);
    }

    #[test]
    fn balanced_ones() {
        let mut range = Range::full();

        while range.write(true, 0x80000000) {
            println!("start = {:x}", range.start);
            println!("len   = {:x}", range.len);
        }

            println!("start = {:x}", range.start);
            println!("len   = {:x}", range.len);

        assert_eq!(range.start, 0xFFFFFFFF);
    }

    #[test]
    fn balanced_zeros() {
        let mut range = Range::full();

        while range.write(false, 0x80000000) {}

        assert_eq!(range.start, 0);
    }
}
<|endoftext|>


<commit_before>use encoding::types::EncodingRef;

use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

#[derive(Copy, Clone, PartialEq)]
pub enum ViMode {
    Insert,
    Normal,
    Replace,
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
    vi_count: u32,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
            vi_count: 0,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

macro_rules! vi_repeat {
    ( $ctx:ident, $x:expr ) => {
        match $ctx.vi_count {
            0 => { $x; }
            _ => for _ in 0..$ctx.vi_count {
                if !$x {
                    break;
                }
            },
        }
        $ctx.vi_count = 0;
    };
    ( $ctx:ident, $operation:expr, $step:expr ) => {
        match $ctx.vi_count {
            0 => { $operation; }
            _ => {
                if $operation {
                    for _ in 1..$ctx.vi_count {
                        if !$step {
                            break;
                        }
                        if !$operation {
                            break;
                        }
                    }
                }
            }
        }
        $ctx.vi_count = 0;
    };
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token, ctx.mode, ctx.vi_mode) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DeleteCharLeftOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_left_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::MoveCursorLeft => {
                    vi_repeat!(ctx, ctx.buf.move_left());
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    vi_repeat!(ctx, ctx.buf.move_right());
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.vi_count = 0;
                    ctx.buf.move_start();
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.vi_count = 0;
                    ctx.buf.move_end();
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.incr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.decr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::NormalMode => {
                    if ctx.vi_mode == ViMode::Insert {
                        // cursor moves left when leaving insert mode
                        ctx.buf.move_left();
                    }
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ReplaceMode => {
                    ctx.vi_mode = ViMode::Replace;
                    Cont(false)
                }
                instr::Instr::Insert => {
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::InsertStart => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_start();
                    Cont(false)
                }
                instr::Instr::Append => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_right();
                    Cont(false)
                }
                instr::Instr::AppendEnd => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_end();
                    Cont(false)
                }
                instr::Instr::Digit(i) => {
                    match (ctx.vi_count, i) {
                        // if count is 0, then 0 moves to the start of a line
                        (0, 0) => ctx.buf.move_start(),
                        // otherwise add a digit to the count
                        (_, i) => ctx.vi_count = ctx.vi_count * 10 + i,
                    }
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordRight => {
                    vi_repeat!(ctx, ctx.buf.move_to_end_of_word());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordWsRight => {
                    vi_repeat!(ctx, ctx.buf.move_to_end_of_word_ws());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordRight => {
                    vi_repeat!(ctx, ctx.buf.move_word());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordWsRight => {
                    vi_repeat!(ctx, ctx.buf.move_word_ws());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordLeft => {
                    vi_repeat!(ctx, ctx.buf.move_word_back());
                    Cont(false)
                }
                instr::Instr::MoveWordWsLeft => {
                    vi_repeat!(ctx, ctx.buf.move_word_ws_back());
                    Cont(false)
                }
                instr::Instr::Substitute => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
                instr::Instr::ReplaceAtCursor(text) => {
                    vi_repeat!(
                        ctx,
                        {
                            ctx.buf.replace_chars_at_cursor(text.clone());
                            true
                        },
                        {
                            ctx.buf.move_right();
                            ctx.buf.exclude_eol()
                        }
                    );
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<commit_msg>prevent integer overflow in vi_count<commit_after>use encoding::types::EncodingRef;

use std::u32;
use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

#[derive(Copy, Clone, PartialEq)]
pub enum ViMode {
    Insert,
    Normal,
    Replace,
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
    vi_count: u32,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
            vi_count: 0,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

macro_rules! vi_repeat {
    ( $ctx:ident, $x:expr ) => {
        match $ctx.vi_count {
            0 => { $x; }
            _ => for _ in 0..$ctx.vi_count {
                if !$x {
                    break;
                }
            },
        }
        $ctx.vi_count = 0;
    };
    ( $ctx:ident, $operation:expr, $step:expr ) => {
        match $ctx.vi_count {
            0 => { $operation; }
            _ => {
                if $operation {
                    for _ in 1..$ctx.vi_count {
                        if !$step {
                            break;
                        }
                        if !$operation {
                            break;
                        }
                    }
                }
            }
        }
        $ctx.vi_count = 0;
    };
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token, ctx.mode, ctx.vi_mode) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DeleteCharLeftOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_left_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::MoveCursorLeft => {
                    vi_repeat!(ctx, ctx.buf.move_left());
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    vi_repeat!(ctx, ctx.buf.move_right());
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.vi_count = 0;
                    ctx.buf.move_start();
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.vi_count = 0;
                    ctx.buf.move_end();
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.incr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.decr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::NormalMode => {
                    if ctx.vi_mode == ViMode::Insert {
                        // cursor moves left when leaving insert mode
                        ctx.buf.move_left();
                    }
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ReplaceMode => {
                    ctx.vi_mode = ViMode::Replace;
                    Cont(false)
                }
                instr::Instr::Insert => {
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::InsertStart => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_start();
                    Cont(false)
                }
                instr::Instr::Append => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_right();
                    Cont(false)
                }
                instr::Instr::AppendEnd => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_end();
                    Cont(false)
                }
                instr::Instr::Digit(i) => {
                    match (ctx.vi_count, i) {
                        // if count is 0, then 0 moves to the start of a line
                        (0, 0) => ctx.buf.move_start(),
                        // otherwise add a digit to the count
                        (_, i) => {
                            if ctx.vi_count <= (u32::MAX - i) / 10 {
                                ctx.vi_count = ctx.vi_count * 10 + i
                            }
                        }
                    }
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordRight => {
                    vi_repeat!(ctx, ctx.buf.move_to_end_of_word());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordWsRight => {
                    vi_repeat!(ctx, ctx.buf.move_to_end_of_word_ws());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordRight => {
                    vi_repeat!(ctx, ctx.buf.move_word());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordWsRight => {
                    vi_repeat!(ctx, ctx.buf.move_word_ws());
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordLeft => {
                    vi_repeat!(ctx, ctx.buf.move_word_back());
                    Cont(false)
                }
                instr::Instr::MoveWordWsLeft => {
                    vi_repeat!(ctx, ctx.buf.move_word_ws_back());
                    Cont(false)
                }
                instr::Instr::Substitute => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
                instr::Instr::ReplaceAtCursor(text) => {
                    vi_repeat!(
                        ctx,
                        {
                            ctx.buf.replace_chars_at_cursor(text.clone());
                            true
                        },
                        {
                            ctx.buf.move_right();
                            ctx.buf.exclude_eol()
                        }
                    );
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<|endoftext|>


<commit_before>//! Provides set of implementation for hir's objects that allows get back location in file.

use either::Either;
use hir_def::{
    nameres::{ModuleOrigin, ModuleSource},
    src::{HasChildSource, HasSource as _},
    Lookup, VariantId,
};
use hir_expand::InFile;
use syntax::ast;

use crate::{
    db::HirDatabase, Const, ConstParam, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
    MacroDef, Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
};

pub trait HasSource {
    type Ast;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<Self::Ast>;
}

/// NB: Module is !HasSource, because it has two source nodes at the same time:
/// definition and declaration.
impl Module {
    /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
    pub fn definition_source(self, db: &dyn HirDatabase) -> InFile<ModuleSource> {
        let def_map = db.crate_def_map(self.id.krate);
        def_map[self.id.local_id].definition_source(db.upcast())
    }

    pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool {
        let def_map = db.crate_def_map(self.id.krate);
        match def_map[self.id.local_id].origin {
            ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs,
            _ => false,
        }
    }

    /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
    /// `None` for the crate root.
    pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> {
        let def_map = db.crate_def_map(self.id.krate);
        def_map[self.id.local_id].declaration_source(db.upcast())
    }
}

impl HasSource for Field {
    type Ast = FieldSource;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<FieldSource> {
        let var = VariantId::from(self.parent);
        let src = var.child_source(db.upcast());
        src.map(|it| match it[self.id].clone() {
            Either::Left(it) => FieldSource::Pos(it),
            Either::Right(it) => FieldSource::Named(it),
        })
    }
}
impl HasSource for Struct {
    type Ast = ast::Struct;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Struct> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for Union {
    type Ast = ast::Union;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Union> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for Enum {
    type Ast = ast::Enum;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Enum> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for Variant {
    type Ast = ast::Variant;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Variant> {
        self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone())
    }
}
impl HasSource for Function {
    type Ast = ast::Fn;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Fn> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for Const {
    type Ast = ast::Const;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Const> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for Static {
    type Ast = ast::Static;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Static> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for Trait {
    type Ast = ast::Trait;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Trait> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for TypeAlias {
    type Ast = ast::TypeAlias;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::TypeAlias> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}
impl HasSource for MacroDef {
    type Ast = ast::Macro;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Macro> {
        InFile {
            file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id,
            value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()),
        }
    }
}
impl HasSource for Impl {
    type Ast = ast::Impl;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Impl> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }
}

impl HasSource for TypeParam {
    type Ast = Either<ast::Trait, ast::TypeParam>;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
        let child_source = self.id.parent.child_source(db.upcast());
        child_source.map(|it| it[self.id.local_id].clone())
    }
}

impl HasSource for LifetimeParam {
    type Ast = ast::LifetimeParam;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
        let child_source = self.id.parent.child_source(db.upcast());
        child_source.map(|it| it[self.id.local_id].clone())
    }
}

impl HasSource for ConstParam {
    type Ast = ast::ConstParam;
    fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
        let child_source = self.id.parent.child_source(db.upcast());
        child_source.map(|it| it[self.id.local_id].clone())
    }
}
<commit_msg>Implement new HasSource::source for all implementors of HasSource<commit_after>//! Provides set of implementation for hir's objects that allows get back location in file.

use either::Either;
use hir_def::{
    nameres::{ModuleOrigin, ModuleSource},
    src::{HasChildSource, HasSource as _},
    Lookup, VariantId,
};
use hir_expand::InFile;
use syntax::ast;

use crate::{
    db::HirDatabase, Const, ConstParam, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
    MacroDef, Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
};

pub trait HasSource {
    type Ast;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<Self::Ast>;
    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
}

/// NB: Module is !HasSource, because it has two source nodes at the same time:
/// definition and declaration.
impl Module {
    /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
    pub fn definition_source(self, db: &dyn HirDatabase) -> InFile<ModuleSource> {
        let def_map = db.crate_def_map(self.id.krate);
        def_map[self.id.local_id].definition_source(db.upcast())
    }

    pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool {
        let def_map = db.crate_def_map(self.id.krate);
        match def_map[self.id.local_id].origin {
            ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs,
            _ => false,
        }
    }

    /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
    /// `None` for the crate root.
    pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> {
        let def_map = db.crate_def_map(self.id.krate);
        def_map[self.id.local_id].declaration_source(db.upcast())
    }
}

impl HasSource for Field {
    type Ast = FieldSource;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<FieldSource> {
        let var = VariantId::from(self.parent);
        let src = var.child_source(db.upcast());
        src.map(|it| match it[self.id].clone() {
            Either::Left(it) => FieldSource::Pos(it),
            Either::Right(it) => FieldSource::Named(it),
        })
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        let var = VariantId::from(self.parent);
        let src = var.child_source(db.upcast());
        let field_source = src.map(|it| match it[self.id].clone() {
            Either::Left(it) => FieldSource::Pos(it),
            Either::Right(it) => FieldSource::Named(it),
        });
        Some(field_source)
    }
}
impl HasSource for Struct {
    type Ast = ast::Struct;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Struct> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for Union {
    type Ast = ast::Union;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Union> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for Enum {
    type Ast = ast::Enum;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Enum> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for Variant {
    type Ast = ast::Variant;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Variant> {
        self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Variant>> {
        Some(self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()))
    }
}
impl HasSource for Function {
    type Ast = ast::Fn;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Fn> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for Const {
    type Ast = ast::Const;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Const> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for Static {
    type Ast = ast::Static;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Static> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for Trait {
    type Ast = ast::Trait;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Trait> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for TypeAlias {
    type Ast = ast::TypeAlias;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::TypeAlias> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}
impl HasSource for MacroDef {
    type Ast = ast::Macro;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Macro> {
        InFile {
            file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id,
            value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()),
        }
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        let ast_id = self.id.ast_id?;
        Some(InFile { file_id: ast_id.file_id, value: ast_id.to_node(db.upcast()) })
    }
}
impl HasSource for Impl {
    type Ast = ast::Impl;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<ast::Impl> {
        self.id.lookup(db.upcast()).source(db.upcast())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        Some(self.id.lookup(db.upcast()).source(db.upcast()))
    }
}

impl HasSource for TypeParam {
    type Ast = Either<ast::Trait, ast::TypeParam>;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
        let child_source = self.id.parent.child_source(db.upcast());
        child_source.map(|it| it[self.id.local_id].clone())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        let child_source = self.id.parent.child_source(db.upcast());
        Some(child_source.map(|it| it[self.id.local_id].clone()))
    }
}

impl HasSource for LifetimeParam {
    type Ast = ast::LifetimeParam;
    fn source_old(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
        let child_source = self.id.parent.child_source(db.upcast());
        child_source.map(|it| it[self.id.local_id].clone())
    }

    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
        let child_source = self.id.parent.child_source(db.upcast());
        Some(child_source.map(|it| it[self.id.local_id].clone()))
    }
}

impl HasSource for ConstParam {
    type Ast = ast::ConstParam;
    fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
        let child_source = self.id.parent.child_source(db.upcast());
        child_source.map(|it| it[self.id.local_id].clone())
    }
}
<|endoftext|>


<commit_before>use ast::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, NumOpExpr, BitOpExpr, LogOpExpr, CompOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use ast::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use ast::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use ast::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use ast::{LogAnd, LogOr};
use ast::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use js::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value};
use js::object::{INSTANCE_PROTOTYPE, PROTOTYPE, ObjectData};
use js::function::{RegularFunc, RegularFunction};
use js::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use std::str::MaybeOwned;
/// An execution engine
pub trait Executor {
	/// Makes a new execution engine
	fn new() -> ~Self;
	/// Sets a global variable
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value;
	/// Gets a global variable
	fn get_global(&self, name:MaybeOwned) -> Value;
	/// Make a new scope
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>>;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> ();
	/// Runs the expression
	fn run(&mut self, expr:&Expr) -> ResultValue;
}
#[deriving(Clone)]
/// An intepreter
pub struct Interpreter {
	/// An object representing the global variables
	global: Value,
	/// The scopes
	scopes: Vec<Gc<RefCell<ObjectData>>>,
}
impl Executor for Interpreter {
	fn new() -> ~Interpreter {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		number::init(global);
		string::init(global);
		uri::init(global);
		return ~Interpreter {global: global, scopes: Vec::new()};
	}
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	fn get_global(&self, name:MaybeOwned) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>> {
		let value = Gc::new(RefCell::new(TreeMap::new()));
		self.scopes.push(value.clone());
		value
	}
	fn destroy_scope(&mut self) -> () {
		self.scopes.pop();
	}
	fn run(&mut self, expr:&Expr) -> ResultValue {
		match expr.def {
			ConstExpr(CNull) => Ok(Gc::new(VNull)),
			ConstExpr(CUndefined) => Ok(Gc::new(VUndefined)),
			ConstExpr(CNum(num)) => Ok(to_value(num)),
			ConstExpr(CInt(num)) => Ok(to_value(num)),
			ConstExpr(CString(ref str)) => Ok(Gc::new(VString(str.to_owned()))),
			ConstExpr(CBool(val)) => Ok(Gc::new(VBoolean(val))),
			ConstExpr(CRegExp(_, _, _)) => Ok(Gc::new(VNull)),
			BlockExpr(ref es) => {
				let mut obj = Gc::new(VNull);
				for e in es.iter() {
					let val = try!(self.run(*e));
					if e == es.last().unwrap() {
						obj = val;
					}
				}
				Ok(obj)
			},
			LocalExpr(ref name) => {
				let mut value = Gc::new(VUndefined);
				for scope in self.scopes.iter().rev() {
					match scope.borrow().borrow().find(name) {
						Some(v) => {
							value = v.value.clone();
							break;
						}
						None => ()
					}
				}
				Ok(if value.borrow() == &VUndefined {
					self.global.borrow().get_field(name.clone().into_maybe_owned())
				} else {
					value
				})
			},
			GetConstFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				Ok(val_obj.borrow().get_field(field.clone().into_maybe_owned()))
			},
			GetFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				let val_field = try!(self.run(*field));
				Ok(val_obj.borrow().get_field(val_field.borrow().to_str().into_maybe_owned()))
			},
			CallExpr(ref callee, ref args) => {
				let (this, func) = match callee.def {
					GetConstFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						(obj, obj.borrow().get_field(field.clone().into_maybe_owned()))
					},
					GetFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						let field = try!(self.run(*field));
						(obj, obj.borrow().get_field(field.borrow().to_str().into_maybe_owned()))
					},
					_ => (self.global.clone(), try!(self.run(callee.clone())))
				};
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				match *func.borrow() {
					VFunction(ref func) => {
						func.borrow().call(self, this, Gc::new(VNull), v_args)
					},
					_ => Err(Gc::new(VUndefined))
				}
			},
			WhileLoopExpr(ref cond, ref expr) => {
				let mut result = Gc::new(VUndefined);
				while try!(self.run(*cond)).borrow().is_true() {
					result = try!(self.run(*expr));
				}
				Ok(result)
			},
			IfExpr(ref cond, ref expr, None) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					Gc::new(VUndefined)
				})
			},
			IfExpr(ref cond, ref expr, Some(ref else_e)) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					try!(self.run(*else_e))
				})
			},
			SwitchExpr(ref val_e, ref vals, ref default) => {
				let val = try!(self.run(*val_e)).borrow().clone();
				let mut result = Gc::new(VNull);
				let mut matched = false;
				for tup in vals.iter() {
					let tup:&(~Expr, Vec<~Expr>) = tup;
					match *tup {
						(ref cond, ref block) if(val == *try!(self.run(*cond)).borrow()) => {
							matched = true;
							let last_expr = block.last().unwrap();
							for expr in block.iter() {
								let e_result = try!(self.run(*expr));
								if expr == last_expr {
									result = e_result;
								}
							}
						},
						_ => ()
					}
				}
				if !matched && default.is_some() {
					result = try!(self.run(*default.as_ref().unwrap()));
				}
				Ok(result)
			},
			ObjectDeclExpr(ref map) => {
				let obj = ValueData::new_obj(Some(self.global));
				for (key, val) in map.iter() {
					obj.borrow().set_field(key.clone().into_maybe_owned(), try!(self.run(val.clone())));
				}
				Ok(obj)
			},
			ArrayDeclExpr(ref arr) => {
				let arr_map = ValueData::new_obj(Some(self.global));
				let mut index : i32 = 0;
				for val in arr.iter() {
					let val = try!(self.run(val.clone()));
					arr_map.borrow().set_field(index.to_str().into_maybe_owned(), val);
					index += 1;
				}
				arr_map.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), self.get_global("Array".into_maybe_owned()).borrow().get_field(PROTOTYPE.into_maybe_owned()));
				arr_map.borrow().set_field("length".into_maybe_owned(), to_value(index));
				Ok(arr_map)
			},
			FunctionDeclExpr(ref name, ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				let val = Gc::new(VFunction(RefCell::new(function)));
				if name.is_some() {
					self.global.borrow().set_field(name.clone().unwrap().into_maybe_owned(), val);
				}
				Ok(val)
			},
			ArrowFunctionDeclExpr(ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				let val = Gc::new(VFunction(RefCell::new(function)));
				Ok(val)
			},
			NumOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					OpAdd => v_a + v_b,
					OpSub => v_a - v_b,
					OpMul => v_a * v_b,
					OpDiv => v_a / v_b,
					OpMod => v_a % v_b
				}))
			},
			BitOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					BitAnd => v_a & v_b,
					BitOr => v_a | v_b,
					BitXor => v_a ^ v_b,
					BitShl => v_a << v_b,
					BitShr => v_a >> v_b
				}))
			},
			CompOpExpr(ref op, ref a, ref b) => {
				let v_r_a = try!(self.run(*a));
				let v_r_b = try!(self.run(*b));
				let v_a = v_r_a.borrow();
				let v_b = v_r_b.borrow();
				Ok(to_value(match *op {
					CompEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompStrictEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompStrictNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompGreaterThan => v_a.to_num() > v_b.to_num(),
					CompGreaterThanOrEqual => v_a.to_num() >= v_b.to_num(),
					CompLessThan => v_a.to_num() < v_b.to_num(),
					CompLessThanOrEqual => v_a.to_num() <= v_b.to_num()
				}))
			},
			LogOpExpr(ref op, ref a, ref b) => {
				let v_a = from_value::<bool>(try!(self.run(*a))).unwrap();
				let v_b = from_value::<bool>(try!(self.run(*b))).unwrap();
				Ok(match *op {
					LogAnd => to_value(v_a && v_b),
					LogOr => to_value(v_a || v_b)
				})
			},
			ConstructExpr(ref callee, ref args) => {
				let func = try!(self.run(*callee));
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				let this = Gc::new(VObject(RefCell::new(TreeMap::new())));
				this.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), func.borrow().get_field(PROTOTYPE.into_maybe_owned()));
				Ok(match *func.borrow() {
					VFunction(ref func) => {
						try!(func.borrow().call(self, this, Gc::new(VNull), v_args));
						this
					},
					_ => Gc::new(VUndefined)
				})
			},
			ReturnExpr(ref ret) => {
				match *ret {
					Some(ref v) =>
						self.run(v.clone()),
					None => Ok(Gc::new(VUndefined))
				}
			},
			ThrowExpr(ref ex) => Err(try!(self.run(*ex))),
			AssignExpr(ref ref_e, ref val_e) => {
				let val = try!(self.run(*val_e));
				match ref_e.def {
					LocalExpr(ref name) => {
						self.global.borrow().set_field(name.clone().into_maybe_owned(), val);
					},
					GetConstFieldExpr(ref obj, ref field) => {
						let val_obj = try!(self.run(*obj));
						val_obj.borrow().set_field(field.clone().into_maybe_owned(), val);
					},
					_ => ()
				}
				Ok(val)
			},
			TypeOfExpr(ref val_e) => {
				let val = try!(self.run(*val_e));
				Ok(to_value(match *val.borrow() {
					VUndefined => "undefined",
					VNull | VObject(_) => "object",
					VBoolean(_) => "boolean",
					VNumber(_) | VInteger(_) => "number",
					VString(_) => "string",
					VFunction(_) => "function"
				}.into_maybe_owned()))
			}
		}
	}
}<commit_msg>Use is_undefined instead of a check<commit_after>use ast::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, NumOpExpr, BitOpExpr, LogOpExpr, CompOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use ast::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use ast::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use ast::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use ast::{LogAnd, LogOr};
use ast::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use js::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value};
use js::object::{INSTANCE_PROTOTYPE, PROTOTYPE, ObjectData};
use js::function::{RegularFunc, RegularFunction};
use js::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use std::str::MaybeOwned;
/// An execution engine
pub trait Executor {
	/// Makes a new execution engine
	fn new() -> ~Self;
	/// Sets a global variable
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value;
	/// Gets a global variable
	fn get_global(&self, name:MaybeOwned) -> Value;
	/// Make a new scope
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>>;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> ();
	/// Runs the expression
	fn run(&mut self, expr:&Expr) -> ResultValue;
}
#[deriving(Clone)]
/// An intepreter
pub struct Interpreter {
	/// An object representing the global variables
	global: Value,
	/// The scopes
	scopes: Vec<Gc<RefCell<ObjectData>>>,
}
impl Executor for Interpreter {
	fn new() -> ~Interpreter {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		number::init(global);
		string::init(global);
		uri::init(global);
		return ~Interpreter {global: global, scopes: Vec::new()};
	}
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	fn get_global(&self, name:MaybeOwned) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>> {
		let value = Gc::new(RefCell::new(TreeMap::new()));
		self.scopes.push(value.clone());
		value
	}
	fn destroy_scope(&mut self) -> () {
		self.scopes.pop();
	}
	fn run(&mut self, expr:&Expr) -> ResultValue {
		match expr.def {
			ConstExpr(CNull) => Ok(Gc::new(VNull)),
			ConstExpr(CUndefined) => Ok(Gc::new(VUndefined)),
			ConstExpr(CNum(num)) => Ok(to_value(num)),
			ConstExpr(CInt(num)) => Ok(to_value(num)),
			ConstExpr(CString(ref str)) => Ok(Gc::new(VString(str.to_owned()))),
			ConstExpr(CBool(val)) => Ok(Gc::new(VBoolean(val))),
			ConstExpr(CRegExp(_, _, _)) => Ok(Gc::new(VNull)),
			BlockExpr(ref es) => {
				let mut obj = Gc::new(VNull);
				for e in es.iter() {
					let val = try!(self.run(*e));
					if e == es.last().unwrap() {
						obj = val;
					}
				}
				Ok(obj)
			},
			LocalExpr(ref name) => {
				let mut value = Gc::new(VUndefined);
				for scope in self.scopes.iter().rev() {
					match scope.borrow().borrow().find(name) {
						Some(v) => {
							value = v.value.clone();
							break;
						}
						None => ()
					}
				}
				Ok(if value.borrow().is_undefined() {
					self.global.borrow().get_field(name.clone().into_maybe_owned())
				} else {
					value
				})
			},
			GetConstFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				Ok(val_obj.borrow().get_field(field.clone().into_maybe_owned()))
			},
			GetFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				let val_field = try!(self.run(*field));
				Ok(val_obj.borrow().get_field(val_field.borrow().to_str().into_maybe_owned()))
			},
			CallExpr(ref callee, ref args) => {
				let (this, func) = match callee.def {
					GetConstFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						(obj, obj.borrow().get_field(field.clone().into_maybe_owned()))
					},
					GetFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						let field = try!(self.run(*field));
						(obj, obj.borrow().get_field(field.borrow().to_str().into_maybe_owned()))
					},
					_ => (self.global.clone(), try!(self.run(callee.clone())))
				};
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				match *func.borrow() {
					VFunction(ref func) => {
						func.borrow().call(self, this, Gc::new(VNull), v_args)
					},
					_ => Err(Gc::new(VUndefined))
				}
			},
			WhileLoopExpr(ref cond, ref expr) => {
				let mut result = Gc::new(VUndefined);
				while try!(self.run(*cond)).borrow().is_true() {
					result = try!(self.run(*expr));
				}
				Ok(result)
			},
			IfExpr(ref cond, ref expr, None) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					Gc::new(VUndefined)
				})
			},
			IfExpr(ref cond, ref expr, Some(ref else_e)) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					try!(self.run(*else_e))
				})
			},
			SwitchExpr(ref val_e, ref vals, ref default) => {
				let val = try!(self.run(*val_e)).borrow().clone();
				let mut result = Gc::new(VNull);
				let mut matched = false;
				for tup in vals.iter() {
					let tup:&(~Expr, Vec<~Expr>) = tup;
					match *tup {
						(ref cond, ref block) if(val == *try!(self.run(*cond)).borrow()) => {
							matched = true;
							let last_expr = block.last().unwrap();
							for expr in block.iter() {
								let e_result = try!(self.run(*expr));
								if expr == last_expr {
									result = e_result;
								}
							}
						},
						_ => ()
					}
				}
				if !matched && default.is_some() {
					result = try!(self.run(*default.as_ref().unwrap()));
				}
				Ok(result)
			},
			ObjectDeclExpr(ref map) => {
				let obj = ValueData::new_obj(Some(self.global));
				for (key, val) in map.iter() {
					obj.borrow().set_field(key.clone().into_maybe_owned(), try!(self.run(val.clone())));
				}
				Ok(obj)
			},
			ArrayDeclExpr(ref arr) => {
				let arr_map = ValueData::new_obj(Some(self.global));
				let mut index : i32 = 0;
				for val in arr.iter() {
					let val = try!(self.run(val.clone()));
					arr_map.borrow().set_field(index.to_str().into_maybe_owned(), val);
					index += 1;
				}
				arr_map.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), self.get_global("Array".into_maybe_owned()).borrow().get_field(PROTOTYPE.into_maybe_owned()));
				arr_map.borrow().set_field("length".into_maybe_owned(), to_value(index));
				Ok(arr_map)
			},
			FunctionDeclExpr(ref name, ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				let val = Gc::new(VFunction(RefCell::new(function)));
				if name.is_some() {
					self.global.borrow().set_field(name.clone().unwrap().into_maybe_owned(), val);
				}
				Ok(val)
			},
			ArrowFunctionDeclExpr(ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				let val = Gc::new(VFunction(RefCell::new(function)));
				Ok(val)
			},
			NumOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					OpAdd => v_a + v_b,
					OpSub => v_a - v_b,
					OpMul => v_a * v_b,
					OpDiv => v_a / v_b,
					OpMod => v_a % v_b
				}))
			},
			BitOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					BitAnd => v_a & v_b,
					BitOr => v_a | v_b,
					BitXor => v_a ^ v_b,
					BitShl => v_a << v_b,
					BitShr => v_a >> v_b
				}))
			},
			CompOpExpr(ref op, ref a, ref b) => {
				let v_r_a = try!(self.run(*a));
				let v_r_b = try!(self.run(*b));
				let v_a = v_r_a.borrow();
				let v_b = v_r_b.borrow();
				Ok(to_value(match *op {
					CompEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompStrictEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompStrictNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompGreaterThan => v_a.to_num() > v_b.to_num(),
					CompGreaterThanOrEqual => v_a.to_num() >= v_b.to_num(),
					CompLessThan => v_a.to_num() < v_b.to_num(),
					CompLessThanOrEqual => v_a.to_num() <= v_b.to_num()
				}))
			},
			LogOpExpr(ref op, ref a, ref b) => {
				let v_a = from_value::<bool>(try!(self.run(*a))).unwrap();
				let v_b = from_value::<bool>(try!(self.run(*b))).unwrap();
				Ok(match *op {
					LogAnd => to_value(v_a && v_b),
					LogOr => to_value(v_a || v_b)
				})
			},
			ConstructExpr(ref callee, ref args) => {
				let func = try!(self.run(*callee));
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				let this = Gc::new(VObject(RefCell::new(TreeMap::new())));
				this.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), func.borrow().get_field(PROTOTYPE.into_maybe_owned()));
				Ok(match *func.borrow() {
					VFunction(ref func) => {
						try!(func.borrow().call(self, this, Gc::new(VNull), v_args));
						this
					},
					_ => Gc::new(VUndefined)
				})
			},
			ReturnExpr(ref ret) => {
				match *ret {
					Some(ref v) =>
						self.run(v.clone()),
					None => Ok(Gc::new(VUndefined))
				}
			},
			ThrowExpr(ref ex) => Err(try!(self.run(*ex))),
			AssignExpr(ref ref_e, ref val_e) => {
				let val = try!(self.run(*val_e));
				match ref_e.def {
					LocalExpr(ref name) => {
						self.global.borrow().set_field(name.clone().into_maybe_owned(), val);
					},
					GetConstFieldExpr(ref obj, ref field) => {
						let val_obj = try!(self.run(*obj));
						val_obj.borrow().set_field(field.clone().into_maybe_owned(), val);
					},
					_ => ()
				}
				Ok(val)
			},
			TypeOfExpr(ref val_e) => {
				let val = try!(self.run(*val_e));
				Ok(to_value(match *val.borrow() {
					VUndefined => "undefined",
					VNull | VObject(_) => "object",
					VBoolean(_) => "boolean",
					VNumber(_) | VInteger(_) => "number",
					VString(_) => "string",
					VFunction(_) => "function"
				}.into_maybe_owned()))
			}
		}
	}
}<|endoftext|>