<commit_before>use crate::StrError;

/// Defines the class of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum GeoClass {
    /// Lines (segments) class
    Lin,

    /// Triangles class
    Tri,

    /// Quadrilaterals class
    Qua,

    /// Tetrahedra class
    Tet,

    /// Hexahedra class
    Hex,
}

/// Defines the kind of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum GeoKind {
    /// Line (segment) with 2 nodes (linear functions)
    Lin2,

    /// Line (segment) with 3 nodes (quadratic functions)
    Lin3,

    /// Line (segment) with 4 nodes (cubic functions)
    Lin4,

    /// Line (segment) with 5 nodes (quartic functions)
    Lin5,

    /// Triangle with 3 nodes (linear edges)
    Tri3,

    /// Triangle with 6 nodes (quadratic edges)
    Tri6,

    /// Triangle with 10 nodes (cubic edges; interior node)
    Tri10,

    /// Triangle with 15 nodes (quartic edges; interior nodes)
    Tri15,

    /// Quadrilateral with 4 nodes (linear edges)
    Qua4,

    /// Quadrilateral with 8 nodes (quadratic edges)
    Qua8,

    /// Quadrilateral with 9 nodes (quadratic edges; interior node)
    Qua9,

    /// Quadrilateral with 12 nodes (cubic edges)
    Qua12,

    /// Quadrilateral with 16 nodes (cubic edges; interior nodes)
    Qua16,

    /// Quadrilateral with 17 nodes (quartic edges; interior node)
    Qua17,

    /// Tetrahedron with 4 nodes (linear faces)
    Tet4,

    /// Tetrahedron with 10 nodes (quadratic faces)
    Tet10,

    /// Hexahedron with 8 nodes (bilinear faces)
    Hex8,

    /// Hexahedron with 20 nodes (quadratic faces)
    Hex20,
}

/// GeoNdim is the (local) space dimension of the geometric feature
pub type GeoNdim = usize;

/// GeoNnode is the number of nodes associated with a geometric feature
pub type GeoNnode = usize;

impl GeoKind {
    /// Holds all (geo_ndim,nnode) pairs
    pub const PAIRS: [(usize, usize); 18] = [
        // Lin
        (1, 2),
        (1, 3),
        (1, 4),
        (1, 5),
        // Tri
        (2, 3),
        (2, 6),
        (2, 10),
        (2, 15),
        // Qua
        (2, 4),
        (2, 8),
        (2, 9),
        (2, 12),
        (2, 16),
        (2, 17),
        // Tet
        (3, 4),
        (3, 10),
        // Hex
        (3, 8),
        (3, 20),
    ];

    /// Holds all enum values
    pub const VALUES: [Self; 18] = [
        // Lin
        Self::Lin2,
        Self::Lin3,
        Self::Lin4,
        Self::Lin5,
        // Tri
        Self::Tri3,
        Self::Tri6,
        Self::Tri10,
        Self::Tri15,
        // Qua
        Self::Qua4,
        Self::Qua8,
        Self::Qua9,
        Self::Qua12,
        Self::Qua16,
        Self::Qua17,
        // Tet
        Self::Tet4,
        Self::Tet10,
        // Hex
        Self::Hex8,
        Self::Hex20,
    ];
}

/// Returns the GeoClass and GeoKind for given geo_ndim and nnode
///
/// # Examples
///
/// ```
/// use gemlab::shapes;
/// use gemlab::StrError;
///
/// fn main() -> Result<(), StrError> {
///     let (geo_ndim, nnode) = (3, 20);
///     let (class, kind) = shapes::geo_class_and_kind(geo_ndim, nnode)?;
///     assert_eq!(class, shapes::GeoClass::Hex);
///     assert_eq!(kind, shapes::GeoKind::Hex20);
///     Ok(())
/// }
/// ```
pub fn geo_class_and_kind(geo_ndim: usize, nnode: usize) -> Result<(GeoClass, GeoKind), StrError> {
    match (geo_ndim, nnode) {
        // Lin
        (1, 2) => Ok((GeoClass::Lin, GeoKind::Lin2)),
        (1, 3) => Ok((GeoClass::Lin, GeoKind::Lin3)),
        (1, 4) => Ok((GeoClass::Lin, GeoKind::Lin4)),
        (1, 5) => Ok((GeoClass::Lin, GeoKind::Lin5)),

        // Tri
        (2, 3) => Ok((GeoClass::Tri, GeoKind::Tri3)),
        (2, 6) => Ok((GeoClass::Tri, GeoKind::Tri6)),
        (2, 10) => Ok((GeoClass::Tri, GeoKind::Tri10)),
        (2, 15) => Ok((GeoClass::Tri, GeoKind::Tri15)),

        // Qua
        (2, 4) => Ok((GeoClass::Qua, GeoKind::Qua4)),
        (2, 8) => Ok((GeoClass::Qua, GeoKind::Qua8)),
        (2, 9) => Ok((GeoClass::Qua, GeoKind::Qua9)),
        (2, 12) => Ok((GeoClass::Qua, GeoKind::Qua12)),
        (2, 16) => Ok((GeoClass::Qua, GeoKind::Qua16)),
        (2, 17) => Ok((GeoClass::Qua, GeoKind::Qua17)),

        // Tet
        (3, 4) => Ok((GeoClass::Tet, GeoKind::Tet4)),
        (3, 10) => Ok((GeoClass::Tet, GeoKind::Tet10)),

        // Hex
        (3, 8) => Ok((GeoClass::Hex, GeoKind::Hex8)),
        (3, 20) => Ok((GeoClass::Hex, GeoKind::Hex20)),
        _ => Err("(geo_ndim,nnode) combination is invalid"),
    }
}

