<commit_before>//! GC heap allocator.

use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::{fmt, mem, ptr};
use bit_vec::BitVec;

// What does this do? You'll never guess!
type HeapId<'a> = PhantomData<::std::cell::Cell<&'a mut ()>>;

/// Total number of Pair objects that can be allocated in a Heap at once.
///
/// XXX BOGUS: At present, this is number is carefully selected (with insider
/// knowledge of both `size_of::<PairStorage>()` on my platform and the `Box`
/// allocator's behavior) so that a HeapStorage object fits in a page and thus
/// the assertions below about HEAP_STORAGE_ALIGN pass.
pub const HEAP_SIZE: usize = 125;

/// We rely on all bits to the right of this bit being 0 in addresses of
/// HeapStorage instances.
const HEAP_STORAGE_ALIGN: usize = 0x1000;

// The heap is (for now) just a big array of Pairs
struct HeapStorage<'a> {
    mark_bits: BitVec,
    mark_entry_point: unsafe fn(*mut ()),
    freelist: *mut (),
    objects: [PairStorage<'a>; HEAP_SIZE]
}

impl<'a> HeapStorage<'a> {
    unsafe fn new() -> Box<HeapStorage<'a>> {
        let mut storage = Box::new(HeapStorage {
            mark_bits: BitVec::from_elem(HEAP_SIZE, false),
            mark_entry_point: mark_entry_point::<PairStorage>,
            freelist: ptr::null_mut(),
            objects: mem::uninitialized()
        });

        // These assertions will likely fail on 32-bit platforms or if someone
        // is somehow using a custom Box allocator. If either one fails, this
        // GC will not work.
        assert_eq!(&mut *storage as *mut HeapStorage<'a> as usize & (HEAP_STORAGE_ALIGN - 1), 0);
        assert!(mem::size_of::<HeapStorage<'a>>() <= HEAP_STORAGE_ALIGN);

        for i in 0 .. HEAP_SIZE {
            let p = &mut storage.objects[i] as *mut PairStorage<'a>;
            storage.add_to_free_list(p);
        }
        storage
    }

    unsafe fn add_to_free_list(&mut self, p: *mut PairStorage<'a>) {
        let listp = p as *mut *mut ();
        *listp = self.freelist;
        assert_eq!(*listp, self.freelist);
        self.freelist = p as *mut ();
    }

    unsafe fn try_alloc(&mut self) -> Option<*mut PairStorage<'a>> {
        let p = self.freelist;
        if p.is_null() {
            None
        } else {
            let listp = p as *mut *mut ();
            self.freelist = *listp;
            Some(p as *mut PairStorage<'a>)
        }
    }

    unsafe fn sweep(&mut self) {
        self.freelist = ptr::null_mut();
        for i in 0 .. HEAP_SIZE {
            let p = &mut self.objects[i] as *mut PairStorage<'a>;
            if !self.mark_bits[i] {
                self.add_to_free_list(p);
            }
        }
    }
}

pub struct Heap<'a> {
    id: HeapId<'a>,
    storage: Box<HeapStorage<'a>>,
    pins: RefCell<HashMap<*mut (), usize>>
}

/// Create a heap, pass it to a callback, then destroy the heap.
///
/// The heap's lifetime is directly tied to this function call, for safety. (So
/// the API is a little wonky --- but how many heaps were you planning on
/// creating?)
pub fn with_heap<F, O>(f: F) -> O
    where F: for<'a> FnOnce(&mut Heap<'a>) -> O
{
    f(&mut Heap::new())
}

/// Trait implemented by all types that can be stored directly in the GC heap:
/// the `Storage` types associated with any `HeapInline` or `HeapRef` type.
///
/// XXX maybe this should not be its own trait - fold into HeapInline?
///
pub unsafe trait Mark<'a> {
    unsafe fn mark(ptr: *mut Self);
}

/// Non-inlined function that serves as an entry point to marking. This is used
/// for marking root set entries.
unsafe fn mark_entry_point<'a, T: Mark<'a>>(addr: *mut ()) {
    Mark::mark(addr as *mut T);
}

/// Trait implemented by all types that can be stored in fields of structs (or,
/// eventually, elements of GCVecs) that are stored in the GC heap.
pub unsafe trait HeapInline<'a> {
    /// The type of the value when it is physically stored in the heap.
    type Storage;

    /// Extract the value from the heap. Do not under any circumstances call
    /// this.  It is for macro-generated code to call; it is impossible for
    /// ordinary users to call this safely, because `ptr` must be a direct,
    /// unwrapped reference to a value stored in the GC heap, which ordinary
    /// users cannot obtain.
    ///
    /// This turns any raw pointers in the `Storage` value into safe
    /// references, so while it's a dangerous function, the result of a correct
    /// call can be safely handed out to user code.
    ///
    unsafe fn from_heap(heap: &Heap<'a>, ptr: &Self::Storage) -> Self;

    /// Convert the value to the form it should have in the heap.
    /// This is for macro-generated code to call.
    fn to_heap(self) -> Self::Storage;
}

impl<'a> Heap<'a> {
    fn new() -> Heap<'a> {
        Heap {
            id: PhantomData,
            storage: unsafe { HeapStorage::new() },
            pins: RefCell::new(HashMap::new())
        }
    }

    /// Add the object `*p` to the root set, protecting it from GC.
    ///
    /// An object that has been pinned *n* times stays in the root set
    /// until it has been unpinned *n* times.
    ///
    /// (Unsafe because if the argument is garbage, a later GC will
    /// crash. Called only from `impl PinnedRef`.)
    unsafe fn pin<T: Mark<'a>>(&self, p: *mut T) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    /// Unpin an object (see `pin`).
    ///
    /// (Unsafe because unpinning an object that other code is still using
    /// causes dangling pointers. Called only from `impl PinnedRef`.)
    unsafe fn unpin<T: Mark<'a>>(&self, p: *mut T) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p as *mut ()).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    pub fn try_alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Option<Pair<'a>> {
        unsafe {
            self.storage.try_alloc()
                .or_else(|| {
                    self.gc();
                    self.storage.try_alloc()
                })
                .map(move |p| {
                    *p = PairStorage {
                        head: pair.0.to_heap(),
                        tail: pair.1.to_heap()
                    };
                    Pair(PinnedRef::new(self, p))
                })
        }
    }

    unsafe fn find_header<T>(_ptr: *const T) -> *mut HeapStorage<'a> {
        let storage_addr = _ptr as usize & !(HEAP_STORAGE_ALIGN - 1);
        storage_addr as *mut HeapStorage<'a>
    }

    unsafe fn find_mark_bit<T>(_ptr: *const T) -> (*mut BitVec, usize) {
        assert_eq!(mem::size_of::<T>(), mem::size_of::<PairStorage<'a>>());
        let storage = Heap::find_header(_ptr);
        let objects_addr = &mut (*storage).objects[0] as *mut PairStorage<'a> as usize;
        let index = (_ptr as usize - objects_addr) / mem::size_of::<PairStorage<'a>>();
        (&mut (*storage).mark_bits as *mut BitVec, index)
    }

    pub unsafe fn get_mark_bit<T>(_ptr: *const T) -> bool {
        let (bits, index) = Heap::find_mark_bit(_ptr);
        (*bits)[index]
    }

    pub unsafe fn set_mark_bit<T>(_ptr: *const T) {
        let (bits, index) = Heap::find_mark_bit(_ptr);
        (*bits).set(index, true);
    }

    pub fn alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Pair<'a> {
        self.try_alloc(pair).expect("out of memory (gc did not collect anything)")
    }

    pub fn alloc_null(&mut self) -> Pair<'a> {
        self.alloc((Value::Null, Value::Null))
    }

    unsafe fn mark(ptr: *mut ()) {
        let storage = Heap::find_header(ptr);
        let mark_fn = (*storage).mark_entry_point;
        mark_fn(ptr);
    }

    unsafe fn gc(&mut self) {
        // mark phase
        self.storage.mark_bits.clear();
        for (&p, _) in self.pins.borrow().iter() {
            Heap::mark(p);
        }

        // sweep phase
        self.storage.sweep();
    }

    #[cfg(test)]
    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

pub struct PinnedRef<'a, T: Mark<'a>> {
    ptr: *mut T,
    heap: *const Heap<'a>,
    heap_id: HeapId<'a>
}

impl<'a, T: Mark<'a>> PinnedRef<'a, T> {
    /// Pin an object, returning a new `PinnedRef` that will unpin it when
    /// dropped. Unsafe because if `p` is not a pointer to a live allocation of
    /// type `T` --- and a complete allocation, not a sub-object of one --- then
    /// later unsafe code will explode.
    unsafe fn new(heap: &Heap<'a>, p: *mut T) -> PinnedRef<'a, T> {
        heap.pin(p);
        PinnedRef {
            ptr: p,
            heap: heap as *const Heap<'a>,
            heap_id: heap.id
        }
    }
}

impl<'a, T: Mark<'a>> Drop for PinnedRef<'a, T> {
    fn drop(&mut self) {
        unsafe {
            (*self.heap).unpin(self.ptr);
        }
    }
}

impl<'a, T: Mark<'a>> Clone for PinnedRef<'a, T> {
    fn clone(&self) -> PinnedRef<'a, T> {
        let &PinnedRef { ptr, heap, heap_id } = self;
        unsafe {
            (*heap).pin(ptr);
        }
        PinnedRef {
            ptr: ptr,
            heap: heap,
            heap_id: heap_id
        }
    }
}

impl<'a, T: Mark<'a>> fmt::Debug for PinnedRef<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "PinnedRef {{ ptr: {:p} }}", self.ptr)
    }
}

impl<'a, T: Mark<'a>> PartialEq for PinnedRef<'a, T> {
    fn eq(&self, other: &PinnedRef<'a, T>) -> bool {
        self.ptr == other.ptr
    }
}

