<commit_before>//! Request objects for non-blocking operations
//!
//! Non-blocking operations such as `immediate_send()` return request objects that borrow any
//! buffers involved in the operation so as to ensure proper access restrictions. In order to
//! release the borrowed buffers from the request objects, a completion operation such as `wait()`
//! or `test()` must be used on the request object. To enforce this rule, the request objects
//! implement a `Drop` bomb which will `panic!()` when a request object is dropped.
//!
//! To handle request completion in a RAII style, requests can be wrapped in either `WaitGuard` or
//! `CancelGuard` which will follow the respective policy for completing the operation upon being
//! dropped instead of `panic!()`ing.
//!
//! # Unfinished features
//!
//! - **3.7**: Nonblocking mode:
//!   - Completion, `MPI_Waitany()`, `MPI_Waitall()`, `MPI_Waitsome()`,
//!   `MPI_Testany()`, `MPI_Testall()`, `MPI_Testsome()`, `MPI_Request_get_status()`
//! - **3.8**:
//!   - Cancellation, `MPI_Cancel()`, `MPI_Test_cancelled()`

use std::mem;
use std::marker::PhantomData;
use std::os::raw::c_int;

use ffi;
use ffi::{MPI_Request, MPI_Status};

use datatype::traits::*;
use point_to_point::Status;
use raw::traits::*;

/// Request object traits
pub mod traits {
    pub use super::Request;
}

/// A request for a non-blocking operation
pub trait Request: AsRaw<Raw = MPI_Request> + AsRawMut {
    /// Returns true for a null request handle.
    fn is_null(&self) -> bool {
        unsafe { self.as_raw() == ffi::RSMPI_REQUEST_NULL }
    }

    /// Wait for an operation to finish.
    ///
    /// Will block execution of the calling thread until the associated operation has finished.
    ///
    /// # Examples
    ///
    /// See `examples/immediate.rs`
    ///
    /// # Standard section(s)
    ///
    /// 3.7.3
    fn wait(mut self) -> Status
        where Self: Sized
    {
        let mut status: MPI_Status = unsafe { mem::uninitialized() };
        unsafe {
            ffi::MPI_Wait(self.as_raw_mut(), &mut status);
        }
        assert!(self.is_null());
        mem::forget(self);
        Status::from_raw(status)
    }

    /// Test whether an operation has finished.
    ///
    /// If the operation has finished returns the `Status` otherwise returns the unfinished
    /// `Request`.
    /// # Examples
    ///
    /// See `examples/immediate.rs`
    ///
    /// # Standard section(s)
    ///
    /// 3.7.3
    fn test(mut self) -> Result<Status, Self>
        where Self: Sized
    {
        let mut status: MPI_Status = unsafe { mem::uninitialized() };
        let mut flag: c_int = 0;
        unsafe {
            ffi::MPI_Test(self.as_raw_mut(), &mut flag, &mut status);
        }
        assert!(flag == 0 || self.is_null());
        if flag != 0 {
            mem::forget(self);
            Result::Ok(Status::from_raw(status))
        } else {
            Result::Err(self)
        }
    }

    /// Cancel an operation.
    ///
    /// # Examples
    ///
    /// See `examples/immediate.rs`
    ///
    /// # Standard section(s)
    ///
    /// 3.8.4
    fn cancel(mut self)
        where Self: Sized
    {
        unsafe {
            ffi::MPI_Cancel(self.as_raw_mut());
            ffi::MPI_Request_free(self.as_raw_mut());
        }
        assert!(self.is_null());
        mem::forget(self);
    }
}

/// A request object for an non-blocking operation that holds no references
///
/// # Examples
///
/// See `examples/immediate_barrier.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct PlainRequest(MPI_Request);

impl PlainRequest {
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request) -> PlainRequest {
        PlainRequest(request)
    }
}

