<commit_before>use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::{ContextOf, AContext};
use framebuffer::FramebufferBinding;
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, BufferBinding,
             ArrayBufferBinding, ArrayBufferContext,
             ElementArrayBufferBinding, ElementArrayBufferContext};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    // TODO: Implement this safely (take a generic `C` that has a trait bound)
    pub unsafe fn enable<V>(&self) -> Result<(), AttribError>
        where V: VertexData
    {
        let gl = Context::current_context();
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V: VertexData>(&self, gl_buffer: &ArrayBufferBinding)
        -> Result<(), AttribError>
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                gl_buffer.vertex_attrib_pointer(
                    program_attrib,
                    vertex_attrib.ty.components,
                    vertex_attrib.ty.data,
                    vertex_attrib.ty.normalize,
                    vertex_attrib.stride,
                    vertex_attrib.offset
                );
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const VertexBuffer<T>>
}

impl<'a, T: VertexData + 'a> VertexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }
}

impl<'a> FramebufferBinding<'a> {
    pub fn draw_arrays_range_vbo<V>(&mut self,
                                    gl_vbo: &VertexBufferBinding<V>,
                                    mode: DrawingMode,
                                    start: u32,
                                    length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= *gl_vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    pub fn draw_arrays_vbo<V>(&mut self,
                              gl_vbo: &VertexBufferBinding<V>,
                              mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   *gl_vbo.count);
        }
    }

    pub fn draw_n_elements_buffered_vbo<V, I>(&mut self,
                                              gl_vbo: &VertexBufferBinding<V>,
                                              gl_ibo: &IndexBufferBinding<I>,
                                              mode: DrawingMode,
                                              length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= *gl_ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    pub fn draw_elements_buffered_vbo<V, I>(&mut self,
                                            gl_vbo: &VertexBufferBinding<V>,
                                            gl_ibo: &IndexBufferBinding<I>,
                                            mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          *gl_ibo.count,
                                          I::index_datum_type());
        }
    }

    pub fn draw_n_elements_vbo<V, I>(&mut self,
                                     gl_vbo: &VertexBufferBinding<'a, V>,
                                     mode: DrawingMode,
                                     count: usize,
                                     indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    pub fn draw_elements_vbo<V, I>(&mut self,
                                   gl_vbo: &VertexBufferBinding<'a, V>,
                                   mode: DrawingMode,
                                   indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

pub trait VertexBufferContext {
    type Rest;

    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData;
}

impl<C> VertexBufferContext for C
    where C: ArrayBufferContext
{
    type Rest = C::Rest;

    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData
    {
        // TODO: Cleanup error handling
        let (gl_array_buffer, rest) = match vbo.attrib_binder {
            Some(ref binder) => {
                let buf = &mut vbo.buffer;
                let (gl_buffer, rest) = self.bind_array_buffer(buf);
                unsafe { binder.enable::<V>().unwrap() };
                binder.bind::<V>(&gl_buffer).unwrap();
                (gl_buffer, rest)
            },
            None => {
                panic!("No attribute bindings provided for vertex buffer");
            }
        };

        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                count: &mut vbo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub trait IndexBufferContext {
    type Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum;
}

impl<C> IndexBufferContext for C
    where C: ElementArrayBufferContext
{
    type Rest = C::Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum
    {
        let (gl_be, rest) = self.bind_element_array_buffer(&mut ibo.buffer);
        (
            IndexBufferBinding {
                gl_buffer: gl_be,
                count: &mut ibo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const IndexBuffer<T>>
}

impl<'a, T: IndexDatum + 'a> IndexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: IndexData
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.index_bytes(), usage);
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<commit_msg>Implement `AttribBinder.enable()` safely<commit_after>use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::{ContextOf, AContext};
use framebuffer::FramebufferBinding;
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, BufferBinding,
             ArrayBufferBinding, ArrayBufferContext,
             ElementArrayBufferBinding, ElementArrayBufferContext};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    pub fn enable<V, C>(&self, gl: &mut C) -> Result<(), AttribError>
        where V: VertexData, C: AContext
    {
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V: VertexData>(&self, gl_buffer: &ArrayBufferBinding)
        -> Result<(), AttribError>
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                gl_buffer.vertex_attrib_pointer(
                    program_attrib,
                    vertex_attrib.ty.components,
                    vertex_attrib.ty.data,
                    vertex_attrib.ty.normalize,
                    vertex_attrib.stride,
                    vertex_attrib.offset
                );
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const VertexBuffer<T>>
}

impl<'a, T: VertexData + 'a> VertexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }
}

impl<'a> FramebufferBinding<'a> {
    pub fn draw_arrays_range_vbo<V>(&mut self,
                                    gl_vbo: &VertexBufferBinding<V>,
                                    mode: DrawingMode,
                                    start: u32,
                                    length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= *gl_vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    pub fn draw_arrays_vbo<V>(&mut self,
                              gl_vbo: &VertexBufferBinding<V>,
                              mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   *gl_vbo.count);
        }
    }

    pub fn draw_n_elements_buffered_vbo<V, I>(&mut self,
                                              gl_vbo: &VertexBufferBinding<V>,
                                              gl_ibo: &IndexBufferBinding<I>,
                                              mode: DrawingMode,
                                              length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= *gl_ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    pub fn draw_elements_buffered_vbo<V, I>(&mut self,
                                            gl_vbo: &VertexBufferBinding<V>,
                                            gl_ibo: &IndexBufferBinding<I>,
                                            mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          *gl_ibo.count,
                                          I::index_datum_type());
        }
    }

    pub fn draw_n_elements_vbo<V, I>(&mut self,
                                     gl_vbo: &VertexBufferBinding<'a, V>,
                                     mode: DrawingMode,
                                     count: usize,
                                     indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    pub fn draw_elements_vbo<V, I>(&mut self,
                                   gl_vbo: &VertexBufferBinding<'a, V>,
                                   mode: DrawingMode,
                                   indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

pub trait VertexBufferContext {
    type Rest;

    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData;
}

impl<C> VertexBufferContext for C
    where C: ArrayBufferContext,
          C::Rest: AContext
{
    type Rest = C::Rest;

    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData
    {
        // TODO: Cleanup error handling
        let (gl_array_buffer, rest) = match vbo.attrib_binder {
            Some(ref binder) => {
                let buf = &mut vbo.buffer;
                let (gl_buffer, mut rest) = self.bind_array_buffer(buf);
                binder.enable::<V, _>(&mut rest).unwrap();
                binder.bind::<V>(&gl_buffer).unwrap();
                (gl_buffer, rest)
            },
            None => {
                panic!("No attribute bindings provided for vertex buffer");
            }
        };

        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                count: &mut vbo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub trait IndexBufferContext {
    type Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum;
}

impl<C> IndexBufferContext for C
    where C: ElementArrayBufferContext
{
    type Rest = C::Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum
    {
        let (gl_be, rest) = self.bind_element_array_buffer(&mut ibo.buffer);
        (
            IndexBufferBinding {
                gl_buffer: gl_be,
                count: &mut ibo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const IndexBuffer<T>>
}

impl<'a, T: IndexDatum + 'a> IndexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: IndexData
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.index_bytes(), usage);
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// 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.

use std::fmt;
use std::str::FromStr;
use syntax::{ast, ext};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic;
use syntax::{attr, codemap};
use syntax::parse::token;
use syntax::ptr::P;
use syntax::ext::base::ItemDecorator;

/// A component modifier.
#[derive(Copy, Clone, PartialEq)]
enum Modifier {
    /// Corresponds to the `#[normalized]` attribute.
    ///
    /// Normalizes the component at runtime. Unsigned integers are normalized to
    /// `[0, 1]`. Signed integers are normalized to `[-1, 1]`.
    Normalized,
    /// Corresponds to the `#[as_float]` attribute.
    ///
    /// Casts the component to a float precision floating-point number at runtime.
    AsFloat,
    /// Corresponds to the `#[as_double]` attribute.
    ///
    /// Specifies a high-precision float.
    AsDouble,
}

impl fmt::Debug for Modifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Modifier::Normalized => write!(f, "normalized"),
            Modifier::AsFloat => write!(f, "as_float"),
            Modifier::AsDouble => write!(f, "as_double"),
        }
    }
}

impl FromStr for Modifier {
    type Err = ();

    fn from_str(src: &str) -> Result<Modifier, ()> {
        match src {
            "normalized" => Ok(Modifier::Normalized),
            "as_float" => Ok(Modifier::AsFloat),
            "as_double" => Ok(Modifier::AsDouble),
            _ => Err(()),
        }
    }
}

/// Scan through the field's attributes and extract a relevant modifier. If
/// multiple modifier attributes are found, use the first modifier and emit a
/// warning.
fn find_modifier(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                 attributes: &[ast::Attribute]) -> Option<Modifier> {
    attributes.iter().fold(None, |modifier, attribute| {
        match attribute.node.value.node {
            ast::MetaWord(ref word) => {
                word.parse().ok().and_then(|new_modifier| {
                    attr::mark_used(attribute);
                    modifier.map_or(Some(new_modifier), |modifier| {
                        cx.span_warn(span, &format!(
                            "Extra attribute modifier detected: `#[{:?}]` - \
                            ignoring in favour of `#[{:?}]`.", new_modifier, modifier
                        ));
                        None
                    })
                }).or(modifier)
            },
            _ => modifier,
        }
    })
}

/// Find a `gfx::attrib::Type` that describes the given type identifier.
fn decode_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
               ty_ident: &ast::Ident, modifier: Option<Modifier>,
               path_root: ast::Ident) -> P<ast::Expr> {
    let ty_str = ty_ident.name.as_str();
    match ty_str {
        "f32" | "f64" => {
            let kind = cx.ident_of(match modifier {
                None | Some(Modifier::AsFloat) => "Default",
                Some(Modifier::AsDouble) => "Precision",
                Some(Modifier::Normalized) => {
                    cx.span_warn(span, &format!(
                        "Incompatible float modifier attribute: `#[{:?}]`", modifier
                    ));
                    ""
                }
            });
            let size = cx.ident_of(&format!("F{}", &ty_str[1..]));
            quote_expr!(cx, $path_root::gfx::attrib::Type::Float($path_root::gfx::attrib::FloatSubType::$kind,
                                                                 $path_root::gfx::attrib::FloatSize::$size))
        },
        "u8" | "u16" | "u32" | "u64" |
        "i8" | "i16" | "i32" | "i64" => {
            let sign = cx.ident_of({
                if ty_str.starts_with("i") { "Signed" } else { "Unsigned" }
            });
            let kind = cx.ident_of(match modifier {
                None => "Raw",
                Some(Modifier::Normalized) => "Normalized",
                Some(Modifier::AsFloat) => "AsFloat",
                Some(Modifier::AsDouble) => {
                    cx.span_warn(span, &format!(
                        "Incompatible int modifier attribute: `#[{:?}]`", modifier
                    ));
                    ""
                }
            });
            let size = cx.ident_of(&format!("U{}", &ty_str[1..]));
            quote_expr!(cx, $path_root::gfx::attrib::Type::Int($path_root::gfx::attrib::IntSubType::$kind,
                                                               $path_root::gfx::attrib::IntSize::$size,
                                                               $path_root::gfx::attrib::SignFlag::$sign))
        },
        ty_str => {
            cx.span_err(span, &format!("Unrecognized component type: `{:?}`",
                                      ty_str));
            cx.expr_tuple(span, vec![])
        },
    }
}

fn decode_count_and_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                         field: &ast::StructField,
                         path_root: ast::Ident) -> (P<ast::Expr>, P<ast::Expr>) {
    let modifier = find_modifier(cx, span, &field.node.attrs);
    match field.node.ty.node {
        ast::TyPath(_,ref p) => (
            cx.expr_lit(span, ast::LitInt(1, ast::UnsuffixedIntLit(ast::Plus))),
            decode_type(cx, span, &p.segments[0].identifier, modifier, path_root),
        ),
        ast::TyFixedLengthVec(ref pty, ref expr) => (expr.clone(), match pty.node {
            ast::TyPath(_,ref p) => {
                decode_type(cx, span, &p.segments[0].identifier, modifier, path_root)
            },
            _ => {
                cx.span_err(span, &format!("Unsupported fixed vector sub-type: \
                                          `{:?}`",pty.node));
                cx.expr_tuple(span, vec![])
            },
        }),
        _ => {
            cx.span_err(span, &format!("Unsupported attribute type: `{:?}`",
                                      field.node.ty.node));
            (cx.expr_tuple(span, vec![]), cx.expr_tuple(span, vec![]))
        },
    }
}

/// Generates the the method body for `gfx::VertexFormat::generate`.
fn method_body(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                   substr: &generic::Substructure,
                   path_root: ast::Ident) -> P<ast::Expr> {
    match *substr.fields {
        generic::StaticStruct(ref definition, generic::Named(ref fields)) => {
            let attribute_pushes = definition.fields.iter().zip(fields.iter())
                .map(|(def, &(ident, _))| {
                    let struct_ident = substr.type_ident;
                    let buffer_expr = &substr.nonself_args[0];
                    let (count_expr, type_expr) = decode_count_and_type(cx, span, def, path_root);
                    let ident_str = match super::find_name(cx, span, &def.node.attrs) {
                        Some(name) => name,
                        None => token::get_ident(ident),
                    };
                    let ident_str = &ident_str[..];
                    let instance_expr = cx.expr_u8(span, 0); // not supposed to be set by the macro
                    quote_expr!(cx, {
                        attributes.push($path_root::gfx::Attribute {
                            name: $ident_str.to_string(),
                            buffer: $buffer_expr.clone(),
                            format: $path_root::gfx::attrib::Format {
                                elem_count: $count_expr,
                                elem_type: $type_expr,
                                offset: unsafe {
                                    let x: $struct_ident = ::std::mem::uninitialized();
                                    let offset = (&x.$ident as *const _ as usize) -
                                        (&x as *const _ as usize);
                                    ::std::mem::forget(x);
                                    offset as $path_root::gfx::attrib::Offset
                                },
                                stride: { use std::mem;
                                    mem::size_of::<$struct_ident>() as
                                        $path_root::gfx::attrib::Stride
                                },
                                instance_rate: $instance_expr,
                            }
                        });
                    })
                }).collect::<Vec<P<ast::Expr>>>();
            let capacity = fields.len();
            quote_expr!(cx, {
                let mut attributes = Vec::with_capacity($capacity);
                $attribute_pushes;
                attributes
            })
        },
        _ => {
            cx.span_err(span, "Unable to implement `gfx::VertexFormat::generate` \
                              on a non-structure");
            cx.expr_tuple(span, vec![])
        }
    }
}

#[derive(Copy, Clone)]
pub struct VertexFormat;

impl ItemDecorator for VertexFormat {
    /// Derive a `gfx::VertexFormat` implementation for the `struct`
    fn expand(&self, context: &mut ext::base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: &mut FnMut(P<ast::Item>)) {
        // Insert the `gfx` reexport module
        let path_root = super::extern_crate_hack(context, span, |i| (*push)(i));
        let fixup = |item| {
            (*push)(super::fixup_extern_crate_paths(item, path_root))
        };

        // `impl<R: gfx::Resources> gfx::VertexFormat for $item`
        generic::TraitDef {
            span: span,
            attributes: Vec::new(),
            path: generic::ty::Path::new(
                vec![super::EXTERN_CRATE_HACK, "gfx", "VertexFormat"],
            ),
            additional_bounds: Vec::new(),
            generics: generic::ty::LifetimeBounds::empty(),
            methods: vec![
                // `fn generate(gfx::RawBufferHandle) -> Vec<gfx::Attribute>`
                generic::MethodDef {
                    name: "generate",
                    generics: generic::ty::LifetimeBounds {
                        lifetimes: Vec::new(),
                        bounds: vec![
                            ("R", vec![
                                generic::ty::Path::new(vec![
                                    super::EXTERN_CRATE_HACK, "gfx", "Resources"
                                ]),
                            ]),
                        ],
                    },
                    explicit_self: None,
                    args: vec![
                        generic::ty::Literal(generic::ty::Path {
                            path: vec![super::EXTERN_CRATE_HACK, "gfx", "RawBufferHandle"],
                            lifetime: None,
                            params: vec![box generic::ty::Literal(generic::ty::Path::new_local("R"))],
                            global: false,
                        }),
                    ],
                    ret_ty: generic::ty::Literal(
                        generic::ty::Path {
                            path: vec!["Vec"],
                            lifetime: None,
                            params: vec![
                                box generic::ty::Literal(generic::ty::Path {
                                    path: vec![super::EXTERN_CRATE_HACK, "gfx", "Attribute"],
                                    lifetime: None,
                                    params: vec![box generic::ty::Literal(
                                        generic::ty::Path::new_local("R")
                                    )],
                                    global: false,
                                }),
                            ],
                            global: false,
                        },
                    ),
                    attributes: Vec::new(),
                    // generate the method body
                    combine_substructure: generic::combine_substructure(
                        box |c, s, ss| method_body(c, s, ss, path_root)),
                },
            ],
            associated_types: Vec::new(),
        }.expand(context, meta_item, item, fixup);
    }
}
<commit_msg>fixup -> &mut fixup<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// 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.

use std::fmt;
use std::str::FromStr;
use syntax::{ast, ext};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic;
use syntax::{attr, codemap};
use syntax::parse::token;
use syntax::ptr::P;
use syntax::ext::base::ItemDecorator;

/// A component modifier.
#[derive(Copy, Clone, PartialEq)]
enum Modifier {
    /// Corresponds to the `#[normalized]` attribute.
    ///
    /// Normalizes the component at runtime. Unsigned integers are normalized to
    /// `[0, 1]`. Signed integers are normalized to `[-1, 1]`.
    Normalized,
    /// Corresponds to the `#[as_float]` attribute.
    ///
    /// Casts the component to a float precision floating-point number at runtime.
    AsFloat,
    /// Corresponds to the `#[as_double]` attribute.
    ///
    /// Specifies a high-precision float.
    AsDouble,
}

impl fmt::Debug for Modifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Modifier::Normalized => write!(f, "normalized"),
            Modifier::AsFloat => write!(f, "as_float"),
            Modifier::AsDouble => write!(f, "as_double"),
        }
    }
}