/// Returns the (min,max,delta) limits on the reference domain
///
/// # Tri and Tet
///
/// * `ξ_min` = 0.0
/// * `ξ_max` = 1.0
/// * `Δξ` = 1.0
///
/// # Lin, Qua, Hex
///
/// * `ξ_min` = -1.0
/// * `ξ_max` = +1.0
/// * `Δξ` = 2.0
#[inline]
pub fn ref_domain_limits(class: GeoClass) -> (f64, f64, f64) {
    if class == GeoClass::Tri || class == GeoClass::Tet {
        (0.0, 1.0, 1.0)
    } else {
        (-1.0, 1.0, 2.0)
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use crate::{
        shapes::{geo_class_and_kind, GeoClass, GeoKind},
        StrError,
    };
    use std::collections::HashSet;

    #[test]
    fn derive_works() {
        let class = GeoClass::Tri.clone();
        let kind = GeoKind::Tri6.clone();
        let classes = HashSet::from([GeoClass::Tri, GeoClass::Qua]);
        let kinds = HashSet::from([GeoKind::Tri3, GeoKind::Qua4]);
        assert_eq!(class, GeoClass::Tri);
        assert_eq!(kind, GeoKind::Tri6);
        assert_eq!(format!("{:?}", class), "Tri");
        assert_eq!(format!("{:?}", kind), "Tri6");
        assert_eq!(classes.contains(&GeoClass::Tri), true);
        assert_eq!(kinds.contains(&GeoKind::Tri3), true);
    }

    #[test]
    fn data_is_consistent() -> Result<(), StrError> {
        for kind in GeoKind::VALUES {
            match kind {
                // Lin
                GeoKind::Lin2 => {
                    assert_eq!(geo_class_and_kind(1, 2)?, (GeoClass::Lin, kind));
                }
                GeoKind::Lin3 => {
                    assert_eq!(geo_class_and_kind(1, 3)?, (GeoClass::Lin, kind));
                }
                GeoKind::Lin4 => {
                    assert_eq!(geo_class_and_kind(1, 4)?, (GeoClass::Lin, kind));
                }
                GeoKind::Lin5 => {
                    assert_eq!(geo_class_and_kind(1, 5)?, (GeoClass::Lin, kind));
                }

                // Tri
                GeoKind::Tri3 => {
                    assert_eq!(geo_class_and_kind(2, 3)?, (GeoClass::Tri, kind));
                }
                GeoKind::Tri6 => {
                    assert_eq!(geo_class_and_kind(2, 6)?, (GeoClass::Tri, kind));
                }
                GeoKind::Tri10 => {
                    assert_eq!(geo_class_and_kind(2, 10)?, (GeoClass::Tri, kind));
                }
                GeoKind::Tri15 => {
                    assert_eq!(geo_class_and_kind(2, 15)?, (GeoClass::Tri, kind));
                }

                // Qua
                GeoKind::Qua4 => {
                    assert_eq!(geo_class_and_kind(2, 4)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua8 => {
                    assert_eq!(geo_class_and_kind(2, 8)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua9 => {
                    assert_eq!(geo_class_and_kind(2, 9)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua12 => {
                    assert_eq!(geo_class_and_kind(2, 12)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua16 => {
                    assert_eq!(geo_class_and_kind(2, 16)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua17 => {
                    assert_eq!(geo_class_and_kind(2, 17)?, (GeoClass::Qua, kind));
                }

                // Tet
                GeoKind::Tet4 => {
                    assert_eq!(geo_class_and_kind(3, 4)?, (GeoClass::Tet, kind));
                }
                GeoKind::Tet10 => {
                    assert_eq!(geo_class_and_kind(3, 10)?, (GeoClass::Tet, kind));
                }

                // Hex
                GeoKind::Hex8 => {
                    assert_eq!(geo_class_and_kind(3, 8)?, (GeoClass::Hex, kind));
                }
                GeoKind::Hex20 => {
                    assert_eq!(geo_class_and_kind(3, 20)?, (GeoClass::Hex, kind));
                }
            }
        }
        Ok(())
    }
}
<commit_msg>Add example<commit_after>use crate::StrError;

/// Defines the class of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum GeoClass {
    /// Lines (segments) class
    Lin,

    /// Triangles class
    Tri,

    /// Quadrilaterals class
    Qua,

    /// Tetrahedra class
    Tet,

    /// Hexahedra class
    Hex,
}

/// Defines the kind of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum GeoKind {
    /// Line (segment) with 2 nodes (linear functions)
    Lin2,

    /// Line (segment) with 3 nodes (quadratic functions)
    Lin3,

    /// Line (segment) with 4 nodes (cubic functions)
    Lin4,

    /// Line (segment) with 5 nodes (quartic functions)
    Lin5,

    /// Triangle with 3 nodes (linear edges)
    Tri3,

    /// Triangle with 6 nodes (quadratic edges)
    Tri6,

    /// Triangle with 10 nodes (cubic edges; interior node)
    Tri10,

    /// Triangle with 15 nodes (quartic edges; interior nodes)
    Tri15,

    /// Quadrilateral with 4 nodes (linear edges)
    Qua4,

    /// Quadrilateral with 8 nodes (quadratic edges)
    Qua8,

    /// Quadrilateral with 9 nodes (quadratic edges; interior node)
    Qua9,

    /// Quadrilateral with 12 nodes (cubic edges)
    Qua12,

    /// Quadrilateral with 16 nodes (cubic edges; interior nodes)
    Qua16,

    /// Quadrilateral with 17 nodes (quartic edges; interior node)
    Qua17,

    /// Tetrahedron with 4 nodes (linear faces)
    Tet4,

    /// Tetrahedron with 10 nodes (quadratic faces)
    Tet10,

    /// Hexahedron with 8 nodes (bilinear faces)
    Hex8,

    /// Hexahedron with 20 nodes (quadratic faces)
    Hex20,
}

/// GeoNdim is the (local) space dimension of the geometric feature
pub type GeoNdim = usize;

/// GeoNnode is the number of nodes associated with a geometric feature
pub type GeoNnode = usize;

impl GeoKind {
    /// Holds all (geo_ndim,nnode) pairs
    pub const PAIRS: [(usize, usize); 18] = [
        // Lin
        (1, 2),
        (1, 3),
        (1, 4),
        (1, 5),
        // Tri
        (2, 3),
        (2, 6),
        (2, 10),
        (2, 15),
        // Qua
        (2, 4),
        (2, 8),
        (2, 9),
        (2, 12),
        (2, 16),
        (2, 17),
        // Tet
        (3, 4),
        (3, 10),
        // Hex
        (3, 8),
        (3, 20),
    ];

    /// Holds all enum values
    pub const VALUES: [Self; 18] = [
        // Lin
        Self::Lin2,
        Self::Lin3,
        Self::Lin4,
        Self::Lin5,
        // Tri
        Self::Tri3,
        Self::Tri6,
        Self::Tri10,
        Self::Tri15,
        // Qua
        Self::Qua4,
        Self::Qua8,
        Self::Qua9,
        Self::Qua12,
        Self::Qua16,
        Self::Qua17,
        // Tet
        Self::Tet4,
        Self::Tet10,
        // Hex
        Self::Hex8,
        Self::Hex20,
    ];
}

/// Returns the GeoClass and GeoKind for given geo_ndim and nnode
///
/// # Examples
///
/// ```
/// use gemlab::shapes;
/// use gemlab::StrError;
///
/// fn main() -> Result<(), StrError> {
///     let (geo_ndim, nnode) = (3, 20);
///     let (class, kind) = shapes::geo_class_and_kind(geo_ndim, nnode)?;
///     assert_eq!(class, shapes::GeoClass::Hex);
///     assert_eq!(kind, shapes::GeoKind::Hex20);
///     Ok(())
/// }
/// ```
pub fn geo_class_and_kind(geo_ndim: usize, nnode: usize) -> Result<(GeoClass, GeoKind), StrError> {
    match (geo_ndim, nnode) {
        // Lin
        (1, 2) => Ok((GeoClass::Lin, GeoKind::Lin2)),
        (1, 3) => Ok((GeoClass::Lin, GeoKind::Lin3)),
        (1, 4) => Ok((GeoClass::Lin, GeoKind::Lin4)),
        (1, 5) => Ok((GeoClass::Lin, GeoKind::Lin5)),

        // Tri
        (2, 3) => Ok((GeoClass::Tri, GeoKind::Tri3)),
        (2, 6) => Ok((GeoClass::Tri, GeoKind::Tri6)),
        (2, 10) => Ok((GeoClass::Tri, GeoKind::Tri10)),
        (2, 15) => Ok((GeoClass::Tri, GeoKind::Tri15)),

        // Qua
        (2, 4) => Ok((GeoClass::Qua, GeoKind::Qua4)),
        (2, 8) => Ok((GeoClass::Qua, GeoKind::Qua8)),
        (2, 9) => Ok((GeoClass::Qua, GeoKind::Qua9)),
        (2, 12) => Ok((GeoClass::Qua, GeoKind::Qua12)),
        (2, 16) => Ok((GeoClass::Qua, GeoKind::Qua16)),
        (2, 17) => Ok((GeoClass::Qua, GeoKind::Qua17)),

        // Tet
        (3, 4) => Ok((GeoClass::Tet, GeoKind::Tet4)),
        (3, 10) => Ok((GeoClass::Tet, GeoKind::Tet10)),

        // Hex
        (3, 8) => Ok((GeoClass::Hex, GeoKind::Hex8)),
        (3, 20) => Ok((GeoClass::Hex, GeoKind::Hex20)),
        _ => Err("(geo_ndim,nnode) combination is invalid"),
    }
}

/// Returns the (min,max,delta) limits on the reference domain
///
/// # Tri and Tet
///
/// * `ξ_min` = 0.0
/// * `ξ_max` = 1.0
/// * `Δξ` = 1.0
///
/// # Lin, Qua, Hex
///
/// * `ξ_min` = -1.0
/// * `ξ_max` = +1.0
/// * `Δξ` = 2.0
///
/// # Examples
///
/// ```
/// use gemlab::shapes;
///
/// let (min, max, del) = shapes::ref_domain_limits(shapes::GeoClass::Tet);
/// assert_eq!(min, 0.0);
/// assert_eq!(max, 1.0);
/// assert_eq!(del, 1.0);
///
/// let (min, max, del) = shapes::ref_domain_limits(shapes::GeoClass::Hex);
/// assert_eq!(min, -1.0);
/// assert_eq!(max, 1.0);
/// assert_eq!(del, 2.0);
/// ```
#[inline]
pub fn ref_domain_limits(class: GeoClass) -> (f64, f64, f64) {
    if class == GeoClass::Tri || class == GeoClass::Tet {
        (0.0, 1.0, 1.0)
    } else {
        (-1.0, 1.0, 2.0)
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use crate::{
        shapes::{geo_class_and_kind, GeoClass, GeoKind},
        StrError,
    };
    use std::collections::HashSet;

    #[test]
    fn derive_works() {
        let class = GeoClass::Tri.clone();
        let kind = GeoKind::Tri6.clone();
        let classes = HashSet::from([GeoClass::Tri, GeoClass::Qua]);
        let kinds = HashSet::from([GeoKind::Tri3, GeoKind::Qua4]);
        assert_eq!(class, GeoClass::Tri);
        assert_eq!(kind, GeoKind::Tri6);
        assert_eq!(format!("{:?}", class), "Tri");
        assert_eq!(format!("{:?}", kind), "Tri6");
        assert_eq!(classes.contains(&GeoClass::Tri), true);
        assert_eq!(kinds.contains(&GeoKind::Tri3), true);
    }

    #[test]
    fn data_is_consistent() -> Result<(), StrError> {
        for kind in GeoKind::VALUES {
            match kind {
                // Lin
                GeoKind::Lin2 => {
                    assert_eq!(geo_class_and_kind(1, 2)?, (GeoClass::Lin, kind));
                }
                GeoKind::Lin3 => {
                    assert_eq!(geo_class_and_kind(1, 3)?, (GeoClass::Lin, kind));
                }
                GeoKind::Lin4 => {
                    assert_eq!(geo_class_and_kind(1, 4)?, (GeoClass::Lin, kind));
                }
                GeoKind::Lin5 => {
                    assert_eq!(geo_class_and_kind(1, 5)?, (GeoClass::Lin, kind));
                }

                // Tri
                GeoKind::Tri3 => {
                    assert_eq!(geo_class_and_kind(2, 3)?, (GeoClass::Tri, kind));
                }
                GeoKind::Tri6 => {
                    assert_eq!(geo_class_and_kind(2, 6)?, (GeoClass::Tri, kind));
                }
                GeoKind::Tri10 => {
                    assert_eq!(geo_class_and_kind(2, 10)?, (GeoClass::Tri, kind));
                }
                GeoKind::Tri15 => {
                    assert_eq!(geo_class_and_kind(2, 15)?, (GeoClass::Tri, kind));
                }

                // Qua
                GeoKind::Qua4 => {
                    assert_eq!(geo_class_and_kind(2, 4)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua8 => {
                    assert_eq!(geo_class_and_kind(2, 8)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua9 => {
                    assert_eq!(geo_class_and_kind(2, 9)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua12 => {
                    assert_eq!(geo_class_and_kind(2, 12)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua16 => {
                    assert_eq!(geo_class_and_kind(2, 16)?, (GeoClass::Qua, kind));
                }
                GeoKind::Qua17 => {
                    assert_eq!(geo_class_and_kind(2, 17)?, (GeoClass::Qua, kind));
                }

                // Tet
                GeoKind::Tet4 => {
                    assert_eq!(geo_class_and_kind(3, 4)?, (GeoClass::Tet, kind));
                }
                GeoKind::Tet10 => {
                    assert_eq!(geo_class_and_kind(3, 10)?, (GeoClass::Tet, kind));
                }

                // Hex
                GeoKind::Hex8 => {
                    assert_eq!(geo_class_and_kind(3, 8)?, (GeoClass::Hex, kind));
                }
                GeoKind::Hex20 => {
                    assert_eq!(geo_class_and_kind(3, 20)?, (GeoClass::Hex, kind));
                }
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use client::Api;
use failure::{Error, ResultExt};
use states::{Download, Idle, Poll, State, StateChangeImpl, StateMachine};

#[derive(Debug, PartialEq)]
pub struct Probe {}

create_state_step!(Probe => Idle);
create_state_step!(Probe => Poll);

/// Implements the state change for State<Probe>.
impl StateChangeImpl for State<Probe> {
    fn to_next_state(mut self) -> Result<StateMachine, Error> {
        use chrono::Duration;
        use client::ProbeResponse;
        use std::thread;

        let r = loop {
            let probe = Api::new(&self.settings, &self.runtime_settings, &self.firmware).probe();
            if let Err(e) = probe {
                error!("{}", e);
                self.runtime_settings.polling.retries += 1;
                thread::sleep(Duration::seconds(1).to_std().unwrap());
            } else {
                self.runtime_settings.polling.retries = 0;
                break probe?;
            }
        };

        self.runtime_settings.polling.extra_interval = match r {
            ProbeResponse::ExtraPoll(s) => {
                info!("Delaying the probing as requested by the server.");
                Some(Duration::seconds(s))
            }
            _ => None,
        };

        // Save any changes we due the probing
        if !self.settings.storage.read_only {
            debug!("Saving runtime settings.");
            self.runtime_settings
                .save()
                .context("Saving runtime due probe changes")?;
        } else {
            debug!("Skipping runtime settings save, read-only mode enabled.");
        }

        match r {
            ProbeResponse::ExtraPoll(_) => {
                debug!("Moving to Poll state due the extra polling interval.");
                Ok(StateMachine::Poll(self.into()))
            }

            ProbeResponse::Update(u) => {
                // Ensure the package is compatible
                u.compatible_with(&self.firmware)?;

                if Some(u.package_uid()) == self.applied_package_uid {
                    info!(
                        "Not applying the update package. Same package has already been installed."
                    );
                    debug!("Moving to Idle state as this update package is already installed.");
                    Ok(StateMachine::Idle(self.into()))
                } else {
                    debug!("Moving to Download state to process the update package.");
                    Ok(StateMachine::Download(State {
                        settings: self.settings,
                        runtime_settings: self.runtime_settings,
                        firmware: self.firmware,
                        applied_package_uid: None,
                        state: Download { update_package: u },
                    }))
                }
            }

            ProbeResponse::NoUpdate => {
                debug!("Moving to Idle state as no update is available.");
                Ok(StateMachine::Idle(self.into()))
            }
        }
    }
}

#[test]
fn update_not_available() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::NoUpdate);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Idle);
}

#[test]
fn update_available() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::HasUpdate);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::HasUpdate)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Download);
}

#[test]
fn invalid_hardware() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::InvalidHardware);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::InvalidHardware)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert!(machine.is_err(), "Did not catch an incompatible hardware");
}

#[test]
fn extra_poll_interval() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::ExtraPoll);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::ExtraPoll)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Poll);
}

#[test]
fn skip_same_package_uid() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use client::ProbeResponse;
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::HasUpdate).expect(2);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    // We first get the package_uid that will be returned so we can
    // use it for the upcoming test.
    //
    // This has been done so we don't need to manually update it every
    // time we change the package payload.
    let package_uid = {
        let probe = Api::new(
            &Settings::default(),
            &RuntimeSettings::default(),
            &Metadata::new(&create_fake_metadata(FakeDevice::HasUpdate)).unwrap(),
        ).probe()
            .unwrap();

        if let ProbeResponse::Update(u) = probe {
            Some(u.package_uid())
        } else {
            None
        }
    };

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::HasUpdate)).unwrap(),
        applied_package_uid: package_uid,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Idle);
}

#[test]
fn error() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    // The server here waits for the second request which includes the
    // retries to succeed.
    let mock = create_mock_server(FakeServer::ErrorOnce);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Idle);
}
<commit_msg>states: probe: reorder match of response<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use client::Api;
use failure::{Error, ResultExt};
use states::{Download, Idle, Poll, State, StateChangeImpl, StateMachine};

#[derive(Debug, PartialEq)]
pub struct Probe {}

create_state_step!(Probe => Idle);
create_state_step!(Probe => Poll);

/// Implements the state change for State<Probe>.
impl StateChangeImpl for State<Probe> {
    fn to_next_state(mut self) -> Result<StateMachine, Error> {
        use chrono::Duration;
        use client::ProbeResponse;
        use std::thread;

        let r = loop {
            let probe = Api::new(&self.settings, &self.runtime_settings, &self.firmware).probe();
            if let Err(e) = probe {
                error!("{}", e);
                self.runtime_settings.polling.retries += 1;
                thread::sleep(Duration::seconds(1).to_std().unwrap());
            } else {
                self.runtime_settings.polling.retries = 0;
                break probe?;
            }
        };

        self.runtime_settings.polling.extra_interval = match r {
            ProbeResponse::ExtraPoll(s) => {
                info!("Delaying the probing as requested by the server.");
                Some(Duration::seconds(s))
            }
            _ => None,
        };

        // Save any changes we due the probing
        if !self.settings.storage.read_only {
            debug!("Saving runtime settings.");
            self.runtime_settings
                .save()
                .context("Saving runtime due probe changes")?;
        } else {
            debug!("Skipping runtime settings save, read-only mode enabled.");
        }

        match r {
            ProbeResponse::NoUpdate => {
                debug!("Moving to Idle state as no update is available.");
                Ok(StateMachine::Idle(self.into()))
            }

            ProbeResponse::ExtraPoll(_) => {
                debug!("Moving to Poll state due the extra polling interval.");
                Ok(StateMachine::Poll(self.into()))
            }

            ProbeResponse::Update(u) => {
                // Ensure the package is compatible
                u.compatible_with(&self.firmware)?;

                if Some(u.package_uid()) == self.applied_package_uid {
                    info!(
                        "Not applying the update package. Same package has already been installed."
                    );
                    debug!("Moving to Idle state as this update package is already installed.");
                    Ok(StateMachine::Idle(self.into()))
                } else {
                    debug!("Moving to Download state to process the update package.");
                    Ok(StateMachine::Download(State {
                        settings: self.settings,
                        runtime_settings: self.runtime_settings,
                        firmware: self.firmware,
                        applied_package_uid: None,
                        state: Download { update_package: u },
                    }))
                }
            }
        }
    }
}

#[test]
fn update_not_available() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::NoUpdate);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Idle);
}