impl AsRaw for PlainRequest {
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl AsRawMut for PlainRequest {
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl Request for PlainRequest {}

impl Drop for PlainRequest {
    fn drop(&mut self) {
        assert!(self.is_null(),
                "request dropped without ascertaining completion.");
    }
}

/// A request object for a non-blocking operation that holds a reference to an immutable buffer
///
/// # Examples
///
/// See `examples/immediate.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct ReadRequest<'b, Buf: ?Sized>(MPI_Request, PhantomData<&'b Buf>) where Buf: 'b + Buffer;

impl<'b, Buf: ?Sized> ReadRequest<'b, Buf> where Buf: 'b + Buffer
{
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request, _: &'b Buf) -> ReadRequest<'b, Buf> {
        ReadRequest(request, PhantomData)
    }
}

impl<'b, Buf: ?Sized> AsRaw for ReadRequest<'b, Buf> where Buf: 'b + Buffer
{
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl<'b, Buf: ?Sized> AsRawMut for ReadRequest<'b, Buf> where Buf: 'b + Buffer
{
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl<'b, Buf: ?Sized> Request for ReadRequest<'b, Buf> where Buf: 'b + Buffer
{}

impl<'b, Buf: ?Sized> Drop for ReadRequest<'b, Buf> where Buf: 'b + Buffer
{
    fn drop(&mut self) {
        assert!(self.is_null(),
                "read request dropped without ascertaining completion.");
    }
}

/// A request object for a non-blocking operation that holds a reference to a mutable buffer
///
/// # Examples
///
/// See `examples/immediate.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct WriteRequest<'b, Buf: ?Sized>(MPI_Request, PhantomData<&'b mut Buf>)
    where Buf: 'b + BufferMut;

impl<'b, Buf: ?Sized> WriteRequest<'b, Buf> where Buf: 'b + BufferMut
{
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request, _: &'b Buf) -> WriteRequest<'b, Buf> {
        WriteRequest(request, PhantomData)
    }
}

impl<'b, Buf: ?Sized> AsRaw for WriteRequest<'b, Buf> where Buf: 'b + BufferMut
{
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl<'b, Buf: ?Sized> AsRawMut for WriteRequest<'b, Buf> where Buf: 'b + BufferMut
{
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl<'b, Buf: ?Sized> Request for WriteRequest<'b, Buf> where Buf: 'b + BufferMut
{}

impl<'b, Buf: ?Sized> Drop for WriteRequest<'b, Buf> where Buf: 'b + BufferMut
{
    fn drop(&mut self) {
        assert!(self.is_null(),
                "write request dropped without ascertaining completion.");
    }
}

/// A request object for a non-blocking operation that holds a reference to a mutable and an
/// immutable buffer
///
/// # Examples
///
/// See `examples/immediate_gather.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct ReadWriteRequest<'s, 'r, S: ?Sized, R: ?Sized>(MPI_Request,
                                                          PhantomData<&'s S>,
                                                          PhantomData<&'r mut R>)
    where S: 's + Buffer,
          R: 'r + BufferMut;

impl<'s, 'r, S: ?Sized, R: ?Sized> ReadWriteRequest<'s, 'r, S, R>
    where S: 's + Buffer,
          R: 'r + BufferMut
{
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request, _: &'s S, _: &'r R) -> ReadWriteRequest<'s, 'r, S, R> {
        ReadWriteRequest(request, PhantomData, PhantomData)
    }
}

impl<'s, 'r, S: ?Sized, R: ?Sized> AsRaw for ReadWriteRequest<'s, 'r, S, R>
    where S: 's + Buffer,
          R: 'r + BufferMut
{
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl<'s, 'r, S: ?Sized, R: ?Sized> AsRawMut for ReadWriteRequest<'s, 'r, S, R>
    where S: 's + Buffer,
          R: 'r + BufferMut
{
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl<'s, 'r, S: ?Sized, R: ?Sized> Request for ReadWriteRequest<'s, 'r, S, R>
    where S: 's + Buffer,
          R: 'r + BufferMut
{}

impl<'s, 'r, S: ?Sized, R: ?Sized> Drop for ReadWriteRequest<'s, 'r, S, R>
    where S: 's + Buffer,
          R: 'r + BufferMut
{
    fn drop(&mut self) {
        assert!(self.is_null(),
                "read-write request dropped without ascertaining completion.");
    }
}

/// Guard object that waits for the completion of an operation when it is dropped
///
/// # Examples
///
/// See `examples/immediate.rs`
pub struct WaitGuard<Req: Request>(Option<Req>);

impl<Req> Drop for WaitGuard<Req> where Req: Request
{
    fn drop(&mut self) {
        self.0.take().map(|mut req| {
            unsafe {
                ffi::MPI_Wait(req.as_raw_mut(), ffi::RSMPI_STATUS_IGNORE);
            }
            assert!(req.is_null());
            mem::forget(req);
        });
    }
}

impl<Req> From<Req> for WaitGuard<Req> where Req: Request
{
    fn from(req: Req) -> WaitGuard<Req> {
        WaitGuard(Some(req))
    }
}

/// Guard object that cancels an operation when it is dropped
///
/// # Examples
///
/// See `examples/immediate.rs`
pub struct CancelGuard<Req>(Option<Req>) where Req: Request;

impl<Req> Drop for CancelGuard<Req> where Req: Request
{
    fn drop(&mut self) {
        self.0.take().map(|mut req| {
            unsafe {
                ffi::MPI_Cancel(req.as_raw_mut());
                ffi::MPI_Request_free(req.as_raw_mut());
            }
            assert!(req.is_null());
            mem::forget(req);
        });
    }
}

impl<Req> From<Req> for CancelGuard<Req> where Req: Request
{
    fn from(req: Req) -> CancelGuard<Req> {
        CancelGuard(Some(req))
    }
}
<commit_msg>refactor: remove bounds on types borrowed by requests<commit_after>//! Request objects for non-blocking operations
//!
//! Non-blocking operations such as `immediate_send()` return request objects that borrow any
//! buffers involved in the operation so as to ensure proper access restrictions. In order to
//! release the borrowed buffers from the request objects, a completion operation such as `wait()`
//! or `test()` must be used on the request object. To enforce this rule, the request objects
//! implement a `Drop` bomb which will `panic!()` when a request object is dropped.
//!
//! To handle request completion in a RAII style, requests can be wrapped in either `WaitGuard` or
//! `CancelGuard` which will follow the respective policy for completing the operation upon being
//! dropped instead of `panic!()`ing.
//!
//! # Unfinished features
//!
//! - **3.7**: Nonblocking mode:
//!   - Completion, `MPI_Waitany()`, `MPI_Waitall()`, `MPI_Waitsome()`,
//!   `MPI_Testany()`, `MPI_Testall()`, `MPI_Testsome()`, `MPI_Request_get_status()`
//! - **3.8**:
//!   - Cancellation, `MPI_Cancel()`, `MPI_Test_cancelled()`

use std::mem;
use std::marker::PhantomData;
use std::os::raw::c_int;

use ffi;
use ffi::{MPI_Request, MPI_Status};

use point_to_point::Status;
use raw::traits::*;

/// Request object traits
pub mod traits {
    pub use super::Request;
}

/// A request for a non-blocking operation
pub trait Request: AsRaw<Raw = MPI_Request> + AsRawMut {
    /// Returns true for a null request handle.
    fn is_null(&self) -> bool {
        unsafe { self.as_raw() == ffi::RSMPI_REQUEST_NULL }
    }

    /// Wait for an operation to finish.
    ///
    /// Will block execution of the calling thread until the associated operation has finished.
    ///
    /// # Examples
    ///
    /// See `examples/immediate.rs`
    ///
    /// # Standard section(s)
    ///
    /// 3.7.3
    fn wait(mut self) -> Status
        where Self: Sized
    {
        let mut status: MPI_Status = unsafe { mem::uninitialized() };
        unsafe {
            ffi::MPI_Wait(self.as_raw_mut(), &mut status);
        }
        assert!(self.is_null());
        mem::forget(self);
        Status::from_raw(status)
    }

    /// Test whether an operation has finished.
    ///
    /// If the operation has finished returns the `Status` otherwise returns the unfinished
    /// `Request`.
    /// # Examples
    ///
    /// See `examples/immediate.rs`
    ///
    /// # Standard section(s)
    ///
    /// 3.7.3
    fn test(mut self) -> Result<Status, Self>
        where Self: Sized
    {
        let mut status: MPI_Status = unsafe { mem::uninitialized() };
        let mut flag: c_int = 0;
        unsafe {
            ffi::MPI_Test(self.as_raw_mut(), &mut flag, &mut status);
        }
        assert!(flag == 0 || self.is_null());
        if flag != 0 {
            mem::forget(self);
            Result::Ok(Status::from_raw(status))
        } else {
            Result::Err(self)
        }
    }

    /// Cancel an operation.
    ///
    /// # Examples
    ///
    /// See `examples/immediate.rs`
    ///
    /// # Standard section(s)
    ///
    /// 3.8.4
    fn cancel(mut self)
        where Self: Sized
    {
        unsafe {
            ffi::MPI_Cancel(self.as_raw_mut());
            ffi::MPI_Request_free(self.as_raw_mut());
        }
        assert!(self.is_null());
        mem::forget(self);
    }
}

/// A request object for an non-blocking operation that holds no references
///
/// # Examples
///
/// See `examples/immediate_barrier.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct PlainRequest(MPI_Request);

impl PlainRequest {
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request) -> PlainRequest {
        PlainRequest(request)
    }
}

impl AsRaw for PlainRequest {
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl AsRawMut for PlainRequest {
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl Request for PlainRequest {}

impl Drop for PlainRequest {
    fn drop(&mut self) {
        assert!(self.is_null(),
                "request dropped without ascertaining completion.");
    }
}

/// A request object for a non-blocking operation that holds a reference to an immutable buffer
///
/// # Examples
///
/// See `examples/immediate.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct ReadRequest<'b, Buf: 'b + ?Sized>(MPI_Request, PhantomData<&'b Buf>);

impl<'b, Buf: 'b + ?Sized> ReadRequest<'b, Buf> {
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request, _: &'b Buf) -> ReadRequest<'b, Buf> {
        ReadRequest(request, PhantomData)
    }
}

impl<'b, Buf: 'b + ?Sized> AsRaw for ReadRequest<'b, Buf> {
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl<'b, Buf: 'b + ?Sized> AsRawMut for ReadRequest<'b, Buf> {
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl<'b, Buf: 'b + ?Sized> Request for ReadRequest<'b, Buf> {}

impl<'b, Buf: 'b + ?Sized> Drop for ReadRequest<'b, Buf> {
    fn drop(&mut self) {
        assert!(self.is_null(),
                "read request dropped without ascertaining completion.");
    }
}

/// A request object for a non-blocking operation that holds a reference to a mutable buffer
///
/// # Examples
///
/// See `examples/immediate.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct WriteRequest<'b, Buf: 'b + ?Sized>(MPI_Request, PhantomData<&'b mut Buf>);

impl<'b, Buf: 'b + ?Sized> WriteRequest<'b, Buf> {
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request, _: &'b Buf) -> WriteRequest<'b, Buf> {
        WriteRequest(request, PhantomData)
    }
}

impl<'b, Buf: 'b + ?Sized> AsRaw for WriteRequest<'b, Buf> {
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl<'b, Buf: 'b + ?Sized> AsRawMut for WriteRequest<'b, Buf> {
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl<'b, Buf: 'b + ?Sized> Request for WriteRequest<'b, Buf> {}

impl<'b, Buf: 'b + ?Sized> Drop for WriteRequest<'b, Buf> {
    fn drop(&mut self) {
        assert!(self.is_null(),
                "write request dropped without ascertaining completion.");
    }
}

/// A request object for a non-blocking operation that holds a reference to a mutable and an
/// immutable buffer
///
/// # Examples
///
/// See `examples/immediate_gather.rs`
///
/// # Standard section(s)
///
/// 3.7.1
#[must_use]
pub struct ReadWriteRequest<'s, 'r, S: 's + ?Sized, R: 'r + ?Sized>(MPI_Request,
                                                                    PhantomData<&'s S>,
                                                                    PhantomData<&'r mut R>);

impl<'s, 'r, S: 's + ?Sized, R: 'r + ?Sized> ReadWriteRequest<'s, 'r, S, R> {
    /// Construct a request object from the raw MPI type
    pub fn from_raw(request: MPI_Request, _: &'s S, _: &'r R) -> ReadWriteRequest<'s, 'r, S, R> {
        ReadWriteRequest(request, PhantomData, PhantomData)
    }
}

impl<'s, 'r, S: 's + ?Sized, R: 'r + ?Sized> AsRaw for ReadWriteRequest<'s, 'r, S, R> {
    type Raw = MPI_Request;
    unsafe fn as_raw(&self) -> Self::Raw {
        self.0
    }
}

impl<'s, 'r, S: 's + ?Sized, R: 'r + ?Sized> AsRawMut for ReadWriteRequest<'s, 'r, S, R> {
    unsafe fn as_raw_mut(&mut self) -> *mut <Self as AsRaw>::Raw {
        &mut (self.0)
    }
}

impl<'s, 'r, S: 's + ?Sized, R: 'r + ?Sized> Request for ReadWriteRequest<'s, 'r, S, R> {}

impl<'s, 'r, S: 's + ?Sized, R: 'r + ?Sized> Drop for ReadWriteRequest<'s, 'r, S, R> {
    fn drop(&mut self) {
        assert!(self.is_null(),
                "read-write request dropped without ascertaining completion.");
    }
}

/// Guard object that waits for the completion of an operation when it is dropped
///
/// # Examples
///
/// See `examples/immediate.rs`
pub struct WaitGuard<Req>(Option<Req>) where Req: Request;

impl<Req> Drop for WaitGuard<Req> where Req: Request
{
    fn drop(&mut self) {
        self.0.take().map(|mut req| {
            unsafe {
                ffi::MPI_Wait(req.as_raw_mut(), ffi::RSMPI_STATUS_IGNORE);
            }
            assert!(req.is_null());
            mem::forget(req);
        });
    }
}

impl<Req> From<Req> for WaitGuard<Req> where Req: Request
{
    fn from(req: Req) -> WaitGuard<Req> {
        WaitGuard(Some(req))
    }
}

/// Guard object that cancels an operation when it is dropped
///
/// # Examples
///
/// See `examples/immediate.rs`
pub struct CancelGuard<Req>(Option<Req>) where Req: Request;

impl<Req> Drop for CancelGuard<Req> where Req: Request
{
    fn drop(&mut self) {
        self.0.take().map(|mut req| {
            unsafe {
                ffi::MPI_Cancel(req.as_raw_mut());
                ffi::MPI_Request_free(req.as_raw_mut());
            }
            assert!(req.is_null());
            mem::forget(req);
        });
    }
}

impl<Req> From<Req> for CancelGuard<Req> where Req: Request
{
    fn from(req: Req) -> CancelGuard<Req> {
        CancelGuard(Some(req))
    }
}
<|endoftext|>


<commit_before>//! Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
//! Assumes Von Nuemann's neighborhood.

#![cfg(test)]

use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::EvolutionState;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::twodim::TwodimGrid;
use utils::find_cell;

enum Stage {
    Collision,
    Transport,
}

impl Default for Stage {
    fn default() -> Self { Stage::Collision }
}

struct HPPState {
    stage: Stage,
}

impl HPPState {
    fn new() -> Self { HPPState { stage: Stage::Collision } }
}

impl EvolutionState for HPPState {
    fn update(&mut self) {
        self.stage = match self.stage {
            Stage::Collision => Stage::Transport,
            Stage::Transport => Stage::Collision,
        };
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Down,
    Right,
    Left,
    Up,
}

impl Direction {
    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right,
        }
    }
}

#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
struct Particles([bool; 4]);

impl Particles {
    fn set(&mut self, direction: &Direction, exists: bool) {
        let index = *direction as usize;
        self.0[index] = exists;
    }

    pub fn get(&self, direction: &Direction) -> bool {
        let index = *direction as usize;
        self.0[index]
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: Particles,
    coord: (i32, i32),
}

impl Cell for HPP {
    type Coord = (i32, i32);
    type State = HPPState;

    fn update<'a, I>(&'a mut self, old: &'a Self, neighbors: I, state: &Self::State)
        where I: Iterator<Item = Option<&'a Self>>,
    {

        match state.stage {
            Stage::Collision => self.collision(old, neighbors),
            Stage::Transport => self.transport(old, neighbors),
        };
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP { coord: (coord.x(), coord.y()), ..Default::default() }
    }

    fn coord(&self) -> &Self::Coord { &self.coord }

    fn set_coord<C: Coord>(&mut self, coord: &C) { self.coord = (coord.x(), coord.y()); }
}

impl HPP {
    fn collision<'a, I>(&'a mut self, old: &'a Self, neighbors: I)
        where I: Iterator<Item = Option<&'a Self>>,
    {
        let mut new = Particles::default();

        let has_head_on = |d: &Direction, op_d: &Direction| {
            old.particles.get(d) && old.particles.get(op_d) &&
            !old.particles.get(&d.perpendicular()) &&
            !old.particles.get(&op_d.perpendicular())
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(_) => {
                    let opposite = direction.opposite();

                    let head_on = has_head_on(direction, &opposite);
                    if head_on {
                        new.set(&direction.perpendicular(), true);
                        new.set(&opposite.perpendicular(), true);
                    } else {
                        let exists = new.get(direction) || old.particles.get(direction);
                        new.set(direction, exists);
                    }
                },
                // Rebound
                None => {
                    let opposite = direction.opposite();
                    let head_on = has_head_on(direction, &opposite);
                    if head_on {
                        new.set(&direction.perpendicular(), true);
                        new.set(&opposite.perpendicular(), true);
                    } else if old.particles.get(direction) {
                        new.set(&opposite, true);
                    }
                },
            }
        }

        self.particles = new;
    }

    fn transport<'a, I>(&mut self, old: &'a Self, neighbors: I)
        where I: Iterator<Item = Option<&'a Self>>,
    {

        let mut new = Particles::default();

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {
            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();
                    if neighbor.particles.get(&opposite) {
                        new.set(&opposite, true);
                    }
                },
                None => {
                    if old.particles.get(direction) {
                        new.set(direction, true);
                    }
                },
            }
        }

        self.particles = new;
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

fn pretty_print<G: Grid<Cell = HPP>>(grid: &G) {
    let dim = grid.size();
    let iter_order =
        [vec![Direction::Down], vec![Direction::Right, Direction::Left], vec![Direction::Up]];

    println!("");

    for y in 0..dim.y() {

        for dirs in &iter_order {
            print!("|");
            for x in 0..dim.x() {
                let cell = find_cell(grid.cells(), x, y);
                for d in dirs.iter() {
                    let to_print = match *d {
                        Direction::Down => {
                            if cell.particles.get(d) {
                                " v |"
                            } else {
                                "   |"
                            }
                        },
                        Direction::Right => {
                            if cell.particles.get(d) {
                                "> "
                            } else {
                                "  "
                            }
                        },
                        Direction::Left => {
                            if cell.particles.get(d) {
                                "<|"
                            } else {
                                " |"
                            }
                        },
                        Direction::Up => {
                            if cell.particles.get(d) {
                                " ^ |"
                            } else {
                                "   |"
                            }
                        },
                    };
                    print!("{}", to_print);
                }
            }
            println!("");
        }
        for _ in 0..dim.x() {
            print!(" ---");
        }
        println!("");
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

impl HPPRulesTestConsumer {
    pub fn new() -> Self { HPPRulesTestConsumer }

    fn particles_count(&self, cells: &[HPP], direction: &Direction) -> i32 {

        cells.iter()
            .filter(|c| c.particles.get(direction))
            .count() as i32
    }

    fn test_collision<G: Grid<Cell = HPP>>(&self, grid: &G) {
        println!("Collision");

        let left_particle_count = self.particles_count(grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = find_cell(grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particles.get(&Direction::Right), true);

        let head_on_up = find_cell(grid.cells(), 1, 0);
        assert_eq!(head_on_up.particles.get(&Direction::Up), true);

        let head_on_down = find_cell(grid.cells(), 1, 0);
        assert_eq!(head_on_down.particles.get(&Direction::Down), true);
    }

    fn test_transport<G: Grid<Cell = HPP>>(&self, grid: &G) {
        println!("Transport");

        let simple_move_to_right = find_cell(grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particles.get(&Direction::Right), true);

        let move_to_right_after_rebound = find_cell(grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particles.get(&Direction::Right),
                   true);

        let move_to_down_after_head_on = find_cell(grid.cells(), 1, 1);
        assert_eq!(move_to_down_after_head_on.particles.get(&Direction::Down),
                   true);

        let fixed_to_up_after_head_on = find_cell(grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particles.get(&Direction::Up),
                   true);
    }
}

impl Consumer for HPPRulesTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid<Cell = Self::Cell>>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        pretty_print(grid);

        match grid.state().stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial          collision        transport
    // | * |> <| * |    | * |^ v| * |    | * |  v| * |
    // |  <| * | * | => |>  | * | * | => | * |> v| * |
    // |>  | * | * |    |>  | * | * |    | * |>  | * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = Particles([false, false, true, false]);
    let right_particle = Particles([false, true, false, false]);
    let about_to_collide = Particles([false, true, true, false]);

    let cells = vec![
        HPP { particles: about_to_collide, coord: (1, 0) },
        HPP { particles: left_particle, coord: (0, 1) },
        HPP { particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let evolution_state = HPPState::new();
    let mut grid: TwodimGrid<HPP, _, _> = TwodimGrid::new(3, 3, nhood, evolution_state, 1);
    grid.set_cells(cells);

    pretty_print(&grid);

    let right_particles_count = grid.cells()
        .iter()
        .filter(|c| c.particles.get(&Direction::Right))
        .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction,
}

impl HPPSpreadTestConsumer {
    pub fn new() -> Self {
        HPPSpreadTestConsumer {
            cur_y: 0,
            cur_direction: Direction::Down,
        }
    }

    fn particles_count(&self, cells: &[HPP], direction: &Direction) -> i32 {

        cells.iter()
            .filter(|c| c.particles.get(direction))
            .count() as i32
    }

    fn test_collision<G: Grid<Cell = HPP>>(&mut self, grid: &G) {
        println!("Collision");

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count = self.particles_count(grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        } else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid<Cell = HPP>>(&mut self, grid: &G) {
        println!("Transport");

        let particles_count = self.particles_count(grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(grid.cells()
            .iter()
            .filter(|c| c.coord().y() == self.cur_y)
            .all(|c| c.particles.get(&self.cur_direction)));

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid<Cell = Self::Cell>>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        pretty_print(grid);

        match grid.state().stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = Particles([false, false, false, true]);

    let mut cells = Vec::new();
    for x in 0..5 {
        cells.push(HPP {
            particles: down_particle,
            coord: (x, 0),
        });
    }

    let nhood = VonNeumannNhood::new();
    let evolution_state = HPPState::new();
    let mut grid: TwodimGrid<HPP, _, _> = TwodimGrid::new(5, 5, nhood, evolution_state, 1);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}
<commit_msg>Setup function for test and benches.<commit_after>//! Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
//! Assumes Von Nuemann's neighborhood.

#![cfg(test)]

use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::EvolutionState;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::twodim::TwodimGrid;
use grid::twodim::GridCoord;
use utils::find_cell;

enum Stage {
    Collision,
    Transport,
}

impl Default for Stage {
    fn default() -> Self { Stage::Collision }
}

struct HPPState {
    stage: Stage,
}

impl HPPState {
    fn new() -> Self { HPPState { stage: Stage::Collision } }
}

impl EvolutionState for HPPState {
    fn update(&mut self) {
        self.stage = match self.stage {
            Stage::Collision => Stage::Transport,
            Stage::Transport => Stage::Collision,
        };
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Down,
    Right,
    Left,
    Up,
}

impl Direction {
    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right,
        }
    }
}

#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
struct Particles([bool; 4]);

impl Particles {
    fn set(&mut self, direction: &Direction, exists: bool) {
        let index = *direction as usize;
        self.0[index] = exists;
    }

    pub fn get(&self, direction: &Direction) -> bool {
        let index = *direction as usize;
        self.0[index]
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: Particles,
    coord: (i32, i32),
}

impl Cell for HPP {
    type Coord = (i32, i32);
    type State = HPPState;

    fn update<'a, I>(&'a mut self, old: &'a Self, neighbors: I, state: &Self::State)
        where I: Iterator<Item = Option<&'a Self>>,
    {

        match state.stage {
            Stage::Collision => self.collision(old, neighbors),
            Stage::Transport => self.transport(old, neighbors),
        };
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP { coord: (coord.x(), coord.y()), ..Default::default() }
    }

    fn coord(&self) -> &Self::Coord { &self.coord }

    fn set_coord<C: Coord>(&mut self, coord: &C) { self.coord = (coord.x(), coord.y()); }
}

impl HPP {
    fn collision<'a, I>(&'a mut self, old: &'a Self, neighbors: I)
        where I: Iterator<Item = Option<&'a Self>>,
    {
        let mut new = Particles::default();

        let has_head_on = |d: &Direction, op_d: &Direction| {
            old.particles.get(d) && old.particles.get(op_d) &&
            !old.particles.get(&d.perpendicular()) &&
            !old.particles.get(&op_d.perpendicular())
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(_) => {
                    let opposite = direction.opposite();

                    let head_on = has_head_on(direction, &opposite);
                    if head_on {
                        new.set(&direction.perpendicular(), true);
                        new.set(&opposite.perpendicular(), true);
                    } else {
                        let exists = new.get(direction) || old.particles.get(direction);
                        new.set(direction, exists);
                    }
                },
                // Rebound
                None => {
                    let opposite = direction.opposite();
                    let head_on = has_head_on(direction, &opposite);
                    if head_on {
                        new.set(&direction.perpendicular(), true);
                        new.set(&opposite.perpendicular(), true);
                    } else if old.particles.get(direction) {
                        new.set(&opposite, true);
                    }
                },
            }
        }

        self.particles = new;
    }

    fn transport<'a, I>(&mut self, old: &'a Self, neighbors: I)
        where I: Iterator<Item = Option<&'a Self>>,
    {

        let mut new = Particles::default();

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {
            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();
                    if neighbor.particles.get(&opposite) {
                        new.set(&opposite, true);
                    }
                },
                None => {
                    if old.particles.get(direction) {
                        new.set(direction, true);
                    }
                },
            }
        }

        self.particles = new;
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

fn pretty_print<G: Grid<Cell = HPP>>(grid: &G) {
    let dim = grid.size();
    let iter_order =
        [vec![Direction::Down], vec![Direction::Right, Direction::Left], vec![Direction::Up]];

    println!("");

    for y in 0..dim.y() {

        for dirs in &iter_order {
            print!("|");
            for x in 0..dim.x() {
                let cell = find_cell(grid.cells(), x, y);
                for d in dirs.iter() {
                    let to_print = match *d {
                        Direction::Down => {
                            if cell.particles.get(d) {
                                " v |"
                            } else {
                                "   |"
                            }
                        },
                        Direction::Right => {
                            if cell.particles.get(d) {
                                "> "
                            } else {
                                "  "
                            }
                        },
                        Direction::Left => {
                            if cell.particles.get(d) {
                                "<|"
                            } else {
                                " |"
                            }
                        },
                        Direction::Up => {
                            if cell.particles.get(d) {
                                " ^ |"
                            } else {
                                "   |"
                            }
                        },
                    };
                    print!("{}", to_print);
                }
            }
            println!("");
        }
        for _ in 0..dim.x() {
            print!(" ---");
        }
        println!("");
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

impl HPPRulesTestConsumer {
    pub fn new() -> Self { HPPRulesTestConsumer }

    fn particles_count(&self, cells: &[HPP], direction: &Direction) -> i32 {

        cells.iter()
            .filter(|c| c.particles.get(direction))
            .count() as i32
    }

    fn test_collision<G: Grid<Cell = HPP>>(&self, grid: &G) {
        println!("Collision");

        let left_particle_count = self.particles_count(grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = find_cell(grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particles.get(&Direction::Right), true);

        let head_on_up = find_cell(grid.cells(), 1, 0);
        assert_eq!(head_on_up.particles.get(&Direction::Up), true);

        let head_on_down = find_cell(grid.cells(), 1, 0);
        assert_eq!(head_on_down.particles.get(&Direction::Down), true);
    }

    fn test_transport<G: Grid<Cell = HPP>>(&self, grid: &G) {
        println!("Transport");

        let simple_move_to_right = find_cell(grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particles.get(&Direction::Right), true);

        let move_to_right_after_rebound = find_cell(grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particles.get(&Direction::Right),
                   true);

        let move_to_down_after_head_on = find_cell(grid.cells(), 1, 1);
        assert_eq!(move_to_down_after_head_on.particles.get(&Direction::Down),
                   true);

        let fixed_to_up_after_head_on = find_cell(grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particles.get(&Direction::Up),
                   true);
    }
}

impl Consumer for HPPRulesTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid<Cell = Self::Cell>>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        pretty_print(grid);

        match grid.state().stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial          collision        transport
    // | * |> <| * |    | * |^ v| * |    | * |  v| * |
    // |  <| * | * | => |>  | * | * | => | * |> v| * |
    // |>  | * | * |    |>  | * | * |    | * |>  | * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = Particles([false, false, true, false]);
    let right_particle = Particles([false, true, false, false]);
    let about_to_collide = Particles([false, true, true, false]);

    let cells = vec![
        HPP { particles: about_to_collide, coord: (1, 0) },
        HPP { particles: left_particle, coord: (0, 1) },
        HPP { particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let evolution_state = HPPState::new();
    let mut grid: TwodimGrid<HPP, _, _> = TwodimGrid::new(3, 3, nhood, evolution_state, 1);
    grid.set_cells(cells);

    pretty_print(&grid);

    let right_particles_count = grid.cells()
        .iter()
        .filter(|c| c.particles.get(&Direction::Right))
        .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction,
}

impl HPPSpreadTestConsumer {
    pub fn new() -> Self {
        HPPSpreadTestConsumer {
            cur_y: 0,
            cur_direction: Direction::Down,
        }
    }

    fn particles_count(&self, cells: &[HPP], direction: &Direction) -> i32 {

        cells.iter()
            .filter(|c| c.particles.get(direction))
            .count() as i32
    }

    fn test_collision<G: Grid<Cell = HPP>>(&mut self, grid: &G) {
        println!("Collision");

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count = self.particles_count(grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        } else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid<Cell = HPP>>(&mut self, grid: &G) {
        println!("Transport");

        let particles_count = self.particles_count(grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(grid.cells()
            .iter()
            .filter(|c| c.coord().y() == self.cur_y)
            .all(|c| c.particles.get(&self.cur_direction)));

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid<Cell = Self::Cell>>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        pretty_print(grid);

        match grid.state().stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

fn setup_spread_test (rows: u32, cols: i32, threads: u32)
         -> Sequential<HPP,
                TwodimGrid<HPP, VonNeumannNhood<GridCoord>, HPPState>,
                HPPSpreadTestConsumer> {

    let down_particle = Particles([false, false, false, true]);

    let mut cells = Vec::new();
    for x in 0..cols {
        cells.push(HPP {
            particles: down_particle,
            coord: (x, 0),
        });
    }

    let nhood = VonNeumannNhood::new();
    let evolution_state = HPPState::new();
    let mut grid: TwodimGrid<HPP, _, _> =
        TwodimGrid::new(rows, cols as u32, nhood, evolution_state, threads);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    Sequential::new(grid, consumer)
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let mut engine = setup_spread_test(5, 5, 1);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}
<|endoftext|>


<commit_before>use core::cell::Cell;
use hil::{Driver,Callback};
use hil::gpio::{GPIOPin,InputMode,InterruptMode,Client};

pub struct GPIO<'a, G: GPIOPin + 'a> {
    pins: &'a [&'a G],
    callback: Cell<Option<Callback>>,
}

impl<'a, G: GPIOPin> GPIO<'a, G> {
    pub fn new(pins: &'a [&'a G]) -> GPIO<'a, G> {
        GPIO {
            pins: pins,
            callback: Cell::new(None),
        }
    }

    fn configure_input_pin(&self, pin_num: usize, config: usize) -> isize {
        let pins = self.pins.as_ref();
        match config {
            0 => {
                pins[pin_num].enable_input(InputMode::PullUp);
                0
            },

            1 => {
                pins[pin_num].enable_input(InputMode::PullDown);
                0
            },

            2 => {
                pins[pin_num].enable_input(InputMode::PullNone);
                0
            },

            _ => -1
        }
    }

    fn configure_interrupt(&self, pin_num: usize, config: usize) -> isize {
        let pins = self.pins.as_ref();
        match config {
            0 => {
                pins[pin_num].enable_interrupt(pin_num, InterruptMode::Change);
                0
            },

            1 => {
                pins[pin_num].enable_interrupt(pin_num, InterruptMode::RisingEdge);
                0
            },

            2 => {
                pins[pin_num].enable_interrupt(pin_num, InterruptMode::FallingEdge);
                0
            },

            _ => -1
        }
    }
}

impl<'a, G: GPIOPin> Client for GPIO<'a, G> {
    fn fired(&self, pin_num: usize) {
        // schedule callback with the pin number
        if self.callback.get().is_some() {
            self.callback.get().unwrap().schedule(pin_num, 0, 0);
        }
    }
}

impl<'a, G: GPIOPin> Driver for GPIO<'a, G> {
    fn subscribe(&self, subscribe_num: usize, callback: Callback) -> isize {
        match subscribe_num {
            // subscribe to all pin interrupts
            // (no affect or reliance on individual pins being configured as interrupts)
            0 => {
                self.callback.set(Some(callback));
                0
            }

            // default
            _ => -1
        }
    }

    fn command(&self, command_num: usize, data: usize) -> isize {
        let pins = self.pins.as_ref();
        match command_num {
            // enable output
            0 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].enable_output();
                    0
                }
            },

            // set pin
            1 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].set();
                    0
                }
            },

            // clear pin
            2  => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].clear();
                    0
                }
            },

            // toggle pin
            3 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].toggle();
                    0
                }
            },

            // enable and configure input
            4 => {
                //XXX: this is clunky
                // data == ((pin_config << 8) | pin)
                // this allows two values to be passed into a command interface
                let pin_num = data & 0xFF;
                let pin_config = (data >> 8) & 0xFF;
                if pin_num >= pins.len() {
                    -1
                } else {
                   let err_code = self.configure_input_pin(pin_num, pin_config);
                   err_code
                }
            },

            // read input
            5 => {
                if data >= pins.len() {
                    -1
                } else {
                    let pin_state = pins[data].read();
                    pin_state as isize
                }
            },

            // enable and configure interrupts on pin, also sets pin as input
            // (no affect or reliance on registered callback)
            6 => {
                // TODO(brghena): this is clunky
                // data == ((irq_config << 16) | (pin_config << 8) | pin)
                // this allows three values to be passed into a command interface
                let pin_num = data & 0xFF;
                let pin_config = (data >>  8) & 0xFF;
                let irq_config = (data >> 16) & 0xFF;
                if pin_num >= pins.len() {
                    -1
                } else {
                    let mut err_code = self.configure_input_pin(pin_num, pin_config);
                    if err_code == 0 {
                        err_code = self.configure_interrupt(pin_num, irq_config);
                    }
                    err_code
                }
            },

            // disable interrupts on pin, also disables pin
            // (no affect or reliance on registered callback)
            7 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].disable_interrupt();
                    pins[data].disable();
                    0
                }
            },