impl FromStr for Modifier {
    type Err = ();

    fn from_str(src: &str) -> Result<Modifier, ()> {
        match src {
            "normalized" => Ok(Modifier::Normalized),
            "as_float" => Ok(Modifier::AsFloat),
            "as_double" => Ok(Modifier::AsDouble),
            _ => Err(()),
        }
    }
}

/// Scan through the field's attributes and extract a relevant modifier. If
/// multiple modifier attributes are found, use the first modifier and emit a
/// warning.
fn find_modifier(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                 attributes: &[ast::Attribute]) -> Option<Modifier> {
    attributes.iter().fold(None, |modifier, attribute| {
        match attribute.node.value.node {
            ast::MetaWord(ref word) => {
                word.parse().ok().and_then(|new_modifier| {
                    attr::mark_used(attribute);
                    modifier.map_or(Some(new_modifier), |modifier| {
                        cx.span_warn(span, &format!(
                            "Extra attribute modifier detected: `#[{:?}]` - \
                            ignoring in favour of `#[{:?}]`.", new_modifier, modifier
                        ));
                        None
                    })
                }).or(modifier)
            },
            _ => modifier,
        }
    })
}

/// Find a `gfx::attrib::Type` that describes the given type identifier.
fn decode_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
               ty_ident: &ast::Ident, modifier: Option<Modifier>,
               path_root: ast::Ident) -> P<ast::Expr> {
    let ty_str = ty_ident.name.as_str();
    match ty_str {
        "f32" | "f64" => {
            let kind = cx.ident_of(match modifier {
                None | Some(Modifier::AsFloat) => "Default",
                Some(Modifier::AsDouble) => "Precision",
                Some(Modifier::Normalized) => {
                    cx.span_warn(span, &format!(
                        "Incompatible float modifier attribute: `#[{:?}]`", modifier
                    ));
                    ""
                }
            });
            let size = cx.ident_of(&format!("F{}", &ty_str[1..]));
            quote_expr!(cx, $path_root::gfx::attrib::Type::Float($path_root::gfx::attrib::FloatSubType::$kind,
                                                                 $path_root::gfx::attrib::FloatSize::$size))
        },
        "u8" | "u16" | "u32" | "u64" |
        "i8" | "i16" | "i32" | "i64" => {
            let sign = cx.ident_of({
                if ty_str.starts_with("i") { "Signed" } else { "Unsigned" }
            });
            let kind = cx.ident_of(match modifier {
                None => "Raw",
                Some(Modifier::Normalized) => "Normalized",
                Some(Modifier::AsFloat) => "AsFloat",
                Some(Modifier::AsDouble) => {
                    cx.span_warn(span, &format!(
                        "Incompatible int modifier attribute: `#[{:?}]`", modifier
                    ));
                    ""
                }
            });
            let size = cx.ident_of(&format!("U{}", &ty_str[1..]));
            quote_expr!(cx, $path_root::gfx::attrib::Type::Int($path_root::gfx::attrib::IntSubType::$kind,
                                                               $path_root::gfx::attrib::IntSize::$size,
                                                               $path_root::gfx::attrib::SignFlag::$sign))
        },
        ty_str => {
            cx.span_err(span, &format!("Unrecognized component type: `{:?}`",
                                      ty_str));
            cx.expr_tuple(span, vec![])
        },
    }
}