#[test]
fn update_available() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::HasUpdate);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::HasUpdate)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Download);
}

#[test]
fn invalid_hardware() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::InvalidHardware);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::InvalidHardware)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert!(machine.is_err(), "Did not catch an incompatible hardware");
}

#[test]
fn extra_poll_interval() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::ExtraPoll);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::ExtraPoll)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Poll);
}

#[test]
fn skip_same_package_uid() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use client::ProbeResponse;
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    let mock = create_mock_server(FakeServer::HasUpdate).expect(2);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    // We first get the package_uid that will be returned so we can
    // use it for the upcoming test.
    //
    // This has been done so we don't need to manually update it every
    // time we change the package payload.
    let package_uid = {
        let probe = Api::new(
            &Settings::default(),
            &RuntimeSettings::default(),
            &Metadata::new(&create_fake_metadata(FakeDevice::HasUpdate)).unwrap(),
        ).probe()
            .unwrap();

        if let ProbeResponse::Update(u) = probe {
            Some(u.package_uid())
        } else {
            None
        }
    };

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::HasUpdate)).unwrap(),
        applied_package_uid: package_uid,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Idle);
}

#[test]
fn error() {
    use super::*;
    use client::tests::{create_mock_server, FakeServer};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mktemp::Temp;

    // The server here waits for the second request which includes the
    // retries to succeed.
    let mock = create_mock_server(FakeServer::ErrorOnce);
    let tmpfile = Temp::new_file().unwrap().to_path_buf();

    let machine = StateMachine::Probe(State {
        settings: Settings::default(),
        runtime_settings: RuntimeSettings::new()
            .load(tmpfile.to_str().unwrap())
            .unwrap(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Probe {},
    }).move_to_next_state();

    mock.assert();

    assert_state!(machine, Idle);
}
<|endoftext|>


<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

#![allow(dead_code)]

use term::*;
use term::Term::*;
/*
use self::Closure::*;
use self::Expression::*;
use std::collections::VecDeque;

#[derive(Debug, PartialEq, Clone)]
enum Closure {
    Depth(usize),
    TermInEnv(Term, Environment)
}

#[derive(Debug, PartialEq)]
enum Expression {
    Evaluation(Closure),
    Application(Box<Expression>, Term, Environment)
}

type Environment = VecDeque<Closure>;

fn whnf(term: Term, mut env: Environment) -> Expression {
    match term {
        Var(i) => match env.remove(i - 1) {
            Some(Depth(i)) => Evaluation(Depth(i)),
            Some(TermInEnv(t, e)) => whnf(t, e),
            None => unreachable!()
        },
        Abs(t) => Evaluation(TermInEnv(Abs(t), env)),
        App(lhs, rhs) => match whnf(*lhs, env.clone()) { // TODO: try to remove this clone()
            Evaluation(TermInEnv(Abs(lhs2), mut env2)) => {
                env2.push_front(TermInEnv(*rhs, env));
                whnf(*lhs2, env2)
            },
            expr => Application(Box::new(expr), *rhs, env)
        }
    }
}

fn reduce(depth: usize, exp: Expression) -> Term {
    match exp {
        Application(lhs, rhs, env) => app(reduce(depth, *lhs), nf(depth, rhs, env)),
        Evaluation(TermInEnv(Abs(term), mut env)) => {
            env.push_front(Depth(depth));
            abs(nf(depth + 1, *term, env))
        },
        Evaluation(TermInEnv(term, _)) => term,
        Evaluation(Depth(i)) => Var(depth - i)
    }
}

fn nf(depth: usize, term: Term, env: Environment) -> Term {
    reduce(depth, whnf(term, env))
}

/// Returns a term reduced to its normal form. Consumes its argument.
///
/// # Example
///
/// ```
/// use lambda_calculus::booleans::{if_else, tru};
/// use lambda_calculus::arithmetic::{zero, one, succ};
/// use lambda_calculus::reduction::normalize;
///
/// assert_eq!(normalize(succ().app(zero())), one());
/// assert_eq!(normalize(if_else().app(tru()).app(one()).app(zero())), one());
/// ```
fn normalize(term: Term) -> Term {
    nf(0, term, VecDeque::new())
}
*/
/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// // SUCC := λ λ λ 2 (3 2 1), ONE := λ λ 2 1
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(&*format!("{}", succ_one), "λλ2((λλ21)21)");
    /// ```
    pub fn beta_once(&mut self) {
        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_once(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    let reduced = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("    {} reduces to {}", self, reduced) };
                    *self = reduced;
                } else {
                    self.lhs_ref_mut().unwrap().beta_once();
                    self.rhs_ref_mut().unwrap().beta_once()
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            let tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// // SUCC := λ λ λ 2 (3 2 1), ONE := λ λ 2 1
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(&*format!("{}", beta_once(succ_one)), "λλ2((λλ21)21)");
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

#[cfg(test)]
mod test {
    use combinators::i;
    use parser::parse;
    use super::{apply, abs};
    use super::Term::Var;
    use arithmetic::zero;

    #[test]
    fn applying() {
        let lhs    = parse(&"λλ42(λ13)").unwrap();
        let rhs    = parse(&"λ51").unwrap();
        let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
        assert_eq!(apply(lhs, &rhs), Ok(result));

        assert_eq!(i().app(zero()).eval().unwrap(), abs(abs(Var(1))));
    }
}
<commit_msg>remove superfluous test<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

#![allow(dead_code)]

use term::*;
use term::Term::*;
/*
use self::Closure::*;
use self::Expression::*;
use std::collections::VecDeque;

#[derive(Debug, PartialEq, Clone)]
enum Closure {
    Depth(usize),
    TermInEnv(Term, Environment)
}

#[derive(Debug, PartialEq)]
enum Expression {
    Evaluation(Closure),
    Application(Box<Expression>, Term, Environment)
}

type Environment = VecDeque<Closure>;

fn whnf(term: Term, mut env: Environment) -> Expression {
    match term {
        Var(i) => match env.remove(i - 1) {
            Some(Depth(i)) => Evaluation(Depth(i)),
            Some(TermInEnv(t, e)) => whnf(t, e),
            None => unreachable!()
        },
        Abs(t) => Evaluation(TermInEnv(Abs(t), env)),
        App(lhs, rhs) => match whnf(*lhs, env.clone()) { // TODO: try to remove this clone()
            Evaluation(TermInEnv(Abs(lhs2), mut env2)) => {
                env2.push_front(TermInEnv(*rhs, env));
                whnf(*lhs2, env2)
            },
            expr => Application(Box::new(expr), *rhs, env)
        }
    }
}

fn reduce(depth: usize, exp: Expression) -> Term {
    match exp {
        Application(lhs, rhs, env) => app(reduce(depth, *lhs), nf(depth, rhs, env)),
        Evaluation(TermInEnv(Abs(term), mut env)) => {
            env.push_front(Depth(depth));
            abs(nf(depth + 1, *term, env))
        },
        Evaluation(TermInEnv(term, _)) => term,
        Evaluation(Depth(i)) => Var(depth - i)
    }
}

fn nf(depth: usize, term: Term, env: Environment) -> Term {
    reduce(depth, whnf(term, env))
}

/// Returns a term reduced to its normal form. Consumes its argument.
///
/// # Example
///
/// ```
/// use lambda_calculus::booleans::{if_else, tru};
/// use lambda_calculus::arithmetic::{zero, one, succ};
/// use lambda_calculus::reduction::normalize;
///
/// assert_eq!(normalize(succ().app(zero())), one());
/// assert_eq!(normalize(if_else().app(tru()).app(one()).app(zero())), one());
/// ```
fn normalize(term: Term) -> Term {
    nf(0, term, VecDeque::new())
}
*/
/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// // SUCC := λ λ λ 2 (3 2 1), ONE := λ λ 2 1
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(&*format!("{}", succ_one), "λλ2((λλ21)21)");
    /// ```
    pub fn beta_once(&mut self) {
        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_once(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    let reduced = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("    {} reduces to {}", self, reduced) };
                    *self = reduced;
                } else {
                    self.lhs_ref_mut().unwrap().beta_once();
                    self.rhs_ref_mut().unwrap().beta_once()
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            let tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// // SUCC := λ λ λ 2 (3 2 1), ONE := λ λ 2 1
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(&*format!("{}", beta_once(succ_one)), "λλ2((λλ21)21)");
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

#[cfg(test)]
mod test {

}
<|endoftext|>


<commit_before>//! The generic implementation of a "handle" proxy object used throughout wlroots-rs.

use std::{clone::Clone, cell::Cell, fmt, rc::Weak, hash::{Hash, Hasher}, ptr,
          panic, marker::PhantomData};

use errors::{HandleErr, HandleResult};

/// A non-owned reference counted handle to a resource.
///
/// The resource could be destroyed at any time, it depends on the resource.
///
/// For example an output is destroyed when it's physical output is "disconnected"
/// on DRM. "disconnected" depends on the output (e.g. sometimes turning it off counts
/// as "disconnected").
/// However, when the backend is instead headless an output lives until it is
/// destroyed explicitly by the library user.
///
/// Some resources are completely controlled by the user. For example although
/// you refer to a `Seat` with handles it is only destroyed when you call the
/// special destroy method on the seat handle.
///
/// Please refer to the specific resource documentation for a description of
/// the lifetime particular to that resource.
pub struct Handle<D, T, W: Handleable<D, T> + Sized> {
    pub(crate) ptr: *mut T,
    pub(crate) handle: Weak<Cell<bool>>,
    pub(crate) _marker: PhantomData<W>,
    pub(crate) data: D
}

pub trait Handleable<D, T> {
    /// Constructs the resource manager from a raw pointer of the resource
    /// this handleable manages. **This should increment the reference count**.
    ///
    /// # Safety
    /// The pointer must be valid and must already have been set up by wlroots-rs
    /// internal mechanisms. If you have to ask, it probably isn't.
    #[doc(hidden)]
    unsafe fn from_ptr(resource_ptr: *mut T) -> Self where Self: Sized;

    /// Gets the pointer to the resource this object manages.
    #[doc(hidden)]
    unsafe fn as_ptr(&self) -> *mut T;

    /// Gets the resource from the handle.
    ///
    /// This is used internally to upgrade a handle, and should not be used.
    /// Thus the documentation is hidden and it's marked `unsafe`.
    ///
    /// If you _need_ to use this, use `Handle::upgrade` instead.
    #[doc(hidden)]
    unsafe fn from_handle(&Handle<D, T, Self>) -> HandleResult<Self> where Self: Sized;

    /// Creates a weak reference to the resource.
    fn weak_reference(&self) -> Handle<D, T, Self> where Self: Sized;
}

impl <D: Clone, T, W: Handleable<D, T>> Clone for Handle<D, T, W> {
    fn clone(&self) -> Self {
        Handle { ptr: self.ptr,
                 handle: self.handle.clone(),
                 _marker: PhantomData,
                 data: self.data.clone()
        }
    }
}

impl <D, T, W: Handleable<D, T>> fmt::Debug for Handle<D, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Handle with pointer: {:p}", self.ptr)
    }
}

impl <D, T, W: Handleable<D, T>> Hash for Handle<D, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.ptr.hash(state);
    }
}