impl<'a, T: Mark<'a>> Eq for PinnedRef<'a, T> {}

pub trait GCRef {
    #[cfg(test)]
    fn address(&self) -> usize;
}

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        unsafe impl<'a> Mark<'a> for $t {
            unsafe fn mark(_ptr: *mut $t) {}
        }

        unsafe impl<'a> HeapInline<'a> for $t {
            type Storage = Self;
            fn to_heap(self) -> $t { self }
            unsafe fn from_heap(_heap: &Heap<'a>, v: &$t) -> $t { (*v).clone() }
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

use std::rc::Rc;
gc_trivial_impl!(Rc<String>);

/*
// 'static types are heap-safe because ref types are never 'static.
// Unfortunately I can't make the compiler understand this: the rules
// to prevent conflicting trait impls make this conflict with almost
// everything.
unsafe impl<'a, T: Clone + 'static> Mark<'a> for T { ...trivial... }
unsafe impl<'a, T: Clone + 'static> HeapInline<'a> for T { ...trivial... }
*/

// === Pair, the reference type

gc_ref_type! {
    pub struct Pair / PairStorage<'a> {
        head / set_head: Value<'a>,
        tail / set_tail: Value<'a>
    }
}

// === Values (a heap-inline enum)

gc_inline_enum! {
    pub enum Value / ValueStorage <'a> {
        Null,
        Int(i32),
        Str(Rc<String>),  // <-- equality is by value
        Pair(Pair<'a>)  // <-- equality is by pointer
    }
}
<commit_msg>Add some comments on HeapInline.<commit_after>//! GC heap allocator.

use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::{fmt, mem, ptr};
use bit_vec::BitVec;

// What does this do? You'll never guess!
type HeapId<'a> = PhantomData<::std::cell::Cell<&'a mut ()>>;

/// Total number of Pair objects that can be allocated in a Heap at once.
///
/// XXX BOGUS: At present, this is number is carefully selected (with insider
/// knowledge of both `size_of::<PairStorage>()` on my platform and the `Box`
/// allocator's behavior) so that a HeapStorage object fits in a page and thus
/// the assertions below about HEAP_STORAGE_ALIGN pass.
pub const HEAP_SIZE: usize = 125;

/// We rely on all bits to the right of this bit being 0 in addresses of
/// HeapStorage instances.
const HEAP_STORAGE_ALIGN: usize = 0x1000;

// The heap is (for now) just a big array of Pairs
struct HeapStorage<'a> {
    mark_bits: BitVec,
    mark_entry_point: unsafe fn(*mut ()),
    freelist: *mut (),
    objects: [PairStorage<'a>; HEAP_SIZE]
}

impl<'a> HeapStorage<'a> {
    unsafe fn new() -> Box<HeapStorage<'a>> {
        let mut storage = Box::new(HeapStorage {
            mark_bits: BitVec::from_elem(HEAP_SIZE, false),
            mark_entry_point: mark_entry_point::<PairStorage>,
            freelist: ptr::null_mut(),
            objects: mem::uninitialized()
        });

        // These assertions will likely fail on 32-bit platforms or if someone
        // is somehow using a custom Box allocator. If either one fails, this
        // GC will not work.
        assert_eq!(&mut *storage as *mut HeapStorage<'a> as usize & (HEAP_STORAGE_ALIGN - 1), 0);
        assert!(mem::size_of::<HeapStorage<'a>>() <= HEAP_STORAGE_ALIGN);

        for i in 0 .. HEAP_SIZE {
            let p = &mut storage.objects[i] as *mut PairStorage<'a>;
            storage.add_to_free_list(p);
        }
        storage
    }

    unsafe fn add_to_free_list(&mut self, p: *mut PairStorage<'a>) {
        let listp = p as *mut *mut ();
        *listp = self.freelist;
        assert_eq!(*listp, self.freelist);
        self.freelist = p as *mut ();
    }

    unsafe fn try_alloc(&mut self) -> Option<*mut PairStorage<'a>> {
        let p = self.freelist;
        if p.is_null() {
            None
        } else {
            let listp = p as *mut *mut ();
            self.freelist = *listp;
            Some(p as *mut PairStorage<'a>)
        }
    }

    unsafe fn sweep(&mut self) {
        self.freelist = ptr::null_mut();
        for i in 0 .. HEAP_SIZE {
            let p = &mut self.objects[i] as *mut PairStorage<'a>;
            if !self.mark_bits[i] {
                self.add_to_free_list(p);
            }
        }
    }
}

pub struct Heap<'a> {
    id: HeapId<'a>,
    storage: Box<HeapStorage<'a>>,
    pins: RefCell<HashMap<*mut (), usize>>
}

/// Create a heap, pass it to a callback, then destroy the heap.
///
/// The heap's lifetime is directly tied to this function call, for safety. (So
/// the API is a little wonky --- but how many heaps were you planning on
/// creating?)
pub fn with_heap<F, O>(f: F) -> O
    where F: for<'a> FnOnce(&mut Heap<'a>) -> O
{
    f(&mut Heap::new())
}

/// Trait implemented by all types that can be stored directly in the GC heap:
/// the `Storage` types associated with any `HeapInline` or `HeapRef` type.
///
/// XXX maybe this should not be its own trait - fold into HeapInline?
///
pub unsafe trait Mark<'a> {
    unsafe fn mark(ptr: *mut Self);
}

/// Non-inlined function that serves as an entry point to marking. This is used
/// for marking root set entries.
unsafe fn mark_entry_point<'a, T: Mark<'a>>(addr: *mut ()) {
    Mark::mark(addr as *mut T);
}

/// Trait implemented by all types that can be stored in fields of structs (or,
/// eventually, elements of GCVecs) that are stored in the GC heap.
///
/// This trait is unsafe to implement for several reasons, ranging from:
///
/// *   The common-sense: API users aren't supposed to know or care about this.
///     It is only public so that the public macros can see it.
///     Use `gc_ref_type!` and `gc_inline_enum!`.
///
/// *   To the obvious: `Storage` objects are full of pointers and if `to_heap`
///     puts garbage into them, GC will crash.
///
/// *   To the subtle: `from_heap` receives a non-mut reference to a heap
///     value. But there may exist gc-references to that value, in which case
///     `from_heap` (or other code it calls) could modify the value while this
///     direct, non-mut reference exists, which could lead to crashes (due to
///     changing enums if nothing else) - all without using any unsafe code.
///
pub unsafe trait HeapInline<'a> {
    /// The type of the value when it is physically stored in the heap.
    type Storage;

    /// Extract the value from the heap. Do not under any circumstances call
    /// this.  It is for macro-generated code to call; it is impossible for
    /// ordinary users to call this safely, because `ptr` must be a direct,
    /// unwrapped reference to a value stored in the GC heap, which ordinary
    /// users cannot obtain.
    ///
    /// This turns any raw pointers in the `Storage` value into safe
    /// references, so while it's a dangerous function, the result of a correct
    /// call can be safely handed out to user code.
    ///
    unsafe fn from_heap(heap: &Heap<'a>, ptr: &Self::Storage) -> Self;

    /// Convert the value to the form it should have in the heap.
    /// This is for macro-generated code to call.
    fn to_heap(self) -> Self::Storage;
}

impl<'a> Heap<'a> {
    fn new() -> Heap<'a> {
        Heap {
            id: PhantomData,
            storage: unsafe { HeapStorage::new() },
            pins: RefCell::new(HashMap::new())
        }
    }

    /// Add the object `*p` to the root set, protecting it from GC.
    ///
    /// An object that has been pinned *n* times stays in the root set
    /// until it has been unpinned *n* times.
    ///
    /// (Unsafe because if the argument is garbage, a later GC will
    /// crash. Called only from `impl PinnedRef`.)
    unsafe fn pin<T: Mark<'a>>(&self, p: *mut T) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    /// Unpin an object (see `pin`).
    ///
    /// (Unsafe because unpinning an object that other code is still using
    /// causes dangling pointers. Called only from `impl PinnedRef`.)
    unsafe fn unpin<T: Mark<'a>>(&self, p: *mut T) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p as *mut ()).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    pub fn try_alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Option<Pair<'a>> {
        unsafe {
            self.storage.try_alloc()
                .or_else(|| {
                    self.gc();
                    self.storage.try_alloc()
                })
                .map(move |p| {
                    *p = PairStorage {
                        head: pair.0.to_heap(),
                        tail: pair.1.to_heap()
                    };
                    Pair(PinnedRef::new(self, p))
                })
        }
    }

    unsafe fn find_header<T>(_ptr: *const T) -> *mut HeapStorage<'a> {
        let storage_addr = _ptr as usize & !(HEAP_STORAGE_ALIGN - 1);
        storage_addr as *mut HeapStorage<'a>
    }

    unsafe fn find_mark_bit<T>(_ptr: *const T) -> (*mut BitVec, usize) {
        assert_eq!(mem::size_of::<T>(), mem::size_of::<PairStorage<'a>>());
        let storage = Heap::find_header(_ptr);
        let objects_addr = &mut (*storage).objects[0] as *mut PairStorage<'a> as usize;
        let index = (_ptr as usize - objects_addr) / mem::size_of::<PairStorage<'a>>();
        (&mut (*storage).mark_bits as *mut BitVec, index)
    }

    pub unsafe fn get_mark_bit<T>(_ptr: *const T) -> bool {
        let (bits, index) = Heap::find_mark_bit(_ptr);
        (*bits)[index]
    }

    pub unsafe fn set_mark_bit<T>(_ptr: *const T) {
        let (bits, index) = Heap::find_mark_bit(_ptr);
        (*bits).set(index, true);
    }

    pub fn alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Pair<'a> {
        self.try_alloc(pair).expect("out of memory (gc did not collect anything)")
    }

    pub fn alloc_null(&mut self) -> Pair<'a> {
        self.alloc((Value::Null, Value::Null))
    }

    unsafe fn mark(ptr: *mut ()) {
        let storage = Heap::find_header(ptr);
        let mark_fn = (*storage).mark_entry_point;
        mark_fn(ptr);
    }

    unsafe fn gc(&mut self) {
        // mark phase
        self.storage.mark_bits.clear();
        for (&p, _) in self.pins.borrow().iter() {
            Heap::mark(p);
        }

        // sweep phase
        self.storage.sweep();
    }

    #[cfg(test)]
    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