fn decode_count_and_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                         field: &ast::StructField,
                         path_root: ast::Ident) -> (P<ast::Expr>, P<ast::Expr>) {
    let modifier = find_modifier(cx, span, &field.node.attrs);
    match field.node.ty.node {
        ast::TyPath(_,ref p) => (
            cx.expr_lit(span, ast::LitInt(1, ast::UnsuffixedIntLit(ast::Plus))),
            decode_type(cx, span, &p.segments[0].identifier, modifier, path_root),
        ),
        ast::TyFixedLengthVec(ref pty, ref expr) => (expr.clone(), match pty.node {
            ast::TyPath(_,ref p) => {
                decode_type(cx, span, &p.segments[0].identifier, modifier, path_root)
            },
            _ => {
                cx.span_err(span, &format!("Unsupported fixed vector sub-type: \
                                          `{:?}`",pty.node));
                cx.expr_tuple(span, vec![])
            },
        }),
        _ => {
            cx.span_err(span, &format!("Unsupported attribute type: `{:?}`",
                                      field.node.ty.node));
            (cx.expr_tuple(span, vec![]), cx.expr_tuple(span, vec![]))
        },
    }
}

/// Generates the the method body for `gfx::VertexFormat::generate`.
fn method_body(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                   substr: &generic::Substructure,
                   path_root: ast::Ident) -> P<ast::Expr> {
    match *substr.fields {
        generic::StaticStruct(ref definition, generic::Named(ref fields)) => {
            let attribute_pushes = definition.fields.iter().zip(fields.iter())
                .map(|(def, &(ident, _))| {
                    let struct_ident = substr.type_ident;
                    let buffer_expr = &substr.nonself_args[0];
                    let (count_expr, type_expr) = decode_count_and_type(cx, span, def, path_root);
                    let ident_str = match super::find_name(cx, span, &def.node.attrs) {
                        Some(name) => name,
                        None => token::get_ident(ident),
                    };
                    let ident_str = &ident_str[..];
                    let instance_expr = cx.expr_u8(span, 0); // not supposed to be set by the macro
                    quote_expr!(cx, {
                        attributes.push($path_root::gfx::Attribute {
                            name: $ident_str.to_string(),
                            buffer: $buffer_expr.clone(),
                            format: $path_root::gfx::attrib::Format {
                                elem_count: $count_expr,
                                elem_type: $type_expr,
                                offset: unsafe {
                                    let x: $struct_ident = ::std::mem::uninitialized();
                                    let offset = (&x.$ident as *const _ as usize) -
                                        (&x as *const _ as usize);
                                    ::std::mem::forget(x);
                                    offset as $path_root::gfx::attrib::Offset
                                },
                                stride: { use std::mem;
                                    mem::size_of::<$struct_ident>() as
                                        $path_root::gfx::attrib::Stride
                                },
                                instance_rate: $instance_expr,
                            }
                        });
                    })
                }).collect::<Vec<P<ast::Expr>>>();
            let capacity = fields.len();
            quote_expr!(cx, {
                let mut attributes = Vec::with_capacity($capacity);
                $attribute_pushes;
                attributes
            })
        },
        _ => {
            cx.span_err(span, "Unable to implement `gfx::VertexFormat::generate` \
                              on a non-structure");
            cx.expr_tuple(span, vec![])
        }
    }
}

