<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// 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::collections::BTreeMap;

use handlebars::{Handlebars, Helper, HelperDef, Renderable, RenderContext, RenderError};
use serde_json::Value as Json;

use super::super::RenderResult;
use super::{to_json, JsonTruthy};

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

impl HelperDef for EachAliveHelper {
    fn call(&self, h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> RenderResult<()> {
        let value =
            h.param(0)
                .ok_or_else(|| RenderError::new("Param not found for helper \"eachAlive\""))?;
        if let Some(template) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value
                .path_root()
                .map(|p| format!("{}/{}", rc.get_path(), p));
            println!("{}", value.value());
            let rendered = match (value.value().is_truthy(), value.value()) {
                (true, &Json::Array(ref list)) => {
                    let len = list.len();
                    for i in 0..len {
                        let member = list[i]
                            .as_object()
                            .ok_or_else(|| {
                                RenderError::new(format!("Param value is not a valid census \
                                member. Parameter content is: {:?}",
                                                         list[i]))
                            })?;
                        if member.contains_key("alive") && member["alive"].as_bool().unwrap() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), to_json(&(i == 0usize)));
                            local_rc.set_local_var("@last".to_string(), to_json(&(i == len - 1)));
                            local_rc.set_local_var("@index".to_string(), to_json(&i));

                            if let Some(block_param) = h.block_param() {
                                let mut map = BTreeMap::new();
                                map.insert(block_param.to_string(), to_json(&list[i]));
                                local_rc.push_block_context(&map);
                            }

                            template.render(r, &mut local_rc)?;

                            if h.block_param().is_some() {
                                local_rc.pop_block_context();
                            }
                        }
                    }
                    Ok(())
                }
                (true, &Json::Object(ref obj)) => {
                    let mut first: bool = true;
                    if !obj.contains_key("alive") || !obj["alive"].as_bool().unwrap() {
                        return Ok(());
                    }
                    for k in obj.keys() {
                        let mut local_rc = rc.derive();
                        if let Some(ref p) = local_path_root {
                            local_rc.push_local_path_root(p.clone());
                        }
                        local_rc.set_local_var("@first".to_string(), to_json(&first));
                        local_rc.set_local_var("@key".to_string(), to_json(k));

                        if first {
                            first = false;
                        }

                        if let Some(inner_path) = value.path() {
                            let new_path =
                                format!("{}/{}.[{}]", local_rc.get_path(), inner_path, k);
                            local_rc.set_path(new_path);
                        }

                        if let Some((bp_key, bp_val)) = h.block_param_pair() {
                            let mut map = BTreeMap::new();
                            map.insert(bp_key.to_string(), to_json(k));
                            map.insert(bp_val.to_string(), to_json(obj.get(k).unwrap()));
                            local_rc.push_block_context(&map);
                        }

                        template.render(r, &mut local_rc)?;

                        if h.block_param().is_some() {
                            local_rc.pop_block_context();
                        }

                        if local_path_root.is_some() {
                            local_rc.pop_local_path_root();
                        }
                    }
                    Ok(())
                }
                (false, _) => {
                    if let Some(else_template) = h.inverse() {
                        else_template.render(r, rc)?;
                    }
                    Ok(())
                }
                _ => Err(RenderError::new(format!("Param type is not iterable: {:?}", template))),
            };

            rc.demote_local_vars();
            return rendered;
        }
        Ok(())
    }
}

pub static EACH_ALIVE: EachAliveHelper = EachAliveHelper;
<commit_msg>Remove debug output from EachAlive helper<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// 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::collections::BTreeMap;

use handlebars::{Handlebars, Helper, HelperDef, Renderable, RenderContext, RenderError};
use serde_json::Value as Json;

use super::super::RenderResult;
use super::{to_json, JsonTruthy};

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

impl HelperDef for EachAliveHelper {
    fn call(&self, h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> RenderResult<()> {
        let value =
            h.param(0)
                .ok_or_else(|| RenderError::new("Param not found for helper \"eachAlive\""))?;
        if let Some(template) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value
                .path_root()
                .map(|p| format!("{}/{}", rc.get_path(), p));
            let rendered = match (value.value().is_truthy(), value.value()) {
                (true, &Json::Array(ref list)) => {
                    let len = list.len();
                    for i in 0..len {
                        let member = list[i]
                            .as_object()
                            .ok_or_else(|| {
                                RenderError::new(format!("Param value is not a valid census \
                                member. Parameter content is: {:?}",
                                                         list[i]))
                            })?;
                        if member.contains_key("alive") && member["alive"].as_bool().unwrap() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), to_json(&(i == 0usize)));
                            local_rc.set_local_var("@last".to_string(), to_json(&(i == len - 1)));
                            local_rc.set_local_var("@index".to_string(), to_json(&i));

                            if let Some(block_param) = h.block_param() {
                                let mut map = BTreeMap::new();
                                map.insert(block_param.to_string(), to_json(&list[i]));
                                local_rc.push_block_context(&map);
                            }

                            template.render(r, &mut local_rc)?;

                            if h.block_param().is_some() {
                                local_rc.pop_block_context();
                            }
                        }
                    }
                    Ok(())
                }
                (true, &Json::Object(ref obj)) => {
                    let mut first: bool = true;
                    if !obj.contains_key("alive") || !obj["alive"].as_bool().unwrap() {
                        return Ok(());
                    }
                    for k in obj.keys() {
                        let mut local_rc = rc.derive();
                        if let Some(ref p) = local_path_root {
                            local_rc.push_local_path_root(p.clone());
                        }
                        local_rc.set_local_var("@first".to_string(), to_json(&first));
                        local_rc.set_local_var("@key".to_string(), to_json(k));

                        if first {
                            first = false;
                        }

                        if let Some(inner_path) = value.path() {
                            let new_path =
                                format!("{}/{}.[{}]", local_rc.get_path(), inner_path, k);
                            local_rc.set_path(new_path);
                        }

                        if let Some((bp_key, bp_val)) = h.block_param_pair() {
                            let mut map = BTreeMap::new();
                            map.insert(bp_key.to_string(), to_json(k));
                            map.insert(bp_val.to_string(), to_json(obj.get(k).unwrap()));
                            local_rc.push_block_context(&map);
                        }

                        template.render(r, &mut local_rc)?;

                        if h.block_param().is_some() {
                            local_rc.pop_block_context();
                        }

                        if local_path_root.is_some() {
                            local_rc.pop_local_path_root();
                        }
                    }
                    Ok(())
                }
                (false, _) => {
                    if let Some(else_template) = h.inverse() {
                        else_template.render(r, rc)?;
                    }
                    Ok(())
                }
                _ => Err(RenderError::new(format!("Param type is not iterable: {:?}", template))),
            };

            rc.demote_local_vars();
            return rendered;
        }
        Ok(())
    }
}

pub static EACH_ALIVE: EachAliveHelper = EachAliveHelper;
<|endoftext|>


<commit_before>use super::SlotClock;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use types::Slot;

pub use std::time::SystemTimeError;

/// Determines the present slot based upon the present system time.
#[derive(Clone)]
pub struct SystemTimeSlotClock {
    genesis_slot: Slot,
    genesis_duration: Duration,
    slot_duration: Duration,
}

impl SlotClock for SystemTimeSlotClock {
    fn new(genesis_slot: Slot, genesis_duration: Duration, slot_duration: Duration) -> Self {
        if slot_duration.as_millis() == 0 {
            panic!("SystemTimeSlotClock cannot have a < 1ms slot duration.");
        }

        Self {
            genesis_slot,
            genesis_duration,
            slot_duration,
        }
    }

    fn now(&self) -> Option<Slot> {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).ok()?;
        let genesis = self.genesis_duration;