impl <D, T, W: Handleable<D, T>> PartialEq for Handle<D, T, W> {
    fn eq(&self, other: &Handle<D, T, W>) -> bool {
        self.ptr == other.ptr
    }
}

impl <D, T, W: Handleable<D, T>> Eq for Handle<D, T, W> {}

impl <D: Default, T, W: Handleable<D, T>> Default for Handle<D, T, W> {
    /// Constructs a new handle that is always invalid. Calling `run` on this
    /// will always fail.
    ///
    /// This is useful for pre-filling a value before it's provided by the server, or
    /// for mocking/testing.
    fn default() -> Self {
        Handle { ptr: ptr::null_mut(),
                 handle: Weak::new(),
                 _marker: PhantomData,
                 data: D::default() }
    }
}

impl <D, T, W: Handleable<D, T>> Handle<D, T, W> {
    /// Creates an output::Handle from the raw pointer, using the saved
    /// user data to recreate the memory model.
    ///
    /// # Panics
    /// This function is allowed to panic when attempting to upgrade the handle.
    pub(crate) unsafe fn from_ptr(ptr: *mut T) -> Handle<D, T, W> {
        let wrapped_resource = W::from_ptr(ptr);
        wrapped_resource.weak_reference()
    }

    /// Get the pointer to the resource this manages.
    ///
    /// # Safety
    /// There's no guarantees that this pointer is not dangling.
    #[doc(hidden)]
    pub unsafe fn as_ptr(&self) -> *mut T {
        self.ptr
    }