#[derive(Copy, Clone)]
pub struct VertexFormat;

impl ItemDecorator for VertexFormat {
    /// Derive a `gfx::VertexFormat` implementation for the `struct`
    fn expand(&self, context: &mut ext::base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: &mut FnMut(P<ast::Item>)) {
        // Insert the `gfx` reexport module
        let path_root = super::extern_crate_hack(context, span, |i| (*push)(i));
        let mut fixup = |item| {
            (*push)(super::fixup_extern_crate_paths(item, path_root))
        };

        // `impl<R: gfx::Resources> gfx::VertexFormat for $item`
        generic::TraitDef {
            span: span,
            attributes: Vec::new(),
            path: generic::ty::Path::new(
                vec![super::EXTERN_CRATE_HACK, "gfx", "VertexFormat"],
            ),
            additional_bounds: Vec::new(),
            generics: generic::ty::LifetimeBounds::empty(),
            methods: vec![
                // `fn generate(gfx::RawBufferHandle) -> Vec<gfx::Attribute>`
                generic::MethodDef {
                    name: "generate",
                    generics: generic::ty::LifetimeBounds {
                        lifetimes: Vec::new(),
                        bounds: vec![
                            ("R", vec![
                                generic::ty::Path::new(vec![
                                    super::EXTERN_CRATE_HACK, "gfx", "Resources"
                                ]),
                            ]),
                        ],
                    },
                    explicit_self: None,
                    args: vec![
                        generic::ty::Literal(generic::ty::Path {
                            path: vec![super::EXTERN_CRATE_HACK, "gfx", "RawBufferHandle"],
                            lifetime: None,
                            params: vec![box generic::ty::Literal(generic::ty::Path::new_local("R"))],
                            global: false,
                        }),
                    ],
                    ret_ty: generic::ty::Literal(
                        generic::ty::Path {
                            path: vec!["Vec"],
                            lifetime: None,
                            params: vec![
                                box generic::ty::Literal(generic::ty::Path {
                                    path: vec![super::EXTERN_CRATE_HACK, "gfx", "Attribute"],
                                    lifetime: None,
                                    params: vec![box generic::ty::Literal(
                                        generic::ty::Path::new_local("R")
                                    )],
                                    global: false,
                                }),
                            ],
                            global: false,
                        },
                    ),
                    attributes: Vec::new(),
                    // generate the method body
                    combine_substructure: generic::combine_substructure(
                        box |c, s, ss| method_body(c, s, ss, path_root)),
                },
            ],
            associated_types: Vec::new(),
        }.expand(context, meta_item, item, &mut fixup);
    }
}
<|endoftext|>