        if now > genesis {
            let since_genesis = now
                .checked_sub(genesis)
                .expect("Control flow ensures now is greater than genesis");
            let slot =
                Slot::from((since_genesis.as_millis() / self.slot_duration.as_millis()) as u64);
            Some(slot + self.genesis_slot)
        } else {
            None
        }
    }

    fn duration_to_next_slot(&self) -> Option<Duration> {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).ok()?;
        let genesis = self.genesis_duration;

        let slot_start = |slot: Slot| -> Duration {
            let slot = slot.as_u64() as u32;
            genesis + slot * self.slot_duration
        };

        if now > genesis {
            Some(
                slot_start(self.now()? + 1)
                    .checked_sub(now)
                    .expect("The next slot cannot start before now"),
            )
        } else {
            Some(
                genesis
                    .checked_sub(now)
                    .expect("Control flow ensures genesis is greater than or equal to now"),
            )
        }
    }

    fn slot_duration(&self) -> Duration {
        self.slot_duration
    }
}

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

    /*
     * Note: these tests are using actual system times and could fail if they are executed on a
     * very slow machine.
     */
    #[test]
    fn test_slot_now() {
        let genesis_slot = Slot::new(0);

        let prior_genesis = |milliseconds_prior: u64| {
            SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .expect("should get system time")
                - Duration::from_millis(milliseconds_prior)
        };

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(0), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(0)));

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(5_000), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(5)));

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(500), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(0)));
        assert!(clock.duration_to_next_slot().unwrap() < Duration::from_millis(500));

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(1_500), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(1)));
        assert!(clock.duration_to_next_slot().unwrap() < Duration::from_millis(500));
    }

    #[test]
    #[should_panic]
    fn zero_seconds() {
        SystemTimeSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(0));
    }

    #[test]
    #[should_panic]
    fn zero_millis() {
        SystemTimeSlotClock::new(
            Slot::new(0),
            Duration::from_secs(0),
            Duration::from_millis(0),
        );
    }

    #[test]
    #[should_panic]
    fn less_than_one_millis() {
        SystemTimeSlotClock::new(
            Slot::new(0),
            Duration::from_secs(0),
            Duration::from_nanos(999),
        );
    }
}
<commit_msg>Allow slot clock to work on genesis (#573)<commit_after>use super::SlotClock;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use types::Slot;

pub use std::time::SystemTimeError;

/// Determines the present slot based upon the present system time.
#[derive(Clone)]
pub struct SystemTimeSlotClock {
    genesis_slot: Slot,
    genesis_duration: Duration,
    slot_duration: Duration,
}

impl SlotClock for SystemTimeSlotClock {
    fn new(genesis_slot: Slot, genesis_duration: Duration, slot_duration: Duration) -> Self {
        if slot_duration.as_millis() == 0 {
            panic!("SystemTimeSlotClock cannot have a < 1ms slot duration.");
        }

        Self {
            genesis_slot,
            genesis_duration,
            slot_duration,
        }
    }

    fn now(&self) -> Option<Slot> {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).ok()?;
        let genesis = self.genesis_duration;

        if now >= genesis {
            let since_genesis = now
                .checked_sub(genesis)
                .expect("Control flow ensures now is greater than or equal to genesis");
            let slot =
                Slot::from((since_genesis.as_millis() / self.slot_duration.as_millis()) as u64);
            Some(slot + self.genesis_slot)
        } else {
            None
        }
    }

    fn duration_to_next_slot(&self) -> Option<Duration> {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).ok()?;
        let genesis = self.genesis_duration;

        let slot_start = |slot: Slot| -> Duration {
            let slot = slot.as_u64() as u32;
            genesis + slot * self.slot_duration
        };

        if now >= genesis {
            Some(
                slot_start(self.now()? + 1)
                    .checked_sub(now)
                    .expect("The next slot cannot start before now"),
            )
        } else {
            Some(
                genesis
                    .checked_sub(now)
                    .expect("Control flow ensures genesis is greater than or equal to now"),
            )
        }
    }

    fn slot_duration(&self) -> Duration {
        self.slot_duration
    }
}

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

    /*
     * Note: these tests are using actual system times and could fail if they are executed on a
     * very slow machine.
     */
    #[test]
    fn test_slot_now() {
        let genesis_slot = Slot::new(0);

        let prior_genesis = |milliseconds_prior: u64| {
            SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .expect("should get system time")
                - Duration::from_millis(milliseconds_prior)
        };

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(0), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(0)));

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(5_000), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(5)));

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(500), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(0)));
        assert!(clock.duration_to_next_slot().unwrap() <= Duration::from_millis(500));

        let clock =
            SystemTimeSlotClock::new(genesis_slot, prior_genesis(1_500), Duration::from_secs(1));
        assert_eq!(clock.now(), Some(Slot::new(1)));
        assert!(clock.duration_to_next_slot().unwrap() <= Duration::from_millis(500));
    }

    #[test]
    #[should_panic]
    fn zero_seconds() {
        SystemTimeSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(0));
    }

    #[test]
    #[should_panic]
    fn zero_millis() {
        SystemTimeSlotClock::new(
            Slot::new(0),
            Duration::from_secs(0),
            Duration::from_millis(0),
        );
    }

    #[test]
    #[should_panic]
    fn less_than_one_millis() {
        SystemTimeSlotClock::new(
            Slot::new(0),
            Duration::from_secs(0),
            Duration::from_nanos(999),
        );
    }
}
<|endoftext|>


<commit_before>use crate::{
    backend::renderer::{ImportAll, Renderer, Texture},
    desktop::{window::Window, PopupManager},
    utils::{Logical, Physical, Point, Rectangle, Scale},
};
use std::{
    cell::{RefCell, RefMut},
    collections::HashMap,
};

use super::SpaceElement;

#[derive(Default)]
pub struct WindowState {
    pub location: Point<i32, Logical>,
    // TODO: How to handle that?
    pub drawn: bool,
    pub z_index: u8,
}

pub type WindowUserdata = RefCell<HashMap<usize, WindowState>>;
pub fn window_state(space: usize, w: &Window) -> RefMut<'_, WindowState> {
    let userdata = w.user_data();
    userdata.insert_if_missing(WindowUserdata::default);
    RefMut::map(userdata.get::<WindowUserdata>().unwrap().borrow_mut(), |m| {
        m.entry(space).or_default()
    })
}

pub fn window_rect(window: &Window, space_id: &usize) -> Rectangle<i32, Logical> {
    let loc = window_loc(window, space_id);
    let mut wgeo = window.bbox();
    wgeo.loc += loc;
    wgeo
}

pub fn window_loc(window: &Window, space_id: &usize) -> Point<i32, Logical> {
    window
        .user_data()
        .get::<RefCell<HashMap<usize, WindowState>>>()
        .unwrap()
        .borrow()
        .get(space_id)
        .unwrap()
        .location
}

impl<R, E> SpaceElement<R, E> for Window
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: Texture + 'static,
    E: crate::backend::renderer::output::element::RenderElement<R>
        + From<crate::backend::renderer::output::element::surface::WaylandSurfaceRenderElement>,
{
    fn location(&self, space_id: usize) -> Point<i32, Logical> {
        window_loc(self, &space_id) - self.geometry().loc
    }

    fn geometry(&self, space_id: usize) -> Rectangle<i32, Logical> {
        window_rect(self, &space_id)
    }

    fn z_index(&self, space_id: usize) -> u8 {
        window_state(space_id, self).z_index
    }

    fn render_elements(&self, location: Point<i32, Physical>, scale: Scale<f64>) -> Vec<E> {
        let surface = self.toplevel().wl_surface();

        let mut render_elements: Vec<E> = Vec::new();
        let popup_render_elements =
            PopupManager::popups_for_surface(surface).flat_map(|(popup, popup_offset)| {
                let offset = (self.geometry().loc + popup_offset - popup.geometry().loc)
                    .to_f64()
                    .to_physical(scale)
                    .to_i32_round();

                crate::backend::renderer::output::element::surface::surfaces_from_surface_tree(
                    popup.wl_surface(),
                    location + offset,
                    scale,
                )
            });

        render_elements.extend(popup_render_elements);

        render_elements.extend(
            crate::backend::renderer::output::element::surface::surfaces_from_surface_tree(
                surface, location, scale,
            ),
        );

        render_elements
    }
}
<commit_msg>remove unused code<commit_after>use crate::{
    backend::renderer::{ImportAll, Renderer, Texture},
    desktop::{window::Window, PopupManager},
    utils::{Logical, Physical, Point, Rectangle, Scale},
};
use std::{
    cell::{RefCell, RefMut},
    collections::HashMap,
};