pub struct PinnedRef<'a, T: Mark<'a>> {
    ptr: *mut T,
    heap: *const Heap<'a>,
    heap_id: HeapId<'a>
}

impl<'a, T: Mark<'a>> PinnedRef<'a, T> {
    /// Pin an object, returning a new `PinnedRef` that will unpin it when
    /// dropped. Unsafe because if `p` is not a pointer to a live allocation of
    /// type `T` --- and a complete allocation, not a sub-object of one --- then
    /// later unsafe code will explode.
    unsafe fn new(heap: &Heap<'a>, p: *mut T) -> PinnedRef<'a, T> {
        heap.pin(p);
        PinnedRef {
            ptr: p,
            heap: heap as *const Heap<'a>,
            heap_id: heap.id
        }
    }
}

impl<'a, T: Mark<'a>> Drop for PinnedRef<'a, T> {
    fn drop(&mut self) {
        unsafe {
            (*self.heap).unpin(self.ptr);
        }
    }
}

impl<'a, T: Mark<'a>> Clone for PinnedRef<'a, T> {
    fn clone(&self) -> PinnedRef<'a, T> {
        let &PinnedRef { ptr, heap, heap_id } = self;
        unsafe {
            (*heap).pin(ptr);
        }
        PinnedRef {
            ptr: ptr,
            heap: heap,
            heap_id: heap_id
        }
    }
}

impl<'a, T: Mark<'a>> fmt::Debug for PinnedRef<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "PinnedRef {{ ptr: {:p} }}", self.ptr)
    }
}

impl<'a, T: Mark<'a>> PartialEq for PinnedRef<'a, T> {
    fn eq(&self, other: &PinnedRef<'a, T>) -> bool {
        self.ptr == other.ptr
    }
}

impl<'a, T: Mark<'a>> Eq for PinnedRef<'a, T> {}

pub trait GCRef {
    #[cfg(test)]
    fn address(&self) -> usize;
}

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        unsafe impl<'a> Mark<'a> for $t {
            unsafe fn mark(_ptr: *mut $t) {}
        }

        unsafe impl<'a> HeapInline<'a> for $t {
            type Storage = Self;
            fn to_heap(self) -> $t { self }
            unsafe fn from_heap(_heap: &Heap<'a>, v: &$t) -> $t { (*v).clone() }
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

use std::rc::Rc;
gc_trivial_impl!(Rc<String>);

/*
// 'static types are heap-safe because ref types are never 'static.
// Unfortunately I can't make the compiler understand this: the rules
// to prevent conflicting trait impls make this conflict with almost
// everything.
unsafe impl<'a, T: Clone + 'static> Mark<'a> for T { ...trivial... }
unsafe impl<'a, T: Clone + 'static> HeapInline<'a> for T { ...trivial... }
*/

// === Pair, the reference type

gc_ref_type! {
    pub struct Pair / PairStorage<'a> {
        head / set_head: Value<'a>,
        tail / set_tail: Value<'a>
    }
}

// === Values (a heap-inline enum)

gc_inline_enum! {
    pub enum Value / ValueStorage <'a> {
        Null,
        Int(i32),
        Str(Rc<String>),  // <-- equality is by value
        Pair(Pair<'a>)  // <-- equality is by pointer
    }
}
<|endoftext|>


<commit_before>extern crate bindgen;
extern crate pkg_config;

use bindgen::RustTarget;
use pkg_config::Error;
use std::env;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {
            let bindings = bindgen::builder()
                .header("wrapper.h")
                .block_extern_crate(true)
                .rust_target(RustTarget::Stable_1_0)
                .generate()
                .expect("Unable to generate bindings");
            let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
            bindings
                .write_to_file(out_path.join("bindings.rs"))
                .expect("Couldn't write bindings");
        }
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}
<commit_msg>Ensure fitsfile is opaque<commit_after>extern crate bindgen;
extern crate pkg_config;

use bindgen::RustTarget;
use pkg_config::Error;
use std::env;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {
            let bindings = bindgen::builder()
                .header("wrapper.h")
                .block_extern_crate(true)
                .opaque_type("fitsfile")
                .opaque_type("FITSfile")
                .rust_target(RustTarget::Stable_1_0)
                .generate()
                .expect("Unable to generate bindings");
            let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
            bindings
                .write_to_file(out_path.join("bindings.rs"))
                .expect("Couldn't write bindings");
        }
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}
<|endoftext|>


<commit_before>extern crate alloc;

use crate::{
	block_current_task, get_priority, getpid, wakeup_task, yield_now, Priority, Tid, NO_PRIORITIES,
};
use alloc::collections::vec_deque::VecDeque;
use core::cell::UnsafeCell;
use core::hint;
use core::ops::{Deref, DerefMut, Drop};
use core::sync::atomic::{AtomicUsize, Ordering};

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completely fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
	queue: AtomicUsize,
	dequeue: AtomicUsize,
	data: UnsafeCell<T>,
}

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

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
	dequeue: &'a AtomicUsize,
	data: &'a mut T,
}

impl<T> Spinlock<T> {
	pub const fn new(user_data: T) -> Spinlock<T> {
		Spinlock {
			queue: AtomicUsize::new(0),
			dequeue: AtomicUsize::new(1),
			data: UnsafeCell::new(user_data),
		}
	}

	#[inline]
	fn obtain_lock(&self) {
		let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
		let mut counter: u16 = 0;
		while self.dequeue.load(Ordering::SeqCst) != ticket {
			counter += 1;
			if counter < 100 {
				hint::spin_loop();
			} else {
				counter = 0;
				unsafe {
					yield_now();
				}
			}
		}
	}

	#[inline]
	pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
		self.obtain_lock();
		SpinlockGuard {
			dequeue: &self.dequeue,
			data: &mut *self.data.get(),
		}
	}
}

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

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

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

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
	/// The dropping of the SpinlockGuard will release the lock it was created from.
	fn drop(&mut self) {
		self.dequeue.fetch_add(1, Ordering::SeqCst);
	}
}

/// Realize a priority queue for tasks
pub struct PriorityQueue {
	queues: [Option<VecDeque<Tid>>; NO_PRIORITIES],
	prio_bitmap: u64,
}

impl PriorityQueue {
	pub const fn new() -> PriorityQueue {
		PriorityQueue {
			queues: [
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None,
			],
			prio_bitmap: 0,
		}
	}

	/// Add a task id by its priority to the queue
	pub fn push(&mut self, prio: Priority, id: Tid) {
		let i: usize = prio.into().into();
		self.prio_bitmap |= (1 << i) as u64;
		if let Some(queue) = &mut self.queues[i] {
			queue.push_back(id);
		} else {
			let mut queue = VecDeque::new();
			queue.push_back(id);
			self.queues[i] = Some(queue);
		}
	}

	fn pop_from_queue(&mut self, queue_index: usize) -> Option<Tid> {
		if let Some(queue) = &mut self.queues[queue_index] {
			let id = queue.pop_front();

			if queue.is_empty() {
				self.prio_bitmap &= !(1 << queue_index as u64);
			}

			id
		} else {
			None
		}
	}

	/// Pop the task handle with the highest priority from the queue
	pub fn pop(&mut self) -> Option<Tid> {
		for i in 0..NO_PRIORITIES {
			if self.prio_bitmap & (1 << i) != 0 {
				return self.pop_from_queue(i);
			}
		}

		None
	}
}

struct MutexInner {
	locked: bool,
	blocked_task: PriorityQueue,
}

impl MutexInner {
	pub const fn new() -> MutexInner {
		MutexInner {
			locked: false,
			blocked_task: PriorityQueue::new(),
		}
	}
}

pub struct Mutex {
	inner: Spinlock<MutexInner>,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
	pub const fn new() -> Mutex {
		Mutex {
			inner: Spinlock::new(MutexInner::new()),
		}
	}

	#[inline]
	pub unsafe fn init(&mut self) {
		self.inner = Spinlock::new(MutexInner::new());
	}

	#[inline]
	pub unsafe fn lock(&self) {
		loop {
			let mut guard = self.inner.lock();
			if !guard.locked {
				guard.locked = true;
				return;
			} else {
				let prio = get_priority();
				let id = getpid();

				guard.blocked_task.push(prio, id);
				block_current_task();
				drop(guard);
				yield_now();
			}
		}
	}

	#[inline]
	pub unsafe fn unlock(&self) {
		let mut guard = self.inner.lock();
		guard.locked = false;
		if let Some(tid) = guard.blocked_task.pop() {
			wakeup_task(tid);
		}
	}

	#[inline]
	pub unsafe fn try_lock(&self) -> bool {
		let mut guard = self.inner.lock();
		if guard.locked == false {
			guard.locked = true;
		}
		guard.locked
	}

	#[inline]
	pub unsafe fn destroy(&self) {}
}
<commit_msg>remove clippy warnings<commit_after>extern crate alloc;