<commit_before>use asm_ops::*;

pub fn compile(ops: &Vec<AsmOp>){
    for op in ops{
        match *op {
            AsmOp::Add(ref dest, ref operand) => match *operand{
                AsmOperand::Register(ref source) => println!("add {:?}, {:?}", dest, source),
                AsmOperand::Value(ref i) => println!("add {:?}, {:?}", dest, i)
            },
            _ => {}
        }
    }
}
<commit_msg>Added assembly code generation<commit_after>use asm_ops::*;

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;

pub fn compile(ops: &Vec<AsmOp>){
    let mut str = "use64\nmov RAX, 1\n".to_string();
    for op in ops{
        match *op {
            AsmOp::Add(ref dest, ref operand) => match *operand{
                AsmOperand::Register(ref source) => str = str + &format!("add {:?}, {:?}\n", dest, source),
                AsmOperand::Value(ref i) => str = str + &format!("add {:?}, {:?}\n", dest, i)
            },
            _ => {}
        }
    }
    println!("\nOutput assembly is:\n{}",str);
}
<|endoftext|>


<commit_before>use std::string::CowString;
use std::fmt;
use std::collections::HashMap;
use std::rc::Rc;
use std::ops::Deref;

use error::{CompileError, CompileErrorKind, RuntimeError};
use datum::Datum;
use runtime::{Inst, MemRef, RDatum, RuntimeData};

/// Syntax variables
#[derive(Copy, Clone, PartialEq)]
pub enum Syntax {
    /// `lambda` syntax
    Lambda
}

/// Environment variables in the global environment
pub enum EnvVar {
    /// Syntax variables
    Syntax(Syntax),

    /// Primitive functions
    PrimFunc(&'static str, Rc<fn(&[RDatum]) -> Result<RDatum, RuntimeError>>),

    /// Compiled library functions
    Procedure(Rc<Vec<Inst>>)
}

/// Compiler compiles Datum into a bytecode evaluates it
pub struct Compiler<'g> {
    /// Global environment
    global_env: &'g HashMap<CowString<'static>, EnvVar>
}

struct CodeGenContext {
    code: Vec<Inst>,
    link_size: usize
}

impl<'g> Compiler<'g> {
    /// Creates a new compiler with given environment
    pub fn new<'a>(global_env: &'a HashMap<CowString<'static>, EnvVar>) -> Compiler<'a> {
        Compiler {
            global_env: global_env
        }
    }

    /// Compiles the datum into a bytecode evaluates it
    pub fn compile(&mut self, datum: &RDatum) -> Result<Vec<Inst>, CompileError> {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        try!(self.compile_expr(&[], &[], &mut ctx, datum));
        ctx.code.push(Inst::Return);
        return Ok(ctx.code);
    }

    fn compile_call(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        let mut arg_count = 0;
        for d in datum.iter() {
            match d {
                Ok(d) => {
                    try!(self.compile_expr(static_scope, args, ctx, &d));
                    arg_count += 1;
                },
                Err(()) => return Err(CompileError { kind: CompileErrorKind::DottedEval })
            }
        }

        if arg_count == 0 {
            Err(CompileError { kind: CompileErrorKind::NullEval })
        } else {
            ctx.code.push(Inst::Call(arg_count - 1));
            Ok(())
        }
    }

    fn compile_block(&mut self, static_scope: &[Vec<CowString<'static>>],
                      args: &[CowString<'static>], body: &RDatum)
            -> Result<CodeGenContext, CompileError>
    {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        for expr in body.iter() {
            if let Ok(e) = expr {
                try!(self.compile_expr(static_scope, args, &mut ctx, &e));
            } else {
                return Err(CompileError { kind: CompileErrorKind::DottedBody });
            }
        }

        ctx.code.push(Inst::Return);

        return Ok(ctx);
    }

    fn compile_expr(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        match datum {
            &Datum::Cons(ref h, ref t) =>
                if let &Datum::Sym(ref n) = h.borrow().deref() {
                    if let Some(&EnvVar::Syntax(Syntax::Lambda)) = self.global_env.get(n) {
                        if let &Datum::Cons(ref cur_args, ref body) = t.borrow().deref() {
                            let new_scope = {
                                let mut nenv = static_scope.to_vec();
                                nenv.push(args.to_vec());
                                nenv
                            };

                            let new_args = {
                                let mut nargs = Vec::new();
                                for arg in cur_args.borrow().iter() {
                                    if let Ok(Datum::Sym(s)) = arg {
                                        nargs.push(s)
                                    } else {
                                        return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax });
                                    }
                                }
                                nargs
                            };

                            let block_ctx = try!(self.compile_block(
                                    new_scope.as_slice(),
                                    new_args.as_slice(),
                                    body.borrow().deref()
                            ));

                            ctx.code.push(Inst::PushArg(MemRef::Closure(
                                        Rc::new(block_ctx.code),
                                        block_ctx.link_size
                            )));
                            return Ok(());
                        } else {
                            return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax })
                        }
                    } else {
                        self.compile_call(static_scope, args, ctx, datum)
                    }
                } else {
                    self.compile_call(static_scope, args, ctx, datum)
                },
            &Datum::Nil => Err(CompileError { kind: CompileErrorKind::NullEval }),
            &Datum::Sym(ref sym) => {
                if let Some(i) = range(0, args.len()).find(|&i| args[i] == *sym) {
                    ctx.code.push(Inst::PushArg(MemRef::Arg(i)));
                    return Ok(());
                }

                // (0, static_scope[-1]), (1, static_scope[-2]), (2, static_scope[-3]), ...
                for (i, up_args) in static_scope.iter().rev().enumerate() {
                    for (j, arg) in up_args.iter().enumerate() {
                        if *arg == *sym {
                            if ctx.link_size < i+1 {
                                ctx.link_size = i+1;
                            }
                            ctx.code.push(Inst::PushArg(MemRef::UpValue(i, j)));
                            return Ok(());
                        }
                    }
                }

                match self.global_env.get(sym) {
                    Some(data) => match data {
                        &EnvVar::Syntax(_) =>
                            Err(CompileError { kind: CompileErrorKind::SyntaxReference }),
                        &EnvVar::PrimFunc(ref name, ref func) => {
                            ctx.code.push(Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc(
                                                name.clone(),
                                                func.clone()
                            )))));
                            Ok(())
                        },
                        &EnvVar::Procedure(ref code) => {
                            ctx.code.push(Inst::PushArg(MemRef::Closure(code.clone(), 0)));
                            Ok(())
                        }
                    },
                    None =>
                        Err(CompileError { kind: CompileErrorKind::UnboundVariable })
                }
            },
            _ => {
                ctx.code.push(Inst::PushArg(MemRef::Const(datum.clone())));
                Ok(())
            }
        }
    }
}

