use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::ops::Deref;
use std::cmp::{PartialOrd, Ordering};
use std::sync::{Arc, Weak};
use std::convert::Infallible;
use std::hash::{Hash, Hasher};
use crate::value::{
ValId, ValueEnum, ValueData, ValueDesc, Value,
error::RegionAlreadyFused
};
#[derive(Debug, Clone)]
pub struct Region(Arc<RegionData>);
impl Region {
pub fn new() -> Region { Region::new_in(WeakRegion::default()) }
pub fn new_in(parent: WeakRegion) -> Region {
let depth = parent.upgrade().map(|parent| parent.depth).unwrap_or(0) + 1;
let result = Region(Arc::new(RegionData {
parent,
depth,
params: RwLock::new(Parameters::new()),
_private: ()
}));
let weak = result.downgrade();
result.params.write().this = weak;
result
}
#[inline] pub fn downgrade(&self) -> WeakRegion { WeakRegion(Arc::downgrade(&self.0)) }
}
impl PartialEq for Region {
fn eq(&self, other: &Region) -> bool { Arc::ptr_eq(&self.0, &other.0) }
}
impl Eq for Region {}
impl Hash for Region {
#[inline] fn hash<H: Hasher>(&self, hasher: &mut H) {
Arc::as_ptr(&self.0).hash(hasher)
}
}
impl PartialOrd for Region {
fn partial_cmp(&self, other: &Region) -> Option<Ordering> {
if self == other { return Some(Ordering::Equal) }
let ordering = self.depth.cmp(&other.depth);
let (deeper, shallower) = match ordering {
Ordering::Equal => { return None },
Ordering::Greater => (self, other),
Ordering::Less => (other, self)
};
let mut parent = deeper.parent.upgrade()?;
while parent.depth > shallower.depth {
parent = parent.parent.upgrade()?;
}
if &parent == shallower {
Some(ordering.reverse())
} else {
None
}
}
}
impl PartialEq<WeakRegion> for Region {
fn eq(&self, other: &WeakRegion) -> bool {
if let Some(other) = other.upgrade() { other.eq(self) } else { false }
}
}
impl PartialOrd<WeakRegion> for Region {
fn partial_cmp(&self, other: &WeakRegion) -> Option<Ordering> {
if other.is_null() { Some(Ordering::Less) }
else { self.partial_cmp(&other.upgrade()?) }
}
}
impl PartialEq<Region> for WeakRegion {
fn eq(&self, other: &Region) -> bool { other.eq(self) }
}
impl PartialOrd<Region> for WeakRegion {
fn partial_cmp(&self, other: &Region) -> Option<Ordering> {
other.partial_cmp(self).map(Ordering::reverse)
}
}
impl Deref for Region {
type Target = RegionData;
#[inline] fn deref(&self) -> &RegionData { self.0.deref() }
}
#[derive(Debug, Clone, Default)]
pub struct WeakRegion(pub Weak<RegionData>);
impl WeakRegion {
#[inline] pub fn upgrade(&self) -> Option<Region> { self.0.upgrade().map(Region) }
#[inline] pub fn is_null(&self) -> bool { self == &WeakRegion::default() }
#[inline] pub fn outer<'a>(&'a self, other: &'a WeakRegion) -> Option<&'a WeakRegion> {
self.partial_cmp(other).map(|ord| match ord { Ordering::Greater => self, _ => other })
}
#[inline] pub fn inner<'a>(&'a self, other: &'a WeakRegion) -> Option<&'a WeakRegion> {
self.partial_cmp(other).map(|ord| match ord { Ordering::Less => self, _ => other })
}
#[inline] pub fn innermost<'a, I>(&'a self, mut regions: I)
-> Result<&'a WeakRegion, (&'a WeakRegion, &'a WeakRegion)>
where I: Iterator<Item=&'a WeakRegion> {
let mut res = self;
while let Some(region) = regions.next() {
match res.partial_cmp(region) {
None => return Err((res, region)),
Some(Ordering::Less) => res = region,
_ => {}
}
}
Ok(res)
}
}
impl PartialEq for WeakRegion {
fn eq(&self, other: &WeakRegion) -> bool { Weak::ptr_eq(&self.0, &other.0) }
}
impl Eq for WeakRegion {}
impl Hash for WeakRegion {
#[inline] fn hash<H: Hasher>(&self, hasher: &mut H) {
self.0.as_ptr().hash(hasher)
}
}
impl PartialOrd for WeakRegion {
fn partial_cmp(&self, other: &WeakRegion) -> Option<Ordering> {
if self == other {
Some(Ordering::Equal)
} else if self.is_null() {
Some(Ordering::Greater)
} else if other.is_null() {
Some(Ordering::Less)
} else {
let left = self.upgrade()?;
let right = other.upgrade()?;
let ordering = left.partial_cmp(&right);
debug_assert_ne!(ordering, Some(Ordering::Equal));
ordering
}
}
}
#[derive(Debug)]
pub struct RegionData {
pub parent: WeakRegion,
pub depth: usize,
pub params: RwLock<Parameters>,
_private: ()
}
impl RegionData {
pub fn params(&self) -> RwLockReadGuard<Parameters> { self.params.read() }
pub fn params_mut(&self) -> RwLockWriteGuard<Parameters> { self.params.write() }
pub fn add_param(&self, desc: ParameterDesc) -> (usize, ValId) {
let mut params = self.params_mut();
let ix = params.add_param(desc).expect("Unfused region");
let param = params.arr()[ix].clone();
(ix, param)
}
pub fn add_with_ty(&self, ty: ValId) -> (usize, ValId) {
let mut params = self.params_mut();
let ix = params.add_with_ty(ty).expect("Unfused region");
let param = params.arr()[ix].clone();
(ix, param)
}
pub fn this(&self) -> WeakRegion { self.params().this() }
}
#[derive(Debug)]
pub struct Parameters {
arr: Vec<ValId>,
this: WeakRegion,
fused: bool
}
impl Parameters {
fn new() -> Parameters {
Parameters { arr: Vec::new(), this: WeakRegion::default(), fused: false }
}
pub fn add_param(&mut self, desc: ParameterDesc) -> Result<usize, RegionAlreadyFused> {
if self.fused { return Err(RegionAlreadyFused) } let ix = self.arr.len();
let param = Parameter {
ty: desc.ty,
region: self.this.clone(),
ix
};
let node = ValId::from(param);
self.arr.push(node);
Ok(ix)
}
pub fn add_with_ty(&mut self, ty: ValId) -> Result<usize, RegionAlreadyFused> {
self.add_param(ParameterDesc { ty })
}
pub fn arr(&self) -> &[ValId] { &self.arr }
pub fn this(&self) -> WeakRegion { self.this.clone() }
pub fn fused(&self) -> bool { self.fused }
pub fn fuse(&mut self) { self.fused = true }
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParameterDesc {
pub ty: ValId
}
#[derive(Debug, Clone, Hash)]
pub struct Parameter {
pub ty: ValId,
region: WeakRegion,
ix: usize
}
impl From<Parameter> for ValueEnum {
fn from(param: Parameter) -> ValueEnum { ValueEnum::Parameter(param) }
}
impl From<Parameter> for ValueData {
fn from(param: Parameter) -> ValueData {
let region = param.region.clone();
ValueData::with_region(ValueEnum::Parameter(param), region)
}
}
impl From<Parameter> for ValId {
fn from(param: Parameter) -> ValId {
ValId::try_new(ValueData::from(param)).expect("Impossible")
}
}
impl ValueDesc for Parameter {
type Err = Infallible;
}
impl Value for Parameter {}
impl Parameter {
pub fn ix(&self) -> usize { self.ix }
pub fn region(&self) -> &WeakRegion { &self.region }
}
impl PartialEq for Parameter {
fn eq(&self, other: &Parameter) -> bool {
self.region == other.region && self.ix == other.ix
}
}
impl Eq for Parameter {}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::value::primitive::{Unit, logical::{Unary, Bool}};
use crate::value::expr::SexprArgs;
use smallvec::smallvec;
use std::convert::TryInto;
#[test]
fn nested_region_construction() {
let region = Region::new();
let nested_region = Region::new_in(region.downgrade());
let other_region = Region::new();
let null = WeakRegion::default();
assert_eq!(nested_region.parent, region.downgrade());
assert_eq!(nested_region.this(), nested_region.downgrade());
assert_eq!(region.this(), region.downgrade());
assert_eq!(region.parent, WeakRegion::default());
assert_eq!(region.depth, 1);
assert_eq!(other_region.parent, WeakRegion::default());
assert_eq!(other_region.depth, 1);
assert_eq!(nested_region.depth, 2);
assert_eq!(region, region);
assert_eq!(nested_region, nested_region);
assert_eq!(other_region, other_region);
assert_ne!(region, other_region);
assert_ne!(region, nested_region);
assert_ne!(other_region, nested_region);
assert_eq!(region.partial_cmp(&nested_region), Some(Ordering::Greater));
assert_eq!(region.partial_cmp(&null), Some(Ordering::Less));
assert_eq!(region.partial_cmp(&other_region), None);
assert_eq!(nested_region.partial_cmp(®ion), Some(Ordering::Less));
assert_eq!(nested_region.partial_cmp(&other_region), None);
}
#[test]
fn parameters_are_added_to_region() {
let region = Region::new();
let unit_ty = ValId::from(Unit);
let bool_ty = ValId::from(Bool);
let mut params = region.params_mut();
assert_eq!(params.arr().len(), 0);
let tys = [unit_ty.clone(), bool_ty.clone(), unit_ty.clone()];
for (i, ty) in tys.iter().enumerate() {
assert_eq!(i, params.add_with_ty(ty.clone()).expect("Unfused region"))
}
assert_eq!(params.arr().len(), tys.len());
let weak = region.downgrade();
for (i, a) in params.arr().iter().enumerate() {
let data = a.data();
match &data.value {
ValueEnum::Parameter(p) => {
assert_eq!(p.ix, i);
assert_eq!(p.region, weak);
assert_eq!(p.ty, tys[i])
},
v => panic!("Bad parameter value {} @ params[{}]", v, i)
}
}
let not_args = SexprArgs(
smallvec![params.arr()[1].clone(), Unary::Not.into()]
);
let not: ValId = not_args.try_into().expect("This is a valid expression");
{
let not = not.data();
assert_eq!(
not.region, weak,
"Wrong region for (not #parameter): is null = {}",
not.region == WeakRegion::default()
);
}
}
}