use super::SpaceElement;

#[derive(Default)]
pub struct WindowState {
    pub location: Point<i32, Logical>,
    pub drawn: bool,
    pub z_index: u8,
}

pub type WindowUserdata = RefCell<HashMap<usize, WindowState>>;
pub fn window_state(space: usize, w: &Window) -> RefMut<'_, WindowState> {
    let userdata = w.user_data();
    userdata.insert_if_missing(WindowUserdata::default);
    RefMut::map(userdata.get::<WindowUserdata>().unwrap().borrow_mut(), |m| {
        m.entry(space).or_default()
    })
}

pub fn window_rect(window: &Window, space_id: &usize) -> Rectangle<i32, Logical> {
    let loc = window_loc(window, space_id);
    let mut wgeo = window.bbox();
    wgeo.loc += loc;
    wgeo
}

pub fn window_loc(window: &Window, space_id: &usize) -> Point<i32, Logical> {
    window
        .user_data()
        .get::<RefCell<HashMap<usize, WindowState>>>()
        .unwrap()
        .borrow()
        .get(space_id)
        .unwrap()
        .location
}

impl<R, E> SpaceElement<R, E> for Window
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: Texture + 'static,
    E: crate::backend::renderer::output::element::RenderElement<R>
        + From<crate::backend::renderer::output::element::surface::WaylandSurfaceRenderElement>,
{
    fn location(&self, space_id: usize) -> Point<i32, Logical> {
        window_loc(self, &space_id) - self.geometry().loc
    }

    fn geometry(&self, space_id: usize) -> Rectangle<i32, Logical> {
        window_rect(self, &space_id)
    }

    fn z_index(&self, space_id: usize) -> u8 {
        window_state(space_id, self).z_index
    }

    fn render_elements(&self, location: Point<i32, Physical>, scale: Scale<f64>) -> Vec<E> {
        let surface = self.toplevel().wl_surface();

        let mut render_elements: Vec<E> = Vec::new();
        let popup_render_elements =
            PopupManager::popups_for_surface(surface).flat_map(|(popup, popup_offset)| {
                let offset = (self.geometry().loc + popup_offset - popup.geometry().loc)
                    .to_f64()
                    .to_physical(scale)
                    .to_i32_round();

                crate::backend::renderer::output::element::surface::surfaces_from_surface_tree(
                    popup.wl_surface(),
                    location + offset,
                    scale,
                )
            });

        render_elements.extend(popup_render_elements);

        render_elements.extend(
            crate::backend::renderer::output::element::surface::surfaces_from_surface_tree(
                surface, location, scale,
            ),
        );

        render_elements
    }
}
<|endoftext|>


<commit_before>use token::Token;
use context::Context;
use operator::Operator;
use operator_offset::OperatorOffset;
use operator_or_token::OperatorOrToken;
use token_offset::TokenOffset;
use ast::expression::Expression;

pub struct ExpressionEvaluator<'a> {
    context: &'a Context,
    pub paren_level: i32,
    last_was_an_operator: bool,
    value_stack: Vec<Expression>,
    op_stack: Vec<OperatorOffset>,
}

impl<'a> ExpressionEvaluator<'a> {
    pub fn evaluate(expr: Expression, context: &Context) -> Expression {
        match expr {
            Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                token: Token::String(_), ..
            })) => {
                context.get_variable(&t)
                       .unwrap_or(Expression::Value(
                            OperatorOrToken::Token(t)
                       ))
            },
            Expression::List(exprs) => {
                let evaluator = ExpressionEvaluator::new(context);
                evaluator.evaluate_list(exprs)
            },
            other => other,
        }
    }

    pub fn new(context: &Context) -> ExpressionEvaluator {
        ExpressionEvaluator {
            context: context,
            paren_level: 0,
            last_was_an_operator: true,
            value_stack: Vec::new(),
            op_stack: Vec::new(),
        }
    }

    pub fn evaluate_list(mut self, exprs: Vec<Expression>) -> Expression {

        // Split into value stacks and operator stacks
        let mut exprs = exprs.into_iter();

        while let Some(part) = exprs.next() {
            debug!("Processing list item {:#?}", part);

            if part.is_number() {
                if self.last_was_an_operator {
                    debug!("Push on value stack {:#?}", part);
                    self.value_stack.push(part);
                } else {
                    debug!("Number, last_was_an_operator=false, paren_level={}", self.paren_level);

                    if self.paren_level > 0 {
                        while !self.op_stack.is_empty() &&
                              self.op_stack.last().unwrap().operator !=
                                  Operator::LeftParen {

                            debug!("op stack last {:#?}", self.op_stack.last());
                            self.math_machine();
                        }
                    }

                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        part,
                    );

                    debug!("Push on list on value stack {:#?}", list);

                    self.value_stack.push(list);
                }

                self.last_was_an_operator = false;
            } else if part.is_right_paren() {
                debug!("RIGHT PAREN");
                debug!("op stack = {:#?}", self.op_stack);

                let mut last_operator = self.op_stack.pop();
                while last_operator.is_some() &&
                      last_operator.unwrap().operator !=
                          Operator::LeftParen {

                    debug!("last operator before right paren = {:#?}", last_operator);
                    self.op_stack.push(last_operator.unwrap());
                    self.math_machine();
                    last_operator = self.op_stack.pop();
                }
                self.last_was_an_operator = false;
                self.paren_level -= 1;
            } else if part.is_left_paren() {
                let oo = part.extract_operator_offset();
                debug!("Push on op stack Leftparen");
                self.op_stack.push(oo);
                self.last_was_an_operator = true;
                self.paren_level += 1;
            } else if part.is_operator() {
                let oo = part.extract_operator_offset();
                if let Some(&last_operator) = self.op_stack.last() {
                    if last_operator
                           .operator
                           .same_or_greater_precedence(oo.operator) {
                        self.math_machine();
                    }
                }
                debug!("Push on op stack {:#?}", oo);
                self.op_stack.push(oo);
                self.last_was_an_operator = true;
            } else if part.is_string() {
                let t = part.extract_token_offset();
                let var_eval = self.context.get_variable(&t)
                                .unwrap_or(Expression::Value(
                                    OperatorOrToken::Token(t)
                                ));

                if self.last_was_an_operator {
                    self.value_stack.push(var_eval);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        var_eval,
                    );
                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;
            } else if let Expression::List(list) = part {
                debug!("Push list on value stack {:#?}", list);
                self.value_stack.push(Expression::List(list));
                self.last_was_an_operator = false;
            } else {
                if self.last_was_an_operator {
                    self.value_stack.push(part);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        part,
                    );
                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;
            }
        }

        debug!("PROCESS THE STACKS!");
        debug!("Op stack = {:#?}", self.op_stack);
        debug!("Value stack = {:#?}", self.value_stack);

        // Process the stacks
        while !self.op_stack.is_empty() {
            self.math_machine()
        }

        self.value_stack.pop().unwrap()
    }

    fn math_machine(&mut self) {
        debug!("Math machine:");

        let op     = self.get_operator();
        let second = self.get_value();
        let first  = self.get_value();

        let math_result = Expression::apply_math(
            op, first, second, self.context, self.paren_level,
        );
        debug!("Math result: {:#?}", math_result);

        self.value_stack.push(math_result);
    }

    fn get_operator(&mut self) -> OperatorOffset {
        // Math machine only gets called while there are operators on
        // the operator stack, so unwrap should be fine.
        let op = self.op_stack.pop().unwrap();
        debug!("op = {:#?}", op);
        op
    }

    fn get_value(&mut self) -> Expression {
        // TODO: Turn this into a SassError
        let val = self.value_stack.pop()
                      .expect("Expected an argument on the value stack");
        debug!("val = {:#?}", val);
        val
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::Token;
    use token_offset::TokenOffset;
    use operator_or_token::OperatorOrToken;
    use operator::Operator;
    use operator_offset::OperatorOffset;
    use context::Context;
    use ast::expression::Expression;

    fn one() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 1.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn two() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 2.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn plus() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Plus, offset: None }
        )
    }

    fn slash() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Slash, offset: None }
        )
    }

    fn right_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::RightParen, offset: None }
        )
    }

    fn left_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::LeftParen, offset: None }
        )
    }

    #[test]
    fn it_evaluates_a_list_adding_fractions() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(plus()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::Value(OperatorOrToken::Token(
                TokenOffset {
                    token: Token::Number {
                        value: 1.0,
                        units: None,
                        computed: true,
                    },
                    offset: None,
                }
            ))
        );
    }

    #[test]
    fn it_evaluates_a_list_with_division_and_string_concat() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(plus()),
            Expression::Value(left_paren()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(right_paren()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::String(String::from("10.5")),
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                ))
            ])
        );
    }

    #[test]
    fn it_does_not_divide_list_with_only_slashes() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(two()),
            ])
        );
    }
}
<commit_msg>Rearrange some code to make it more similar to other code<commit_after>use token::Token;
use context::Context;
use operator::Operator;
use operator_offset::OperatorOffset;
use operator_or_token::OperatorOrToken;
use token_offset::TokenOffset;
use ast::expression::Expression;