impl fmt::Show for Syntax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "lambda")
    }
}

#[cfg(test)]
mod test {
    use std::borrow::Cow;
    use std::rc::Rc;
    use datum::Datum;
    use runtime::{Inst, MemRef, RuntimeData};
    use base::libbase;
    use primitive::PRIM_ADD;
    use super::Compiler;

    #[test]
    fn test_simple_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(1), num!(2)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_nested_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(3), list![sym!("+"), num!(1), num!(2)]]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_lambda() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::Call(1),
            Inst::Return
        ]);
        let code = compiler.compile(&list![
                           list![sym!("lambda"), list![sym!("x")],
                                list![sym!("+"), sym!("x"), num!(2)]],
                            num!(1)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_upvalue() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::UpValue(0, 0)),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::Call(2),
            Inst::Return
        ];
        let g = vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 1)),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(g), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(1),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::Call(1),
            Inst::Return
        ]);

        // ((
        //   (lambda (x)            # = g
        //     (lambda (y) (+ x y)) # = f
        //   ) 2) 3)
        let code = compiler.compile(&list![
            list![
                list![sym!("lambda"), list![sym!("x")],
                    list![sym!("lambda"), list![sym!("y")],
                        list![sym!("+"), sym!("x"), sym!("y")]]],
                num!(2)],
            num!(3)
        ]);
        assert_eq!(expected, code)
    }
}
<commit_msg>Refactor lambda compilation<commit_after>use std::string::CowString;
use std::fmt;
use std::collections::HashMap;
use std::rc::Rc;
use std::ops::Deref;

use error::{CompileError, CompileErrorKind, RuntimeError};
use datum::Datum;
use runtime::{Inst, MemRef, RDatum, RuntimeData};

/// Syntax variables
#[derive(Copy, Clone, PartialEq)]
pub enum Syntax {
    /// `lambda` syntax
    Lambda
}

/// Environment variables in the global environment
pub enum EnvVar {
    /// Syntax variables
    Syntax(Syntax),

    /// Primitive functions
    PrimFunc(&'static str, Rc<fn(&[RDatum]) -> Result<RDatum, RuntimeError>>),

    /// Compiled library functions
    Procedure(Rc<Vec<Inst>>)
}

/// Compiler compiles Datum into a bytecode evaluates it
pub struct Compiler<'g> {
    /// Global environment
    global_env: &'g HashMap<CowString<'static>, EnvVar>
}

struct CodeGenContext {
    code: Vec<Inst>,
    link_size: usize
}

impl<'g> Compiler<'g> {
    /// Creates a new compiler with given environment
    pub fn new<'a>(global_env: &'a HashMap<CowString<'static>, EnvVar>) -> Compiler<'a> {
        Compiler {
            global_env: global_env
        }
    }

    /// Compiles the datum into a bytecode evaluates it
    pub fn compile(&mut self, datum: &RDatum) -> Result<Vec<Inst>, CompileError> {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        try!(self.compile_expr(&[], &[], &mut ctx, datum));
        ctx.code.push(Inst::Return);
        return Ok(ctx.code);
    }

    fn compile_call(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        let mut arg_count = 0;
        for d in datum.iter() {
            match d {
                Ok(d) => {
                    try!(self.compile_expr(static_scope, args, ctx, &d));
                    arg_count += 1;
                },
                Err(()) => return Err(CompileError { kind: CompileErrorKind::DottedEval })
            }
        }

        if arg_count == 0 {
            Err(CompileError { kind: CompileErrorKind::NullEval })
        } else {
            ctx.code.push(Inst::Call(arg_count - 1));
            Ok(())
        }
    }

    fn compile_block(&mut self, static_scope: &[Vec<CowString<'static>>],
                      args: &[CowString<'static>], body: &RDatum)
            -> Result<CodeGenContext, CompileError>
    {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        for expr in body.iter() {
            if let Ok(e) = expr {
                try!(self.compile_expr(static_scope, args, &mut ctx, &e));
            } else {
                return Err(CompileError { kind: CompileErrorKind::DottedBody });
            }
        }

        ctx.code.push(Inst::Return);

        return Ok(ctx);
    }

    fn compile_lambda(&mut self, static_scope: &[Vec<CowString<'static>>],
                      args: &[CowString<'static>], ctx: &mut CodeGenContext, tail: &RDatum)
            -> Result<(), CompileError>
    {
        if let &Datum::Cons(ref cur_args, ref body) = tail {
            let new_scope = {
                let mut nenv = static_scope.to_vec();
                nenv.push(args.to_vec());
                nenv
            };

            let new_args = {
                let mut nargs = Vec::new();
                for arg in cur_args.borrow().iter() {
                    if let Ok(Datum::Sym(s)) = arg {
                        nargs.push(s)
                    } else {
                        return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax });
                    }
                }
                nargs
            };

            let block_ctx = try!(self.compile_block(
                    new_scope.as_slice(),
                    new_args.as_slice(),
                    body.borrow().deref()
            ));

            ctx.code.push(Inst::PushArg(MemRef::Closure(
                    Rc::new(block_ctx.code),
                    block_ctx.link_size
            )));

            return Ok(());
        } else {
            return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax })
        }
    }

    fn compile_expr(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        match datum {
            &Datum::Cons(ref h, ref t) =>
                if let &Datum::Sym(ref n) = h.borrow().deref() {
                    match self.global_env.get(n) {
                        Some(&EnvVar::Syntax(Syntax::Lambda)) =>
                            self.compile_lambda(static_scope, args, ctx, t.borrow().deref()),
                        _ =>
                            self.compile_call(static_scope, args, ctx, datum)
                    }
                } else {
                    self.compile_call(static_scope, args, ctx, datum)
                },
            &Datum::Nil => Err(CompileError { kind: CompileErrorKind::NullEval }),
            &Datum::Sym(ref sym) => {
                if let Some(i) = range(0, args.len()).find(|&i| args[i] == *sym) {
                    ctx.code.push(Inst::PushArg(MemRef::Arg(i)));
                    return Ok(());
                }

                // (0, static_scope[-1]), (1, static_scope[-2]), (2, static_scope[-3]), ...
                for (i, up_args) in static_scope.iter().rev().enumerate() {
                    for (j, arg) in up_args.iter().enumerate() {
                        if *arg == *sym {
                            if ctx.link_size < i+1 {
                                ctx.link_size = i+1;
                            }
                            ctx.code.push(Inst::PushArg(MemRef::UpValue(i, j)));
                            return Ok(());
                        }
                    }
                }

                match self.global_env.get(sym) {
                    Some(data) => match data {
                        &EnvVar::Syntax(_) =>
                            Err(CompileError { kind: CompileErrorKind::SyntaxReference }),
                        &EnvVar::PrimFunc(ref name, ref func) => {
                            ctx.code.push(Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc(
                                                name.clone(),
                                                func.clone()
                            )))));
                            Ok(())
                        },
                        &EnvVar::Procedure(ref code) => {
                            ctx.code.push(Inst::PushArg(MemRef::Closure(code.clone(), 0)));
                            Ok(())
                        }
                    },
                    None =>
                        Err(CompileError { kind: CompileErrorKind::UnboundVariable })
                }
            },
            _ => {
                ctx.code.push(Inst::PushArg(MemRef::Const(datum.clone())));
                Ok(())
            }
        }
    }
}