use crate::{
	block_current_task, get_priority, getpid, wakeup_task, yield_now, Priority, Tid, NO_PRIORITIES,
};
use alloc::collections::vec_deque::VecDeque;
use core::cell::UnsafeCell;
use core::hint;
use core::ops::{Deref, DerefMut, Drop};
use core::sync::atomic::{AtomicUsize, Ordering};

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completely fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
	queue: AtomicUsize,
	dequeue: AtomicUsize,
	data: UnsafeCell<T>,
}

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

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
	dequeue: &'a AtomicUsize,
	data: &'a mut T,
}

impl<T> Spinlock<T> {
	pub const fn new(user_data: T) -> Spinlock<T> {
		Spinlock {
			queue: AtomicUsize::new(0),
			dequeue: AtomicUsize::new(1),
			data: UnsafeCell::new(user_data),
		}
	}

	#[inline]
	fn obtain_lock(&self) {
		let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
		let mut counter: u16 = 0;
		while self.dequeue.load(Ordering::SeqCst) != ticket {
			counter += 1;
			if counter < 100 {
				hint::spin_loop();
			} else {
				counter = 0;
				unsafe {
					yield_now();
				}
			}
		}
	}

	#[inline]
	pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
		self.obtain_lock();
		SpinlockGuard {
			dequeue: &self.dequeue,
			data: &mut *self.data.get(),
		}
	}
}

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

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

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

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
	/// The dropping of the SpinlockGuard will release the lock it was created from.
	fn drop(&mut self) {
		self.dequeue.fetch_add(1, Ordering::SeqCst);
	}
}

/// Realize a priority queue for tasks
pub struct PriorityQueue {
	queues: [Option<VecDeque<Tid>>; NO_PRIORITIES],
	prio_bitmap: u64,
}

impl PriorityQueue {
	pub const fn new() -> PriorityQueue {
		PriorityQueue {
			queues: [
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None,
			],
			prio_bitmap: 0,
		}
	}

	/// Add a task id by its priority to the queue
	pub fn push(&mut self, prio: Priority, id: Tid) {
		let i: usize = prio.into().into();
		self.prio_bitmap |= (1 << i) as u64;
		if let Some(queue) = &mut self.queues[i] {
			queue.push_back(id);
		} else {
			let mut queue = VecDeque::new();
			queue.push_back(id);
			self.queues[i] = Some(queue);
		}
	}

	fn pop_from_queue(&mut self, queue_index: usize) -> Option<Tid> {
		if let Some(queue) = &mut self.queues[queue_index] {
			let id = queue.pop_front();

			if queue.is_empty() {
				self.prio_bitmap &= !(1 << queue_index as u64);
			}

			id
		} else {
			None
		}
	}

	/// Pop the task handle with the highest priority from the queue
	pub fn pop(&mut self) -> Option<Tid> {
		for i in 0..NO_PRIORITIES {
			if self.prio_bitmap & (1 << i) != 0 {
				return self.pop_from_queue(i);
			}
		}

		None
	}
}

struct MutexInner {
	locked: bool,
	blocked_task: PriorityQueue,
}

impl MutexInner {
	pub const fn new() -> MutexInner {
		MutexInner {
			locked: false,
			blocked_task: PriorityQueue::new(),
		}
	}
}

pub struct Mutex {
	inner: Spinlock<MutexInner>,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
	pub const fn new() -> Mutex {
		Mutex {
			inner: Spinlock::new(MutexInner::new()),
		}
	}

	#[inline]
	pub unsafe fn init(&mut self) {
		self.inner = Spinlock::new(MutexInner::new());
	}

	#[inline]
	pub unsafe fn lock(&self) {
		loop {
			let mut guard = self.inner.lock();
			if !guard.locked {
				guard.locked = true;
				return;
			} else {
				let prio = get_priority();
				let id = getpid();

				guard.blocked_task.push(prio, id);
				block_current_task();
				drop(guard);
				yield_now();
			}
		}
	}

	#[inline]
	pub unsafe fn unlock(&self) {
		let mut guard = self.inner.lock();
		guard.locked = false;
		if let Some(tid) = guard.blocked_task.pop() {
			wakeup_task(tid);
		}
	}

	#[inline]
	pub unsafe fn try_lock(&self) -> bool {
		let mut guard = self.inner.lock();
		if !guard.locked {
			guard.locked = true;
		}
		guard.locked
	}

	#[inline]
	pub unsafe fn destroy(&self) {}
}
<|endoftext|>


<commit_before>use ast::{Meta, Program, Parameter, Stmt, Expr, Operator};
use std::collections::HashMap;
use util::get_pos;

const OFFSET: usize = 6010000;

type SymbolTables = Vec<HashMap<String, SymbolEntry>>;

#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct SymbolEntry {
    id: usize,
    name: String,
    mem_loc: usize,
}

impl SymbolEntry {
    pub fn new(id: usize, name: String, mem_loc: &mut usize) -> SymbolEntry {
        *mem_loc += 4;
        SymbolEntry {
            id: id,
            name: name,
            mem_loc: *mem_loc,
        }
    }
}

pub fn compile(program: &Program, source_code: &String) -> Vec<String> {
    fn traverse(
        stmt: &Meta<Stmt>,
        symbol_tables: &mut SymbolTables,
        stmts: &mut Vec<String>,
        source_code: &String,
        count: &mut usize,
    ) {
        stmts.push(format!(
            "\n; line {}",
            get_pos(source_code, stmt.byte_offset).0
        ));

        match stmt.inside {
            Stmt::Block(ref statements) => {
                symbol_tables.push(HashMap::new());
                for stmt in statements {
                    traverse(&stmt, symbol_tables, stmts, source_code, count);
                }
                symbol_tables.pop();
            }
            Stmt::Declaration(ref parameters) => {
                for parameter in parameters {
                    if let Some(table_for_scope) = symbol_tables.last_mut() {
                        table_for_scope.insert(
                            parameter.identifier.clone(),
                            SymbolEntry::new(
                                stmt.byte_offset,
                                parameter.identifier.clone(),
                                count,
                            ),
                        );
                    }
                }
            }
            Stmt::Function(ref _name, ref parameters, ref statement) => {
                for parameter in parameters {
                    if let Some(table_for_scope) = symbol_tables.last_mut() {
                        table_for_scope.insert(
                            parameter.identifier.clone(),
                            SymbolEntry::new(
                                stmt.byte_offset,
                                parameter.identifier.clone(),
                                count,
                            ),
                        );
                    }
                }
                traverse(&*statement, symbol_tables, stmts, source_code, count);
            }
            Stmt::Write(ref expr) => {
                let expr_val = compile_expr(&expr, symbol_tables, stmts, count);
                stmts.push(format!("mov r0, #1")); // configure for stdout
                stmts.push(format!("ldr r1, ={}", OFFSET + expr_val));
                stmts.push(format!("swi 0x6b")); // print val in r1
            }
            Stmt::While(_, ref statement) => {
                traverse(&*statement, symbol_tables, stmts, source_code, count)
            }
            Stmt::For(_, _, _, ref statement) => {
                traverse(&*statement, symbol_tables, stmts, source_code, count)
            }
            Stmt::Expr(ref expr) => {
                compile_expr(&expr, symbol_tables, stmts, count);
            }
            _ => (),

        }
    }

    let mut symbol_tables: SymbolTables = vec![HashMap::new()];
    let mut stmts = vec![];
    let mut count = 0;
    for statement in program {
        traverse(
            statement,
            &mut symbol_tables,
            &mut stmts,
            source_code,
            &mut count,
        );
    }

    stmts.push(format!("swi 0x11"));

    println!("; Symbol table:");
    for symbol_table in symbol_tables {
        for (k, symbol) in &symbol_table {
            println!("; {}={:?}", k, symbol);
        }
    }

    stmts.clone()
}