pub struct ExpressionEvaluator<'a> {
    context: &'a Context,
    pub paren_level: i32,
    last_was_an_operator: bool,
    value_stack: Vec<Expression>,
    op_stack: Vec<OperatorOffset>,
}

impl<'a> ExpressionEvaluator<'a> {
    pub fn evaluate(expr: Expression, context: &Context) -> Expression {
        match expr {
            Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                token: Token::String(_), ..
            })) => {
                context.get_variable(&t)
                       .unwrap_or(Expression::Value(
                            OperatorOrToken::Token(t)
                       ))
            },
            Expression::List(exprs) => {
                let evaluator = ExpressionEvaluator::new(context);
                evaluator.evaluate_list(exprs)
            },
            other => other,
        }
    }

    pub fn new(context: &Context) -> ExpressionEvaluator {
        ExpressionEvaluator {
            context: context,
            paren_level: 0,
            last_was_an_operator: true,
            value_stack: Vec::new(),
            op_stack: Vec::new(),
        }
    }

    pub fn evaluate_list(mut self, exprs: Vec<Expression>) -> Expression {

        // Split into value stacks and operator stacks
        let mut exprs = exprs.into_iter();

        while let Some(part) = exprs.next() {
            debug!("Processing list item {:#?}", part);

            if part.is_number() {
                if !self.last_was_an_operator {
                    debug!("Number, last_was_an_operator=false, paren_level={}", self.paren_level);

                    if self.paren_level > 0 {
                        while !self.op_stack.is_empty() &&
                              self.op_stack.last().unwrap().operator !=
                                  Operator::LeftParen {

                            debug!("op stack last {:#?}", self.op_stack.last());
                            self.math_machine();
                        }
                    }
                }

                if self.last_was_an_operator {
                    debug!("Push on value stack {:#?}", part);
                    self.value_stack.push(part);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        part,
                    );

                    debug!("Push on list on value stack {:#?}", list);

                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;

            } else if part.is_right_paren() {
                debug!("RIGHT PAREN");
                debug!("op stack = {:#?}", self.op_stack);

                let mut last_operator = self.op_stack.pop();
                while last_operator.is_some() &&
                      last_operator.unwrap().operator !=
                          Operator::LeftParen {

                    debug!("last operator before right paren = {:#?}", last_operator);
                    self.op_stack.push(last_operator.unwrap());
                    self.math_machine();
                    last_operator = self.op_stack.pop();
                }
                self.last_was_an_operator = false;
                self.paren_level -= 1;
            } else if part.is_left_paren() {
                let oo = part.extract_operator_offset();
                debug!("Push on op stack Leftparen");
                self.op_stack.push(oo);
                self.last_was_an_operator = true;
                self.paren_level += 1;
            } else if part.is_operator() {
                let oo = part.extract_operator_offset();
                if let Some(&last_operator) = self.op_stack.last() {
                    if last_operator
                           .operator
                           .same_or_greater_precedence(oo.operator) {
                        self.math_machine();
                    }
                }
                debug!("Push on op stack {:#?}", oo);
                self.op_stack.push(oo);
                self.last_was_an_operator = true;
            } else if part.is_string() {
                let t = part.extract_token_offset();
                let var_eval = self.context.get_variable(&t)
                                .unwrap_or(Expression::Value(
                                    OperatorOrToken::Token(t)
                                ));

                if self.last_was_an_operator {
                    self.value_stack.push(var_eval);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        var_eval,
                    );
                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;
            } else if let Expression::List(list) = part {
                debug!("Push list on value stack {:#?}", list);
                self.value_stack.push(Expression::List(list));
                self.last_was_an_operator = false;
            } else {
                if self.last_was_an_operator {
                    self.value_stack.push(part);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        part,
                    );
                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;
            }
        }

        debug!("PROCESS THE STACKS!");
        debug!("Op stack = {:#?}", self.op_stack);
        debug!("Value stack = {:#?}", self.value_stack);

        // Process the stacks
        while !self.op_stack.is_empty() {
            self.math_machine()
        }

        self.value_stack.pop().unwrap()
    }

    fn math_machine(&mut self) {
        debug!("Math machine:");

        let op     = self.get_operator();
        let second = self.get_value();
        let first  = self.get_value();

        let math_result = Expression::apply_math(
            op, first, second, self.context, self.paren_level,
        );
        debug!("Math result: {:#?}", math_result);

        self.value_stack.push(math_result);
    }

    fn get_operator(&mut self) -> OperatorOffset {
        // Math machine only gets called while there are operators on
        // the operator stack, so unwrap should be fine.
        let op = self.op_stack.pop().unwrap();
        debug!("op = {:#?}", op);
        op
    }

    fn get_value(&mut self) -> Expression {
        // TODO: Turn this into a SassError
        let val = self.value_stack.pop()
                      .expect("Expected an argument on the value stack");
        debug!("val = {:#?}", val);
        val
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::Token;
    use token_offset::TokenOffset;
    use operator_or_token::OperatorOrToken;
    use operator::Operator;
    use operator_offset::OperatorOffset;
    use context::Context;
    use ast::expression::Expression;

    fn one() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 1.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn two() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 2.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn plus() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Plus, offset: None }
        )
    }

    fn slash() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Slash, offset: None }
        )
    }

    fn right_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::RightParen, offset: None }
        )
    }

    fn left_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::LeftParen, offset: None }
        )
    }

    #[test]
    fn it_evaluates_a_list_adding_fractions() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(plus()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::Value(OperatorOrToken::Token(
                TokenOffset {
                    token: Token::Number {
                        value: 1.0,
                        units: None,
                        computed: true,
                    },
                    offset: None,
                }
            ))
        );
    }

    #[test]
    fn it_evaluates_a_list_with_division_and_string_concat() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(plus()),
            Expression::Value(left_paren()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(right_paren()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::String(String::from("10.5")),
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                ))
            ])
        );
    }

    #[test]
    fn it_does_not_divide_list_with_only_slashes() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(two()),
            ])
        );
    }
}
<|endoftext|>