impl fmt::Show for Syntax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "lambda")
    }
}

#[cfg(test)]
mod test {
    use std::borrow::Cow;
    use std::rc::Rc;
    use datum::Datum;
    use runtime::{Inst, MemRef, RuntimeData};
    use base::libbase;
    use primitive::PRIM_ADD;
    use super::Compiler;

    #[test]
    fn test_simple_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(1), num!(2)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_nested_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(3), list![sym!("+"), num!(1), num!(2)]]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_lambda() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::Call(1),
            Inst::Return
        ]);
        let code = compiler.compile(&list![
                           list![sym!("lambda"), list![sym!("x")],
                                list![sym!("+"), sym!("x"), num!(2)]],
                            num!(1)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_upvalue() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::UpValue(0, 0)),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::Call(2),
            Inst::Return
        ];
        let g = vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 1)),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(g), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(1),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::Call(1),
            Inst::Return
        ]);

        // ((
        //   (lambda (x)            # = g
        //     (lambda (y) (+ x y)) # = f
        //   ) 2) 3)
        let code = compiler.compile(&list![
            list![
                list![sym!("lambda"), list![sym!("x")],
                    list![sym!("lambda"), list![sym!("y")],
                        list![sym!("+"), sym!("x"), sym!("y")]]],
                num!(2)],
            num!(3)
        ]);
        assert_eq!(expected, code)
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate libr;
use std::ptr;
use std::env;
use std::ffi::{CStr, CString};

use libr::ffi::internals::*;
use libr::ffi::embedded::{self, Rf_endEmbeddedR, Rf_initEmbeddedR};
use libr::ffi::ext::parse::{ParseStatus, R_ParseVector};

fn main() {
    if let Err(_) = env::var("R_HOME") {
        panic!("Rembedded test need R_HOME be setted");
    }

    let mut s = Box::new(vec![CString::new("R").unwrap().into_raw(),
                              CString::new("--quiet").unwrap().into_raw(),
                              CString::new("--no-save").unwrap().into_raw()]);
    unsafe {
        embedded::Rf_initEmbeddedR(s.len() as i32, s.as_mut_ptr());
        // Plot
        let mut status = ParseStatus::PARSE_OK;
        let mut had_error: libc::c_int = 0;
        let mut tmp = Rf_protect(Rf_mkString(CString::new("{pdf(\"03-plot.pdf\"); plot(1:10, pch=\"+\"); print(1:10)}").unwrap().into_raw()));
        let mut e = Rf_protect(R_ParseVector(tmp, 1, &mut status, R_NilValue));
        Rf_PrintValue(e);
        R_tryEval(VECTOR_ELT(e,0), R_GlobalEnv, &mut had_error);
        Rf_unprotect(2);
        embedded::Rf_endEmbeddedR(0);
    }
}
<commit_msg>Cargo fmt<commit_after>extern crate libc;
extern crate libr;
use std::ptr;
use std::env;
use std::ffi::{CStr, CString};

use libr::ffi::internals::*;
use libr::ffi::embedded::{self, Rf_endEmbeddedR, Rf_initEmbeddedR};
use libr::ffi::ext::parse::{ParseStatus, R_ParseVector};

fn main() {
    if let Err(_) = env::var("R_HOME") {
        panic!("Rembedded test need R_HOME be setted");
    }

    let mut s = Box::new(vec![CString::new("R").unwrap().into_raw(),
                              CString::new("--quiet").unwrap().into_raw(),
                              CString::new("--no-save").unwrap().into_raw()]);
    unsafe {
        embedded::Rf_initEmbeddedR(s.len() as i32, s.as_mut_ptr());
        // Plot
        let mut status = ParseStatus::PARSE_OK;
        let mut had_error: libc::c_int = 0;
        let mut tmp = Rf_protect(Rf_mkString(CString::new("{pdf(\"03-plot.pdf\"); plot(1:10, \
                                                           pch=\"+\"); print(1:10)}")
                                                 .unwrap()
                                                 .into_raw()));
        let mut e = Rf_protect(R_ParseVector(tmp, 1, &mut status, R_NilValue));
        Rf_PrintValue(e);
        R_tryEval(VECTOR_ELT(e, 0), R_GlobalEnv, &mut had_error);
        Rf_unprotect(2);
        embedded::Rf_endEmbeddedR(0);
    }
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
use std::collections::HashMap;

use optimizer::*;

pub fn restore_on_err(
    rule: OptimizedRule,
    rules_to_exprs: &HashMap<String, OptimizedExpr>
) -> OptimizedRule {
    match rule {
        OptimizedRule { name, ty, expr } => {
            let expr = expr.map_bottom_up(|expr| wrap_branching_exprs(expr, rules_to_exprs));

            OptimizedRule { name, ty, expr }
        }
    }
}

fn wrap_branching_exprs(
    expr: OptimizedExpr,
    rules_to_exprs: &HashMap<String, OptimizedExpr>
) -> OptimizedExpr {
    match expr {
        OptimizedExpr::Opt(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                OptimizedExpr::Opt(Box::new(OptimizedExpr::RestoreOnErr(expr)))
            } else {
                OptimizedExpr::Opt(expr)
            }
        }
        OptimizedExpr::Choice(lhs, rhs) => {
            let wrapped_lhs = if child_modifies_state(&lhs, rules_to_exprs) {
                Box::new(OptimizedExpr::RestoreOnErr(lhs))
            } else {
                lhs
            };
            let wrapped_rhs = if child_modifies_state(&rhs, rules_to_exprs) {
                Box::new(OptimizedExpr::RestoreOnErr(rhs))
            } else {
                rhs
            };
            OptimizedExpr::Choice(wrapped_lhs, wrapped_rhs)
        }
        OptimizedExpr::Rep(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                OptimizedExpr::Rep(Box::new(OptimizedExpr::RestoreOnErr(expr)))
            } else {
                OptimizedExpr::Rep(expr)
            }
        }
        _ => expr
    }
}

