<commit_before>use core::{
    cell::UnsafeCell,
    fmt,
    ops::{Deref, DerefMut},
    sync::atomic::{spin_loop_hint as cpu_relax, AtomicUsize, Ordering},
};

/// A ticket lock for mutual exclusion based on spinning.
///
/// A ticket lock is similair to a queue management system.
/// When a thread tries to lock the values,
/// it is assigned a ticket, and it spins until its ticket is the next in line.
/// When releasing the value, the next ticket will be processed.
pub struct TicketMutex<T: ?Sized> {
    next_ticket: AtomicUsize,
    next_serving: AtomicUsize,
    value: UnsafeCell<T>,
}

/// A guard that protects some data.
///
/// When the guard is dropped, the next ticket will be processed.
pub struct TicketMutexGuard<'a, T: ?Sized + 'a> {
    next_serving: &'a AtomicUsize,
    ticket: usize,
    value: &'a mut T,
}

impl<'a, T: ?Sized> TicketMutexGuard<'a, T> {
    /// Leak the lock guard, yielding a mutable reference to the underlying data.
    ///
    /// Note that this function will permanently lock the original lock.
    ///
    /// ```
    /// let mylock = spin::Mutex::new(0);
    ///
    /// let data: &mut i32 = spin::MutexGuard::leak(mylock.lock());
    ///
    /// *data = 1;
    /// assert_eq!(*data, 1);
    /// ```
    #[inline]
    pub fn leak(this: Self) -> &'a mut T {
        let data = this.value as *mut _; // Keep it in pointer form temporarily to avoid double-aliasing
        core::mem::forget(this);
        unsafe { &mut *data }
    }
}

unsafe impl<T: ?Sized + Send> Sync for TicketMutex<T> {}
unsafe impl<T: ?Sized + Send> Send for TicketMutex<T> {}

impl<T> TicketMutex<T> {
    /// Creates a new `TicketMutex` that wraps the given value.
    ///
    /// This method can be used in static context.
    ///
    /// ```
    /// static LOCK: spin::mutex::TicketMutex<i32> = spin::mutex::TicketMutex::new(123);
    ///
    /// # fn main() {
    /// let value = LOCK.lock();
    /// assert_eq!(*value, 123);
    /// drop(value);
    /// # }
    /// ```
    pub const fn new(value: T) -> Self {
        Self {
            next_ticket: AtomicUsize::new(0),
            next_serving: AtomicUsize::new(0),
            value: UnsafeCell::new(value),
        }
    }

    /// Unwraps the inner value of this lock.
    pub fn into_inner(self) -> T {
        self.value.into_inner()
    }
}

impl<T: ?Sized + fmt::Debug> fmt::Debug for TicketMutex<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.try_lock() {
            Some(guard) => write!(f, "Mutex {{ data: ")
                .and_then(|()| (&*guard).fmt(f))
                .and_then(|()| write!(f, "}}")),
            None => write!(f, "Mutex {{ <locked> }}"),
        }
    }
}

impl<T: ?Sized> TicketMutex<T> {
    /// Spins the thread until the value can be locked.
    ///
    /// The returned value can be used to modify the value,
    /// and will be unlocked after dropping the return value.
    pub fn lock(&self) -> TicketMutexGuard<T> {
        let ticket = self.next_ticket.fetch_add(1, Ordering::Relaxed);

        while self.next_serving.load(Ordering::Acquire) != ticket {
            cpu_relax();
        }

        TicketMutexGuard {
            next_serving: &self.next_serving,
            ticket,
            // Safety
            // We know that we are the next ticket to be served,
            // so there's no other thread accessing the value.
            //
            // Every other thread has another ticket number so it's
            // definitely stuck in the spin loop above.
            value: unsafe { &mut *self.value.get() },
        }
    }

    /// Tries to lock this lock. If it's already locked, `None` is returned,
    /// otherwise a guard that protects the data is returned.
    pub fn try_lock(&self) -> Option<TicketMutexGuard<T>> {
        let ticket = self
            .next_ticket
            .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |ticket| {
                if self.next_serving.load(Ordering::Acquire) == ticket {
                    Some(ticket + 1)
                } else {
                    None
                }
            });

        ticket.ok().map(|ticket| TicketMutexGuard {
            next_serving: &self.next_serving,
            ticket,
            // Safety
            // We have a ticket that is equal to the next_serving ticket, so we know:
            // - that no other thread can have the same ticket id as this thread
            // - that we are the next one to be served so we have exclusive access to the value
            value: unsafe { &mut *self.value.get() },
        })
    }

    /// Returns a mutable reference to the underlying data.
    ///
    /// This will not lock this lock, since this method borrows
    /// this lock mutably so no locking is needed.
    pub fn get_mut(&mut self) -> &mut T {
        // Safety:
        // We know that there are no other references to `self`,
        // so it's safe to return a exclusive reference to the value.
        unsafe { &mut *self.value.get() }
    }
}

impl<T> From<T> for TicketMutex<T> {
    fn from(value: T) -> Self {
        Self::new(value)
    }
}

impl<T: ?Sized + Default> Default for TicketMutex<T> {
    fn default() -> TicketMutex<T> {
        TicketMutex::new(Default::default())
    }
}

impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for TicketMutexGuard<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}

impl<'a, T: ?Sized + fmt::Display> fmt::Display for TicketMutexGuard<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&**self, f)
    }
}

impl<'a, T: ?Sized> Deref for TicketMutexGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.value
    }
}

impl<'a, T: ?Sized> DerefMut for TicketMutexGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        self.value
    }
}

impl<'a, T: ?Sized> Drop for TicketMutexGuard<'a, T> {
    fn drop(&mut self) {
        let new_ticket = self.ticket + 1;
        self.next_serving.store(new_ticket, Ordering::Release);
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::thread;

    use super::*;

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    #[test]
    fn smoke() {
        let m = TicketMutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        static M: TicketMutex<()> = TicketMutex::new(());
        static mut CNT: u32 = 0;
        const J: u32 = 1000;
        const K: u32 = 3;

        fn inc() {
            for _ in 0..J {
                unsafe {
                    let _g = M.lock();
                    CNT += 1;
                }
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(unsafe { CNT }, J * K * 2);
    }

    #[test]
    fn try_lock() {
        let mutex = TicketMutex::new(42);

        // First lock succeeds
        let a = mutex.try_lock();
        assert_eq!(a.as_ref().map(|r| **r), Some(42));

        // Additional lock failes
        let b = mutex.try_lock();
        assert!(b.is_none());

        // After dropping lock, it succeeds again
        ::core::mem::drop(a);
        let c = mutex.try_lock();
        assert_eq!(c.as_ref().map(|r| **r), Some(42));
    }

    #[test]
    fn test_into_inner() {
        let m = TicketMutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = TicketMutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(TicketMutex::new(1));
        let arc2 = Arc::new(TicketMutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(TicketMutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || -> () {
            struct Unwinder {
                i: Arc<TicketMutex<i32>>,
            }
            impl Drop for Unwinder {
                fn drop(&mut self) {
                    *self.i.lock() += 1;
                }
            }
            let _u = Unwinder { i: arc2 };
            panic!();
        })
        .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &TicketMutex<[i32]> = &TicketMutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }
}
<commit_msg>Add force_unlock to ticket lock to sync apis<commit_after>use core::{
    cell::UnsafeCell,
    fmt,
    ops::{Deref, DerefMut},
    sync::atomic::{spin_loop_hint as cpu_relax, AtomicUsize, Ordering},
};

/// A ticket lock for mutual exclusion based on spinning.
///
/// A ticket lock is similair to a queue management system.
/// When a thread tries to lock the values,
/// it is assigned a ticket, and it spins until its ticket is the next in line.
/// When releasing the value, the next ticket will be processed.
pub struct TicketMutex<T: ?Sized> {
    next_ticket: AtomicUsize,
    next_serving: AtomicUsize,
    value: UnsafeCell<T>,
}

/// A guard that protects some data.
///
/// When the guard is dropped, the next ticket will be processed.
pub struct TicketMutexGuard<'a, T: ?Sized + 'a> {
    next_serving: &'a AtomicUsize,
    ticket: usize,
    value: &'a mut T,
}

impl<'a, T: ?Sized> TicketMutexGuard<'a, T> {
    /// Leak the lock guard, yielding a mutable reference to the underlying data.
    ///
    /// Note that this function will permanently lock the original lock.
    ///
    /// ```
    /// let mylock = spin::Mutex::new(0);
    ///
    /// let data: &mut i32 = spin::MutexGuard::leak(mylock.lock());
    ///
    /// *data = 1;
    /// assert_eq!(*data, 1);
    /// ```
    #[inline]
    pub fn leak(this: Self) -> &'a mut T {
        let data = this.value as *mut _; // Keep it in pointer form temporarily to avoid double-aliasing
        core::mem::forget(this);
        unsafe { &mut *data }
    }
}

unsafe impl<T: ?Sized + Send> Sync for TicketMutex<T> {}
unsafe impl<T: ?Sized + Send> Send for TicketMutex<T> {}

impl<T> TicketMutex<T> {
    /// Creates a new `TicketMutex` that wraps the given value.
    ///
    /// This method can be used in static context.
    ///
    /// ```
    /// static LOCK: spin::mutex::TicketMutex<i32> = spin::mutex::TicketMutex::new(123);
    ///
    /// # fn main() {
    /// let value = LOCK.lock();
    /// assert_eq!(*value, 123);
    /// drop(value);
    /// # }
    /// ```
    pub const fn new(value: T) -> Self {
        Self {
            next_ticket: AtomicUsize::new(0),
            next_serving: AtomicUsize::new(0),
            value: UnsafeCell::new(value),
        }
    }

    /// Unwraps the inner value of this lock.
    pub fn into_inner(self) -> T {
        self.value.into_inner()
    }
}

impl<T: ?Sized + fmt::Debug> fmt::Debug for TicketMutex<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.try_lock() {
            Some(guard) => write!(f, "Mutex {{ data: ")
                .and_then(|()| (&*guard).fmt(f))
                .and_then(|()| write!(f, "}}")),
            None => write!(f, "Mutex {{ <locked> }}"),
        }
    }
}

impl<T: ?Sized> TicketMutex<T> {
    /// Spins the thread until the value can be locked.
    ///
    /// The returned value can be used to modify the value,
    /// and will be unlocked after dropping the return value.
    pub fn lock(&self) -> TicketMutexGuard<T> {
        let ticket = self.next_ticket.fetch_add(1, Ordering::Relaxed);

        while self.next_serving.load(Ordering::Acquire) != ticket {
            cpu_relax();
        }

        TicketMutexGuard {
            next_serving: &self.next_serving,
            ticket,
            // Safety
            // We know that we are the next ticket to be served,
            // so there's no other thread accessing the value.
            //
            // Every other thread has another ticket number so it's
            // definitely stuck in the spin loop above.
            value: unsafe { &mut *self.value.get() },
        }
    }