    /// Run a function with a reference to the resource if it's still alive.
    ///
    /// Returns the result of the function, if successful.
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the resource
    /// to a short lived scope of an anonymous function,
    /// this function ensures the resource does not live longer
    /// than it exists.
    ///
    /// # Panics
    /// This function will panic if multiple mutable borrows are detected.
    /// This will happen if you call `upgrade` directly within this callback,
    /// or if a handle to the same resource was upgraded some where else up the stack.
    pub fn run<F, R>(&self, runner: F) -> HandleResult<R>
        where F: FnOnce(&mut W) -> R
    {
        let mut wrapped_obj = unsafe { self.upgrade()? };
        // We catch panics here to deal with an extreme edge case.
        //
        // If the library user catches panics from the `run` function then their
        // resource used flag will still be set to `true` when it should be set
        // to `false`.
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut wrapped_obj)));
        self.handle.upgrade().map(|check| {
            // Sanity check that it hasn't been tampered with. If so, we should just panic.
            // If we are currently panicking this will abort.
            if !check.get() {
                wlr_log!(WLR_ERROR, "After running callback, mutable lock was false");
                panic!("Lock in incorrect state!");
            }
            check.set(false);
        });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    /// Upgrades a handle to a reference to the backing object.
    ///
    /// # Safety
    /// This returns an "owned" value when really you don't own it all.
    /// Depending on the type, it's possible that the resource will be freed
    /// once this returned value is dropped, causing a possible double free.
    /// Potentially it instead is just unbound, it depends on the resource.
    ///
    /// Regardless, you should not use this interface. Use the `run` method.
    #[doc(hidden)]
    pub unsafe fn upgrade(&self) -> HandleResult<W> {
        self.handle.upgrade()
            .ok_or(HandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let wrapper_obj = W::from_handle(self)?;
                if check.get() {
                    return Err(HandleErr::AlreadyDropped)
                }
                check.set(true);
                Ok(wrapper_obj)
            })

    }
}
<commit_msg>Fix warning in stable<commit_after>//! The generic implementation of a "handle" proxy object used throughout wlroots-rs.

use std::{clone::Clone, cell::Cell, fmt, rc::Weak, hash::{Hash, Hasher}, ptr,
          panic, marker::PhantomData};

use errors::{HandleErr, HandleResult};

/// A non-owned reference counted handle to a resource.
///
/// The resource could be destroyed at any time, it depends on the resource.
///
/// For example an output is destroyed when it's physical output is "disconnected"
/// on DRM. "disconnected" depends on the output (e.g. sometimes turning it off counts
/// as "disconnected").
/// However, when the backend is instead headless an output lives until it is
/// destroyed explicitly by the library user.
///
/// Some resources are completely controlled by the user. For example although
/// you refer to a `Seat` with handles it is only destroyed when you call the
/// special destroy method on the seat handle.
///
/// Please refer to the specific resource documentation for a description of
/// the lifetime particular to that resource.
pub struct Handle<D, T, W: Handleable<D, T> + Sized> {
    pub(crate) ptr: *mut T,
    pub(crate) handle: Weak<Cell<bool>>,
    pub(crate) _marker: PhantomData<W>,
    pub(crate) data: D
}

pub trait Handleable<D, T> {
    /// Constructs the resource manager from a raw pointer of the resource
    /// this handleable manages. **This should increment the reference count**.
    ///
    /// # Safety
    /// The pointer must be valid and must already have been set up by wlroots-rs
    /// internal mechanisms. If you have to ask, it probably isn't.
    #[doc(hidden)]
    unsafe fn from_ptr(resource_ptr: *mut T) -> Self where Self: Sized;

    /// Gets the pointer to the resource this object manages.
    #[doc(hidden)]
    unsafe fn as_ptr(&self) -> *mut T;

    /// Gets the resource from the handle.
    ///
    /// This is used internally to upgrade a handle, and should not be used.
    /// Thus the documentation is hidden and it's marked `unsafe`.
    ///
    /// If you _need_ to use this, use `Handle::upgrade` instead.
    #[doc(hidden)]
    unsafe fn from_handle(&Handle<D, T, Self>) -> HandleResult<Self> where Self: Sized;