            // disable pin
            8 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].disable();
                    0
                }
            }

            // default
            _ => -1
        }
    }
}

<commit_msg>Added pin value to GPIO driver callback<commit_after>use core::cell::Cell;
use hil::{Driver,Callback};
use hil::gpio::{GPIOPin,InputMode,InterruptMode,Client};

pub struct GPIO<'a, G: GPIOPin + 'a> {
    pins: &'a [&'a G],
    callback: Cell<Option<Callback>>,
}

impl<'a, G: GPIOPin> GPIO<'a, G> {
    pub fn new(pins: &'a [&'a G]) -> GPIO<'a, G> {
        GPIO {
            pins: pins,
            callback: Cell::new(None),
        }
    }

    fn configure_input_pin(&self, pin_num: usize, config: usize) -> isize {
        let pins = self.pins.as_ref();
        match config {
            0 => {
                pins[pin_num].enable_input(InputMode::PullUp);
                0
            },

            1 => {
                pins[pin_num].enable_input(InputMode::PullDown);
                0
            },

            2 => {
                pins[pin_num].enable_input(InputMode::PullNone);
                0
            },

            _ => -1
        }
    }

    fn configure_interrupt(&self, pin_num: usize, config: usize) -> isize {
        let pins = self.pins.as_ref();
        match config {
            0 => {
                pins[pin_num].enable_interrupt(pin_num, InterruptMode::Change);
                0
            },

            1 => {
                pins[pin_num].enable_interrupt(pin_num, InterruptMode::RisingEdge);
                0
            },

            2 => {
                pins[pin_num].enable_interrupt(pin_num, InterruptMode::FallingEdge);
                0
            },

            _ => -1
        }
    }
}