    /// Force unlock the ticket lock, by serving the next ticket.
    ///
    /// # Safety
    ///
    /// This is *extremely* unsafe if the lock is not held by the current
    /// thread. However, this can be useful in some instances for exposing the
    /// lock to FFI that doesn't know how to deal with RAII.
    pub unsafe fn force_unlock(&self) {
        self.next_serving.fetch_add(1, Ordering::Release);
    }

    /// Tries to lock this lock. If it's already locked, `None` is returned,
    /// otherwise a guard that protects the data is returned.
    pub fn try_lock(&self) -> Option<TicketMutexGuard<T>> {
        let ticket = self
            .next_ticket
            .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |ticket| {
                if self.next_serving.load(Ordering::Acquire) == ticket {
                    Some(ticket + 1)
                } else {
                    None
                }
            });

        ticket.ok().map(|ticket| TicketMutexGuard {
            next_serving: &self.next_serving,
            ticket,
            // Safety
            // We have a ticket that is equal to the next_serving ticket, so we know:
            // - that no other thread can have the same ticket id as this thread
            // - that we are the next one to be served so we have exclusive access to the value
            value: unsafe { &mut *self.value.get() },
        })
    }

    /// Returns a mutable reference to the underlying data.
    ///
    /// This will not lock this lock, since this method borrows
    /// this lock mutably so no locking is needed.
    pub fn get_mut(&mut self) -> &mut T {
        // Safety:
        // We know that there are no other references to `self`,
        // so it's safe to return a exclusive reference to the value.
        unsafe { &mut *self.value.get() }
    }
}

impl<T> From<T> for TicketMutex<T> {
    fn from(value: T) -> Self {
        Self::new(value)
    }
}

impl<T: ?Sized + Default> Default for TicketMutex<T> {
    fn default() -> TicketMutex<T> {
        TicketMutex::new(Default::default())
    }
}

impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for TicketMutexGuard<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}

impl<'a, T: ?Sized + fmt::Display> fmt::Display for TicketMutexGuard<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&**self, f)
    }
}

impl<'a, T: ?Sized> Deref for TicketMutexGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.value
    }
}

impl<'a, T: ?Sized> DerefMut for TicketMutexGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        self.value
    }
}

impl<'a, T: ?Sized> Drop for TicketMutexGuard<'a, T> {
    fn drop(&mut self) {
        let new_ticket = self.ticket + 1;
        self.next_serving.store(new_ticket, Ordering::Release);
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::thread;

    use super::*;

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    #[test]
    fn smoke() {
        let m = TicketMutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        static M: TicketMutex<()> = TicketMutex::new(());
        static mut CNT: u32 = 0;
        const J: u32 = 1000;
        const K: u32 = 3;

        fn inc() {
            for _ in 0..J {
                unsafe {
                    let _g = M.lock();
                    CNT += 1;
                }
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(unsafe { CNT }, J * K * 2);
    }

    #[test]
    fn try_lock() {
        let mutex = TicketMutex::new(42);

        // First lock succeeds
        let a = mutex.try_lock();
        assert_eq!(a.as_ref().map(|r| **r), Some(42));

        // Additional lock failes
        let b = mutex.try_lock();
        assert!(b.is_none());

        // After dropping lock, it succeeds again
        ::core::mem::drop(a);
        let c = mutex.try_lock();
        assert_eq!(c.as_ref().map(|r| **r), Some(42));
    }

    #[test]
    fn test_into_inner() {
        let m = TicketMutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = TicketMutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(TicketMutex::new(1));
        let arc2 = Arc::new(TicketMutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(TicketMutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || -> () {
            struct Unwinder {
                i: Arc<TicketMutex<i32>>,
            }
            impl Drop for Unwinder {
                fn drop(&mut self) {
                    *self.i.lock() += 1;
                }
            }
            let _u = Unwinder { i: arc2 };
            panic!();
        })
        .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &TicketMutex<[i32]> = &TicketMutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }
}
<|endoftext|>


<commit_before>//use std::collections::HashMap;
use std::ffi::{CString};
use libc::{c_int};

use super::ffi;

use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement};
use types::{ToSql};

impl SqliteConnection {
}

impl<'conn> SqliteStatement<'conn> {
    /*pub fn parameter_names(&self) -> HashMap<String, i32> {
        let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) };
        let mut index_by_name = HashMap::with_capacity(n as usize);
        for i in 1..n+1 {
            let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) };
            if !c_name.is_null() {
                let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() };
                index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n);
            }
        }
        index_by_name
    }*/

    /// Return the index of an SQL parameter given its name.
    /// Return None if `name` is invalid (NulError) or if no matching parameter is found.
    pub fn parameter_index(&self, name: &str) -> Option<i32> {
        unsafe {
            CString::new(name).ok().and_then(|c_name|
                match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) {
                    0 => None, // A zero is returned if no matching parameter is found.
                    n => Some(n)
                }
            )

        }
    }

    /// Execute the prepared statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    pub fn named_execute(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        unsafe {
            for &(name, value) in params {
                let i = try!(self.parameter_index(name).ok_or(SqliteError{
                    code: ffi::SQLITE_MISUSE,
                    message: format!("Invalid parameter name: {}", name)
                }));
                try!(self.conn.decode_result(value.bind_parameter(self.stmt, i)));
            }
            self.execute_()
        }
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;

   #[test]
    fn test_named_execute() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)").unwrap();
        stmt.named_execute(&[(":name", &"one")]).unwrap();
    }
}<commit_msg>Add SqliteStatement.named_query.<commit_after>//use std::collections::HashMap;
use std::ffi::{CString};
use libc::{c_int};

use super::ffi;

use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows};
use types::{ToSql};

impl SqliteConnection {
}

impl<'conn> SqliteStatement<'conn> {
    /*pub fn parameter_names(&self) -> HashMap<String, i32> {
        let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) };
        let mut index_by_name = HashMap::with_capacity(n as usize);
        for i in 1..n+1 {
            let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) };
            if !c_name.is_null() {
                let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() };
                index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n);
            }
        }
        index_by_name
    }*/

    /// Return the index of an SQL parameter given its name.
    /// Return None if `name` is invalid (NulError) or if no matching parameter is found.
    pub fn parameter_index(&self, name: &str) -> Option<i32> {
        unsafe {
            CString::new(name).ok().and_then(|c_name|
                match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) {
                    0 => None, // A zero is returned if no matching parameter is found.
                    n => Some(n)
                }
            )

        }
    }

    /// Execute the prepared statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    pub fn named_execute(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        unsafe {
            try!(self.bind_named_parameters(params));
            self.execute_()
        }
    }

    /// Execute the prepared statement with named parameter(s), returning an iterator over the resulting rows.
    pub fn named_query<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> SqliteResult<SqliteRows<'a>> {
        self.reset_if_needed();

        unsafe {
            try!(self.bind_named_parameters(params));
        }

        self.needs_reset = true;
        Ok(SqliteRows::new(self))
    }

    unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> {
        for &(name, value) in params {
            let i = try!(self.parameter_index(name).ok_or(SqliteError{
                code: ffi::SQLITE_MISUSE,
                message: format!("Invalid parameter name: {}", name)
            }));
            try!(self.conn.decode_result(value.bind_parameter(self.stmt, i)));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;

   #[test]
    fn test_named_execute() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)").unwrap();
        stmt.named_execute(&[(":name", &"one")]).unwrap();
    }

   #[test]
    fn test_named_query() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap();
        stmt.named_query(&[(":name", &"one")]).unwrap();
    }
}<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::rc::Rc;

use var::{js_str_key, JsVarValue};

use jsrs_common::backend::Backend;
use jsrs_common::types::coerce::{AsNumber, AsString};
use jsrs_common::types::js_str::JsStrStruct;
use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};