<commit_before>//! This module contains the compiler. The compiler parses source code and generates byte code
//! suitable for execution by the VM.
//!
//! The complation process can be divided into the following phases:
//!
//! 1. Parsing
//! 2. Checking
//! 3. Resolving
//! 4. Linting / Optimizing
//! 5. Emitting
//!
//! This module provides several helper methods that will either wrap up the whole compilation
//! process or stop after a step, to enable more advanced uses of the compiler.
//!
//! Below is a brief description of what each phase does.
//!
//! ## Parsing
//!
//! This phase runs the generated Lea parser. It is generated at compile-time from the Parsing
//! Expression Grammar (PEG) in `lea.rustpeg` with help of the `rust-peg` crate.
//!
//! After the generated parser was run, an additional expression parser is run. The generated
//! parser will collect binary expressions in a list, ignoring operator precedences. It returns a
//! `ERawOp` node for all expressions. The expression parser in `expr_parser.rs` runs over all
//! `ERawOp`s, applies the shunting-yard algorithm, builds a tree of `EBinOp`s, and replaces the
//! AST node with the result.
//!
//! This could be done in the PEG parser, but since `rust-peg` doesn't support memoization, it
//! leads to a significant slowdown. Additionally, this would require redefining all operator
//! precedences as PEG rules (which duplicates code that shouldn't ever be duplicate), and
//! supporting both left- and right-associative operators is easier in a shunting-yard parser.
//!
//! ## Checking
//!
//! In `check.rs`, a `Checker` visitor is implemented, which runs over an AST and verifies that all
//! statements and expressions are used in a valid context. Since the PEG parser has no state, it
//! cannot do this (and it is better - though slower - to do it this way, since it separates
//! syntax error and context-dependant errors).
//!
//! For example, the checker will deny a `break` statement used outside of a loop and the varargs
//! expression `...` outside of a varargs function.
//!
//! ## Resolving
//!
//! The resolver implemented in `resolve.rs` transforms any named variable (`VNamed` in the AST) to
//! a local, upvalue, or global reference. This makes it easy to see which identifier refers to
//! which variable (might be useful for code highlighting in IDEs) and makes the emitter a bit
//! simpler, since it doesn't have to figure out what an identifier means if it encounters it: It
//! already knows, since the resolver stored it in the AST.
//!
//! ## Linting / Optimizing
//!
//! In this step, all configured Linters and Optimizers are run on the resolved AST. Internally, we
//! make to distinction between a Linter and an Optimizer: Both take a `Function` node, do
//! something to it, and return a transformed `Function` along with a list of warnings to be
//! issued.
//!
//! Most Linters will just return the unmodified function and generate some warnings, while most
//! optimizers will modify the AST, but return no warnings. Of course, there might be exceptions.
//!
//! Linters and Optimizers are implemented in the files inside the "transform" directory:
//!
//! * `fold.rs` implements a constant folding optimization that looks for operators that are
//!   applied to literals, applies them at compile-time, and replaces the node with the result.
//!   This is a basic optimization performed by most compilers (Lua does this too).
//!
//! * `globalwrite.rs` implements a Linter that will issue a warning for all assignments to
//!   globals. Global variables are generally meant to hold the standard library functions, and not
//!   much more. User code should always prefer locals, and modules are meant to return the module
//!   when loaded, which should be assigned to a local by the code that `require`d the module. If
//!   you still need to access a global (for example, when setting up a sandbox), you can do so
//!   explicitly by indexing `_ENV`.
//!
//! * `deprecated_ops.rs` is a Linter that emits a warning whenever a Lua operator is used that has
//!   an equivalent replacement in Lea.
//!
//! ## Emitting
//!
//! The byte code emitter for the compiler still needs to be written. It will take an AST and
//! convert it to machine code for the virtual machine.

#![feature(append)]
#![allow(trivial_casts)]

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate rustc_serialize;
extern crate term;
extern crate unicode_segmentation;

extern crate lea_core;
extern crate lea_ast as ast;
extern crate lea_parser as parser;

use transform::{Transform, LintMode};
use emitter::emit_func;

use ast::Function;

use std::default::Default;

mod errors;
pub mod transform;
pub mod check;
pub mod emitter;
pub mod prettyprint;
pub mod resolve;

pub use errors::*;
pub use lea_core::fndata::FnData;

/// Defines options that can be used to tweak the compilation process (optimizations, linters, ...)
pub struct CompileConfig<'a> {
    /// AST transforms to apply. All transforms are applied in the order specified here.
    trans: Vec<(&'a Transform, LintMode)>,
}

impl <'a> CompileConfig<'a> {
    /// Creates an empty `CompileConfig` that applies no optimizations or lints
    pub fn empty() -> CompileConfig<'a> {
        CompileConfig {
            trans: vec![],
        }
    }

    /// Creates a `CompileConfig` that will apply named lints. Returns `None` if a Lint doesn't
    /// exist (TODO).
    pub fn named_lints(names: &[&str]) -> Option<CompileConfig<'a>> {
        let mut conf = CompileConfig::empty();
        for name in names {
            match transform::TRANSFORMS.get(name) {
                None => { return None; },
                Some(tr) => {
                    conf.trans.push((tr, LintMode::Warn));
                }
            }
        }

        Some(conf)
    }

    pub fn add_transform(&mut self, tr: &'a Transform, mode: LintMode) {
        self.trans.push((tr, mode));
    }
}

impl <'a> Default for CompileConfig<'a> {
    /// Creates a default `CompileConfig` that applies the default set of optimizers and linters
    fn default() -> CompileConfig<'a> {
        CompileConfig {
            trans: transform::TRANSFORMS_DEFAULT.iter().map(|&(name, lvl)| (transform::TRANSFORMS.get(name).unwrap(), lvl)).collect(),
        }
    }
}

/// Output of a successful compilation.
pub struct CompileOutput<'a> {
    /// Warnings generated by Linters
    pub warns: Vec<Warning>,
    /// AST node of main function
    pub main: Function<'a>,
    /// Prototype of main function, to be instantiated and run by the VM
    pub mainproto: FnData,
}

/// Parses and checks the given source code (stops after compilation step 2). Returns a valid
/// `Function` node on success.
///
/// If this succeeds, this means that the program described by the source code is valid and can be
/// compiled.
pub fn parse_and_check<'a>(code: &'a str) -> CompileResult<Function<'a>> {
    match parser::parse_main(code) {
        Err(e) => Err(ErrParse(e)),
        Ok(main) => {
            let res = check::check_func(&main);
            match res {
                Err(errs) => Err(ErrCheck(errs)),
                Ok(()) => {
                    Ok(main)
                }
            }
        }
    }
}

/// Parses, checks and resolves the given source code (stops after compilation step 3). Returns the
/// resolved main function on success.
pub fn parse_and_resolve<'a>(code: &'a str) -> CompileResult<Function<'a>> {
    let mut main = try!(parse_and_check(code));
    main = resolve::resolve_func(main);

    Ok(main)
}

/// Applies all transforms in the given `CompileConfig` to a `main` function. Returns all warnings
/// generated by the transforms. `main` must be resolved beforehand.
///
/// If any transform is configured to cause a compilation error, a corresponding error will be
/// returned.
pub fn apply_transforms<'a>(mut main: Function<'a>, conf: &CompileConfig) -> (Function<'a>, CompileResult<Vec<Warning>>) {
    let mut warnings: Vec<Warning> = vec![];
    let mut errwarns: Vec<Warning> = vec![];  // warnings handled as errors
    for &(tr, mode) in &conf.trans {
        let (newmain, mut new_warns) = tr(main);
        main = newmain;

        if mode == LintMode::Error && new_warns.len() > 0 {
            errwarns.append(&mut new_warns.clone());
        }

        warnings.append(&mut new_warns);
    }

    (main, if errwarns.is_empty() {
        Ok(warnings)
    } else {
        Err(ErrLint(errwarns))
    })
}