impl<'a, G: GPIOPin> Client for GPIO<'a, G> {
    fn fired(&self, pin_num: usize) {
        // read the value of the pin
        let pins = self.pins.as_ref();
        let pin_state = pins[pin_num].read();

        // schedule callback with the pin number and value
        if self.callback.get().is_some() {
            self.callback.get().unwrap().schedule(pin_num, pin_state as usize, 0);
        }
    }
}

impl<'a, G: GPIOPin> Driver for GPIO<'a, G> {
    fn subscribe(&self, subscribe_num: usize, callback: Callback) -> isize {
        match subscribe_num {
            // subscribe to all pin interrupts
            // (no affect or reliance on individual pins being configured as interrupts)
            0 => {
                self.callback.set(Some(callback));
                0
            }

            // default
            _ => -1
        }
    }

    fn command(&self, command_num: usize, data: usize) -> isize {
        let pins = self.pins.as_ref();
        match command_num {
            // enable output
            0 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].enable_output();
                    0
                }
            },

            // set pin
            1 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].set();
                    0
                }
            },

            // clear pin
            2  => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].clear();
                    0
                }
            },

            // toggle pin
            3 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].toggle();
                    0
                }
            },

            // enable and configure input
            4 => {
                //XXX: this is clunky
                // data == ((pin_config << 8) | pin)
                // this allows two values to be passed into a command interface
                let pin_num = data & 0xFF;
                let pin_config = (data >> 8) & 0xFF;
                if pin_num >= pins.len() {
                    -1
                } else {
                   let err_code = self.configure_input_pin(pin_num, pin_config);
                   err_code
                }
            },

            // read input
            5 => {
                if data >= pins.len() {
                    -1
                } else {
                    let pin_state = pins[data].read();
                    pin_state as isize
                }
            },

            // enable and configure interrupts on pin, also sets pin as input
            // (no affect or reliance on registered callback)
            6 => {
                // TODO(brghena): this is clunky
                // data == ((irq_config << 16) | (pin_config << 8) | pin)
                // this allows three values to be passed into a command interface
                let pin_num = data & 0xFF;
                let pin_config = (data >>  8) & 0xFF;
                let irq_config = (data >> 16) & 0xFF;
                if pin_num >= pins.len() {
                    -1
                } else {
                    let mut err_code = self.configure_input_pin(pin_num, pin_config);
                    if err_code == 0 {
                        err_code = self.configure_interrupt(pin_num, irq_config);
                    }
                    err_code
                }
            },

            // disable interrupts on pin, also disables pin
            // (no affect or reliance on registered callback)
            7 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].disable_interrupt();
                    pins[data].disable();
                    0
                }
            },

            // disable pin
            8 => {
                if data >= pins.len() {
                    -1
                } else {
                    pins[data].disable();
                    0
                }
            }

            // default
            _ => -1
        }
    }
}