fn var_type_as_number(var: &JsVar, ptr: Option<&JsPtrEnum>) -> f64 {
    match ptr {
        Some(ref ptr) => ptr.as_number(),
        None => var.as_number()
    }
}

pub fn array_push(state: Rc<RefCell<Backend>>, this: Option<JsPtrEnum>, args: Vec<(JsVar, Option<JsPtrEnum>)>) -> (JsVar, Option<JsPtrEnum>) {
    // TODO: Change panics to actual errors
    let mut this_obj = match this.clone() {
        Some(JsPtrEnum::JsObj(obj)) => obj,
        Some(_) => panic!("Trying to push onto array, but `this` is not an object"),
        None => panic!("Trying to push onto array, but `this` is None")
    };

    let length_var = match this_obj.dict.get(&js_str_key("length")) {
        Some(js_var) => js_var.binding.clone(),
        None => panic!("No length field on array"),
    };

    println!("length_var: {:#?}", length_var);

    let mut length_ptr = match state.borrow_mut().load(&length_var) {
        Ok((_, Some(JsPtrEnum::NativeVar(native_var)))) => native_var,
        Ok((_, Some(_))) => panic!("Array length pointer is not a native variable"),
        Ok((_, None)) => panic!("No pointer for array length"),
        Err(e) => panic!("{}", e),
    };

    let length = match length_ptr.get(state.clone(), this.clone()).0.t {
        JsType::JsNum(f) => f,
        _ => panic!("Array length value is not a number"),
    };

    let new_length = JsVar::new(JsType::JsNum(length + args.len() as f64));
    length_ptr.set(state.clone(), this, new_length.clone(), None);
    let state_ref = state.borrow_mut();
    let alloc_box = state_ref.get_alloc_box();

    for (i, (var, ptr)) in args.into_iter().enumerate() {
        let key = JsKey::JsStr(JsStrStruct::new(&JsType::JsNum(length + i as f64).as_string()));
        this_obj.add_key(key, var, ptr, &mut *(alloc_box.borrow_mut()));
    }

    (new_length, None)
}

pub fn array_length_setter(state: Rc<RefCell<Backend>>, old_var: JsVar, old_ptr: Option<JsPtrEnum>, this: Option<JsPtrEnum>,
               new_var: JsVar, new_ptr: Option<JsPtrEnum>) -> JsVarValue {
    let new_len = var_type_as_number(&new_var, new_ptr.as_ref());
    let old_len = var_type_as_number(&old_var, old_ptr.as_ref());

    if !(new_len.is_normal() || new_len == 0.0) || new_len.is_sign_negative() || new_len != new_len.trunc() {
        // TODO: Return a `RangeError` instead of panicking.
        panic!("Invalid array length:\n var: {:#?}\nptr: {:#?}", new_var, new_ptr);
    }

    let mut this_obj = match this {
        Some(JsPtrEnum::JsObj(obj)) => obj,
        Some(_) => panic!("Trying to set array length, but `this` is not an object"),
        None => panic!("Trying to set array length, but `this` is None")
    };

    let new_len_int = new_len as i32;
    let old_len_int = old_len as i32;

    let state_ref = state.borrow_mut();

    if new_len > old_len {
        for i in old_len_int..new_len_int {
            let key = JsKey::JsStr(JsStrStruct::new(&JsType::JsNum(i as f64).as_string()));
            let alloc_box = state_ref.get_alloc_box();
            this_obj.add_key(key, JsVar::new(JsType::JsUndef), None, &mut *(alloc_box.borrow_mut()));
        }
    }

    if new_len < old_len {
        for i in new_len_int..old_len_int {
            let key = js_str_key(&JsType::JsNum(i as f64).as_string());
            let alloc_box = state_ref.get_alloc_box();
            println!("key: {:#?}", key);
            this_obj.remove_key(&key, &mut *(alloc_box.borrow_mut()));
        }
    }

    (JsVar::new(JsType::JsNum(new_len)), None)
}
<commit_msg>partial fix for length setter issue<commit_after>use std::cell::RefCell;
use std::rc::Rc;

use var::{js_str_key, JsVarValue};

use jsrs_common::backend::Backend;
use jsrs_common::types::coerce::{AsNumber, AsString};
use jsrs_common::types::js_str::JsStrStruct;
use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};

fn var_type_as_number(var: &JsVar, ptr: Option<&JsPtrEnum>) -> f64 {
    match ptr {
        Some(ref ptr) => ptr.as_number(),
        None => var.as_number()
    }
}

pub fn array_push(state: Rc<RefCell<Backend>>, this: Option<JsPtrEnum>, args: Vec<(JsVar, Option<JsPtrEnum>)>) -> (JsVar, Option<JsPtrEnum>) {
    // TODO: Change panics to actual errors
    let mut this_obj = match this.clone() {
        Some(JsPtrEnum::JsObj(obj)) => obj,
        Some(_) => panic!("Trying to push onto array, but `this` is not an object"),
        None => panic!("Trying to push onto array, but `this` is None")
    };

    let mut length_ptr = match this_obj.dict.get(&js_str_key("length")) {
        Some(js_var) => {
            let state_ref = state.borrow_mut();
            let alloc_box = state_ref.get_alloc_box();
            let alloc_ref = alloc_box.borrow_mut();
            match alloc_ref.find_id(&js_var.unique).map(|p| p.borrow().clone()) {
                Some(JsPtrEnum::NativeVar(native_var)) => native_var,
                Some(_) => panic!("Array length pointer is not a native variable"),
                None => panic!("No pointer for array length"),
            }
        }
        None => panic!("No length field on array"),
    };

    let length = match length_ptr.get(state.clone(), this.clone()).0.t {
        JsType::JsNum(f) => f,
        _ => panic!("Array length value is not a number"),
    };

    let new_length = JsVar::new(JsType::JsNum(length + args.len() as f64));
    length_ptr.set(state.clone(), this, new_length.clone(), None);
    let state_ref = state.borrow_mut();
    let alloc_box = state_ref.get_alloc_box();

    for (i, (var, ptr)) in args.into_iter().enumerate() {
        let key = JsKey::JsStr(JsStrStruct::new(&JsType::JsNum(length + i as f64).as_string()));
        this_obj.add_key(key, var, ptr, &mut *(alloc_box.borrow_mut()));
    }

    (new_length, None)
}

pub fn array_length_setter(state: Rc<RefCell<Backend>>, old_var: JsVar, old_ptr: Option<JsPtrEnum>, this: Option<JsPtrEnum>,
               new_var: JsVar, new_ptr: Option<JsPtrEnum>) -> JsVarValue {
    let new_len = var_type_as_number(&new_var, new_ptr.as_ref());
    let old_len = var_type_as_number(&old_var, old_ptr.as_ref());

    if !(new_len.is_normal() || new_len == 0.0) || new_len.is_sign_negative() || new_len != new_len.trunc() {
        // TODO: Return a `RangeError` instead of panicking.
        panic!("Invalid array length:\n var: {:#?}\nptr: {:#?}", new_var, new_ptr);
    }

    let mut this_obj = match this {
        Some(JsPtrEnum::JsObj(obj)) => obj,
        Some(_) => panic!("Trying to set array length, but `this` is not an object"),
        None => panic!("Trying to set array length, but `this` is None")
    };

    let new_len_int = new_len as i32;
    let old_len_int = old_len as i32;

    let state_ref = state.borrow_mut();

    if new_len > old_len {
        for i in old_len_int..new_len_int {
            let key = JsKey::JsStr(JsStrStruct::new(&JsType::JsNum(i as f64).as_string()));
            let alloc_box = state_ref.get_alloc_box();
            this_obj.add_key(key, JsVar::new(JsType::JsUndef), None, &mut *(alloc_box.borrow_mut()));
        }
    }

    if new_len < old_len {
        for i in new_len_int..old_len_int {
            let key = js_str_key(&JsType::JsNum(i as f64).as_string());
            let alloc_box = state_ref.get_alloc_box();
            println!("key: {:#?}", key);
            this_obj.remove_key(&key, &mut *(alloc_box.borrow_mut()));
        }
    }

    (JsVar::new(JsType::JsNum(new_len)), None)
}
<|endoftext|>


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

//! An implementation of Ken Perlin's [Improved Noise]
//! (http://mrl.nyu.edu/~perlin/noise/) algorithm.

use std::num::{cast, mul_add};
use std::rand::Rng;

pub trait Perlin<T> {
    fn perlin(&self, ctx: &PerlinContext) -> T;
}

// impl<T: Float> Perlin<T> for T {
//     fn perlin(&self, ctx: &PerlinContext) -> T {
//         ctx.gen1(self)
//     }
// }

impl<T: Float> Perlin<T> for (T,) {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            (ref x,) => ctx.gen1(x)
        }
    }
}

impl<T: Float> Perlin<T> for (T, T) {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            (ref x, ref y) => ctx.gen2(x, y)
        }
    }
}

impl<T: Float> Perlin<T> for (T, T, T) {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            (ref x, ref y, ref z) => ctx.gen3(x, y, z)
        }
    }
}

impl<T: Float> Perlin<T> for [T, ..1] {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            [ref x] => ctx.gen1(x),
        }
    }
}

impl<T: Float> Perlin<T> for [T, ..2] {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            [ref x, ref y] => ctx.gen2(x, y),
        }
    }
}

impl<T: Float> Perlin<T> for [T, ..3] {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            [ref x, ref y, ref z] => ctx.gen3(x, y, z),
        }
    }
}