fn child_modifies_state(
    expr: &OptimizedExpr,
    rules_to_exprs: &HashMap<String, OptimizedExpr>
) -> bool {
    expr.iter_top_down().any(|expr| match expr {
        OptimizedExpr::Push(_) => true,
        OptimizedExpr::Ident(ref s) if s == "pop" => true,
        OptimizedExpr::Ident(ref name) => {
            let mut map = rules_to_exprs.clone();
            match map.remove(name) {
                Some(rule_expr) => child_modifies_state(&rule_expr, &map),
                None => false
            }
        }
        _ => false
    })
}

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

    #[test]
    fn restore_no_stack_children() {
        let rules = vec![
            OptimizedRule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Opt(Box::new(Str(String::from("a"))))
            },
        ];

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(
            restore_on_err(rules[0].clone(), rules_to_map),
            rules[0].clone()
        );
    }

    #[test]
    fn restore_with_child_stack_ops() {
        let rules = vec![
            OptimizedRule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Rep(Box::new(Push(Box::new(Str(String::from("a"))))))
            },
        ];

        let restored = OptimizedRule {
            name: "rule".to_owned(),
            ty: RuleType::Normal,
            expr: Rep(Box::new(RestoreOnErr(Box::new(Push(Box::new(Str(
                String::from("a")
            )))))))
        };

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(restore_on_err(rules[0].clone(), rules_to_map), restored);
    }

    #[test]
    fn restore_choice_branch_with_and_branch_without() {
        let rules = vec![
            OptimizedRule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Choice(
                    Box::new(Push(Box::new(Str(String::from("a"))))),
                    Box::new(Str(String::from("a")))
                )
            },
        ];

        let restored = OptimizedRule {
            name: "rule".to_owned(),
            ty: RuleType::Normal,
            expr: Choice(
                Box::new(RestoreOnErr(Box::new(Push(Box::new(Str(String::from(
                    "a"
                ))))))),
                Box::new(Str(String::from("a")))
            )
        };

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(restore_on_err(rules[0].clone(), rules_to_map), restored);
    }
}
<commit_msg>fix small restorer bug<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
use std::collections::HashMap;

use optimizer::*;

pub fn restore_on_err(
    rule: OptimizedRule,
    rules_to_exprs: &HashMap<String, OptimizedExpr>
) -> OptimizedRule {
    match rule {
        OptimizedRule { name, ty, expr } => {
            let expr = expr.map_bottom_up(|expr| wrap_branching_exprs(expr, rules_to_exprs));

            OptimizedRule { name, ty, expr }
        }
    }
}

fn wrap_branching_exprs(
    expr: OptimizedExpr,
    rules_to_exprs: &HashMap<String, OptimizedExpr>
) -> OptimizedExpr {
    match expr {
        OptimizedExpr::Opt(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                OptimizedExpr::Opt(Box::new(OptimizedExpr::RestoreOnErr(expr)))
            } else {
                OptimizedExpr::Opt(expr)
            }
        }
        OptimizedExpr::Choice(lhs, rhs) => {
            let wrapped_lhs = if child_modifies_state(&lhs, rules_to_exprs) {
                Box::new(OptimizedExpr::RestoreOnErr(lhs))
            } else {
                lhs
            };
            let wrapped_rhs = if child_modifies_state(&rhs, rules_to_exprs) {
                Box::new(OptimizedExpr::RestoreOnErr(rhs))
            } else {
                rhs
            };
            OptimizedExpr::Choice(wrapped_lhs, wrapped_rhs)
        }
        OptimizedExpr::Rep(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                OptimizedExpr::Rep(Box::new(OptimizedExpr::RestoreOnErr(expr)))
            } else {
                OptimizedExpr::Rep(expr)
            }
        }
        _ => expr
    }
}

fn child_modifies_state(
    expr: &OptimizedExpr,
    rules_to_exprs: &HashMap<String, OptimizedExpr>
) -> bool {
    expr.iter_top_down().any(|expr| match expr {
        OptimizedExpr::Push(_) => true,
        OptimizedExpr::Ident(ref s) if s == "POP" => true,
        OptimizedExpr::Ident(ref name) => {
            let mut map = rules_to_exprs.clone();
            match map.remove(name) {
                Some(rule_expr) => child_modifies_state(&rule_expr, &map),
                None => false
            }
        }
        _ => false
    })
}

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

    #[test]
    fn restore_no_stack_children() {
        let rules = vec![
            OptimizedRule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Opt(Box::new(Str(String::from("a"))))
            },
        ];

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(
            restore_on_err(rules[0].clone(), rules_to_map),
            rules[0].clone()
        );
    }

    #[test]
    fn restore_with_child_stack_ops() {
        let rules = vec![
            OptimizedRule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Rep(Box::new(Push(Box::new(Str(String::from("a"))))))
            },
        ];

        let restored = OptimizedRule {
            name: "rule".to_owned(),
            ty: RuleType::Normal,
            expr: Rep(Box::new(RestoreOnErr(Box::new(Push(Box::new(Str(
                String::from("a")
            )))))))
        };

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(restore_on_err(rules[0].clone(), rules_to_map), restored);
    }

    #[test]
    fn restore_choice_branch_with_and_branch_without() {
        let rules = vec![
            OptimizedRule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Choice(
                    Box::new(Push(Box::new(Str(String::from("a"))))),
                    Box::new(Str(String::from("a")))
                )
            },
        ];

        let restored = OptimizedRule {
            name: "rule".to_owned(),
            ty: RuleType::Normal,
            expr: Choice(
                Box::new(RestoreOnErr(Box::new(Push(Box::new(Str(String::from(
                    "a"
                ))))))),
                Box::new(Str(String::from("a")))
            )
        };

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(restore_on_err(rules[0].clone(), rules_to_map), restored);
    }
}
<|endoftext|>