<|endoftext|>


<commit_before>use std::collections::HashMap;

pub struct Scanner {
    source: String,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: i32,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source,
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
        }
    }

    pub fn scan_tokens(&mut self) -> Vec<Token> {
        while !self.is_at_end() {
            self.start = self.current;
            match self.scan_token() {
                Ok(token) => {
                    if !token.token_type.is_ignored() {
                        self.tokens.push(token);
                    }
                }
                Err(_) => {}
            }
        }
        self.tokens
            .push(Token::new(TokenType::EOF, "", Literal::Nil, self.line));
        self.tokens.to_vec()
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn scan_token(&mut self) -> Result<Token, ScanError> {
        let c = self.advance();
        match c {
            '(' => Ok(self.create_token(TokenType::LEFT_PAREN)),
            ')' => Ok(self.create_token(TokenType::RIGHT_PAREN)),
            '{' => Ok(self.create_token(TokenType::LEFT_BRACE)),
            '}' => Ok(self.create_token(TokenType::RIGHT_BRACE)),
            ',' => Ok(self.create_token(TokenType::COMMA)),
            '.' => Ok(self.create_token(TokenType::DOT)),
            '-' => Ok(self.create_token(TokenType::MINUS)),
            '+' => Ok(self.create_token(TokenType::PLUS)),
            ';' => Ok(self.create_token(TokenType::SEMICOLON)),
            '*' => Ok(self.create_token(TokenType::STAR)),
            '!' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::BANG_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::BANG))
                }
            }
            '=' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::EQUAL_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::EQUAL))
                }
            }
            '<' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::LESS_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::LESS))
                }
            }
            '>' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::GREATER_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::GREATER))
                }
            }
            '/' => {
                if self.match_char('/') {
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Ok(self.create_token(TokenType::COMMENT))
                } else {
                    Ok(self.create_token(TokenType::SLASH))
                }
            }
            ' ' | '\r' | '\t' => Ok(self.create_token(TokenType::WHITESPACE)),
            '\n' => {
                self.line += 1;
                Ok(self.create_token(TokenType::NEWLINE))
            }
            '"' => self.scan_string(),
            c if c.is_digit(10) => self.scan_number(),
            c if c.is_alphabetic() => self.scan_identifier(),
            _ => Err(ScanError::new(self.line, "Unexpected character.")),
        }
    }

    fn scan_string(&mut self) -> Result<Token, ScanError> {
        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' {
                self.line += 1;
            }
            self.advance();
        }
        // Unterminated string.
        if self.is_at_end() {
            return Err(ScanError::new(self.line, "Unterminated string."));
        }

        // The closing ".
        self.advance();

        // Trim the surrounding quotes.
        let value = &self.source[self.start + 1..self.current - 1];
        Ok(self.create_token_with_literal(TokenType::STRING, Literal::String(value.to_owned())))
    }

    fn scan_number(&mut self) -> Result<Token, ScanError> {
        while self.peek().is_digit(10) {
            self.advance();
        }

        // Look for a fractional part.
        if self.peek() == '.' && self.peek_next().is_digit(10) {
            // Consume the "."
            self.advance();
            while self.peek().is_digit(10) {
                self.advance();
            }
        }
        let value = &self.source[self.start..self.current];
        Ok(self.create_token_with_literal(TokenType::NUMBER,
                                          Literal::Number(value.parse::<f64>().unwrap())))
    }

    fn scan_identifier(&mut self) -> Result<Token, ScanError> {
        while self.peek().is_alphanumeric() {
            self.advance();
        }

        let text = &self.source[self.start..self.current];
        let token_type = match KEYWORDS.get(text) {
            Some(t) => *t,
            None => TokenType::IDENTIFIER,
        };

        Ok(self.create_token(token_type))
    }

    fn peek(&self) -> char {
        if self.is_at_end() {
            return '\0';
        }
        let b = self.source.as_bytes();
        b[self.current] as char
    }

    fn peek_next(&self) -> char {
        if self.current + 1 >= self.source.len() {
            return '\0';
        }
        let b = self.source.as_bytes();
        b[self.current + 1] as char
    }

    fn match_char(&self, c: char) -> bool {
        self.peek() == c
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        let b = self.source.as_bytes();
        b[self.current - 1] as char
    }

    fn create_token(&self, token_type: TokenType) -> Token {
        self.create_token_with_literal(token_type, Literal::Nil)
    }

    fn create_token_with_literal(&self, token_type: TokenType, literal: Literal) -> Token {
        let s = &self.source[self.start as usize..self.current as usize];
        Token::new(token_type, s, literal, self.line)
    }
}