/// A perlin noise generator
pub struct PerlinContext {
    // permutation table
    priv ptable: ~[u8]//[uint, ..512],
}

impl PerlinContext {
    pub fn new<R: Rng>(rng: &mut R) -> PerlinContext {
        PerlinContext { ptable: rng.gen_vec::<u8>(512) }
    }

    pub fn gen1<T:Float>(&self, x: &T) -> T {
        let X = x.floor().to_uint() as u8;
        let x_ = *x - x.floor();
        let u = fade(x_.clone());

        let A  = self.ptable[X    ];
        let AA = self.ptable[A    ];
        let B  = self.ptable[X + 1];
        let BA = self.ptable[B    ];

        lerp(u.clone(), grad(self.ptable[AA], x_.clone(), cast(0), cast(0)),
                        grad(self.ptable[BA], x_-cast(1), cast(0), cast(0)))
    }

    pub fn gen2<T:Float>(&self, x: &T, y: &T) -> T {
        let X = x.floor().to_uint() as u8;
        let Y = y.floor().to_uint() as u8;

        let x_ = *x - x.floor();
        let y_ = *y - y.floor();

        let u = fade(x_.clone());
        let v = fade(y_.clone());

        let A  = self.ptable[X    ] + Y;
        let AA = self.ptable[A    ];
        let AB = self.ptable[A + 1];
        let B  = self.ptable[X + 1] + Y;
        let BA = self.ptable[B    ];
        let BB = self.ptable[B + 1];

        lerp(v.clone(), lerp(u.clone(), grad(self.ptable[AA], x_.clone(), y_.clone(), cast(0)),
                                        grad(self.ptable[BA], x_-cast(1), y_.clone(), cast(0))),
                        lerp(u.clone(), grad(self.ptable[AB], x_.clone(), y_-cast(1), cast(0)),
                                        grad(self.ptable[BB], x_-cast(1), y_-cast(1), cast(0))))
    }

    pub fn gen3<T:Float>(&self, x: &T, y: &T, z: &T) -> T {
        // Find the unit cube that contains the point
        let X = x.floor().to_uint() as u8;
        let Y = y.floor().to_uint() as u8;
        let Z = z.floor().to_uint() as u8;

        // Find the relative X, Y, Z of point in the cube
        let x_ = *x - x.floor();
        let y_ = *y - y.floor();
        let z_ = *z - z.floor();

        // Compute the fade curves for X, Y, Z
        let u = fade(x_.clone());
        let v = fade(y_.clone());
        let w = fade(z_.clone());

        // Hash coordinates of the 8 cube corners
        let A  = self.ptable[X    ] + Y;
        let AA = self.ptable[A    ] + Z;
        let AB = self.ptable[A + 1] + Z;
        let B  = self.ptable[X + 1] + Y;
        let BA = self.ptable[B    ] + Z;
        let BB = self.ptable[B + 1] + Z;

        // Add the blended results from the 8 corners of the cube
        lerp(w, lerp(v.clone(), lerp(u.clone(), grad(self.ptable[AA    ], x_.clone(), y_.clone(), z_.clone()),
                                                grad(self.ptable[BA    ], x_-cast(1), y_.clone(), z_.clone())),
                                lerp(u.clone(), grad(self.ptable[AB    ], x_.clone(), y_-cast(1), z_.clone()),
                                                grad(self.ptable[BB    ], x_-cast(1), y_-cast(1), z_.clone()))),
                lerp(v.clone(), lerp(u.clone(), grad(self.ptable[AA + 1], x_.clone(), y_.clone(), z_-cast(1)),
                                                grad(self.ptable[BA + 1], x_-cast(1), y_.clone(), z_-cast(1))),
                                lerp(u.clone(), grad(self.ptable[AB + 1], x_.clone(), y_-cast(1), z_-cast(1)),
                                                grad(self.ptable[BB + 1], x_-cast(1), y_-cast(1), z_-cast(1)))))
    }
}

#[inline]
fn fade<T: Float>(t: T) -> T {
    t * t * t * (t * (t * cast(6) - cast(15)) + cast(10))
}

#[inline]
fn lerp<T: Float>(t: T, a: T, b: T) -> T {
    mul_add(t.clone(), b, mul_add(t, -a, a))
}

fn grad<T: Float>(hash: u8, x: T, y: T, z: T) -> T {
    let h = hash & 15;

    let u = match h {
        0..7 => x.clone(),
        _    => y.clone(),
    };
    let v = match h {
        0..3    => y.clone(),
        12 | 14 => x.clone(),
        _       => z.clone(),
    };

    (if (h & 1) == 0 { u } else { -u }) +
    (if (h & 2) == 0 { v } else { -v })
}

<commit_msg>Reduce the number of operations performed in lerp<commit_after>// Copyright 2013 The noise-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An implementation of Ken Perlin's [Improved Noise]
//! (http://mrl.nyu.edu/~perlin/noise/) algorithm.

use std::num::{cast, mul_add};
use std::rand::Rng;

pub trait Perlin<T> {
    fn perlin(&self, ctx: &PerlinContext) -> T;
}

// impl<T: Float> Perlin<T> for T {
//     fn perlin(&self, ctx: &PerlinContext) -> T {
//         ctx.gen1(self)
//     }
// }

impl<T: Float> Perlin<T> for (T,) {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            (ref x,) => ctx.gen1(x)
        }
    }
}

impl<T: Float> Perlin<T> for (T, T) {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            (ref x, ref y) => ctx.gen2(x, y)
        }
    }
}

impl<T: Float> Perlin<T> for (T, T, T) {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            (ref x, ref y, ref z) => ctx.gen3(x, y, z)
        }
    }
}

impl<T: Float> Perlin<T> for [T, ..1] {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            [ref x] => ctx.gen1(x),
        }
    }
}

impl<T: Float> Perlin<T> for [T, ..2] {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            [ref x, ref y] => ctx.gen2(x, y),
        }
    }
}

impl<T: Float> Perlin<T> for [T, ..3] {
    fn perlin(&self, ctx: &PerlinContext) -> T {
        match *self {
            [ref x, ref y, ref z] => ctx.gen3(x, y, z),
        }
    }
}

/// A perlin noise generator
pub struct PerlinContext {
    // permutation table
    priv ptable: ~[u8]//[uint, ..512],
}

impl PerlinContext {
    pub fn new<R: Rng>(rng: &mut R) -> PerlinContext {
        PerlinContext { ptable: rng.gen_vec::<u8>(512) }
    }

    pub fn gen1<T:Float>(&self, x: &T) -> T {
        let X = x.floor().to_uint() as u8;
        let x_ = *x - x.floor();
        let u = fade(x_.clone());

        let A  = self.ptable[X    ];
        let AA = self.ptable[A    ];
        let B  = self.ptable[X + 1];
        let BA = self.ptable[B    ];

        lerp(u.clone(), grad(self.ptable[AA], x_.clone(), cast(0), cast(0)),
                        grad(self.ptable[BA], x_-cast(1), cast(0), cast(0)))
    }

    pub fn gen2<T:Float>(&self, x: &T, y: &T) -> T {
        let X = x.floor().to_uint() as u8;
        let Y = y.floor().to_uint() as u8;

        let x_ = *x - x.floor();
        let y_ = *y - y.floor();

        let u = fade(x_.clone());
        let v = fade(y_.clone());

        let A  = self.ptable[X    ] + Y;
        let AA = self.ptable[A    ];
        let AB = self.ptable[A + 1];
        let B  = self.ptable[X + 1] + Y;
        let BA = self.ptable[B    ];
        let BB = self.ptable[B + 1];

        lerp(v.clone(), lerp(u.clone(), grad(self.ptable[AA], x_.clone(), y_.clone(), cast(0)),
                                        grad(self.ptable[BA], x_-cast(1), y_.clone(), cast(0))),
                        lerp(u.clone(), grad(self.ptable[AB], x_.clone(), y_-cast(1), cast(0)),
                                        grad(self.ptable[BB], x_-cast(1), y_-cast(1), cast(0))))
    }

    pub fn gen3<T:Float>(&self, x: &T, y: &T, z: &T) -> T {
        // Find the unit cube that contains the point
        let X = x.floor().to_uint() as u8;
        let Y = y.floor().to_uint() as u8;
        let Z = z.floor().to_uint() as u8;

        // Find the relative X, Y, Z of point in the cube
        let x_ = *x - x.floor();
        let y_ = *y - y.floor();
        let z_ = *z - z.floor();

        // Compute the fade curves for X, Y, Z
        let u = fade(x_.clone());
        let v = fade(y_.clone());
        let w = fade(z_.clone());

        // Hash coordinates of the 8 cube corners
        let A  = self.ptable[X    ] + Y;
        let AA = self.ptable[A    ] + Z;
        let AB = self.ptable[A + 1] + Z;
        let B  = self.ptable[X + 1] + Y;
        let BA = self.ptable[B    ] + Z;
        let BB = self.ptable[B + 1] + Z;

        // Add the blended results from the 8 corners of the cube
        lerp(w, lerp(v.clone(), lerp(u.clone(), grad(self.ptable[AA    ], x_.clone(), y_.clone(), z_.clone()),
                                                grad(self.ptable[BA    ], x_-cast(1), y_.clone(), z_.clone())),
                                lerp(u.clone(), grad(self.ptable[AB    ], x_.clone(), y_-cast(1), z_.clone()),
                                                grad(self.ptable[BB    ], x_-cast(1), y_-cast(1), z_.clone()))),
                lerp(v.clone(), lerp(u.clone(), grad(self.ptable[AA + 1], x_.clone(), y_.clone(), z_-cast(1)),
                                                grad(self.ptable[BA + 1], x_-cast(1), y_.clone(), z_-cast(1))),
                                lerp(u.clone(), grad(self.ptable[AB + 1], x_.clone(), y_-cast(1), z_-cast(1)),
                                                grad(self.ptable[BB + 1], x_-cast(1), y_-cast(1), z_-cast(1)))))
    }
}

