rain-lang 0.0.1

An implementation of an RVSDG in Rust with a concept of lifetimes
Documentation
/*!
Lambda functions and associated utilities
*/

use smallvec::{smallvec, SmallVec};
use crate::graph::region::Region;
use super::{
    ValId, ValueEnum, ValueData, Value, ValueDesc,
    error::{ValueError, IncomparableRegions, RegionAlreadyFused, RegionError},
    expr::SMALL_SEXPR_SIZE
};

/// The size of a small set of results from a lambda expression
pub const SMALL_RESULT_SIZE: usize = SMALL_SEXPR_SIZE;

/// A lambda function
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub struct Lambda {
    /// The region owned by this lambda function
    region: Region,
    /// The results of this lambda function
    results: SmallVec<[ValId; SMALL_RESULT_SIZE]>
}

impl Lambda {
    /// Create a new lambda function with the given results from the given `Region`
    pub fn new(region: Region, results: SmallVec<[ValId; SMALL_RESULT_SIZE]>)
    -> Result<Lambda, RegionError> {
        // Check region and results. If valid, fuse.
        {
            let mut params = region.params_mut();
            if params.fused() { return Err(RegionAlreadyFused.into()) }
            // Check all results are in the given region
            for result in results.iter() {
                let result_region = &result.data().region;
                if !(result_region >= &region) {
                    let err = IncomparableRegions(
                        smallvec![region.downgrade(), result_region.clone()]
                    );
                    return Err(err.into())
                }
            }
            params.fuse();
        }
        Ok(Lambda { region, results })
    }
    /// Get the region associated with this lambda function
    pub fn region(&self) -> &Region { &self.region }
    /// Get the result array of this lambda function
    pub fn results(&self) -> &[ValId] { &self.results }
    /// Get the dependencies of this lambda function
    pub fn dependencies(&self) -> std::slice::Iter<ValId> { self.results().iter() }
}

impl From<Lambda> for ValueEnum {
    fn from(lambda: Lambda) -> ValueEnum { ValueEnum::Lambda(lambda) }
}

impl From<Lambda> for ValueData {
    fn from(lambda: Lambda) -> ValueData {
        let region = lambda.region().parent.clone();
        ValueData::with_region(lambda.into(), region)
    }
}

impl From<Lambda> for ValId {
    fn from(lambda: Lambda) -> ValId {
        ValId::try_new(ValueData::from(lambda)).expect("Impossible")
    }
}

impl ValueDesc for Lambda {
    type Err = ValueError;
}

impl Value for Lambda {}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::value::primitive::logical::Bool;
    use crate::assert_jeq;

    #[test]
    fn invalid_nested_lambda_result_fails() {
        let region = Region::new();
        let nested_region = Region::new_in(region.downgrade());
        let (iy, y) = nested_region.add_with_ty(Bool.into());
        assert_eq!(iy, 0);
        let err = Lambda::new(region.clone(), smallvec![y.clone()])
            .expect_err("This is an invalid function");
        match err {
            RegionError::IncomparableRegions(err) => {
                assert!(
                    err.0.as_slice() == &[region.downgrade(), nested_region.downgrade()]
                    || err.0.as_slice() == &[nested_region.downgrade(), region.downgrade()]
                )
            },
            err => panic!("Wrong error {:?} (expected incomparable regions)", err)
        }
    }

    #[test]
    fn lambda_on_fused_region_fails() {
        let region = Region::new();
        { region.params_mut().fuse(); }
        let t = ValId::from(true);
        let err = Lambda::new(region.clone(), smallvec![t.clone()])
            .expect_err("This function's region is invalid");
        match err {
            RegionError::RegionAlreadyFused(_) => {},
            err => panic!("Wrong error {:?} (expected region already fused)", err)
        }
    }

    #[test]
    fn constant_bool_lambda() {
        let region = Region::new();
        let t = ValId::from(true);
        let lambda = Lambda::new(region.clone(), smallvec![t.clone()])
            .expect("This is a valid function");
        assert_eq!(lambda.region(), &region);
        assert_eq!(lambda.results().len(), 1);
        assert_jeq!(lambda.results()[0], &t);
        let lambda = ValId::from(lambda);
        //TODO: application
        let _ = lambda;
    }

    #[test]
    fn identity_bool_lambda() {
        let region = Region::new();
        let (_, x) = region.add_with_ty(Bool.into());
        let lambda = Lambda::new(region.clone(), smallvec![x.clone()])
            .expect("This is a valid function");
        assert_eq!(lambda.region(), &region);
        assert_eq!(lambda.results().len(), 1);
        assert_eq!(lambda.results()[0], x);
        let lambda = ValId::from(lambda);
        //TODO: application
        let _ = lambda;
    }
}