    /// Creates a weak reference to the resource.
    fn weak_reference(&self) -> Handle<D, T, Self> where Self: Sized;
}

impl <D: Clone, T, W: Handleable<D, T>> Clone for Handle<D, T, W> {
    fn clone(&self) -> Self {
        Handle { ptr: self.ptr,
                 handle: self.handle.clone(),
                 _marker: PhantomData,
                 data: self.data.clone()
        }
    }
}

impl <D, T, W: Handleable<D, T>> fmt::Debug for Handle<D, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Handle with pointer: {:p}", self.ptr)
    }
}

impl <D, T, W: Handleable<D, T>> Hash for Handle<D, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.ptr.hash(state);
    }
}

impl <D, T, W: Handleable<D, T>> PartialEq for Handle<D, T, W> {
    fn eq(&self, other: &Handle<D, T, W>) -> bool {
        self.ptr == other.ptr
    }
}

impl <D, T, W: Handleable<D, T>> Eq for Handle<D, T, W> {}

impl <D: Default, T, W: Handleable<D, T>> Default for Handle<D, T, W> {
    /// Constructs a new handle that is always invalid. Calling `run` on this
    /// will always fail.
    ///
    /// This is useful for pre-filling a value before it's provided by the server, or
    /// for mocking/testing.
    fn default() -> Self {
        Handle { ptr: ptr::null_mut(),
                 handle: Weak::new(),
                 _marker: PhantomData,
                 data: D::default() }
    }
}

impl <D, T, W: Handleable<D, T>> Handle<D, T, W> {
    /// Creates an output::Handle from the raw pointer, using the saved
    /// user data to recreate the memory model.
    ///
    /// # Panics
    /// This function is allowed to panic when attempting to upgrade the handle.
    #[allow(dead_code)]
    pub(crate) unsafe fn from_ptr(ptr: *mut T) -> Handle<D, T, W> {
        let wrapped_resource = W::from_ptr(ptr);
        wrapped_resource.weak_reference()
    }

    /// Get the pointer to the resource this manages.
    ///
    /// # Safety
    /// There's no guarantees that this pointer is not dangling.
    #[doc(hidden)]
    pub unsafe fn as_ptr(&self) -> *mut T {
        self.ptr
    }

    /// Run a function with a reference to the resource if it's still alive.
    ///
    /// Returns the result of the function, if successful.
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the resource
    /// to a short lived scope of an anonymous function,
    /// this function ensures the resource does not live longer
    /// than it exists.
    ///
    /// # Panics
    /// This function will panic if multiple mutable borrows are detected.
    /// This will happen if you call `upgrade` directly within this callback,
    /// or if a handle to the same resource was upgraded some where else up the stack.
    pub fn run<F, R>(&self, runner: F) -> HandleResult<R>
        where F: FnOnce(&mut W) -> R
    {
        let mut wrapped_obj = unsafe { self.upgrade()? };
        // We catch panics here to deal with an extreme edge case.
        //
        // If the library user catches panics from the `run` function then their
        // resource used flag will still be set to `true` when it should be set
        // to `false`.
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut wrapped_obj)));
        self.handle.upgrade().map(|check| {
            // Sanity check that it hasn't been tampered with. If so, we should just panic.
            // If we are currently panicking this will abort.
            if !check.get() {
                wlr_log!(WLR_ERROR, "After running callback, mutable lock was false");
                panic!("Lock in incorrect state!");
            }
            check.set(false);
        });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    /// Upgrades a handle to a reference to the backing object.
    ///
    /// # Safety
    /// This returns an "owned" value when really you don't own it all.
    /// Depending on the type, it's possible that the resource will be freed
    /// once this returned value is dropped, causing a possible double free.
    /// Potentially it instead is just unbound, it depends on the resource.
    ///
    /// Regardless, you should not use this interface. Use the `run` method.
    #[doc(hidden)]
    pub unsafe fn upgrade(&self) -> HandleResult<W> {
        self.handle.upgrade()
            .ok_or(HandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let wrapper_obj = W::from_handle(self)?;
                if check.get() {
                    return Err(HandleErr::AlreadyDropped)
                }
                check.set(true);
                Ok(wrapper_obj)
            })

    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use types::*;
use num_cpus;
use workerthread::WorkerThread;

/// Provides access to and manages worker threads
pub struct ResourcePool {
    /// Collection of threads
    w_threads: Vec<WorkerThread>,
    /// Backlog queue
    next_worker: usize,
}

impl ResourcePool {
    /// Creates a new pool of worker threads
    pub fn new(num_workers: u8) -> ResourcePool {
        let mut num_workers = num_workers;
        if num_workers < 1 {
            warn!("Must at least have one worker thread. Creating 1");
            num_workers = 1;
        }

        // Initialize woker threads
        let mut w_threads = Vec::<WorkerThread>::with_capacity(num_workers as usize);
        for _ in 0..num_workers {
            w_threads.push(WorkerThread::new());
        }

        ResourcePool {
            w_threads: w_threads,
            next_worker: 0,
        }
    }

    /// Runs the passed function
    pub fn run<T: Fn() + Send + Sync + 'static>(&mut self, task: T) {
        if self.next_worker == self.w_threads.len() {
            self.next_worker = 0;
        }

        let event = Event(Box::into_raw(Box::new(task)));
        let _ = self.w_threads[self.next_worker].sender().send(event);
        self.next_worker += 1;
    }
}
<commit_msg>remote compile debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use types::*;
use workerthread::WorkerThread;

/// Provides access to and manages worker threads
pub struct ResourcePool {
    /// Collection of threads
    w_threads: Vec<WorkerThread>,
    /// Backlog queue
    next_worker: usize,
}

impl ResourcePool {
    /// Creates a new pool of worker threads
    pub fn new(num_workers: u8) -> ResourcePool {
        let mut num_workers = num_workers;
        if num_workers < 1 {
            warn!("Must at least have one worker thread. Creating 1");
            num_workers = 1;
        }

        // Initialize woker threads
        let mut w_threads = Vec::<WorkerThread>::with_capacity(num_workers as usize);
        for _ in 0..num_workers {
            w_threads.push(WorkerThread::new());
        }

        ResourcePool {
            w_threads: w_threads,
            next_worker: 0,
        }
    }

    /// Runs the passed function
    pub fn run<T: Fn() + Send + Sync + 'static>(&mut self, task: T) {
        if self.next_worker == self.w_threads.len() {
            self.next_worker = 0;
        }

        let event = Event(Box::into_raw(Box::new(task)));
        let _ = self.w_threads[self.next_worker].sender().send(event);
        self.next_worker += 1;
    }
}
<|endoftext|>


<commit_before>extern crate regex;

mod matching;
mod support;
mod tokens;
mod lexer;
mod files;
mod parser;

use std::vec::Vec;
use tokens::*;
use lexer::*;
use parser::*;

fn main()
{
    let source_file = "sampleData/BasicArithmatic.java";
    let lex_output_file = "sampleData/output/stable/BasicArithmatic.java.lexed";
    let preprocessed_output_file = "sampleData/output/stable/BasicArithmatic.java.preprocessed";

    let mut tokens: Vec<Token> = lex_file(source_file);

    println!("\n\nFound Tokens:");
    tokens.print_to(lex_output_file, true);

    remove_meta(&mut tokens);
    println!("\n\nPerged Tokens:");
    tokens.print_to(preprocessed_output_file, true);

    compile(tokens);
}

fn compile(tokens: Vec<Token>) -> String
{
    let invalid_types = get_invalid_token_types();
    let invalid_values = get_invalid_token_values();

    let min_index = 0;
    for (index, token) in tokens.iter().enumerate()
    {
        if index < min_index
        {
            continue;
        }

        if invalid_types.contains(&token.name)
        {
            panic!("Unsupported token type: {}", token.name);
        }
        else if invalid_values.contains(&&*token.value)
        {
            panic!("Unsupported token value: {}", token.value);
        }

        if token.value == "class"
        {
            // parse class body
            let (min_index, plp_code) = class_to_plp(&tokens, index);
        }
        /*else if token.name == "type" // || token.type == "identifier"
        {
            // look ahead
            // parse variable declaration
            // OR parse method declaration
        }
        else if token.value == "if"
        {
            // parse if body
            // Unsupported for now
        }
        else if token.name == "construct.conditional"
        {
            // parse conditional
            // Unsupported for now
        }*/
        else
        {
            panic!("Unexpected token: {}\t{}", token.name, token.value);
        }
    }

    String::new()
}