#[inline]
fn fade<T: Float>(t: T) -> T {
    t * t * t * (t * (t * cast(6) - cast(15)) + cast(10))
}

#[inline]
fn lerp<T: Float>(t: T, a: T, b: T) -> T {
    mul_add(t, b - a, a)
}

fn grad<T: Float>(hash: u8, x: T, y: T, z: T) -> T {
    let h = hash & 15;

    let u = match h {
        0..7 => x.clone(),
        _    => y.clone(),
    };
    let v = match h {
        0..3    => y.clone(),
        12 | 14 => x.clone(),
        _       => z.clone(),
    };

    (if (h & 1) == 0 { u } else { -u }) +
    (if (h & 2) == 0 { v } else { -v })
}

<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use super::parser::{FileHeaderParser, Parser, ParserError};
use storage::file_id::*;

use std::fs::File as FSFile;

#[derive(Debug)]
pub enum FileHeaderSpec {
    Null,
    Bool,
    Integer,
    UInteger,
    Float,
    Text,
    Key { name: &'static str, value_type: Box<FileHeaderSpec> },
    Map { keys: Vec<FileHeaderSpec> },
    Array { allowed_types: Vec<FileHeaderSpec> },
}

#[derive(Debug)]
#[derive(Clone)]
pub enum FileHeaderData {
    Null,
    Bool(bool),
    Integer(i64),
    UInteger(u64),
    Float(f64),
    Text(String),
    Key { name: String, value: Box<FileHeaderData> },
    Map { keys: Vec<FileHeaderData> },
    Array { values: Box<Vec<FileHeaderData>> },
}

impl Display for FileHeaderSpec {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match self {
            &FileHeaderSpec::Null       => write!(fmt, "NULL"),
            &FileHeaderSpec::Bool       => write!(fmt, "Bool"),
            &FileHeaderSpec::Integer    => write!(fmt, "Integer"),
            &FileHeaderSpec::UInteger   => write!(fmt, "UInteger"),
            &FileHeaderSpec::Float      => write!(fmt, "Float"),
            &FileHeaderSpec::Text       => write!(fmt, "Text"),
            &FileHeaderSpec::Key{name: ref n, value_type: ref vt} => {
                write!(fmt, "Key({:?}) -> {:?}", n, vt)
            }
            &FileHeaderSpec::Map{keys: ref ks} => {
                write!(fmt, "Map -> {:?}", ks)
            }
            &FileHeaderSpec::Array{allowed_types: ref at}  => {
                write!(fmt, "Array({:?})", at)
            }
        }
    }

}

pub struct MatchError<'a> {
    summary: String,
    expected: &'a FileHeaderSpec,
    found: &'a FileHeaderData
}

impl<'a> MatchError<'a> {

    pub fn new(s: String,
               ex: &'a FileHeaderSpec,
               found: &'a FileHeaderData) -> MatchError<'a> {
        MatchError {
            summary: s,
            expected: ex,
            found: found,
        }
    }

    pub fn format(&self) -> String {
        format!("MatchError: {:?}\nExpected: {:?}\nFound: {:?}\n",
               self.summary, self.expected, self.found)
    }
}

impl<'a> Error for MatchError<'a> {

    fn description(&self) -> &str {
        &self.summary[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl<'a> Debug for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

impl<'a> Display for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

pub fn match_header_spec<'a>(spec: &'a FileHeaderSpec, data: &'a FileHeaderData)
    -> Option<MatchError<'a>>
{
    match (spec, data) {
        (&FileHeaderSpec::Null,     &FileHeaderData::Null)           => { }
        (&FileHeaderSpec::Bool,     &FileHeaderData::Bool(_))        => { }
        (&FileHeaderSpec::Integer,  &FileHeaderData::Integer(_))     => { }
        (&FileHeaderSpec::UInteger, &FileHeaderData::UInteger(_))    => { }
        (&FileHeaderSpec::Float,    &FileHeaderData::Float(_))       => { }
        (&FileHeaderSpec::Text,     &FileHeaderData::Text(_))        => { }

        (
            &FileHeaderSpec::Key{name: ref kname, value_type: ref vtype},
            &FileHeaderData::Key{name: ref n, value: ref val}
        ) => {
            if kname != n {
                unimplemented!();
            }
            return match_header_spec(&*vtype, &*val);
        }

        (
            &FileHeaderSpec::Map{keys: ref sks},
            &FileHeaderData::Map{keys: ref dks}
        ) => {
            for (s, d) in sks.iter().zip(dks.iter()) {
                let res = match_header_spec(s, d);
                if res.is_some() {
                    return res;
                }
            }
        }

        (
            &FileHeaderSpec::Array{allowed_types: ref vtypes},
            &FileHeaderData::Array{values: ref vs}
        ) => {
            for (t, v) in vtypes.iter().zip(vs.iter()) {
                let res = match_header_spec(t, v);
                if res.is_some() {
                    return res;
                }
            }
        }

        (k, v) => {
            return Some(MatchError::new(String::from("Expected type does not match found type"),
                                 k, v
                                 ))
        }
    }
    None
}

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    header  : FileHeaderData,
    data    : String,
    id      : FileID,
}

impl File {

    pub fn new() -> File {
        File {
            header: FileHeaderData::Null,
            data: String::from(""),
            id: File::get_new_file_id(),
        }
    }

    pub fn from_parser_result(id: FileID, header: FileHeaderData, data: String) -> File {
        File {
            header: header,
            data: data,
            id: id,
        }
    }

    pub fn new_with_header(h: FileHeaderData) -> File {
        File {
            header: h,
            data: String::from(""),
            id: File::get_new_file_id(),
        }
    }

    pub fn new_with_data(d: String) -> File {
        File {
            header: FileHeaderData::Null,
            data: d,
            id: File::get_new_file_id(),
        }
    }

    pub fn new_with_content(h: FileHeaderData, d: String) -> File {
        File {
            header: h,
            data: d,
            id: File::get_new_file_id(),
        }
    }

    pub fn contents(&self) -> (FileHeaderData, String) {
        (self.header.clone(), self.data.clone())
    }

    pub fn id(&self) -> FileID {
        self.id.clone()
    }

    fn get_new_file_id() -> FileID {
        use uuid::Uuid;
        Uuid::new_v4().to_hyphenated_string()
    }
}

<commit_msg>Fix: unused import<commit_after>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use super::parser::FileHeaderParser;
use storage::file_id::*;

use std::fs::File as FSFile;

#[derive(Debug)]
pub enum FileHeaderSpec {
    Null,
    Bool,
    Integer,
    UInteger,
    Float,
    Text,
    Key { name: &'static str, value_type: Box<FileHeaderSpec> },
    Map { keys: Vec<FileHeaderSpec> },
    Array { allowed_types: Vec<FileHeaderSpec> },
}

#[derive(Debug)]
#[derive(Clone)]
pub enum FileHeaderData {
    Null,
    Bool(bool),
    Integer(i64),
    UInteger(u64),
    Float(f64),
    Text(String),
    Key { name: String, value: Box<FileHeaderData> },
    Map { keys: Vec<FileHeaderData> },
    Array { values: Box<Vec<FileHeaderData>> },
}

impl Display for FileHeaderSpec {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match self {
            &FileHeaderSpec::Null       => write!(fmt, "NULL"),
            &FileHeaderSpec::Bool       => write!(fmt, "Bool"),
            &FileHeaderSpec::Integer    => write!(fmt, "Integer"),
            &FileHeaderSpec::UInteger   => write!(fmt, "UInteger"),
            &FileHeaderSpec::Float      => write!(fmt, "Float"),
            &FileHeaderSpec::Text       => write!(fmt, "Text"),
            &FileHeaderSpec::Key{name: ref n, value_type: ref vt} => {
                write!(fmt, "Key({:?}) -> {:?}", n, vt)
            }
            &FileHeaderSpec::Map{keys: ref ks} => {
                write!(fmt, "Map -> {:?}", ks)
            }
            &FileHeaderSpec::Array{allowed_types: ref at}  => {
                write!(fmt, "Array({:?})", at)
            }
        }
    }

}

pub struct MatchError<'a> {
    summary: String,
    expected: &'a FileHeaderSpec,
    found: &'a FileHeaderData
}

impl<'a> MatchError<'a> {

    pub fn new(s: String,
               ex: &'a FileHeaderSpec,
               found: &'a FileHeaderData) -> MatchError<'a> {
        MatchError {
            summary: s,
            expected: ex,
            found: found,
        }
    }

    pub fn format(&self) -> String {
        format!("MatchError: {:?}\nExpected: {:?}\nFound: {:?}\n",
               self.summary, self.expected, self.found)
    }
}

impl<'a> Error for MatchError<'a> {