#[derive(Clone, Debug)]
pub struct Token {
    token_type: TokenType,
    lexeme: String,
    literal: Literal,
    line: i32,
}

impl Token {
    pub fn new(token_type: TokenType, lexeme: &str, literal: Literal, line: i32) -> Self {
        Token {
            token_type: token_type,
            lexeme: lexeme.to_owned(),
            literal: literal,
            line: line,
        }
    }
}

#[derive(Clone, Copy, Debug)]
#[allow(non_camel_case_types)]
pub enum TokenType {
    // Single-character tokens.
    LEFT_PAREN,
    RIGHT_PAREN,
    LEFT_BRACE,
    RIGHT_BRACE,
    COMMA,
    DOT,
    MINUS,
    PLUS,
    SEMICOLON,
    SLASH,
    STAR,

    // One or two character tokens.
    BANG,
    BANG_EQUAL,
    EQUAL,
    EQUAL_EQUAL,
    GREATER,
    GREATER_EQUAL,
    LESS,
    LESS_EQUAL,

    // Literals.
    IDENTIFIER,
    STRING,
    NUMBER,

    // Keywords.
    AND,
    CLASS,
    ELSE,
    FALSE,
    FUN,
    FOR,
    IF,
    NIL,
    OR,
    PRINT,
    RETURN,
    SUPER,
    THIS,
    TRUE,
    VAR,
    WHILE,