fn class_to_plp(tokens: &Vec<Token>, start_index: usize) -> (usize, String)
{
    let mut plp_string: String = String::new();
    let mut current_index: usize = start_index;

    for (index, token) in tokens.iter().enumerate()
    {
        current_index = index;
    }

    (current_index + 1, plp_string)
}

fn remove_meta(tokens: &mut Vec<Token>)
{
    // Indecies of tokens vector to be removed
    let mut invalid_indecies: Vec<usize> = Vec::new();

    let mut min_index: usize = 0;
    for (index, token) in tokens.iter().enumerate()
    {
        // Allow forward skipping (by setting min_index)
        if index < min_index
        {
            invalid_indecies.push(index);
        }
        // Remove imports
        else if token.name == "special.import"
        {
            invalid_indecies.push(index);
            min_index = index + 2; // skip the next token (semi-colon)
        }
        // Remove comments
        else if token.name.starts_with("comment")
        {
            invalid_indecies.push(index);
        }
        // Remove all modifiers (public, private, volatile, transient, static, etc.)
        else if token.name.starts_with("mod")
        {
            invalid_indecies.push(index);
        }
        // Remove package declarations
        else if token.name == "special.package"
        {
            invalid_indecies.push(index);
            min_index = find_next_semicolon(tokens, index) + 1;
        }
    }

    // count := how many indecies have already been removed
    // index := the index in the original tokens vector that should be removed
    for (count, index) in invalid_indecies.iter().enumerate()
    {
        // "index" refers to the index before any others were removed. Therefore, it must be offset by the number of removed tokens
        tokens.remove(index - count);
    }
}

fn find_next_semicolon(tokens: &Vec<Token>, start: usize) -> usize
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == ";" { return index + start; }
    }

    return 0;
}
<commit_msg>Refactor<commit_after>extern crate regex;

mod matching;
mod support;
mod tokens;
mod lexer;
mod files;
mod parser;

use std::vec::Vec;
use tokens::*;
use lexer::*;
use parser::*;

fn main()
{
    let source_file = "sampleData/BasicArithmatic.java";
    let lex_output_file = "sampleData/output/stable/BasicArithmatic.java.lexed";
    let preprocessed_output_file = "sampleData/output/stable/BasicArithmatic.java.preprocessed";

    let mut tokens: Vec<Token> = lex_file(source_file);

    println!("\n\nFound Tokens:");
    tokens.print_to(lex_output_file, true);

    remove_meta(&mut tokens);
    println!("\n\nPerged Tokens:");
    tokens.print_to(preprocessed_output_file, true);

    if tokens[0].value != "class"
    {
        panic!("Unexpected token: {}: {}", tokens[0].value, tokens[0].name);
    }

    let (last_index, asm_string) = compile_class(&tokens, 1);
}

fn compile_class(tokens: &Vec<Token>, start_index: usize) -> (usize, String)
{
    let invalid_types = get_invalid_token_types();
    let invalid_values = get_invalid_token_values();

    let mut plp_string: String = String::new();
    let mut current_index: usize = start_index;

    if tokens[current_index].value != "{" { panic!("Expected '{{' received: {}", tokens[current_index].value); }
    else { current_index += 1; }

    for (index, token) in tokens.iter().enumerate()
    {
        if index < current_index
        {
            continue;
        }

        current_index = index;

        // TODO: encapsulate into token_rules.validate(token)
        // Panic! if token type is invalid
        if invalid_types.contains(&token.name)
        {
            panic!("Unsupported token type: {}", token.name);
        }
        // Panic! if token value is invalid
        else if invalid_values.contains(&&*token.value)
        {
            panic!("Unsupported token value: {}", token.value);
        }

        if token.value == "class"
        {
            // parse class body
            let (end_index, compiled_class) = compile_class(&tokens, index + 1);
            plp_string.push_str(&*compiled_class);

            current_index = end_index;
        }
        else if token.name == "type" // || token.name == "identifier"
        {
            // look ahead
            // parse variable declaration
            // OR parse method declaration
        }
        else if token.value == "if"
        {
            // parse if body
            // Unsupported for now
        }
        else if token.name == "construct.conditional"
        {
            // parse conditional
            // Unsupported for now
        }
        else
        {
            panic!("Unexpected token: {}\t{}", token.name, token.value);
        }
    }

    (current_index + 1, plp_string)
}

fn remove_meta(tokens: &mut Vec<Token>)
{
    // Indecies of tokens vector to be removed
    let mut invalid_indecies: Vec<usize> = Vec::new();

    let mut min_index: usize = 0;
    for (index, token) in tokens.iter().enumerate()
    {
        // Allow forward skipping (by setting min_index)
        if index < min_index
        {
            invalid_indecies.push(index);
        }
        // Remove imports
        else if token.name == "special.import"
        {
            invalid_indecies.push(index);
            min_index = index + 2; // skip the next token (semi-colon)
        }
        // Remove comments
        else if token.name.starts_with("comment")
        {
            invalid_indecies.push(index);
        }
        // Remove all modifiers (public, private, volatile, transient, static, etc.)
        else if token.name.starts_with("mod")
        {
            invalid_indecies.push(index);
        }
        // Remove package declarations
        else if token.name == "special.package"
        {
            invalid_indecies.push(index);
            min_index = find_next_semicolon(tokens, index) + 1;
        }
    }

    // count := how many indecies have already been removed
    // index := the index in the original tokens vector that should be removed
    for (count, index) in invalid_indecies.iter().enumerate()
    {
        // "index" refers to the index before any others were removed. Therefore, it must be offset by the number of removed tokens
        tokens.remove(index - count);
    }
}

fn find_next_semicolon(tokens: &Vec<Token>, start: usize) -> usize
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == ";" { return index + start; }
    }

    return 0;
}
<|endoftext|>


<commit_before>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.lock().spawn(f)
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {
    // switch stack so we can park current thread

    call_on_stack(inner, current_rsp, scheduler_stack_top());

    fn inner(current_rsp: uint) -> ! {
        // park current thread
        let current = Thread::from_rsp(current_rsp);
        global().scheduler.lock().park(current);

        schedule_inner();
    }
}

pub unsafe fn schedule() -> ! {
    call_on_stack(schedule_inner, scheduler_stack_top());

    unsafe fn call_on_stack(function: fn() -> !, stack_top: uint) -> ! {
        asm!("call $0;" :: "r"(function), "{rsp}"(stack_top) :: "intel", "volatile");
        panic!("diverging fn returned");
    }
}

fn schedule_inner() -> ! {
    #[allow(improper_ctypes)]
    extern {
        fn pop_registers_and_iret(rsp: uint) -> !;
    }

    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }

    // schedule new thread
    print!("_");
    let new_thread = unsafe{global().scheduler.lock().schedule().unwrap_or_default()};

    match new_thread {
        Thread{state: ThreadState::Active{rsp}} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        Thread{state: ThreadState::New{function}} => {
            println!("new");
            let new_stack_top = new_stack();
            let invoke_fn : fn(Box<FnBox() + Send>) -> ! = invoke;
            unsafe{call_on_stack(invoke_fn, function, new_stack_top)}
        },
    }
}

fn scheduler_stack_top() -> uint {
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    &SCHEDULER_STACK as *const [u8; 4096] as uint + SCHEDULER_STACK_SIZE
}

unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
        : "intel", "volatile");
    panic!("diverging fn returned");
}

pub type GlobalScheduler = Scheduler;
impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        Scheduler::new()
    }
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

pub struct Scheduler {
    threads: RingBuf<Thread>,
}

impl Scheduler {
    pub fn new() -> Scheduler {
        Scheduler{
            threads: RingBuf::new(),
        }
    }

    fn spawn<F, R>(&mut self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&mut self, thread: Thread) {
        self.threads.push_back(thread)
    }

    unsafe fn schedule(&mut self) -> Option<Thread> {
        self.threads.pop_front()
    }
}<commit_msg>enable interrupts; remove prints; add TODO<commit_after>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.lock().spawn(f)
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {
    // switch stack so we can park current thread

    call_on_stack(inner, current_rsp, scheduler_stack_top());

    fn inner(current_rsp: uint) -> ! {
        // park current thread
        let current = Thread::from_rsp(current_rsp);
        global().scheduler.lock().park(current);

        schedule_inner();
    }
}

pub unsafe fn schedule() -> ! {
    call_on_stack(inner, scheduler_stack_top());

    unsafe fn call_on_stack(function: fn() -> !, stack_top: uint) -> ! {
        asm!("call $0;" :: "r"(function), "{rsp}"(stack_top) :: "intel", "volatile");
        panic!("diverging fn returned");
    }

    fn inner() -> ! {
        // TODO FIXME kill current thread and deallocate stack
        schedule_inner()
    }
}