    fn description(&self) -> &str {
        &self.summary[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl<'a> Debug for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

impl<'a> Display for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

pub fn match_header_spec<'a>(spec: &'a FileHeaderSpec, data: &'a FileHeaderData)
    -> Option<MatchError<'a>>
{
    match (spec, data) {
        (&FileHeaderSpec::Null,     &FileHeaderData::Null)           => { }
        (&FileHeaderSpec::Bool,     &FileHeaderData::Bool(_))        => { }
        (&FileHeaderSpec::Integer,  &FileHeaderData::Integer(_))     => { }
        (&FileHeaderSpec::UInteger, &FileHeaderData::UInteger(_))    => { }
        (&FileHeaderSpec::Float,    &FileHeaderData::Float(_))       => { }
        (&FileHeaderSpec::Text,     &FileHeaderData::Text(_))        => { }

        (
            &FileHeaderSpec::Key{name: ref kname, value_type: ref vtype},
            &FileHeaderData::Key{name: ref n, value: ref val}
        ) => {
            if kname != n {
                unimplemented!();
            }
            return match_header_spec(&*vtype, &*val);
        }

        (
            &FileHeaderSpec::Map{keys: ref sks},
            &FileHeaderData::Map{keys: ref dks}
        ) => {
            for (s, d) in sks.iter().zip(dks.iter()) {
                let res = match_header_spec(s, d);
                if res.is_some() {
                    return res;
                }
            }
        }

        (
            &FileHeaderSpec::Array{allowed_types: ref vtypes},
            &FileHeaderData::Array{values: ref vs}
        ) => {
            for (t, v) in vtypes.iter().zip(vs.iter()) {
                let res = match_header_spec(t, v);
                if res.is_some() {
                    return res;
                }
            }
        }

        (k, v) => {
            return Some(MatchError::new(String::from("Expected type does not match found type"),
                                 k, v
                                 ))
        }
    }
    None
}

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    header  : FileHeaderData,
    data    : String,
    id      : FileID,
}

impl File {

    pub fn new() -> File {
        File {
            header: FileHeaderData::Null,
            data: String::from(""),
            id: File::get_new_file_id(),
        }
    }

    pub fn from_parser_result(id: FileID, header: FileHeaderData, data: String) -> File {
        File {
            header: header,
            data: data,
            id: id,
        }
    }

    pub fn new_with_header(h: FileHeaderData) -> File {
        File {
            header: h,
            data: String::from(""),
            id: File::get_new_file_id(),
        }
    }

    pub fn new_with_data(d: String) -> File {
        File {
            header: FileHeaderData::Null,
            data: d,
            id: File::get_new_file_id(),
        }
    }

    pub fn new_with_content(h: FileHeaderData, d: String) -> File {
        File {
            header: h,
            data: d,
            id: File::get_new_file_id(),
        }
    }

    pub fn contents(&self) -> (FileHeaderData, String) {
        (self.header.clone(), self.data.clone())
    }

    pub fn id(&self) -> FileID {
        self.id.clone()
    }

    fn get_new_file_id() -> FileID {
        use uuid::Uuid;
        Uuid::new_v4().to_hyphenated_string()
    }
}

<|endoftext|>


<commit_before>use super::location::{Location, Span};

#[derive(Clone)]
struct StringStream {
    input: Vec<char>,
    index: usize,
    line: i32,
    column: i32,
}

impl StringStream {
    fn new(input: &str) -> Self {
        Self {
            input: input.chars().collect(),
            index: 0,
            line: 1,
            column: 1,
        }
    }

    fn read(&mut self) -> char {
        let character = self
            .input
            .get(self.index)
            .map(|value| *value)
            .unwrap_or('\0');
        self.index += 1;
        if character == '\n' {
            self.line += 1;
            self.column = 1;
        } else {
            self.column += 1;
        }
        character
    }

    fn peek(&self) -> char {
        self.input
            .get(self.index)
            .map(|value| *value)
            .unwrap_or('\0')
    }

    fn read_until(&mut self, target: char) {
        while self.peek() != target {
            let character = self.read();
            assert!(character != '\0');
        }
    }

    fn read_if_match(&mut self, target: &[char]) -> bool {
        // Bounds-check first!
        if (self.index + target.len()) > self.input.len() {
            return false;
        }
        let substr = &self.input[self.index..(self.index + target.len())];
        if substr != target {
            return false;
        }
        // Consume if they match.
        for _ in target {
            self.read();
        }
        true
    }

    fn location(&self) -> Location {
        Location::new(self.index as u32, self.line, self.column)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Token {
    Arrow,
    BraceLeft,
    BraceRight,
    Comma,
    Dot(Location),
    EOF,
    Equals,
    Export,
    Identifier(String, Span),
    Import(Location),
    Integer(i64),
    LeftAngle,
    Let(Location),
    Minus,
    ParenthesesLeft,
    ParenthesesRight,
    Plus,
    Return,
    Star,
    String(String, Span),
    Terminal(char),
    Var(Location),
    While(Location),
}

impl Token {
    pub fn location(&self) -> Option<Location> {
        match self {
            Token::Let(location) | Token::Var(location) => Some(location.clone()),
            Token::Identifier(_, location) => Some(location.start.clone()),
            _ => None,
        }
    }
}

impl Token {
    pub fn newline(self) -> bool {
        match self {
            Token::Terminal(character) => character == '\n',
            _ => false,
        }
    }
}

#[derive(Clone)]
pub struct TokenStream {
    input: StringStream,
    peeking: bool,
    next_token: Option<Token>,
}

impl TokenStream {
    pub fn from_string(input: String) -> Self {
        Self::new(StringStream::new(input.as_str()))
    }

    fn new(input: StringStream) -> Self {
        TokenStream {
            input: input,
            peeking: false,
            next_token: None,
        }
    }

    pub fn backtrack(&mut self, other: &TokenStream) {
        self.input = other.input.clone();
        self.peeking = other.peeking;
        self.next_token = other.next_token.clone();
    }

    pub fn peek(&mut self) -> Token {
        if !self.peeking {
            self.peeking = true;
            self.next_token = Some(self.lex());
        }
        self.next_token.clone().unwrap()
    }

    /// Peeks, and if the peeked token is the same type as the expected then
    /// read that type and return the read (not expected) token.
    pub fn read_if(&mut self, expected: Token) -> Option<Token> {
        use std::mem::discriminant;

        let got = self.peek();
        if discriminant(&expected) == discriminant(&got) {
            Some(self.read())
        } else {
            None
        }
    }

    pub fn read(&mut self) -> Token {
        let token = self.peek();
        self.peeking = false;
        token
    }

    fn lex(&mut self) -> Token {
        self.consume_space_and_comments();

        loop {
            let location = self.input.location();
            let character = self.input.peek();

            if identifier_head(character) {
                return self.lex_identifier(location);
            } else if numeric_head(character) {
                return self.lex_arrow_minus_or_numeric();
            } else if string_head(character) {
                return self.lex_string(location);
            } else {
                self.input.read();
                match character {
                    '\0' => return Token::EOF,
                    '{' => return Token::BraceLeft,
                    '}' => return Token::BraceRight,
                    '(' => return Token::ParenthesesLeft,
                    ')' => return Token::ParenthesesRight,
                    ',' => return Token::Comma,
                    '.' => return Token::Dot(location),
                    '=' => return Token::Equals,
                    '<' => return Token::LeftAngle,
                    '+' => return Token::Plus,
                    '*' => return Token::Star,
                    ';' => return Token::Terminal(character),
                    '\n' => {
                        self.consume_more_newline_terminals();
                        return Token::Terminal(character);
                    }
                    _ => panic!("Unexpected character: {}", character),
                }
            }
        }
    }

    fn lex_identifier(&mut self, start: Location) -> Token {
        let mut identifier = vec![self.input.read()];
        loop {
            let character = self.input.peek();
            if identifier_tail(character) {
                self.input.read();
                identifier.push(character);
            } else {
                break;
            }
        }
        let identifier_string: String = identifier.into_iter().collect();
        match identifier_string.as_str() {
            "export" => Token::Export,
            "let" => Token::Let(start),
            "import" => Token::Import(start),
            "return" => Token::Return,
            "var" => Token::Var(start),
            "while" => Token::While(start),
            _ => Token::Identifier(
                identifier_string,
                Span::new(start.clone(), self.input.location()),
            ),
        }
    }

    fn lex_string(&mut self, start: Location) -> Token {
        let opening = self.input.read();
        assert_eq!(opening, '"');
        let mut characters = vec![];
        let mut end;
        loop {
            let character = self.input.read();
            end = self.input.location();
            if character != '"' {
                characters.push(character);
            } else {
                break;
            }
        }
        let string: String = characters.into_iter().collect();
        Token::String(string, Span::new(start, end))
    }

    fn lex_arrow_minus_or_numeric(&mut self) -> Token {
        let first_character = self.input.read();
        if first_character == '-' && self.input.peek() == '>' {
            self.input.read();
            return Token::Arrow;
        }
        if first_character == '-' && !digit(self.input.peek()) {
            return Token::Minus;
        }
        let mut number = vec![first_character];
        loop {
            let character = self.input.peek();
            if digit(character) {
                self.input.read();
                number.push(character);
            } else {
                break;
            }
        }
        let number_string: String = number.into_iter().collect();
        Token::Integer(number_string.parse().unwrap())
    }

    fn consume_more_newline_terminals(&mut self) {
        loop {
            self.consume_space_and_comments();
            if self.input.peek() == '\n' {
                self.input.read();
                continue;
            } else {
                break;
            }
        }
    }

    fn consume_space_and_comments(&mut self) {
        loop {
            let character = self.input.peek();
            if character == ' ' || character == '\t' {
                self.input.read();
            } else if self.input.read_if_match(&['/', '/']) {
                self.input.read_until('\n');
            } else if self.input.read_if_match(&['/', '*']) {
                loop {
                    if self.input.read_if_match(&['*', '/']) {
                        break;
                    } else {
                        self.input.read();
                    }
                }
            } else {
                // Not any kind of comment.
                break;
            }
        }
    }
}

fn identifier_head(character: char) -> bool {
    alphabetical(character)
}

fn identifier_tail(character: char) -> bool {
    alphabetical(character)
}

fn numeric_head(character: char) -> bool {
    digit(character) || (character == '-')
}

fn string_head(character: char) -> bool {
    character == '"'
}

fn alphabetical(character: char) -> bool {
    (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')
}

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

#[cfg(test)]
mod tests {
    use super::{Location, Span, StringStream, Token, TokenStream};

    fn make_token_stream(input: &str) -> TokenStream {
        let string_stream = StringStream::new(input);
        TokenStream::new(string_stream)
    }

    fn parse(input: &str) -> Vec<Token> {
        let mut token_stream = make_token_stream(input);
        let mut tokens = vec![];
        loop {
            let token = token_stream.read();
            tokens.push(token.clone());
            if token == Token::EOF {
                break;
            }
        }
        return tokens;
    }

    #[test]
    fn it_parses_identifier() {
        assert_eq!(
            parse("foo"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::EOF,
            ]
        );
    }

    #[test]
    fn it_parses_keywords() {
        assert_eq!(
            parse("let"),
            vec![Token::Let(Location::new(0, 1, 1)), Token::EOF],
        );
        assert_eq!(parse("return"), vec![Token::Return, Token::EOF]);
        assert_eq!(
            parse("var"),
            vec![Token::Var(Location::new(0, 1, 1)), Token::EOF],
        );
    }

    #[test]
    fn it_parses_arrows() {
        assert_eq!(
            parse("foo() -> {}"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::ParenthesesLeft,
                Token::ParenthesesRight,
                Token::Arrow,
                Token::BraceLeft,
                Token::BraceRight,
                Token::EOF,
            ],
        );
    }

    #[test]
    fn it_parses_comments_and_terminals() {
        assert_eq!(
            parse("foo /* Comment */ bar"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::Identifier(
                    "bar".to_string(),
                    Span::new(Location::new(18, 1, 19), Location::new(21, 1, 22)),
                ),
                Token::EOF,
            ]
        );

        assert_eq!(
            parse("foo // Comment \n bar"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::Terminal('\n'),
                Token::Identifier(
                    "bar".to_string(),
                    Span::new(Location::new(17, 2, 2), Location::new(20, 2, 5)),
                ),
                Token::EOF,
            ]
        );

        assert_eq!(
            parse(
                "foo
            // Comment about the call
            // Another comment about the call
            bar()"
            ),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::Terminal('\n'),
                Token::Identifier(
                    "bar".to_string(),
                    Span::new(Location::new(100, 4, 13), Location::new(103, 4, 16)),
                ),
                Token::ParenthesesLeft,
                Token::ParenthesesRight,
                Token::EOF,
            ]
        );
    }

    #[test]
    fn it_parses_integers() {
        assert_eq!(parse("1"), vec![Token::Integer(1), Token::EOF]);
        assert_eq!(parse("-1"), vec![Token::Integer(-1), Token::EOF]);
        assert_eq!(
            parse("- 1"),
            vec![Token::Minus, Token::Integer(1), Token::EOF],
        );
        assert_eq!(
            parse("- -2"),
            vec![Token::Minus, Token::Integer(-2), Token::EOF],
        );
        assert_eq!(
            parse("1+2"),
            vec![
                Token::Integer(1),
                Token::Plus,
                Token::Integer(2),
                Token::EOF,
            ],
        );
        assert_eq!(
            parse("1 + 2"),
            vec![
                Token::Integer(1),
                Token::Plus,
                Token::Integer(2),
                Token::EOF,
            ],
        );
        assert_eq!(
            parse("1 + -2"),
            vec![
                Token::Integer(1),
                Token::Plus,
                Token::Integer(-2),
                Token::EOF,
            ],
        );
    }

    #[test]
    fn it_parses_strings() {
        assert_eq!(
            parse("\"\""),
            vec![
                Token::String(
                    "".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(2, 1, 3),)
                ),
                Token::EOF
            ]
        );
        assert_eq!(
            parse("\"abc123\""),
            vec![
                Token::String(
                    "abc123".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(8, 1, 9),)
                ),
                Token::EOF
            ]
        );
    }

    #[test]
    fn it_parses_blocks() {
        assert_eq!(
            parse("{ 1; }"),
            vec![
                Token::BraceLeft,
                Token::Integer(1),
                Token::Terminal(';'),
                Token::BraceRight,
                Token::EOF,
            ]
        );
    }

    #[test]
    fn test_read_if() {
        let input = "a";
        // Check that `read` works correctly.
        let actual = Token::Identifier(
            "a".to_string(),
            Span::new(Location::new(0, 1, 1), Location::new(1, 1, 2)),
        );
        assert_eq!(make_token_stream(input).read(), actual,);
        // Now check that the contents of the expected token don't equal the
        // actual token.
        let expected = Token::Identifier(
            "".to_string(),
            Span::new(Location::unknown(), Location::unknown()),
        );
        assert_ne!(make_token_stream(input).read(), expected);
        // But `read_if` should still return the actual token since they're
        // both `Identifier`s.
        assert_eq!(make_token_stream("a").read_if(expected), Some(actual));
        // And finally if we look for something that isn't an identifier it
        // should return `None`.
        assert_eq!(make_token_stream("a").read_if(Token::ParenthesesLeft), None);
    }
}
<commit_msg>Support digits and underscores in identifiers<commit_after>use super::location::{Location, Span};

#[derive(Clone)]
struct StringStream {
    input: Vec<char>,
    index: usize,
    line: i32,
    column: i32,
}

impl StringStream {
    fn new(input: &str) -> Self {
        Self {
            input: input.chars().collect(),
            index: 0,
            line: 1,
            column: 1,
        }
    }

    fn read(&mut self) -> char {
        let character = self
            .input
            .get(self.index)
            .map(|value| *value)
            .unwrap_or('\0');
        self.index += 1;
        if character == '\n' {
            self.line += 1;
            self.column = 1;
        } else {
            self.column += 1;
        }
        character
    }

    fn peek(&self) -> char {
        self.input
            .get(self.index)
            .map(|value| *value)
            .unwrap_or('\0')
    }

    fn read_until(&mut self, target: char) {
        while self.peek() != target {
            let character = self.read();
            assert!(character != '\0');
        }
    }

    fn read_if_match(&mut self, target: &[char]) -> bool {
        // Bounds-check first!
        if (self.index + target.len()) > self.input.len() {
            return false;
        }
        let substr = &self.input[self.index..(self.index + target.len())];
        if substr != target {
            return false;
        }
        // Consume if they match.
        for _ in target {
            self.read();
        }
        true
    }

    fn location(&self) -> Location {
        Location::new(self.index as u32, self.line, self.column)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Token {
    Arrow,
    BraceLeft,
    BraceRight,
    Comma,
    Dot(Location),
    EOF,
    Equals,
    Export,
    Identifier(String, Span),
    Import(Location),
    Integer(i64),
    LeftAngle,
    Let(Location),
    Minus,
    ParenthesesLeft,
    ParenthesesRight,
    Plus,
    Return,
    Star,
    String(String, Span),
    Terminal(char),
    Var(Location),
    While(Location),
}

impl Token {
    pub fn location(&self) -> Option<Location> {
        match self {
            Token::Let(location) | Token::Var(location) => Some(location.clone()),
            Token::Identifier(_, location) => Some(location.start.clone()),
            _ => None,
        }
    }
}

impl Token {
    pub fn newline(self) -> bool {
        match self {
            Token::Terminal(character) => character == '\n',
            _ => false,
        }
    }
}

#[derive(Clone)]
pub struct TokenStream {
    input: StringStream,
    peeking: bool,
    next_token: Option<Token>,
}

impl TokenStream {
    pub fn from_string(input: String) -> Self {
        Self::new(StringStream::new(input.as_str()))
    }

    fn new(input: StringStream) -> Self {
        TokenStream {
            input: input,
            peeking: false,
            next_token: None,
        }
    }

    pub fn backtrack(&mut self, other: &TokenStream) {
        self.input = other.input.clone();
        self.peeking = other.peeking;
        self.next_token = other.next_token.clone();
    }

    pub fn peek(&mut self) -> Token {
        if !self.peeking {
            self.peeking = true;
            self.next_token = Some(self.lex());
        }
        self.next_token.clone().unwrap()
    }

    /// Peeks, and if the peeked token is the same type as the expected then
    /// read that type and return the read (not expected) token.
    pub fn read_if(&mut self, expected: Token) -> Option<Token> {
        use std::mem::discriminant;

        let got = self.peek();
        if discriminant(&expected) == discriminant(&got) {
            Some(self.read())
        } else {
            None
        }
    }

    pub fn read(&mut self) -> Token {
        let token = self.peek();
        self.peeking = false;
        token
    }

    fn lex(&mut self) -> Token {
        self.consume_space_and_comments();

        loop {
            let location = self.input.location();
            let character = self.input.peek();

            if identifier_head(character) {
                return self.lex_identifier(location);
            } else if numeric_head(character) {
                return self.lex_arrow_minus_or_numeric();
            } else if string_head(character) {
                return self.lex_string(location);
            } else {
                self.input.read();
                match character {
                    '\0' => return Token::EOF,
                    '{' => return Token::BraceLeft,
                    '}' => return Token::BraceRight,
                    '(' => return Token::ParenthesesLeft,
                    ')' => return Token::ParenthesesRight,
                    ',' => return Token::Comma,
                    '.' => return Token::Dot(location),
                    '=' => return Token::Equals,
                    '<' => return Token::LeftAngle,
                    '+' => return Token::Plus,
                    '*' => return Token::Star,
                    ';' => return Token::Terminal(character),
                    '\n' => {
                        self.consume_more_newline_terminals();
                        return Token::Terminal(character);
                    }
                    _ => panic!("Unexpected character: {}", character),
                }
            }
        }
    }

    fn lex_identifier(&mut self, start: Location) -> Token {
        let mut identifier = vec![self.input.read()];
        loop {
            let character = self.input.peek();
            if identifier_tail(character) {
                self.input.read();
                identifier.push(character);
            } else {
                break;
            }
        }
        let identifier_string: String = identifier.into_iter().collect();
        match identifier_string.as_str() {
            "export" => Token::Export,
            "let" => Token::Let(start),
            "import" => Token::Import(start),
            "return" => Token::Return,
            "var" => Token::Var(start),
            "while" => Token::While(start),
            _ => Token::Identifier(
                identifier_string,
                Span::new(start.clone(), self.input.location()),
            ),
        }
    }

    fn lex_string(&mut self, start: Location) -> Token {
        let opening = self.input.read();
        assert_eq!(opening, '"');
        let mut characters = vec![];
        let mut end;
        loop {
            let character = self.input.read();
            end = self.input.location();
            if character != '"' {
                characters.push(character);
            } else {
                break;
            }
        }
        let string: String = characters.into_iter().collect();
        Token::String(string, Span::new(start, end))
    }

    fn lex_arrow_minus_or_numeric(&mut self) -> Token {
        let first_character = self.input.read();
        if first_character == '-' && self.input.peek() == '>' {
            self.input.read();
            return Token::Arrow;
        }
        if first_character == '-' && !digit(self.input.peek()) {
            return Token::Minus;
        }
        let mut number = vec![first_character];
        loop {
            let character = self.input.peek();
            if digit(character) {
                self.input.read();
                number.push(character);
            } else {
                break;
            }
        }
        let number_string: String = number.into_iter().collect();
        Token::Integer(number_string.parse().unwrap())
    }

    fn consume_more_newline_terminals(&mut self) {
        loop {
            self.consume_space_and_comments();
            if self.input.peek() == '\n' {
                self.input.read();
                continue;
            } else {
                break;
            }
        }
    }

    fn consume_space_and_comments(&mut self) {
        loop {
            let character = self.input.peek();
            if character == ' ' || character == '\t' {
                self.input.read();
            } else if self.input.read_if_match(&['/', '/']) {
                self.input.read_until('\n');
            } else if self.input.read_if_match(&['/', '*']) {
                loop {
                    if self.input.read_if_match(&['*', '/']) {
                        break;
                    } else {
                        self.input.read();
                    }
                }
            } else {
                // Not any kind of comment.
                break;
            }
        }
    }
}

fn identifier_head(character: char) -> bool {
    alphabetical(character) || character == '_'
}

fn identifier_tail(character: char) -> bool {
    alphabetical(character) || digit(character) || character == '_'
}

fn numeric_head(character: char) -> bool {
    digit(character) || (character == '-')
}

fn string_head(character: char) -> bool {
    character == '"'
}

fn alphabetical(character: char) -> bool {
    (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')
}

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

#[cfg(test)]
mod tests {
    use super::{Location, Span, StringStream, Token, TokenStream};

    fn make_token_stream(input: &str) -> TokenStream {
        let string_stream = StringStream::new(input);
        TokenStream::new(string_stream)
    }

    fn parse(input: &str) -> Vec<Token> {
        let mut token_stream = make_token_stream(input);
        let mut tokens = vec![];
        loop {
            let token = token_stream.read();
            tokens.push(token.clone());
            if token == Token::EOF {
                break;
            }
        }
        return tokens;
    }

    #[test]
    fn it_parses_identifier() {
        assert_eq!(
            parse("foo"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::EOF,
            ]
        );
        assert_eq!(
            parse("a1_b2"),
            vec![
                Token::Identifier(
                    "a1_b2".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(5, 1, 6)),
                ),
                Token::EOF,
            ]
        );
        assert_eq!(
            parse("_"),
            vec![
                Token::Identifier(
                    "_".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(1, 1, 2)),
                ),
                Token::EOF,
            ]
        );
    }

    #[test]
    fn it_parses_keywords() {
        assert_eq!(
            parse("let"),
            vec![Token::Let(Location::new(0, 1, 1)), Token::EOF],
        );
        assert_eq!(parse("return"), vec![Token::Return, Token::EOF]);
        assert_eq!(
            parse("var"),
            vec![Token::Var(Location::new(0, 1, 1)), Token::EOF],
        );
    }

    #[test]
    fn it_parses_arrows() {
        assert_eq!(
            parse("foo() -> {}"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::ParenthesesLeft,
                Token::ParenthesesRight,
                Token::Arrow,
                Token::BraceLeft,
                Token::BraceRight,
                Token::EOF,
            ],
        );
    }

    #[test]
    fn it_parses_comments_and_terminals() {
        assert_eq!(
            parse("foo /* Comment */ bar"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::Identifier(
                    "bar".to_string(),
                    Span::new(Location::new(18, 1, 19), Location::new(21, 1, 22)),
                ),
                Token::EOF,
            ]
        );

        assert_eq!(
            parse("foo // Comment \n bar"),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::Terminal('\n'),
                Token::Identifier(
                    "bar".to_string(),
                    Span::new(Location::new(17, 2, 2), Location::new(20, 2, 5)),
                ),
                Token::EOF,
            ]
        );

        assert_eq!(
            parse(
                "foo
            // Comment about the call
            // Another comment about the call
            bar()"
            ),
            vec![
                Token::Identifier(
                    "foo".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(3, 1, 4)),
                ),
                Token::Terminal('\n'),
                Token::Identifier(
                    "bar".to_string(),
                    Span::new(Location::new(100, 4, 13), Location::new(103, 4, 16)),
                ),
                Token::ParenthesesLeft,
                Token::ParenthesesRight,
                Token::EOF,
            ]
        );
    }

    #[test]
    fn it_parses_integers() {
        assert_eq!(parse("1"), vec![Token::Integer(1), Token::EOF]);
        assert_eq!(parse("-1"), vec![Token::Integer(-1), Token::EOF]);
        assert_eq!(
            parse("- 1"),
            vec![Token::Minus, Token::Integer(1), Token::EOF],
        );
        assert_eq!(
            parse("- -2"),
            vec![Token::Minus, Token::Integer(-2), Token::EOF],
        );
        assert_eq!(
            parse("1+2"),
            vec![
                Token::Integer(1),
                Token::Plus,
                Token::Integer(2),
                Token::EOF,
            ],
        );
        assert_eq!(
            parse("1 + 2"),
            vec![
                Token::Integer(1),
                Token::Plus,
                Token::Integer(2),
                Token::EOF,
            ],
        );
        assert_eq!(
            parse("1 + -2"),
            vec![
                Token::Integer(1),
                Token::Plus,
                Token::Integer(-2),
                Token::EOF,
            ],
        );
    }

    #[test]
    fn it_parses_strings() {
        assert_eq!(
            parse("\"\""),
            vec![
                Token::String(
                    "".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(2, 1, 3),)
                ),
                Token::EOF
            ]
        );
        assert_eq!(
            parse("\"abc123\""),
            vec![
                Token::String(
                    "abc123".to_string(),
                    Span::new(Location::new(0, 1, 1), Location::new(8, 1, 9),)
                ),
                Token::EOF
            ]
        );
    }

    #[test]
    fn it_parses_blocks() {
        assert_eq!(
            parse("{ 1; }"),
            vec![
                Token::BraceLeft,
                Token::Integer(1),
                Token::Terminal(';'),
                Token::BraceRight,
                Token::EOF,
            ]
        );
    }

    #[test]
    fn test_read_if() {
        let input = "a";
        // Check that `read` works correctly.
        let actual = Token::Identifier(
            "a".to_string(),
            Span::new(Location::new(0, 1, 1), Location::new(1, 1, 2)),
        );
        assert_eq!(make_token_stream(input).read(), actual,);
        // Now check that the contents of the expected token don't equal the
        // actual token.
        let expected = Token::Identifier(
            "".to_string(),
            Span::new(Location::unknown(), Location::unknown()),
        );
        assert_ne!(make_token_stream(input).read(), expected);
        // But `read_if` should still return the actual token since they're
        // both `Identifier`s.
        assert_eq!(make_token_stream("a").read_if(expected), Some(actual));
        // And finally if we look for something that isn't an identifier it
        // should return `None`.
        assert_eq!(make_token_stream("a").read_if(Token::ParenthesesLeft), None);
    }
}
<|endoftext|>