/// Compiles source code into an `FnData` instance. This executes the whole compilation process and
/// emits byte code (TODO).
///
/// Note that this will not return the `Function` node on error, even if parsing succeeded.
///
/// # Parameters
///
/// * `code` - The source code to compile
/// * `source_name` - The name of this source code. Used when reporting errors. This should be
///   a file name or something similar.
/// * `conf` - The `CompileConfig` to use for this compilation
pub fn compile_str<'a>(code: &'a str, source_name: &str, conf: &CompileConfig) -> CompileResult<CompileOutput<'a>> {
    let main = try!(parse_and_resolve(code));
    let (main, tr_res) = apply_transforms(main, &conf);
    let warnings = try!(tr_res);
    let emit_res = emit_func(&main, source_name);

    match emit_res {
        Ok(proto) => Ok(CompileOutput {
            warns: warnings,
            main: main,
            mainproto: proto,
        }),
        Err(errs) => Err(ErrEmit(errs)),
    }
}
<commit_msg>Remove append feature<commit_after>//! This module contains the compiler. The compiler parses source code and generates byte code
//! suitable for execution by the VM.
//!
//! The complation process can be divided into the following phases:
//!
//! 1. Parsing
//! 2. Checking
//! 3. Resolving
//! 4. Linting / Optimizing
//! 5. Emitting
//!
//! This module provides several helper methods that will either wrap up the whole compilation
//! process or stop after a step, to enable more advanced uses of the compiler.
//!
//! Below is a brief description of what each phase does.
//!
//! ## Parsing
//!
//! This phase runs the generated Lea parser. It is generated at compile-time from the Parsing
//! Expression Grammar (PEG) in `lea.rustpeg` with help of the `rust-peg` crate.
//!
//! After the generated parser was run, an additional expression parser is run. The generated
//! parser will collect binary expressions in a list, ignoring operator precedences. It returns a
//! `ERawOp` node for all expressions. The expression parser in `expr_parser.rs` runs over all
//! `ERawOp`s, applies the shunting-yard algorithm, builds a tree of `EBinOp`s, and replaces the
//! AST node with the result.
//!
//! This could be done in the PEG parser, but since `rust-peg` doesn't support memoization, it
//! leads to a significant slowdown. Additionally, this would require redefining all operator
//! precedences as PEG rules (which duplicates code that shouldn't ever be duplicate), and
//! supporting both left- and right-associative operators is easier in a shunting-yard parser.
//!
//! ## Checking
//!
//! In `check.rs`, a `Checker` visitor is implemented, which runs over an AST and verifies that all
//! statements and expressions are used in a valid context. Since the PEG parser has no state, it
//! cannot do this (and it is better - though slower - to do it this way, since it separates
//! syntax error and context-dependant errors).
//!
//! For example, the checker will deny a `break` statement used outside of a loop and the varargs
//! expression `...` outside of a varargs function.
//!
//! ## Resolving
//!
//! The resolver implemented in `resolve.rs` transforms any named variable (`VNamed` in the AST) to
//! a local, upvalue, or global reference. This makes it easy to see which identifier refers to
//! which variable (might be useful for code highlighting in IDEs) and makes the emitter a bit
//! simpler, since it doesn't have to figure out what an identifier means if it encounters it: It
//! already knows, since the resolver stored it in the AST.
//!
//! ## Linting / Optimizing
//!
//! In this step, all configured Linters and Optimizers are run on the resolved AST. Internally, we
//! make to distinction between a Linter and an Optimizer: Both take a `Function` node, do
//! something to it, and return a transformed `Function` along with a list of warnings to be
//! issued.
//!
//! Most Linters will just return the unmodified function and generate some warnings, while most
//! optimizers will modify the AST, but return no warnings. Of course, there might be exceptions.
//!
//! Linters and Optimizers are implemented in the files inside the "transform" directory:
//!
//! * `fold.rs` implements a constant folding optimization that looks for operators that are
//!   applied to literals, applies them at compile-time, and replaces the node with the result.
//!   This is a basic optimization performed by most compilers (Lua does this too).
//!
//! * `globalwrite.rs` implements a Linter that will issue a warning for all assignments to
//!   globals. Global variables are generally meant to hold the standard library functions, and not
//!   much more. User code should always prefer locals, and modules are meant to return the module
//!   when loaded, which should be assigned to a local by the code that `require`d the module. If
//!   you still need to access a global (for example, when setting up a sandbox), you can do so
//!   explicitly by indexing `_ENV`.
//!
//! * `deprecated_ops.rs` is a Linter that emits a warning whenever a Lua operator is used that has
//!   an equivalent replacement in Lea.
//!
//! ## Emitting
//!
//! The byte code emitter for the compiler still needs to be written. It will take an AST and
//! convert it to machine code for the virtual machine.

#![allow(trivial_casts)]

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate rustc_serialize;
extern crate term;
extern crate unicode_segmentation;

extern crate lea_core;
extern crate lea_ast as ast;
extern crate lea_parser as parser;

use transform::{Transform, LintMode};
use emitter::emit_func;

use ast::Function;

use std::default::Default;

mod errors;
pub mod transform;
pub mod check;
pub mod emitter;
pub mod prettyprint;
pub mod resolve;

pub use errors::*;
pub use lea_core::fndata::FnData;

/// Defines options that can be used to tweak the compilation process (optimizations, linters, ...)
pub struct CompileConfig<'a> {
    /// AST transforms to apply. All transforms are applied in the order specified here.
    trans: Vec<(&'a Transform, LintMode)>,
}

impl <'a> CompileConfig<'a> {
    /// Creates an empty `CompileConfig` that applies no optimizations or lints
    pub fn empty() -> CompileConfig<'a> {
        CompileConfig {
            trans: vec![],
        }
    }

    /// Creates a `CompileConfig` that will apply named lints. Returns `None` if a Lint doesn't
    /// exist (TODO).
    pub fn named_lints(names: &[&str]) -> Option<CompileConfig<'a>> {
        let mut conf = CompileConfig::empty();
        for name in names {
            match transform::TRANSFORMS.get(name) {
                None => { return None; },
                Some(tr) => {
                    conf.trans.push((tr, LintMode::Warn));
                }
            }
        }

        Some(conf)
    }

    pub fn add_transform(&mut self, tr: &'a Transform, mode: LintMode) {
        self.trans.push((tr, mode));
    }
}

impl <'a> Default for CompileConfig<'a> {
    /// Creates a default `CompileConfig` that applies the default set of optimizers and linters
    fn default() -> CompileConfig<'a> {
        CompileConfig {
            trans: transform::TRANSFORMS_DEFAULT.iter().map(|&(name, lvl)| (transform::TRANSFORMS.get(name).unwrap(), lvl)).collect(),
        }
    }
}

/// Output of a successful compilation.
pub struct CompileOutput<'a> {
    /// Warnings generated by Linters
    pub warns: Vec<Warning>,
    /// AST node of main function
    pub main: Function<'a>,
    /// Prototype of main function, to be instantiated and run by the VM
    pub mainproto: FnData,
}

/// Parses and checks the given source code (stops after compilation step 2). Returns a valid
/// `Function` node on success.
///
/// If this succeeds, this means that the program described by the source code is valid and can be
/// compiled.
pub fn parse_and_check<'a>(code: &'a str) -> CompileResult<Function<'a>> {
    match parser::parse_main(code) {
        Err(e) => Err(ErrParse(e)),
        Ok(main) => {
            let res = check::check_func(&main);
            match res {
                Err(errs) => Err(ErrCheck(errs)),
                Ok(()) => {
                    Ok(main)
                }
            }
        }
    }
}

/// Parses, checks and resolves the given source code (stops after compilation step 3). Returns the
/// resolved main function on success.
pub fn parse_and_resolve<'a>(code: &'a str) -> CompileResult<Function<'a>> {
    let mut main = try!(parse_and_check(code));
    main = resolve::resolve_func(main);

    Ok(main)
}

