rain-lang 0.0.1

An implementation of an RVSDG in Rust with a concept of lifetimes
Documentation
/*!
The `rain` intermediate representation directed acyclic graph
*/
use std::ops::{Deref, DerefMut};
use std::borrow::Cow;
use std::convert::{TryInto, TryFrom, Infallible};
use std::cmp::Ordering;
use smallvec::SmallVec;
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use either::Either;

use crate::graph::{
    node::{Node, WeakNode, NodeData, View, Backlink},
    region::{WeakRegion, Parameter},
    cons::CacheEntry
};

pub mod primitive;
use primitive::{
    Unit,
    logical::{self, LogicalOp, Bool}
};

pub mod expr;
use expr::Sexpr;


pub mod lambda;
use lambda::Lambda;

pub mod error;
use error::{IncomparableRegions, ValueError};

pub mod judgement;
use judgement::JEq;

pub mod eval;

pub mod cons;
use cons::VALUE_CACHE;

/// The size of a small list of dependents
const SMALL_DEPENDENTS: usize = 2;

/// A `rain` value
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValueEnum {
    /// An S-expression
    Sexpr(Sexpr),
    /// Boolean values
    Bool(bool),
    /// Logical operations
    LogicalOp(LogicalOp),
    /// A lambda function
    Lambda(Lambda),
    /// The boolean type
    BoolTy(Bool),
    /// The unit type
    UnitTy(Unit),
    /// A parameter to a region
    Parameter(Parameter)
}

/// An iterator over the dependencies of a rain value
#[derive(Debug, Clone)]
pub enum Dependencies<'a> {
    /// A slice of dependencies
    Slice(std::slice::Iter<'a, ValId>)
}

impl<'a> Iterator for Dependencies<'a> {
    type Item = &'a ValId;
    fn next(&mut self) -> Option<&'a ValId> {
        match self {
            Dependencies::Slice(s) => s.next()
        }
    }
}

impl ValueEnum {
    /// Check whether this value enum variant can potentially be applied
    pub fn applicable(&self) -> bool {
        use ValueEnum::*;
        match self {
            Sexpr(_) | LogicalOp(_) | Parameter(_) | Lambda(_) => true,
            _ => false
        }
    }
    /// Print a value with a name
    pub fn name_print(&self, fmt: &mut fmt::Formatter, name: Option<&str>)
    -> Result<(), fmt::Error> {
        use ValueEnum::*;
        match (self, name) {
            (Parameter(parameter), Some(name)) => write!(fmt, "({} : {})", name, parameter.ty),
            (value, _) => write!(fmt, "{}", value)
        }
    }
    /// Get the base region of this ValueEnum
    pub fn base_region(&self) -> Cow<WeakRegion> {
        match self {
            ValueEnum::Parameter(p) => Cow::Borrowed(p.region()),
            ValueEnum::Lambda(l) => Cow::Owned(l.region().downgrade()),
            _ => Cow::Owned(WeakRegion::default())
        }
    }
}

/// A trait implemented by representations of `rain` values
pub trait ValueDesc: TryInto<ValId> {
    /// The error type for trying to convert this type into a `ValId`
    type Err:
        From<<Self as TryInto<ValId>>::Error>
        + Into<ValueError>;
    /// Try to make this type into a node
    #[inline] fn to_node<E>(self) -> Result<ValId, E>
    where <Self as TryInto<ValId>>::Error: Into<E> {
        let v: Result<ValId, _> = self.try_into();
        v.map_err(|err| err.into())
    }
}

/// A trait implemented by `rain` values
pub trait Value: ValueDesc + TryInto<ValueEnum> + JEq<ValueEnum> + JEq<Self> + JEq<ValId> {
    /// Try to make this type into a value
    #[inline] fn to_value<E>(self) -> Result<ValueEnum, E>
    where <Self as TryInto<ValueEnum>>::Error: Into<E> {
        let v: Result<ValueEnum, _> = self.try_into();
        v.map_err(|err| err.into())
    }
}

impl ValueDesc for ValueEnum {
    type Err = ValueError;
    fn to_node<E>(self) -> Result<ValId, E>
    where <Self as TryInto<ValId>>::Error: Into<E> {
        match self {
            ValueEnum::Sexpr(s) => s.to_node(),
            v => Node::try_new(ValueData::from(v)).map_err(|err| err.into())
        }
    }
}

impl Value for ValueEnum {}

macro_rules! primitive_value {
    ($p_ty:ty, $e_ty:expr) => {
        impl From<$p_ty> for ValueEnum {
            fn from(p: $p_ty) -> ValueEnum { $e_ty(p) }
        }
        impl From<$p_ty> for ValueData {
            fn from(p: $p_ty) -> ValueData { ValueData::new($e_ty(p)) }
        }
        impl From<$p_ty> for ValId {
            fn from(p: $p_ty) -> ValId {
                Node::try_new(ValueData::new($e_ty(p))).expect("Impossible")
            }
        }
        impl ValueDesc for $p_ty { type Err = Infallible; }
        impl Value for $p_ty {}
    }
}

primitive_value!(bool, ValueEnum::Bool);
primitive_value!(Bool, ValueEnum::BoolTy);
primitive_value!(Unit, ValueEnum::UnitTy);
primitive_value!(LogicalOp, ValueEnum::LogicalOp);
primitive_value!(logical::Binary, |b| ValueEnum::LogicalOp(LogicalOp::Binary(b)));
primitive_value!(logical::Unary, |u| ValueEnum::LogicalOp(LogicalOp::Unary(u)));