fn schedule_inner() -> ! {
    #[allow(improper_ctypes)]
    extern {
        fn pop_registers_and_iret(rsp: uint) -> !;
    }

    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        unsafe{::enable_interrupts()};
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }

    // schedule new thread
    let new_thread = unsafe{global().scheduler.lock().schedule().unwrap_or_default()};

    match new_thread {
        Thread{state: ThreadState::Active{rsp}} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        Thread{state: ThreadState::New{function}} => {
            println!("new");
            let new_stack_top = new_stack();
            unsafe{call_on_stack(invoke, function, new_stack_top)}
        },
    }
}

fn scheduler_stack_top() -> uint {
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    &SCHEDULER_STACK as *const [u8; 4096] as uint + SCHEDULER_STACK_SIZE
}

unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
        : "intel", "volatile");
    panic!("diverging fn returned");
}

pub type GlobalScheduler = Scheduler;
impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        Scheduler::new()
    }
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

pub struct Scheduler {
    threads: RingBuf<Thread>,
}

impl Scheduler {
    pub fn new() -> Scheduler {
        Scheduler{
            threads: RingBuf::new(),
        }
    }

    fn spawn<F, R>(&mut self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&mut self, thread: Thread) {
        self.threads.push_back(thread)
    }

    unsafe fn schedule(&mut self) -> Option<Thread> {
        self.threads.pop_front()
    }
}<|endoftext|>


<commit_before><commit_msg>Put together a basic script evaluation implementation<commit_after><|endoftext|>


<commit_before>use std::os;
use std::io::Command;
use std::io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    submodule_init();
    submodule_update();
    configure();
    build();
    install(&dst);
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn submodule_init() {
    let mut cmd = git();
    cmd.arg("submodule");
    cmd.arg("init");
    run(&mut cmd);
}

fn submodule_update() {
    let mut cmd = git();
    cmd.arg("submodule");
    cmd.arg("update");
    run(&mut cmd);
}

fn git() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let mut cmd = Command::new("git");
    cmd.cwd(&cargo_dir);
    cmd
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join("termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<commit_msg>Make sure this is a git repo<commit_after>use std::os;
use std::io::Command;
use std::io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    init();
    submodule_init();
    submodule_update();
    configure();
    build();
    install(&dst);
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn init() {
    let mut cmd = git();
    cmd.arg("init");
    run(&mut cmd);
}

fn submodule_init() {
    let mut cmd = git();
    cmd.arg("submodule");
    cmd.arg("init");
    run(&mut cmd);
}

fn submodule_update() {
    let mut cmd = git();
    cmd.arg("submodule");
    cmd.arg("update");
    run(&mut cmd);
}

fn git() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let mut cmd = Command::new("git");
    cmd.cwd(&cargo_dir);
    cmd
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join("termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<|endoftext|>


<commit_before>use std::io::Command;
use std::io::fs::PathExtensions;
use std::io::fs;
use std::os;

fn main() {
  let out_dir = os::getenv("OUT_DIR").unwrap();
  let mut objects = Vec::new();

  let files = fs::readdir(&Path::new("src")).unwrap();
  let mut files = files.iter().filter(|p| p.is_file());

  for file in files {
    if let Some(filename) = file.filename_str() {
      let filepath = format!("src/{}", filename);
      let outpath;

      if let Some(basename) = eat_extension(filename, ".c") {
        outpath = format!("{}/{}.o", out_dir, basename);

        Command::new("cc").args(&[filepath.as_slice(), "-c", "-fPIC", "-o"])
                          .arg(outpath.clone())
                          .status().unwrap();
      }
      else { continue }

      objects.push(outpath);
    }
  }

  Command::new("ar").args(&["crus", "libcontext.a"])
                    .args(objects.as_slice())
                    .cwd(&Path::new(&out_dir))
                    .status().unwrap();

  println!("cargo:rustc-flags=-L {} -l context:static", out_dir);
}

fn eat_extension<'a>(s: &'a str, ext: &str) -> Option<&'a str> {
  if s.ends_with(ext) {
    Some(s.slice_to(s.len() - ext.len()))
  }
  else {
    None
  }
}
<commit_msg>clean up warnings in the build script<commit_after>#![allow(unstable)]
use std::io::Command;
use std::io::fs::PathExtensions;
use std::io::fs;
use std::os;

fn main() {
  let out_dir = os::getenv("OUT_DIR").unwrap();
  let mut objects = Vec::new();

  let files = fs::readdir(&Path::new("src")).unwrap();
  let mut files = files.iter().filter(|p| p.is_file());

  for file in files {
    if let Some(filename) = file.filename_str() {
      let filepath = format!("src/{}", filename);
      let outpath;

      if let Some(basename) = eat_extension(filename, ".c") {
        outpath = format!("{}/{}.o", out_dir, basename);

        Command::new("cc").args(&[&*filepath, "-c", "-fPIC", "-o"])
                          .arg(outpath.clone())
                          .status().unwrap();
      }
      else { continue }

      objects.push(outpath);
    }
  }

  Command::new("ar").args(&["crus", "libcontext.a"])
                    .args(objects.as_slice())
                    .cwd(&Path::new(&out_dir))
                    .status().unwrap();

  println!("cargo:rustc-flags=-L {} -l context:static", out_dir);
}

fn eat_extension<'a>(s: &'a str, ext: &str) -> Option<&'a str> {
  if s.ends_with(ext) {
    Some(&s[..s.len() - ext.len()])
  }
  else {
    None
  }
}
<|endoftext|>


<commit_before>// This file is part of rgtk.
//
// rgtk is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// rgtk is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with rgtk.  If not, see <http://www.gnu.org/licenses/>.

#![feature(slicing_syntax, env, path, io, collections)]

extern crate gcc;
extern crate "pkg-config" as pkg_config;

use std::old_io::process::Command;
use gcc::Config;
use std::env;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();

    env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");

    // try to find gtk+-3.0 library
    match pkg_config::find_library("gtk+-3.0") {
        Ok(_) => {},
        Err(e) => panic!("{}", e)
    };

    // call native pkg-config, there is no way to do this with pkg-config for now
    let cmd = match Command::new("pkg-config").arg("--cflags").arg("gtk+-3.0").output() {
        Ok(r) => r,
        Err(e) => panic!("{}", e)
    };

    // make the vector of path to set to gcc::Config
    let output: String = unsafe { String::from_utf8_unchecked(cmd.output) };
    let res: Vec<&str> = output.split(' ').collect();
    let paths: Vec<Path> = res.iter().filter_map(|s| {
        if s.len() > 1 && s.char_at(1) == 'I' { Some(Path::new(&s[2..])) }
        else { None }
    }).collect();

    // build include path
    let mut gcc_conf = Config::new();
    for path in paths {
        gcc_conf.include(path);
    }
    gcc_conf.file("./gtk_glue/gtk_glue.c");

    // build library
    gcc_conf.compile("librgtk_glue.a");

    // say to cargo where it is
    println!("cargo:rustc-flags=-L {:?} -l rgtk_glue:static", out_dir);
}<commit_msg>Don't print out the link commands, gcc itself does<commit_after>// This file is part of rgtk.
//
// rgtk is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// rgtk is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with rgtk.  If not, see <http://www.gnu.org/licenses/>.

#![feature(slicing_syntax, env, path, io, collections)]

extern crate gcc;
extern crate "pkg-config" as pkg_config;

use std::old_io::process::Command;
use gcc::Config;
use std::env;

fn main() {
    env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");

    // try to find gtk+-3.0 library
    match pkg_config::find_library("gtk+-3.0") {
        Ok(_) => {},
        Err(e) => panic!("{}", e)
    };

    // call native pkg-config, there is no way to do this with pkg-config for now
    let cmd = match Command::new("pkg-config").arg("--cflags").arg("gtk+-3.0").output() {
        Ok(r) => r,
        Err(e) => panic!("{}", e)
    };

    // make the vector of path to set to gcc::Config
    let output: String = unsafe { String::from_utf8_unchecked(cmd.output) };
    let res: Vec<&str> = output.split(' ').collect();
    let paths: Vec<Path> = res.iter().filter_map(|s| {
        if s.len() > 1 && s.char_at(1) == 'I' { Some(Path::new(&s[2..])) }
        else { None }
    }).collect();

    // build include path
    let mut gcc_conf = Config::new();
    for path in paths {
        gcc_conf.include(path);
    }
    gcc_conf.file("./gtk_glue/gtk_glue.c");

    // build library
    gcc_conf.compile("librgtk_glue.a");
}
<|endoftext|>