/// Applies all transforms in the given `CompileConfig` to a `main` function. Returns all warnings
/// generated by the transforms. `main` must be resolved beforehand.
///
/// If any transform is configured to cause a compilation error, a corresponding error will be
/// returned.
pub fn apply_transforms<'a>(mut main: Function<'a>, conf: &CompileConfig) -> (Function<'a>, CompileResult<Vec<Warning>>) {
    let mut warnings: Vec<Warning> = Vec::new();
    let mut errwarns: Vec<Warning> = Vec::new();  // warnings handled as errors
    for &(tr, mode) in &conf.trans {
        let (newmain, new_warns) = tr(main);
        main = newmain;

        if mode == LintMode::Error && new_warns.len() > 0 {
            errwarns.extend(new_warns);
        } else {
            warnings.extend(new_warns);
        }
    }

    (main, if errwarns.is_empty() {
        Ok(warnings)
    } else {
        Err(ErrLint(errwarns))
    })
}

/// Compiles source code into an `FnData` instance. This executes the whole compilation process and
/// emits byte code (TODO).
///
/// Note that this will not return the `Function` node on error, even if parsing succeeded.
///
/// # Parameters
///
/// * `code` - The source code to compile
/// * `source_name` - The name of this source code. Used when reporting errors. This should be
///   a file name or something similar.
/// * `conf` - The `CompileConfig` to use for this compilation
pub fn compile_str<'a>(code: &'a str, source_name: &str, conf: &CompileConfig) -> CompileResult<CompileOutput<'a>> {
    let main = try!(parse_and_resolve(code));
    let (main, tr_res) = apply_transforms(main, &conf);
    let warnings = try!(tr_res);
    let emit_res = emit_func(&main, source_name);

    match emit_res {
        Ok(proto) => Ok(CompileOutput {
            warns: warnings,
            main: main,
            mainproto: proto,
        }),
        Err(errs) => Err(ErrEmit(errs)),
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Composable iterator objects

use prelude::*;

pub trait Iterator<A> {
    /// Advance the iterator and return the next value. Return `None` when the end is reached.
    fn next(&mut self) -> Option<A>;
}

pub trait IteratorUtil<A> {
    fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<Self, U>;
    // FIXME: #5898: should be called map
    fn transform<'r, B>(self, f: &'r fn(A) -> B) -> MapIterator<'r, A, B, Self>;
    fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> FilterIterator<'r, A, Self>;
    fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhileIterator<'r, A, Self>;
    fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhileIterator<'r, A, Self>;
    fn skip(self, n: uint) -> SkipIterator<Self>;
    fn take(self, n: uint) -> TakeIterator<Self>;
    fn enumerate(self) -> EnumerateIterator<Self>;
    fn advance(&mut self, f: &fn(A) -> bool);
}

impl<A, T: Iterator<A>> IteratorUtil<A> for T {
    #[inline(always)]
    fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> {
        ZipIterator{a: self, b: other}
    }

    // FIXME: #5898: should be called map
    #[inline(always)]
    fn transform<'r, B>(self, f: &'r fn(A) -> B) -> MapIterator<'r, A, B, T> {
        MapIterator{iter: self, f: f}
    }

    #[inline(always)]
    fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> FilterIterator<'r, A, T> {
        FilterIterator{iter: self, predicate: predicate}
    }

    #[inline(always)]
    fn enumerate(self) -> EnumerateIterator<T> {
        EnumerateIterator{iter: self, count: 0}
    }

    #[inline(always)]
    fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhileIterator<'r, A, T> {
        SkipWhileIterator{iter: self, flag: false, predicate: predicate}
    }

    #[inline(always)]
    fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhileIterator<'r, A, T> {
        TakeWhileIterator{iter: self, flag: false, predicate: predicate}
    }

    #[inline(always)]
    fn skip(self, n: uint) -> SkipIterator<T> {
        SkipIterator{iter: self, n: n}
    }

    #[inline(always)]
    fn take(self, n: uint) -> TakeIterator<T> {
        TakeIterator{iter: self, n: n}
    }

    /// A shim implementing the `for` loop iteration protocol for iterator objects
    #[inline]
    fn advance(&mut self, f: &fn(A) -> bool) {
        loop {
            match self.next() {
                Some(x) => {
                    if !f(x) { return }
                }
                None => return
            }
        }
    }
}

pub struct ZipIterator<T, U> {
    priv a: T,
    priv b: U
}

impl<A, B, T: Iterator<A>, U: Iterator<B>> Iterator<(A, B)> for ZipIterator<T, U> {
    #[inline]
    fn next(&mut self) -> Option<(A, B)> {
        match (self.a.next(), self.b.next()) {
            (Some(x), Some(y)) => Some((x, y)),
            _ => None
        }
    }
}

pub struct FilterIterator<'self, A, T> {
    priv iter: T,
    priv predicate: &'self fn(&A) -> bool
}

impl<'self, A, T: Iterator<A>> Iterator<A> for FilterIterator<'self, A, T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        for self.iter.advance |x| {
            if (self.predicate)(&x) {
                return Some(x);
            } else {
                loop
            }
        }
        None
    }
}

pub struct MapIterator<'self, A, B, T> {
    priv iter: T,
    priv f: &'self fn(A) -> B
}

impl<'self, A, B, T: Iterator<A>> Iterator<B> for MapIterator<'self, A, B, T> {
    #[inline]
    fn next(&mut self) -> Option<B> {
        match self.iter.next() {
            Some(a) => Some((self.f)(a)),
            _ => None
        }
    }
}

pub struct EnumerateIterator<T> {
    priv iter: T,
    priv count: uint
}

impl<A, T: Iterator<A>> Iterator<(uint, A)> for EnumerateIterator<T> {
    #[inline]
    fn next(&mut self) -> Option<(uint, A)> {
        match self.iter.next() {
            Some(a) => {
                let ret = Some((self.count, a));
                self.count += 1;
                ret
            }
            _ => None
        }
    }
}

pub struct SkipWhileIterator<'self, A, T> {
    priv iter: T,
    priv flag: bool,
    priv predicate: &'self fn(&A) -> bool
}

impl<'self, A, T: Iterator<A>> Iterator<A> for SkipWhileIterator<'self, A, T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        let mut next = self.iter.next();
        if self.flag {
            next
        } else {
            loop {
                match next {
                    Some(x) => {
                        if (self.predicate)(&x) {
                            next = self.iter.next();
                            loop
                        } else {
                            self.flag = true;
                            return Some(x)
                        }
                    }
                    None => return None
                }
            }
        }
    }
}

pub struct TakeWhileIterator<'self, A, T> {
    priv iter: T,
    priv flag: bool,
    priv predicate: &'self fn(&A) -> bool
}

impl<'self, A, T: Iterator<A>> Iterator<A> for TakeWhileIterator<'self, A, T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        if self.flag {
            None
        } else {
            match self.iter.next() {
                Some(x) => {
                    if (self.predicate)(&x) {
                        Some(x)
                    } else {
                        self.flag = true;
                        None
                    }
                }
                None => None
            }
        }
    }
}

pub struct SkipIterator<T> {
    priv iter: T,
    priv n: uint
}

impl<A, T: Iterator<A>> Iterator<A> for SkipIterator<T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        let mut next = self.iter.next();
        if self.n == 0 {
            next
        } else {
            let n = self.n;
            for n.times {
                match next {
                    Some(_) => {
                        next = self.iter.next();
                        loop
                    }
                    None => {
                        self.n = 0;
                        return None
                    }
                }
            }
            self.n = 0;
            next
        }
    }
}

pub struct TakeIterator<T> {
    priv iter: T,
    priv n: uint
}