pub fn compile_expr(
    expr: &Meta<Expr>,
    symbol_tables: &mut SymbolTables,
    stmts: &mut Vec<String>,
    count: &mut usize,
) -> usize {
    let curr_mem_loc = *count + 4;
    let mut return_mem = curr_mem_loc;
    let mut val = "".to_string();
    match expr.inside {
        Expr::Binary(ref op, ref e1, ref e2) => {
            val = format!("{:?}", op);
            match op {
                Operator::LogicalAnd => {
                    let label1 = format!(".And1{}", curr_mem_loc);
                    let label2 = format!(".And2{}", curr_mem_loc);
                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_loc));
                    stmts.push(format!("cmp r2, #0"));
                    stmts.push(format!("beq {}", label1));

                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_loc));

                    stmts.push(format!("cmp r3, #0"));
                    stmts.push(format!("beq {}", label1));
                    stmts.push(format!("mov r3, #1"));
                    stmts.push(format!("b {}", label2));

                    stmts.push(format!("{}:", label1));
                    stmts.push(format!("mov r3, #0"));
                    stmts.push(format!("{}:", label2));
                    stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                }
                Operator::LogicalOr => {
                    let label1 = format!(".Or1{}", curr_mem_loc);
                    let label2 = format!(".Or2{}", curr_mem_loc);
                    let label3 = format!(".Or3{}", curr_mem_loc);
                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_loc));
                    stmts.push(format!("cmp r2, #0"));
                    stmts.push(format!("bne {}", label1));

                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_loc));

                    stmts.push(format!("cmp r3, #0"));
                    stmts.push(format!("beq {}", label2));

                    stmts.push(format!("{}:", label1));
                    stmts.push(format!("mov r3, #1"));
                    stmts.push(format!("b {}", label3));

                    stmts.push(format!("{}:", label2));
                    stmts.push(format!("mov r3, #0"));

                    stmts.push(format!("{}:", label3));
                    stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                }
                Operator::Greater | Operator::GreaterEqual | Operator::Less |
                Operator::LessEqual | Operator::Equal | Operator::NotEqual => {
                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("mov r1, #0"));
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_loc));
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_loc));
                    stmts.push(format!("cmp r2, r3"));
                    let instruction = match op {
                        Operator::Greater => "movgt",
                        Operator::GreaterEqual => "movge",
                        Operator::Less => "movlt",
                        Operator::LessEqual => "movle",
                        Operator::Equal => "moveq",
                        Operator::NotEqual => "movne",
                        _ => "",
                    };
                    stmts.push(format!("{} r1, #1", instruction));
                    stmts.push(format!("str r1, ={}", OFFSET + curr_mem_loc));
                }
                Operator::Divide | Operator::Mod => {
                    let label_start = format!(".start{}", curr_mem_loc);
                    let label_end = format!(".end{}", curr_mem_loc);

                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r1, ={}", OFFSET + e1_loc));
                    stmts.push(format!("ldr r2, ={}", OFFSET + e2_loc));
                    stmts.push(format!("mov r3, #0"));

                    stmts.push(format!("cmp r1, r2"));
                    stmts.push(format!("blt {}", label_end));

                    stmts.push(format!("{}:", label_start));
                    stmts.push(format!("sub r1, r1, r2"));
                    stmts.push(format!("add r3, r3, #1"));

                    stmts.push(format!("cmp r1, r2"));
                    stmts.push(format!("bge {}", label_start));
                    stmts.push(format!("{}:", label_end));
                    // r3 = quotient
                    // r1 = remainder

                    if let Operator::Divide = op {
                        stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                    } else {
                        stmts.push(format!("str r1, ={}", OFFSET + curr_mem_loc));
                    }

                }
                _ => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    let e2_val = compile_expr(&*e2, symbol_tables, stmts, count);
                    let instruction_name = match op {
                        Operator::Add => "add",
                        Operator::Subtract => "sub",
                        Operator::Multiply => "mul",
                        Operator::And => "and",
                        Operator::Or => "orr",
                        Operator::Xor => "eor",
                        _ => "add",
                    };
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_val));
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_val));
                    stmts.push(format!(
                        "{} r4, r2, r3",
                        instruction_name,
                    ));
                    stmts.push(format!("str r4, ={}", OFFSET + curr_mem_loc));
                }
            };
        }
        Expr::Unary(ref op, ref e1) => {
            val = format!("{:?}", op);
            match op {
                Operator::PreIncr | Operator::PreDecr => {
                    let mem_loc = match e1.inside {
                        Expr::Identifier(ref name) => {
                            val = name.to_string();
                            symbol_tables.first().unwrap().get(name).unwrap().mem_loc
                        }
                        _ => 0,
                    };
                    stmts.push(format!("ldr r2, ={}", OFFSET + mem_loc));

                    if let Operator::PreIncr = op {
                        stmts.push(format!("add r2, r2, #1"));
                    } else {
                        stmts.push(format!("sub r2, r2, #1"));
                    }
                    stmts.push(format!("ldr r2, ={}", OFFSET + mem_loc));

                    return_mem = mem_loc;
                }
                Operator::PostIncr | Operator::PostDecr => {
                    let mem_loc = match e1.inside {
                        Expr::Identifier(ref name) => {
                            val = name.to_string();
                            symbol_tables.first().unwrap().get(name).unwrap().mem_loc
                        }
                        _ => 0,
                    };
                    stmts.push(format!("ldr r2, ={}", OFFSET + mem_loc));
                    stmts.push(format!("str r2, ={}", OFFSET + curr_mem_loc));
                    if let Operator::PostIncr = op {
                        stmts.push(format!("add r2, r2, #1"));
                    } else {
                        stmts.push(format!("sub r2, r2, #1"));
                    }
                    stmts.push(format!("str r2, ={}", OFFSET + mem_loc));
                    return_mem = curr_mem_loc;
                }
                Operator::Negate => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_val));
                    stmts.push(format!("rsb r2, r2, #0"));
                    stmts.push(format!("str r2, ={}", OFFSET + curr_mem_loc));
                }
                Operator::Positive => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    return_mem = e1_val;
                }
                Operator::Not => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_val));
                    stmts.push(format!("cmp r2, #0"));
                    stmts.push(format!("moveq r3, #1"));
                    stmts.push(format!("movne r3, #0"));
                    stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                }
                _ => (),
            }
        }
        Expr::Assignment(ref lhs, ref rhs) => {
            let mem_loc = match lhs.inside {
                Expr::Identifier(ref name) => {
                    val = name.to_string();
                    symbol_tables.first().unwrap().get(name).unwrap().mem_loc
                }
                _ => 0,
            };
            let rhs_val = compile_expr(&*rhs, symbol_tables, stmts, count);
            stmts.push(format!("ldr r3, ={}", OFFSET + rhs_val));
            stmts.push(format!("str r3, ={}", OFFSET + mem_loc));
        }
        Expr::Identifier(ref name) => {
            val = name.to_string();
            if let Some(symbol_table) = symbol_tables.first_mut() {
                if let Some(symbol) = symbol_table.get(name) {
                    return_mem = symbol.mem_loc;
                }
            }
        }
        Expr::Number(num) => {
            val = num.to_string();
            stmts.push(format!(".equ const{}, {}", curr_mem_loc, num));
            stmts.push(format!("ldr r3, =const{}", curr_mem_loc));
            stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
        }
        _ => (),
    };

    let curr_symbol = SymbolEntry::new(expr.byte_offset, val, count);
    if let Some(symbol_table) = symbol_tables.first_mut() {
        symbol_table.insert(expr.byte_offset.to_string(), curr_symbol.clone());
    }

    return_mem
}
<commit_msg>fix preincr/predecr typo<commit_after>use ast::{Meta, Program, Parameter, Stmt, Expr, Operator};
use std::collections::HashMap;
use util::get_pos;

const OFFSET: usize = 6010000;

type SymbolTables = Vec<HashMap<String, SymbolEntry>>;

#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct SymbolEntry {
    id: usize,
    name: String,
    mem_loc: usize,
}

impl SymbolEntry {
    pub fn new(id: usize, name: String, mem_loc: &mut usize) -> SymbolEntry {
        *mem_loc += 4;
        SymbolEntry {
            id: id,
            name: name,
            mem_loc: *mem_loc,
        }
    }
}

pub fn compile(program: &Program, source_code: &String) -> Vec<String> {
    fn traverse(
        stmt: &Meta<Stmt>,
        symbol_tables: &mut SymbolTables,
        stmts: &mut Vec<String>,
        source_code: &String,
        count: &mut usize,
    ) {
        stmts.push(format!(
            "\n; line {}",
            get_pos(source_code, stmt.byte_offset).0
        ));

        match stmt.inside {
            Stmt::Block(ref statements) => {
                symbol_tables.push(HashMap::new());
                for stmt in statements {
                    traverse(&stmt, symbol_tables, stmts, source_code, count);
                }
                symbol_tables.pop();
            }
            Stmt::Declaration(ref parameters) => {
                for parameter in parameters {
                    if let Some(table_for_scope) = symbol_tables.last_mut() {
                        table_for_scope.insert(
                            parameter.identifier.clone(),
                            SymbolEntry::new(
                                stmt.byte_offset,
                                parameter.identifier.clone(),
                                count,
                            ),
                        );
                    }
                }
            }
            Stmt::Function(ref _name, ref parameters, ref statement) => {
                for parameter in parameters {
                    if let Some(table_for_scope) = symbol_tables.last_mut() {
                        table_for_scope.insert(
                            parameter.identifier.clone(),
                            SymbolEntry::new(
                                stmt.byte_offset,
                                parameter.identifier.clone(),
                                count,
                            ),
                        );
                    }
                }
                traverse(&*statement, symbol_tables, stmts, source_code, count);
            }
            Stmt::Write(ref expr) => {
                let expr_val = compile_expr(&expr, symbol_tables, stmts, count);
                stmts.push(format!("mov r0, #1")); // configure for stdout
                stmts.push(format!("ldr r1, ={}", OFFSET + expr_val));
                stmts.push(format!("swi 0x6b")); // print val in r1
            }
            Stmt::While(_, ref statement) => {
                traverse(&*statement, symbol_tables, stmts, source_code, count)
            }
            Stmt::For(_, _, _, ref statement) => {
                traverse(&*statement, symbol_tables, stmts, source_code, count)
            }
            Stmt::Expr(ref expr) => {
                compile_expr(&expr, symbol_tables, stmts, count);
            }
            _ => (),

        }
    }

    let mut symbol_tables: SymbolTables = vec![HashMap::new()];
    let mut stmts = vec![];
    let mut count = 0;
    for statement in program {
        traverse(
            statement,
            &mut symbol_tables,
            &mut stmts,
            source_code,
            &mut count,
        );
    }

    stmts.push(format!("swi 0x11"));

    println!("; Symbol table:");
    for symbol_table in symbol_tables {
        for (k, symbol) in &symbol_table {
            println!("; {}={:?}", k, symbol);
        }
    }

    stmts.clone()
}