    EOF,

    // IGNORED lexemes
    COMMENT,
    WHITESPACE,
    NEWLINE,
}

impl TokenType {
    fn is_ignored(&self) -> bool {
        let n = *self as u8;
        n > 38
    }
}

#[derive(Clone, Debug)]
pub enum Literal {
    String(String),
    Number(f64),
    Nil,
}

pub struct ScanError {
    line: i32,
    message: String,
}

impl ScanError {
    fn new(line: i32, message: &str) -> Self {
        ScanError {
            line: line,
            message: message.to_owned(),
        }
    }
}

lazy_static! {
    static ref KEYWORDS: HashMap<&'static str, TokenType> = {
        let mut m = HashMap::new();
        m.insert("and", TokenType::AND);
        m.insert("class", TokenType::CLASS);
        m.insert("else", TokenType::ELSE);
        m.insert("false", TokenType::FALSE);
        m.insert("for", TokenType::FOR);
        m.insert("fun", TokenType::FUN);
        m.insert("if", TokenType::IF);
        m.insert("nil", TokenType::NIL);
        m.insert("or", TokenType::OR);
        m.insert("print", TokenType::PRINT);
        m.insert("return", TokenType::RETURN);
        m.insert("super", TokenType::SUPER);
        m.insert("this", TokenType::THIS);
        m.insert("true", TokenType::TRUE);
        m.insert("var", TokenType::VAR);
        m.insert("while", TokenType::WHILE);
        m
    };
}
<commit_msg>add a couple minor tweaks<commit_after>use std::collections::HashMap;

pub struct Scanner {
    source: String,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: i32,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source,
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
        }
    }

    pub fn scan_tokens(&mut self) -> Vec<Token> {
        while !self.is_at_end() {
            self.start = self.current;
            match self.scan_token() {
                Ok(token) => {
                    if !token.token_type.is_ignored() {
                        self.tokens.push(token);
                    }
                }
                Err(_) => {}
            }
        }
        self.tokens
            .push(Token::new(TokenType::EOF, "", Literal::Nil, self.line));
        self.tokens.to_vec()
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn scan_token(&mut self) -> Result<Token, ScanError> {
        let c = self.advance();
        match c {
            '(' => Ok(self.create_token(TokenType::LEFT_PAREN)),
            ')' => Ok(self.create_token(TokenType::RIGHT_PAREN)),
            '{' => Ok(self.create_token(TokenType::LEFT_BRACE)),
            '}' => Ok(self.create_token(TokenType::RIGHT_BRACE)),
            ',' => Ok(self.create_token(TokenType::COMMA)),
            '.' => Ok(self.create_token(TokenType::DOT)),
            '-' => Ok(self.create_token(TokenType::MINUS)),
            '+' => Ok(self.create_token(TokenType::PLUS)),
            ';' => Ok(self.create_token(TokenType::SEMICOLON)),
            '*' => Ok(self.create_token(TokenType::STAR)),
            // TODO: A bit of duplication here. Should refactor at some point.
            '!' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::BANG_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::BANG))
                }
            }
            '=' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::EQUAL_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::EQUAL))
                }
            }
            '<' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::LESS_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::LESS))
                }
            }
            '>' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::GREATER_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::GREATER))
                }
            }
            '/' => {
                if self.match_char('/') {
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Ok(self.create_token(TokenType::COMMENT))
                } else {
                    Ok(self.create_token(TokenType::SLASH))
                }
            }
            ' ' | '\r' | '\t' => Ok(self.create_token(TokenType::WHITESPACE)),
            '\n' => {
                self.line += 1;
                Ok(self.create_token(TokenType::NEWLINE))
            }
            '"' => self.scan_string(),
            c if c.is_digit(10) => self.scan_number(),
            c if c.is_alphabetic() => self.scan_identifier(),
            _ => Err(ScanError::new(self.line, "Unexpected character.")),
        }
    }

    fn scan_string(&mut self) -> Result<Token, ScanError> {
        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' {
                self.line += 1;
            }
            self.advance();
        }
        // Unterminated string.
        if self.is_at_end() {
            return Err(ScanError::new(self.line, "Unterminated string."));
        }

        // The closing ".
        self.advance();

        // Trim the surrounding quotes.
        let value = &self.source[self.start + 1..self.current - 1];
        Ok(self.create_token_with_literal(TokenType::STRING, Literal::String(value.to_owned())))
    }

    fn scan_number(&mut self) -> Result<Token, ScanError> {
        while self.peek().is_digit(10) {
            self.advance();
        }

        // Look for a fractional part.
        if self.peek() == '.' && self.peek_next().is_digit(10) {
            // Consume the "."
            self.advance();
            while self.peek().is_digit(10) {
                self.advance();
            }
        }
        let value = &self.source[self.start..self.current];
        Ok(self.create_token_with_literal(TokenType::NUMBER,
                                          Literal::Number(value.parse::<f64>().unwrap())))
    }

    fn scan_identifier(&mut self) -> Result<Token, ScanError> {
        while self.peek().is_alphanumeric() {
            self.advance();
        }

        let text = &self.source[self.start..self.current];
        let token_type = match KEYWORDS.get(text) {
            Some(t) => *t,
            None => TokenType::IDENTIFIER,
        };

        Ok(self.create_token(token_type))
    }

    fn peek(&self) -> char {
        if self.is_at_end() {
            return '\0';
        }
        let b = self.source.as_bytes();
        b[self.current] as char
    }

    fn peek_next(&self) -> char {
        if self.current + 1 >= self.source.len() {
            return '\0';
        }
        let b = self.source.as_bytes();
        b[self.current + 1] as char
    }

    fn match_char(&self, c: char) -> bool {
        self.peek() == c
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        let b = self.source.as_bytes();
        b[self.current - 1] as char
    }

    fn create_token(&self, token_type: TokenType) -> Token {
        self.create_token_with_literal(token_type, Literal::Nil)
    }

    fn create_token_with_literal(&self, token_type: TokenType, literal: Literal) -> Token {
        let s = &self.source[self.start..self.current];
        Token::new(token_type, s, literal, self.line)
    }
}

#[derive(Clone, Debug)]
pub struct Token {
    token_type: TokenType,
    lexeme: String,
    literal: Literal,
    line: i32,
}

impl Token {
    pub fn new(token_type: TokenType, lexeme: &str, literal: Literal, line: i32) -> Self {
        Token {
            token_type: token_type,
            lexeme: lexeme.to_owned(),
            literal: literal,
            line: line,
        }
    }
}