impl<A, T: Iterator<A>> Iterator<A> for TakeIterator<T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        let next = self.iter.next();
        if self.n != 0 {
            self.n -= 1;
            next
        } else {
            None
        }
    }
}

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

    #[test]
    fn test_iterator_enumerate() {
        let xs = [0u, 1, 2, 3, 4, 5];
        let mut it = xs.iter().enumerate();
        for it.advance |(i, &x): (uint, &uint)| {
            assert_eq!(i, x);
        }
    }

    #[test]
    fn test_iterator_take_while() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19];
        let ys = [0u, 1, 2, 3, 5, 13];
        let mut it = xs.iter().take_while(|&x| *x < 15u);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }

    #[test]
    fn test_iterator_skip_while() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19];
        let ys = [15, 16, 17, 19];
        let mut it = xs.iter().skip_while(|&x| *x < 15u);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }

    #[test]
    fn test_iterator_skip() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30];
        let ys = [13, 15, 16, 17, 19, 20, 30];
        let mut it = xs.iter().skip(5);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }

    #[test]
    fn test_iterator_take() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19];
        let ys = [0u, 1, 2, 3, 5];
        let mut it = xs.iter().take(5);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }
}
<commit_msg>re-organize the iterator module a bit<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Composable iterator objects

use prelude::*;

pub trait Iterator<A> {
    /// Advance the iterator and return the next value. Return `None` when the end is reached.
    fn next(&mut self) -> Option<A>;
}

pub trait IteratorUtil<A> {
    fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<Self, U>;
    // FIXME: #5898: should be called map
    fn transform<'r, B>(self, f: &'r fn(A) -> B) -> MapIterator<'r, A, B, Self>;
    fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> FilterIterator<'r, A, Self>;
    fn enumerate(self) -> EnumerateIterator<Self>;
    fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhileIterator<'r, A, Self>;
    fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhileIterator<'r, A, Self>;
    fn skip(self, n: uint) -> SkipIterator<Self>;
    fn take(self, n: uint) -> TakeIterator<Self>;
    fn advance(&mut self, f: &fn(A) -> bool);
}

impl<A, T: Iterator<A>> IteratorUtil<A> for T {
    #[inline(always)]
    fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> {
        ZipIterator{a: self, b: other}
    }

    // FIXME: #5898: should be called map
    #[inline(always)]
    fn transform<'r, B>(self, f: &'r fn(A) -> B) -> MapIterator<'r, A, B, T> {
        MapIterator{iter: self, f: f}
    }

    #[inline(always)]
    fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> FilterIterator<'r, A, T> {
        FilterIterator{iter: self, predicate: predicate}
    }

    #[inline(always)]
    fn enumerate(self) -> EnumerateIterator<T> {
        EnumerateIterator{iter: self, count: 0}
    }

    #[inline(always)]
    fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhileIterator<'r, A, T> {
        SkipWhileIterator{iter: self, flag: false, predicate: predicate}
    }

    #[inline(always)]
    fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhileIterator<'r, A, T> {
        TakeWhileIterator{iter: self, flag: false, predicate: predicate}
    }

    #[inline(always)]
    fn skip(self, n: uint) -> SkipIterator<T> {
        SkipIterator{iter: self, n: n}
    }

    #[inline(always)]
    fn take(self, n: uint) -> TakeIterator<T> {
        TakeIterator{iter: self, n: n}
    }

    /// A shim implementing the `for` loop iteration protocol for iterator objects
    #[inline]
    fn advance(&mut self, f: &fn(A) -> bool) {
        loop {
            match self.next() {
                Some(x) => {
                    if !f(x) { return }
                }
                None => return
            }
        }
    }
}

pub struct ZipIterator<T, U> {
    priv a: T,
    priv b: U
}

impl<A, B, T: Iterator<A>, U: Iterator<B>> Iterator<(A, B)> for ZipIterator<T, U> {
    #[inline]
    fn next(&mut self) -> Option<(A, B)> {
        match (self.a.next(), self.b.next()) {
            (Some(x), Some(y)) => Some((x, y)),
            _ => None
        }
    }
}

pub struct MapIterator<'self, A, B, T> {
    priv iter: T,
    priv f: &'self fn(A) -> B
}

impl<'self, A, B, T: Iterator<A>> Iterator<B> for MapIterator<'self, A, B, T> {
    #[inline]
    fn next(&mut self) -> Option<B> {
        match self.iter.next() {
            Some(a) => Some((self.f)(a)),
            _ => None
        }
    }
}

pub struct FilterIterator<'self, A, T> {
    priv iter: T,
    priv predicate: &'self fn(&A) -> bool
}

impl<'self, A, T: Iterator<A>> Iterator<A> for FilterIterator<'self, A, T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        for self.iter.advance |x| {
            if (self.predicate)(&x) {
                return Some(x);
            } else {
                loop
            }
        }
        None
    }
}

pub struct EnumerateIterator<T> {
    priv iter: T,
    priv count: uint
}

impl<A, T: Iterator<A>> Iterator<(uint, A)> for EnumerateIterator<T> {
    #[inline]
    fn next(&mut self) -> Option<(uint, A)> {
        match self.iter.next() {
            Some(a) => {
                let ret = Some((self.count, a));
                self.count += 1;
                ret
            }
            _ => None
        }
    }
}

pub struct SkipWhileIterator<'self, A, T> {
    priv iter: T,
    priv flag: bool,
    priv predicate: &'self fn(&A) -> bool
}

impl<'self, A, T: Iterator<A>> Iterator<A> for SkipWhileIterator<'self, A, T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        let mut next = self.iter.next();
        if self.flag {
            next
        } else {
            loop {
                match next {
                    Some(x) => {
                        if (self.predicate)(&x) {
                            next = self.iter.next();
                            loop
                        } else {
                            self.flag = true;
                            return Some(x)
                        }
                    }
                    None => return None
                }
            }
        }
    }
}

pub struct TakeWhileIterator<'self, A, T> {
    priv iter: T,
    priv flag: bool,
    priv predicate: &'self fn(&A) -> bool
}

impl<'self, A, T: Iterator<A>> Iterator<A> for TakeWhileIterator<'self, A, T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        if self.flag {
            None
        } else {
            match self.iter.next() {
                Some(x) => {
                    if (self.predicate)(&x) {
                        Some(x)
                    } else {
                        self.flag = true;
                        None
                    }
                }
                None => None
            }
        }
    }
}

pub struct SkipIterator<T> {
    priv iter: T,
    priv n: uint
}

impl<A, T: Iterator<A>> Iterator<A> for SkipIterator<T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        let mut next = self.iter.next();
        if self.n == 0 {
            next
        } else {
            let n = self.n;
            for n.times {
                match next {
                    Some(_) => {
                        next = self.iter.next();
                        loop
                    }
                    None => {
                        self.n = 0;
                        return None
                    }
                }
            }
            self.n = 0;
            next
        }
    }
}

pub struct TakeIterator<T> {
    priv iter: T,
    priv n: uint
}

impl<A, T: Iterator<A>> Iterator<A> for TakeIterator<T> {
    #[inline]
    fn next(&mut self) -> Option<A> {
        let next = self.iter.next();
        if self.n != 0 {
            self.n -= 1;
            next
        } else {
            None
        }
    }
}

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

    #[test]
    fn test_iterator_enumerate() {
        let xs = [0u, 1, 2, 3, 4, 5];
        let mut it = xs.iter().enumerate();
        for it.advance |(i, &x): (uint, &uint)| {
            assert_eq!(i, x);
        }
    }

    #[test]
    fn test_iterator_take_while() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19];
        let ys = [0u, 1, 2, 3, 5, 13];
        let mut it = xs.iter().take_while(|&x| *x < 15u);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }

    #[test]
    fn test_iterator_skip_while() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19];
        let ys = [15, 16, 17, 19];
        let mut it = xs.iter().skip_while(|&x| *x < 15u);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }

    #[test]
    fn test_iterator_skip() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30];
        let ys = [13, 15, 16, 17, 19, 20, 30];
        let mut it = xs.iter().skip(5);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }

    #[test]
    fn test_iterator_take() {
        let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19];
        let ys = [0u, 1, 2, 3, 5];
        let mut it = xs.iter().take(5);
        let mut i = 0;
        for it.advance |&x: &uint| {
            assert_eq!(x, ys[i]);
            i += 1;
        }
        assert_eq!(i, ys.len());
    }
}
<|endoftext|>