impl Display for ValueEnum {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        match self {
            ValueEnum::Sexpr(x) => write!(fmt, "{}", x),
            ValueEnum::Bool(x) => write!(fmt, "#{}", x),
            ValueEnum::LogicalOp(x) => write!(fmt, "{}", x),
            ValueEnum::BoolTy(b) => write!(fmt, "{}", b),
            ValueEnum::Lambda(_l) => write!(fmt, "#lambda #TODO"), //TODO
            ValueEnum::UnitTy(u) => write!(fmt, "{}", u),
            ValueEnum::Parameter(p) => write!(fmt, "#param({})", p.ty)
        }
    }
}

/// The data associated with a rain value
#[derive(Debug, Clone)]
pub struct ValueData {
    /// The value itself
    pub value: ValueEnum,
    /// The name associated with this value, if any
    pub name: Option<String>,
    /// The region this value is defined in
    pub region: WeakRegion,
    /// The values dependent on this value
    dependents: SmallVec<[WeakId; SMALL_DEPENDENTS]>,
    /*
    /// A self-link
    pub this: WeakId
    */
}

impl<R> View<R> where R: DerefMut<Target=ValueData> {
    /// Add a dependent to the given value
    #[inline]
    pub fn add_dependent(&mut self, dependent: WeakId) {
        self.0.deref_mut().dependents.push(dependent)
    }
    /// Try to set the name of a given value
    pub fn set_name(&mut self, name: String) -> Result<(), String> {
        if self.name.is_none() {
            self.0.name = Some(name);
            Ok(())
        } else {
            Err(name)
        }
    }
}

impl Deref for ValueData {
    type Target = ValueEnum;
    #[inline(always)] fn deref(&self) -> &ValueEnum { &self.value }
}

impl ValueData {
    /*
    /// Check if this value data points to the same value as another
    pub fn ptr_eq<O: HasThis<ValueData>>(&self, other: &O) -> bool {
        self.this.ptr_eq(&other.this())
    }
    /// Check if this value data points to the same allocation as another
    /// Returns false if either value data are not currently in an allocation.
    pub fn alloc_eq<O: HasThis<ValueData>>(&self, other: &O) -> bool {
        self.this.alloc_eq(&other.this())
    }
    */
    /// Create ValueData from a ValueEnum having a given region
    pub fn with_region(value: ValueEnum, region: WeakRegion) -> ValueData {
        ValueData {
            value,
            region,
            name: None,
            dependents: SmallVec::new()
        }
    }
    /// Create ValueData from a ValueEnum with no region
    pub fn new(value: ValueEnum) -> ValueData {
        Self::with_region(value, WeakRegion::default())
    }
    /// Add a dependent to a value
    #[inline]
    pub fn add_dependent(&mut self, dependent: WeakNode<Self>) { self.dependents.push(dependent) }
    /// Iterate over the direct dependencies for the given node
    pub fn dependencies(&self) -> Dependencies {
        match &self.value {
            ValueEnum::Sexpr(s) => Dependencies::Slice(s.dependencies()),
            ValueEnum::Lambda(l) => Dependencies::Slice(l.dependencies()),
            _ => Dependencies::Slice([].iter())
        }
    }
    /// Get the direct dependents of a given node
    pub fn dependents(&self) -> &[WeakId] { self.dependents.deref() }
}

impl Display for ValueData {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        self.value.name_print(fmt, self.name.as_ref().map(|s| s.as_str()))
    }
}

impl From<ValueEnum> for ValueData {
    fn from(value: ValueEnum) -> ValueData { ValueData::new(value) }
}

impl PartialEq for ValueData {
    fn eq(&self, other: &ValueData) -> bool {
        /*self.alloc_eq(other) ||*/ self.value == other.value
    }
}

impl Hash for ValueData {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        self.value.hash(hasher)
    }
}

/// A value ID: a node pointing to value data
pub type ValId = Node<ValueData>;

/// A weak value ID: a weak node optionally pointing to value data
pub type WeakId = WeakNode<ValueData>;

impl NodeData for ValueData {
    type Error = ValueError;
    type CacheAcceptor = CacheEntry<'static, ValueData>;
    #[inline] fn backlink(&mut self, backlink: Backlink<ValueData>) -> Result<(), ValueError> {
        //let this = backlink.downgrade();
        //self.this = this

        let mut region = WeakRegion::default();
        std::mem::swap(&mut self.region, &mut region);

        let max_region = |acc: &mut WeakRegion, region: &WeakRegion| {
            let ord = (*acc).partial_cmp(region);
            match ord {
                None => return Err(
                    ValueError::IncomparableRegions(
                        IncomparableRegions(
                            SmallVec::from([region.clone(), region.clone()])
                        )
                    )
                ),
                Some(Ordering::Greater) => *acc = region.clone(),
                _ => {}
            }
            Ok(())
        };
        max_region(&mut region, &self.value.base_region())?;

        for dependency in self.dependencies() {
            let mut dependency = dependency.data_mut();
            dependency.add_dependent(backlink.downgrade());
            max_region(&mut region, &dependency.region)?;
        }
        self.region = region;
        Ok(())
    }
    #[inline] fn dedup(&mut self) -> Either<ValId, CacheEntry<'static, ValueData>> {
        VALUE_CACHE.deref().cached_entry(self)
    }
}

impl ValId {
    /// Register a value dependent on this value
    #[inline] pub fn add_dependent(&self, dependent: WeakId) {
        self.data_mut().add_dependent(dependent)
    }
}

impl TryFrom<ValueEnum> for ValId {
    type Error = ValueError;
    fn try_from(value: ValueEnum) -> Result<ValId, ValueError> {
        Node::try_new(ValueData::try_from(value)?)
    }
}