#[derive(Clone, Copy, Debug)]
#[allow(non_camel_case_types)]
pub enum TokenType {
    // Single-character tokens.
    LEFT_PAREN,
    RIGHT_PAREN,
    LEFT_BRACE,
    RIGHT_BRACE,
    COMMA,
    DOT,
    MINUS,
    PLUS,
    SEMICOLON,
    SLASH,
    STAR,

    // One or two character tokens.
    BANG,
    BANG_EQUAL,
    EQUAL,
    EQUAL_EQUAL,
    GREATER,
    GREATER_EQUAL,
    LESS,
    LESS_EQUAL,

    // Literals.
    IDENTIFIER,
    STRING,
    NUMBER,

    // Keywords.
    AND,
    CLASS,
    ELSE,
    FALSE,
    FUN,
    FOR,
    IF,
    NIL,
    OR,
    PRINT,
    RETURN,
    SUPER,
    THIS,
    TRUE,
    VAR,
    WHILE,

    EOF,

    // IGNORED lexemes
    COMMENT,
    WHITESPACE,
    NEWLINE,
}

impl TokenType {
    fn is_ignored(&self) -> bool {
        let n = *self as u8;
        n > 38
    }
}

#[derive(Clone, Debug)]
pub enum Literal {
    String(String),
    Number(f64),
    Nil,
}

pub struct ScanError {
    line: i32,
    message: String,
}

impl ScanError {
    fn new(line: i32, message: &str) -> Self {
        ScanError {
            line: line,
            message: message.to_owned(),
        }
    }
}

lazy_static! {
    static ref KEYWORDS: HashMap<&'static str, TokenType> = {
        let mut m = HashMap::new();
        m.insert("and", TokenType::AND);
        m.insert("class", TokenType::CLASS);
        m.insert("else", TokenType::ELSE);
        m.insert("false", TokenType::FALSE);
        m.insert("for", TokenType::FOR);
        m.insert("fun", TokenType::FUN);
        m.insert("if", TokenType::IF);
        m.insert("nil", TokenType::NIL);
        m.insert("or", TokenType::OR);
        m.insert("print", TokenType::PRINT);
        m.insert("return", TokenType::RETURN);
        m.insert("super", TokenType::SUPER);
        m.insert("this", TokenType::THIS);
        m.insert("true", TokenType::TRUE);
        m.insert("var", TokenType::VAR);
        m.insert("while", TokenType::WHILE);
        m
    };
}
<|endoftext|>


<commit_before>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug,Clone,Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
pub struct Position {
    line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position {
            line: 1,
            column: 1,
        }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

pub type Lexeme = String;

#[derive(Debug)]
pub struct TokenWithContext {
    pub token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    pub lexeme: Lexeme,
    /// Position of the first character of the token
    pub position: Position,
}

#[derive(Debug,Clone)]
pub enum ScannerError {
    MissingStringTerminator(Position),
    UnexpectedCharacter(char, Position),
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.current_position.increment_line();
                } else {
                    self.current_position.increment_column();;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn advance_while(&mut self, condition: &Fn(char) -> bool) -> () {
        while self.peek_check(condition) {
            self.advance();
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, ScannerError> {
        self.advance_while(&|c| c != '"');
        if !self.advance_if_match('"') {
            return Err(ScannerError::MissingStringTerminator(self.current_position));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        self.advance_while(&is_digit);
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            self.advance_while(&is_digit);
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        self.advance_while(&is_alphanumeric);
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Option<Result<TokenWithContext, ScannerError>> {
        let initial_position = self.current_position;
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => return None,
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    self.advance_while(&|c| c != '\n');
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => {
                match self.string() {
                    Ok(token) => token,
                    Err(error) => return Some(Err(error)),
                }
            }
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                // TODO: check if position is off by one
                return Some(Err(ScannerError::UnexpectedCharacter(c, self.current_position)));
            }
        };
        Some(Ok(self.add_context(token, initial_position)))
    }
}

pub fn scan(source: &String) -> (Vec<TokenWithContext>, Vec<ScannerError>) {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut errors = Vec::new();
    while let Some(result) = scanner.scan_next() {
        match result {
            Ok(token_with_context) => {
                match token_with_context.token {
                    // Ignoring tokens we don't care about
                    Token::Comment => {}
                    Token::Whitespace => {}
                    _ => tokens.push(token_with_context),
                };
            }
            Err(error) => errors.push(error),
        }
    }
    (tokens, errors)
}

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

    #[test]
    fn single_token() {
        let (tokens, _) = scan(&"+".into());
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let (tokens, _) = scan(&"1+2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn expression_with_whitespaces() {
        let (tokens, _) = scan(&"1 + 2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn assignement_with_comment() {
        let (tokens, _) = scan(&"var a = 1.0; // A comment".into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
    }

    #[test]
    fn multiline_statements() {
        let (tokens, _) = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
            .into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 46); // There are 30 spaces before the text!
    }
}
<commit_msg>FIxed scanning of source containing bad characters and unterminated strings<commit_after>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug,Clone,Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
pub struct Position {
    line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position {
            line: 1,
            column: 1,
        }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

pub type Lexeme = String;

#[derive(Debug)]
pub struct TokenWithContext {
    pub token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    pub lexeme: Lexeme,
    /// Position of the first character of the token
    pub position: Position,
}

#[derive(Debug,Clone)]
pub enum ScannerError {
    MissingStringTerminator(Position),
    UnexpectedCharacter(char, Position),
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.current_position.increment_line();
                } else {
                    self.current_position.increment_column();;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn advance_while(&mut self, condition: &Fn(char) -> bool) -> () {
        while self.peek_check(condition) {
            self.advance();
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, ScannerError> {
        self.advance_while(&|c| c != '"' && c!= '\n');
        if !self.advance_if_match('"') {
            self.current_lexeme = "".into();
            return Err(ScannerError::MissingStringTerminator(self.current_position));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        self.advance_while(&is_digit);
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            self.advance_while(&is_digit);
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        self.advance_while(&is_alphanumeric);
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Option<Result<TokenWithContext, ScannerError>> {
        let initial_position = self.current_position;
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => return None,
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    self.advance_while(&|c| c != '\n');
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => {
                match self.string() {
                    Ok(token) => token,
                    Err(error) => return Some(Err(error)),
                }
            }
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                // TODO: check if position is off by one
                self.current_lexeme = "".into();
                return Some(Err(ScannerError::UnexpectedCharacter(c, self.current_position)));
            }
        };
        Some(Ok(self.add_context(token, initial_position)))
    }
}

pub fn scan(source: &String) -> (Vec<TokenWithContext>, Vec<ScannerError>) {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut errors = Vec::new();
    while let Some(result) = scanner.scan_next() {
        match result {
            Ok(token_with_context) => {
                match token_with_context.token {
                    // Ignoring tokens we don't care about
                    Token::Comment => {}
                    Token::Whitespace => {}
                    _ => tokens.push(token_with_context),
                };
            }
            Err(error) => errors.push(error),
        }
    }
    (tokens, errors)
}

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

    #[test]
    fn single_token() {
        let (tokens, _) = scan(&"+".into());
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let (tokens, _) = scan(&"1+2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn expression_with_whitespaces() {
        let (tokens, _) = scan(&"1 + 2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn assignement_with_comment() {
        let (tokens, _) = scan(&"var a = 1.0; // A comment".into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
    }

    #[test]
    fn multiline_statements() {
        let (tokens, _) = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
            .into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 46); // There are 30 spaces before the text!
    }

    #[test]
    fn expression_with_bad_character() {
        let (tokens, errors) = scan(&"1 + $2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(errors.len(), 1);
    }

    #[test]
    fn statements_with_unterminated_string() {
        let (tokens, errors) = scan(&r#"var a = "Unterminated;
                              var b = "Hello";"#
            .into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        // Literal not reported, it wasn't terminated
        // Semicolon got embedded into the string
        assert_eq!(tokens[3].token, Token::Var);
        assert_eq!(tokens[4].token, Token::Identifier("b".into()));
        assert_eq!(tokens[5].token, Token::Equal);
        assert_eq!(tokens[6].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[7].token, Token::Semicolon);
        assert_eq!(errors.len(), 1);
    }

}
<|endoftext|>