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;
const SMALL_DEPENDENTS: usize = 2;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValueEnum {
Sexpr(Sexpr),
Bool(bool),
LogicalOp(LogicalOp),
Lambda(Lambda),
BoolTy(Bool),
UnitTy(Unit),
Parameter(Parameter)
}
#[derive(Debug, Clone)]
pub enum Dependencies<'a> {
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 {
pub fn applicable(&self) -> bool {
use ValueEnum::*;
match self {
Sexpr(_) | LogicalOp(_) | Parameter(_) | Lambda(_) => true,
_ => false
}
}
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)
}
}
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())
}
}
}
pub trait ValueDesc: TryInto<ValId> {
type Err:
From<<Self as TryInto<ValId>>::Error>
+ Into<ValueError>;
#[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())
}
}
pub trait Value: ValueDesc + TryInto<ValueEnum> + JEq<ValueEnum> + JEq<Self> + JEq<ValId> {
#[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"), ValueEnum::UnitTy(u) => write!(fmt, "{}", u),
ValueEnum::Parameter(p) => write!(fmt, "#param({})", p.ty)
}
}
}
#[derive(Debug, Clone)]
pub struct ValueData {
pub value: ValueEnum,
pub name: Option<String>,
pub region: WeakRegion,
dependents: SmallVec<[WeakId; SMALL_DEPENDENTS]>,
}
impl<R> View<R> where R: DerefMut<Target=ValueData> {
#[inline]
pub fn add_dependent(&mut self, dependent: WeakId) {
self.0.deref_mut().dependents.push(dependent)
}
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 {
pub fn with_region(value: ValueEnum, region: WeakRegion) -> ValueData {
ValueData {
value,
region,
name: None,
dependents: SmallVec::new()
}
}
pub fn new(value: ValueEnum) -> ValueData {
Self::with_region(value, WeakRegion::default())
}
#[inline]
pub fn add_dependent(&mut self, dependent: WeakNode<Self>) { self.dependents.push(dependent) }
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())
}
}
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.value == other.value
}
}
impl Hash for ValueData {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.value.hash(hasher)
}
}
pub type ValId = Node<ValueData>;
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 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 {
#[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)?)
}
}