pub fn compile_expr(
    expr: &Meta<Expr>,
    symbol_tables: &mut SymbolTables,
    stmts: &mut Vec<String>,
    count: &mut usize,
) -> usize {
    let curr_mem_loc = *count + 4;
    let mut return_mem = curr_mem_loc;
    let mut val = "".to_string();
    match expr.inside {
        Expr::Binary(ref op, ref e1, ref e2) => {
            val = format!("{:?}", op);
            match op {
                Operator::LogicalAnd => {
                    let label1 = format!(".And1{}", curr_mem_loc);
                    let label2 = format!(".And2{}", curr_mem_loc);
                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_loc));
                    stmts.push(format!("cmp r2, #0"));
                    stmts.push(format!("beq {}", label1));

                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_loc));

                    stmts.push(format!("cmp r3, #0"));
                    stmts.push(format!("beq {}", label1));
                    stmts.push(format!("mov r3, #1"));
                    stmts.push(format!("b {}", label2));

                    stmts.push(format!("{}:", label1));
                    stmts.push(format!("mov r3, #0"));
                    stmts.push(format!("{}:", label2));
                    stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                }
                Operator::LogicalOr => {
                    let label1 = format!(".Or1{}", curr_mem_loc);
                    let label2 = format!(".Or2{}", curr_mem_loc);
                    let label3 = format!(".Or3{}", curr_mem_loc);
                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_loc));
                    stmts.push(format!("cmp r2, #0"));
                    stmts.push(format!("bne {}", label1));

                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_loc));

                    stmts.push(format!("cmp r3, #0"));
                    stmts.push(format!("beq {}", label2));

                    stmts.push(format!("{}:", label1));
                    stmts.push(format!("mov r3, #1"));
                    stmts.push(format!("b {}", label3));

                    stmts.push(format!("{}:", label2));
                    stmts.push(format!("mov r3, #0"));

                    stmts.push(format!("{}:", label3));
                    stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                }
                Operator::Greater | Operator::GreaterEqual | Operator::Less |
                Operator::LessEqual | Operator::Equal | Operator::NotEqual => {
                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("mov r1, #0"));
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_loc));
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_loc));
                    stmts.push(format!("cmp r2, r3"));
                    let instruction = match op {
                        Operator::Greater => "movgt",
                        Operator::GreaterEqual => "movge",
                        Operator::Less => "movlt",
                        Operator::LessEqual => "movle",
                        Operator::Equal => "moveq",
                        Operator::NotEqual => "movne",
                        _ => "",
                    };
                    stmts.push(format!("{} r1, #1", instruction));
                    stmts.push(format!("str r1, ={}", OFFSET + curr_mem_loc));
                }
                Operator::Divide | Operator::Mod => {
                    let label_start = format!(".start{}", curr_mem_loc);
                    let label_end = format!(".end{}", curr_mem_loc);

                    let e1_loc = compile_expr(&*e1, symbol_tables, stmts, count);
                    let e2_loc = compile_expr(&*e2, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r1, ={}", OFFSET + e1_loc));
                    stmts.push(format!("ldr r2, ={}", OFFSET + e2_loc));
                    stmts.push(format!("mov r3, #0"));

                    stmts.push(format!("cmp r1, r2"));
                    stmts.push(format!("blt {}", label_end));

                    stmts.push(format!("{}:", label_start));
                    stmts.push(format!("sub r1, r1, r2"));
                    stmts.push(format!("add r3, r3, #1"));

                    stmts.push(format!("cmp r1, r2"));
                    stmts.push(format!("bge {}", label_start));
                    stmts.push(format!("{}:", label_end));
                    // r3 = quotient
                    // r1 = remainder

                    if let Operator::Divide = op {
                        stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                    } else {
                        stmts.push(format!("str r1, ={}", OFFSET + curr_mem_loc));
                    }

                }
                _ => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    let e2_val = compile_expr(&*e2, symbol_tables, stmts, count);
                    let instruction_name = match op {
                        Operator::Add => "add",
                        Operator::Subtract => "sub",
                        Operator::Multiply => "mul",
                        Operator::And => "and",
                        Operator::Or => "orr",
                        Operator::Xor => "eor",
                        _ => "add",
                    };
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_val));
                    stmts.push(format!("ldr r3, ={}", OFFSET + e2_val));
                    stmts.push(format!(
                        "{} r4, r2, r3",
                        instruction_name,
                    ));
                    stmts.push(format!("str r4, ={}", OFFSET + curr_mem_loc));
                }
            };
        }
        Expr::Unary(ref op, ref e1) => {
            val = format!("{:?}", op);
            match op {
                Operator::PreIncr | Operator::PreDecr => {
                    let mem_loc = match e1.inside {
                        Expr::Identifier(ref name) => {
                            val = name.to_string();
                            symbol_tables.first().unwrap().get(name).unwrap().mem_loc
                        }
                        _ => 0,
                    };
                    stmts.push(format!("ldr r2, ={}", OFFSET + mem_loc));

                    if let Operator::PreIncr = op {
                        stmts.push(format!("add r2, r2, #1"));
                    } else {
                        stmts.push(format!("sub r2, r2, #1"));
                    }
                    stmts.push(format!("str r2, ={}", OFFSET + mem_loc));

                    return_mem = mem_loc;
                }
                Operator::PostIncr | Operator::PostDecr => {
                    let mem_loc = match e1.inside {
                        Expr::Identifier(ref name) => {
                            val = name.to_string();
                            symbol_tables.first().unwrap().get(name).unwrap().mem_loc
                        }
                        _ => 0,
                    };
                    stmts.push(format!("ldr r2, ={}", OFFSET + mem_loc));
                    stmts.push(format!("str r2, ={}", OFFSET + curr_mem_loc));
                    if let Operator::PostIncr = op {
                        stmts.push(format!("add r2, r2, #1"));
                    } else {
                        stmts.push(format!("sub r2, r2, #1"));
                    }
                    stmts.push(format!("str r2, ={}", OFFSET + mem_loc));
                    return_mem = curr_mem_loc;
                }
                Operator::Negate => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_val));
                    stmts.push(format!("rsb r2, r2, #0"));
                    stmts.push(format!("str r2, ={}", OFFSET + curr_mem_loc));
                }
                Operator::Positive => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    return_mem = e1_val;
                }
                Operator::Not => {
                    let e1_val = compile_expr(&*e1, symbol_tables, stmts, count);
                    stmts.push(format!("ldr r2, ={}", OFFSET + e1_val));
                    stmts.push(format!("cmp r2, #0"));
                    stmts.push(format!("moveq r3, #1"));
                    stmts.push(format!("movne r3, #0"));
                    stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
                }
                _ => (),
            }
        }
        Expr::Assignment(ref lhs, ref rhs) => {
            let mem_loc = match lhs.inside {
                Expr::Identifier(ref name) => {
                    val = name.to_string();
                    symbol_tables.first().unwrap().get(name).unwrap().mem_loc
                }
                _ => 0,
            };
            let rhs_val = compile_expr(&*rhs, symbol_tables, stmts, count);
            stmts.push(format!("ldr r3, ={}", OFFSET + rhs_val));
            stmts.push(format!("str r3, ={}", OFFSET + mem_loc));
        }
        Expr::Identifier(ref name) => {
            val = name.to_string();
            if let Some(symbol_table) = symbol_tables.first_mut() {
                if let Some(symbol) = symbol_table.get(name) {
                    return_mem = symbol.mem_loc;
                }
            }
        }
        Expr::Number(num) => {
            val = num.to_string();
            stmts.push(format!(".equ const{}, {}", curr_mem_loc, num));
            stmts.push(format!("ldr r3, =const{}", curr_mem_loc));
            stmts.push(format!("str r3, ={}", OFFSET + curr_mem_loc));
        }
        _ => (),
    };

    let curr_symbol = SymbolEntry::new(expr.byte_offset, val, count);
    if let Some(symbol_table) = symbol_tables.first_mut() {
        symbol_table.insert(expr.byte_offset.to_string(), curr_symbol.clone());
    }

    return_mem
}
<|endoftext|>


<commit_before>//! Key constants

/// Input key pressed and modifiers
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyEvent(pub KeyCode, pub Modifiers);

impl KeyEvent {
    /// Constant value representing an unmodified press of `KeyCode::Backspace`.
    pub(crate) const BACKSPACE: Self = Self(KeyCode::Backspace, Modifiers::NONE);
    /// Constant value representing an unmodified press of `KeyCode::Enter`.
    pub(crate) const ENTER: Self = Self(KeyCode::Enter, Modifiers::NONE);
    /// Constant value representing an unmodified press of `KeyCode::Esc`.
    pub(crate) const ESC: Self = Self(KeyCode::Esc, Modifiers::NONE);

    /// Constructor from `char` and modifiers
    pub fn new(c: char, mut mods: Modifiers) -> Self {
        use {KeyCode as K, KeyEvent as E, Modifiers as M};

        if !c.is_control() {
            if !mods.is_empty() {
                mods.remove(M::SHIFT); // TODO Validate: no SHIFT even if
                                       // `c` is uppercase
            }
            return E(K::Char(c), mods);
        }
        #[allow(clippy::match_same_arms)]
        match c {
            '\x00' => E(K::Char('@'), mods | M::CTRL), // '\0'
            '\x01' => E(K::Char('A'), mods | M::CTRL),
            '\x02' => E(K::Char('B'), mods | M::CTRL),
            '\x03' => E(K::Char('C'), mods | M::CTRL),
            '\x04' => E(K::Char('D'), mods | M::CTRL),
            '\x05' => E(K::Char('E'), mods | M::CTRL),
            '\x06' => E(K::Char('F'), mods | M::CTRL),
            '\x07' => E(K::Char('G'), mods | M::CTRL), // '\a'
            #[cfg(unix)]
            '\x08' => E(K::Backspace, mods), // '\b'
            #[cfg(windows)]
            '\x08' => E(K::Char('H'), mods | M::CTRL),
            #[cfg(unix)]
            '\x09' => {
                // '\t'
                if mods.contains(M::SHIFT) {
                    mods.remove(M::SHIFT);
                    E(K::BackTab, mods)
                } else {
                    E(K::Tab, mods)
                }
            }
            #[cfg(windows)]
            '\x09' => E(K::Char('I'), mods | M::CTRL),
            '\x0a' => E(K::Char('J'), mods | M::CTRL), // '\n' (10)
            '\x0b' => E(K::Char('K'), mods | M::CTRL),
            '\x0c' => E(K::Char('L'), mods | M::CTRL),
            #[cfg(unix)]
            '\x0d' => E(K::Enter, mods), // '\r' (13)
            #[cfg(windows)]
            '\x0d' => E(K::Char('M'), mods | M::CTRL),
            '\x0e' => E(K::Char('N'), mods | M::CTRL),
            '\x0f' => E(K::Char('O'), mods | M::CTRL),
            '\x10' => E(K::Char('P'), mods | M::CTRL),
            '\x11' => E(K::Char('Q'), mods | M::CTRL),
            '\x12' => E(K::Char('R'), mods | M::CTRL),
            '\x13' => E(K::Char('S'), mods | M::CTRL),
            '\x14' => E(K::Char('T'), mods | M::CTRL),
            '\x15' => E(K::Char('U'), mods | M::CTRL),
            '\x16' => E(K::Char('V'), mods | M::CTRL),
            '\x17' => E(K::Char('W'), mods | M::CTRL),
            '\x18' => E(K::Char('X'), mods | M::CTRL),
            '\x19' => E(K::Char('Y'), mods | M::CTRL),
            '\x1a' => E(K::Char('Z'), mods | M::CTRL),
            '\x1b' => E(K::Esc, mods), // Ctrl-[, '\e'
            '\x1c' => E(K::Char('\\'), mods | M::CTRL),
            '\x1d' => E(K::Char(']'), mods | M::CTRL),
            '\x1e' => E(K::Char('^'), mods | M::CTRL),
            '\x1f' => E(K::Char('_'), mods | M::CTRL),
            '\x7f' => E(K::Backspace, mods), // Rubout, Ctrl-?
            '\u{9b}' => E(K::Esc, mods | M::SHIFT),
            _ => E(K::Null, mods),
        }
    }

    /// Constructor from `char` with Ctrl modifier
    pub fn ctrl(c: char) -> Self {
        Self::new(c, Modifiers::CTRL)
    }

    /// Constructor from `char` with Alt modifier
    pub fn alt(c: char) -> Self {
        Self::new(c, Modifiers::ALT)
    }

    /// ctrl-a => ctrl-A (uppercase)
    /// shift-A => A (no SHIFT modifier)
    /// shift-Tab => BackTab
    pub fn normalize(e: Self) -> Self {
        use {KeyCode as K, KeyEvent as E, Modifiers as M};

        match e {
            E(K::Char(c), m) if c.is_ascii_control() => Self::new(c, m),
            E(K::Char(c), m) if c.is_ascii_lowercase() && m.contains(M::CTRL) => {
                E(K::Char(c.to_ascii_uppercase()), m)
            }
            E(K::Char(c), m) if c.is_ascii_uppercase() && m.contains(M::SHIFT) => {
                E(K::Char(c), m ^ M::SHIFT)
            }
            E(K::Tab, m) if m.contains(M::SHIFT) => E(K::BackTab, m ^ M::SHIFT),
            _ => e,
        }
    }
}

impl From<char> for KeyEvent {
    fn from(c: char) -> Self {
        Self::new(c, Modifiers::NONE)
    }
}

/// Input key pressed
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum KeyCode {
    /// Unsupported escape sequence (on unix platform)
    UnknownEscSeq,
    /// ⌫ or Ctrl-H
    Backspace,
    /// ⇤ (usually Shift-Tab)
    BackTab,
    /// Paste (on unix platform)
    BracketedPasteStart,
    /// Paste (on unix platform)
    BracketedPasteEnd,
    /// Single char
    Char(char),
    /// ⌦
    Delete,
    /// ↓ arrow key
    Down,
    /// ⇲
    End,
    /// ↵ or Ctrl-M
    Enter,
    /// Escape or Ctrl-[
    Esc,
    /// Function key
    F(u8),
    /// ⇱
    Home,
    /// Insert key
    Insert,
    /// ← arrow key
    Left,
    /// \0
    Null,
    /// ⇟
    PageDown,
    /// ⇞
    PageUp,
    /// → arrow key
    Right,
    /// ⇥ or Ctrl-I
    Tab,
    /// ↑ arrow key
    Up,
}

bitflags::bitflags! {
    /// The set of modifier keys that were triggered along with a key press.
    pub struct Modifiers: u8 {
        /// Control modifier
        const CTRL  = 1<<3;
        /// Escape or Alt modifier
        const ALT  = 1<<2;
        /// Shift modifier
        const SHIFT = 1<<1;

        /// No modifier
        const NONE = 0;
        /// Ctrl + Shift
        const CTRL_SHIFT = Self::CTRL.bits | Self::SHIFT.bits;
        /// Alt + Shift
        const ALT_SHIFT = Self::ALT.bits | Self::SHIFT.bits;
        /// Ctrl + Alt
        const CTRL_ALT = Self::CTRL.bits | Self::ALT.bits;
        /// Ctrl + Alt + Shift
        const CTRL_ALT_SHIFT = Self::CTRL.bits | Self::ALT.bits | Self::SHIFT.bits;
    }
}

#[cfg(test)]
mod tests {
    use super::{KeyCode as K, KeyEvent as E, Modifiers as M};

    #[test]
    fn new() {
        assert_eq!(E::ESC, E::new('\x1b', M::NONE));
    }

    #[test]
    fn from() {
        assert_eq!(E(K::Tab, M::NONE), E::from('\t'));
    }

    #[test]
    fn normalize() {
        assert_eq!(E::ctrl('A'), E::normalize(E(K::Char('\x01'), M::NONE)));
        assert_eq!(E::ctrl('A'), E::normalize(E::ctrl('a')));
        assert_eq!(E::from('A'), E::normalize(E(K::Char('A'), M::SHIFT)));
        assert_eq!(E(K::BackTab, M::NONE), E::normalize(E(K::Tab, M::SHIFT)));
    }
}
<commit_msg>Fix test on windows<commit_after>//! Key constants

/// Input key pressed and modifiers
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyEvent(pub KeyCode, pub Modifiers);

impl KeyEvent {
    /// Constant value representing an unmodified press of `KeyCode::Backspace`.
    pub(crate) const BACKSPACE: Self = Self(KeyCode::Backspace, Modifiers::NONE);
    /// Constant value representing an unmodified press of `KeyCode::Enter`.
    pub(crate) const ENTER: Self = Self(KeyCode::Enter, Modifiers::NONE);
    /// Constant value representing an unmodified press of `KeyCode::Esc`.
    pub(crate) const ESC: Self = Self(KeyCode::Esc, Modifiers::NONE);

    /// Constructor from `char` and modifiers
    pub fn new(c: char, mut mods: Modifiers) -> Self {
        use {KeyCode as K, KeyEvent as E, Modifiers as M};

        if !c.is_control() {
            if !mods.is_empty() {
                mods.remove(M::SHIFT); // TODO Validate: no SHIFT even if
                                       // `c` is uppercase
            }
            return E(K::Char(c), mods);
        }
        #[allow(clippy::match_same_arms)]
        match c {
            '\x00' => E(K::Char('@'), mods | M::CTRL), // '\0'
            '\x01' => E(K::Char('A'), mods | M::CTRL),
            '\x02' => E(K::Char('B'), mods | M::CTRL),
            '\x03' => E(K::Char('C'), mods | M::CTRL),
            '\x04' => E(K::Char('D'), mods | M::CTRL),
            '\x05' => E(K::Char('E'), mods | M::CTRL),
            '\x06' => E(K::Char('F'), mods | M::CTRL),
            '\x07' => E(K::Char('G'), mods | M::CTRL), // '\a'
            #[cfg(unix)]
            '\x08' => E(K::Backspace, mods), // '\b'
            #[cfg(windows)]
            '\x08' => E(K::Char('H'), mods | M::CTRL),
            #[cfg(unix)]
            '\x09' => {
                // '\t'
                if mods.contains(M::SHIFT) {
                    mods.remove(M::SHIFT);
                    E(K::BackTab, mods)
                } else {
                    E(K::Tab, mods)
                }
            }
            #[cfg(windows)]
            '\x09' => E(K::Char('I'), mods | M::CTRL),
            '\x0a' => E(K::Char('J'), mods | M::CTRL), // '\n' (10)
            '\x0b' => E(K::Char('K'), mods | M::CTRL),
            '\x0c' => E(K::Char('L'), mods | M::CTRL),
            #[cfg(unix)]
            '\x0d' => E(K::Enter, mods), // '\r' (13)
            #[cfg(windows)]
            '\x0d' => E(K::Char('M'), mods | M::CTRL),
            '\x0e' => E(K::Char('N'), mods | M::CTRL),
            '\x0f' => E(K::Char('O'), mods | M::CTRL),
            '\x10' => E(K::Char('P'), mods | M::CTRL),
            '\x11' => E(K::Char('Q'), mods | M::CTRL),
            '\x12' => E(K::Char('R'), mods | M::CTRL),
            '\x13' => E(K::Char('S'), mods | M::CTRL),
            '\x14' => E(K::Char('T'), mods | M::CTRL),
            '\x15' => E(K::Char('U'), mods | M::CTRL),
            '\x16' => E(K::Char('V'), mods | M::CTRL),
            '\x17' => E(K::Char('W'), mods | M::CTRL),
            '\x18' => E(K::Char('X'), mods | M::CTRL),
            '\x19' => E(K::Char('Y'), mods | M::CTRL),
            '\x1a' => E(K::Char('Z'), mods | M::CTRL),
            '\x1b' => E(K::Esc, mods), // Ctrl-[, '\e'
            '\x1c' => E(K::Char('\\'), mods | M::CTRL),
            '\x1d' => E(K::Char(']'), mods | M::CTRL),
            '\x1e' => E(K::Char('^'), mods | M::CTRL),
            '\x1f' => E(K::Char('_'), mods | M::CTRL),
            '\x7f' => E(K::Backspace, mods), // Rubout, Ctrl-?
            '\u{9b}' => E(K::Esc, mods | M::SHIFT),
            _ => E(K::Null, mods),
        }
    }

    /// Constructor from `char` with Ctrl modifier
    pub fn ctrl(c: char) -> Self {
        Self::new(c, Modifiers::CTRL)
    }

    /// Constructor from `char` with Alt modifier
    pub fn alt(c: char) -> Self {
        Self::new(c, Modifiers::ALT)
    }

    /// ctrl-a => ctrl-A (uppercase)
    /// shift-A => A (no SHIFT modifier)
    /// shift-Tab => BackTab
    pub fn normalize(e: Self) -> Self {
        use {KeyCode as K, KeyEvent as E, Modifiers as M};

        match e {
            E(K::Char(c), m) if c.is_ascii_control() => Self::new(c, m),
            E(K::Char(c), m) if c.is_ascii_lowercase() && m.contains(M::CTRL) => {
                E(K::Char(c.to_ascii_uppercase()), m)
            }
            E(K::Char(c), m) if c.is_ascii_uppercase() && m.contains(M::SHIFT) => {
                E(K::Char(c), m ^ M::SHIFT)
            }
            E(K::Tab, m) if m.contains(M::SHIFT) => E(K::BackTab, m ^ M::SHIFT),
            _ => e,
        }
    }
}

impl From<char> for KeyEvent {
    fn from(c: char) -> Self {
        Self::new(c, Modifiers::NONE)
    }
}

/// Input key pressed
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum KeyCode {
    /// Unsupported escape sequence (on unix platform)
    UnknownEscSeq,
    /// ⌫ or Ctrl-H
    Backspace,
    /// ⇤ (usually Shift-Tab)
    BackTab,
    /// Paste (on unix platform)
    BracketedPasteStart,
    /// Paste (on unix platform)
    BracketedPasteEnd,
    /// Single char
    Char(char),
    /// ⌦
    Delete,
    /// ↓ arrow key
    Down,
    /// ⇲
    End,
    /// ↵ or Ctrl-M
    Enter,
    /// Escape or Ctrl-[
    Esc,
    /// Function key
    F(u8),
    /// ⇱
    Home,
    /// Insert key
    Insert,
    /// ← arrow key
    Left,
    /// \0
    Null,
    /// ⇟
    PageDown,
    /// ⇞
    PageUp,
    /// → arrow key
    Right,
    /// ⇥ or Ctrl-I
    Tab,
    /// ↑ arrow key
    Up,
}

bitflags::bitflags! {
    /// The set of modifier keys that were triggered along with a key press.
    pub struct Modifiers: u8 {
        /// Control modifier
        const CTRL  = 1<<3;
        /// Escape or Alt modifier
        const ALT  = 1<<2;
        /// Shift modifier
        const SHIFT = 1<<1;

        /// No modifier
        const NONE = 0;
        /// Ctrl + Shift
        const CTRL_SHIFT = Self::CTRL.bits | Self::SHIFT.bits;
        /// Alt + Shift
        const ALT_SHIFT = Self::ALT.bits | Self::SHIFT.bits;
        /// Ctrl + Alt
        const CTRL_ALT = Self::CTRL.bits | Self::ALT.bits;
        /// Ctrl + Alt + Shift
        const CTRL_ALT_SHIFT = Self::CTRL.bits | Self::ALT.bits | Self::SHIFT.bits;
    }
}

#[cfg(test)]
mod tests {
    use super::{KeyCode as K, KeyEvent as E, Modifiers as M};

    #[test]
    fn new() {
        assert_eq!(E::ESC, E::new('\x1b', M::NONE));
    }

    #[test]
    #[cfg(unix)]
    fn from() {
        assert_eq!(E(K::Tab, M::NONE), E::from('\t'));
    }

    #[test]
    #[cfg(windows)]
    fn from() {
        assert_eq!(E(K::Char('I'), M::CTRL), E::from('\t'));
    }

    #[test]
    fn normalize() {
        assert_eq!(E::ctrl('A'), E::normalize(E(K::Char('\x01'), M::NONE)));
        assert_eq!(E::ctrl('A'), E::normalize(E::ctrl('a')));
        assert_eq!(E::from('A'), E::normalize(E(K::Char('A'), M::SHIFT)));
        assert_eq!(E(K::BackTab, M::NONE), E::normalize(E(K::Tab, M::SHIFT)));
    }
}
<|endoftext|>


<commit_before>use lisp::LispExpr;
use lisp;
use rusqlite as sql;
use rustc::lint::{LateContext, LintArray, LintContext, LintPass, LateLintPass};
use rustc::middle::ty::TypeVariants;
use rustc_front::hir::*;
use syntax::ast::FloatTy;

#[derive(Debug)]
pub struct Herbie {
    pub subs: Option<Vec<(LispExpr, LispExpr)>>,
}

impl Herbie {

    pub fn new() -> Herbie {
        Herbie {
            subs: None,
        }
    }

    pub fn init(&mut self) -> Result<(), sql::Error> {
        let conn = try!(sql::Connection::open_with_flags("Herbie.db", sql::SQLITE_OPEN_READ_ONLY));
        let mut query = try!(conn.prepare("SELECT * FROM HerbieResults"));

        self.subs = Some(try!(query.query(&[])).filter_map(|row| {
            match row {
                Ok(row) => {
                    let cmdin : String = row.get(1);
                    let cmdout : String = row.get(2);
                    // row.get(3) is opts ↔ Herbies options
                    let errin = row.get_checked(4).unwrap_or(0.);
                    let errout = row.get_checked(5).unwrap_or(0.);

                    if cmdin == cmdout || errin <= errout {
                        return None;
                    }

                    let mut parser = lisp::Parser::new();
                    match parser.parse(&cmdin) {
                        Ok(cmdin) => {
                            match parser.parse(&cmdout) {
                                Ok(cmdout) => {
                                    Some((cmdin, cmdout))
                                }
                                Err(..) => None,
                            }
                        }
                        Err(..) => None,
                    }
                }
                Err(..) => None,
            }
        }).collect());

        Ok(())
    }

}

declare_lint!(pub HERBIE, Warn,
              "checks for numerical instability");

impl LintPass for Herbie {
    fn get_lints(&self) -> LintArray {
        lint_array!(HERBIE)
    }
}

impl LateLintPass for Herbie {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        let ty = cx.tcx.expr_ty(expr);

        if ty.sty != TypeVariants::TyFloat(FloatTy::TyF64) {
            return;
        }

        if self.subs.is_none() {
            self.init().expect("Could not initialize Herbie-Lint");
        }

        for &(ref cmdin, ref cmdout) in self.subs.as_ref().unwrap() {
            if let Some(bindings) = LispExpr::match_expr(expr, cmdin) {
                cx.span_lint(HERBIE, expr.span, "Numerically unstable expression");
                cx.sess().span_suggestion(expr.span, "Try this", cmdout.to_rust(cx, &bindings));
            }
        }
    }
}
<commit_msg>Add better error message on SQL errors<commit_after>use lisp::LispExpr;
use lisp;
use rusqlite as sql;
use rustc::lint::{LateContext, LintArray, LintContext, LintPass, LateLintPass};
use rustc::middle::ty::TypeVariants;
use rustc_front::hir::*;
use syntax::ast::FloatTy;

#[derive(Debug)]
pub struct Herbie {
    initialized: bool,
    subs: Vec<(LispExpr, LispExpr)>,
}

impl Herbie {

    pub fn new() -> Herbie {
        Herbie {
            initialized: false,
            subs: Vec::new(),
        }
    }

    pub fn init(&mut self) -> Result<(), sql::Error> {
        if self.initialized {
            return Ok(())
        }

        self.initialized = true;

        let conn = try!(sql::Connection::open_with_flags("Herbie.db", sql::SQLITE_OPEN_READ_ONLY));
        let mut query = try!(conn.prepare("SELECT * FROM HerbieResults"));

        self.subs = try!(query.query(&[])).filter_map(|row| {
            match row {
                Ok(row) => {
                    let cmdin : String = row.get(1);
                    let cmdout : String = row.get(2);
                    // row.get(3) is opts ↔ Herbies options
                    let errin = row.get_checked(4).unwrap_or(0.);
                    let errout = row.get_checked(5).unwrap_or(0.);

                    if cmdin == cmdout || errin <= errout {
                        return None;
                    }

                    let mut parser = lisp::Parser::new();
                    match parser.parse(&cmdin) {
                        Ok(cmdin) => {
                            match parser.parse(&cmdout) {
                                Ok(cmdout) => {
                                    Some((cmdin, cmdout))
                                }
                                Err(..) => None,
                            }
                        }
                        Err(..) => None,
                    }
                }
                Err(..) => None,
            }
        }).collect();

        Ok(())
    }

}

declare_lint!(pub HERBIE, Warn,
              "checks for numerical instability");

impl LintPass for Herbie {
    fn get_lints(&self) -> LintArray {
        lint_array!(HERBIE)
    }
}

impl LateLintPass for Herbie {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        let ty = cx.tcx.expr_ty(expr);

        if ty.sty != TypeVariants::TyFloat(FloatTy::TyF64) {
            return;
        }

        if let Err(err) =  self.init() {
            cx.span_lint_note(
                HERBIE,
                cx.krate.span,
                "Could not initialize Herbie-Lint",
                cx.krate.span,
                &format!("Got SQL error: {}", err)
            );
        }

        for &(ref cmdin, ref cmdout) in &self.subs {
            if let Some(bindings) = LispExpr::match_expr(expr, cmdin) {
                cx.span_lint(HERBIE, expr.span, "Numerically unstable expression");
                cx.sess().span_suggestion(expr.span, "Try this", cmdout.to_rust(cx, &bindings));
            }
        }
    }
}
<|endoftext|>