use crate::dag::{Dag, DagLike, NoSharing};
use crate::types::union_bound::PointerLike;
use super::{Bound, BoundRef, Context};
use std::fmt;
use std::sync::Arc;
#[derive(Clone)]
pub enum Incomplete {
Free(String),
Cycle,
Sum(Arc<Incomplete>, Arc<Incomplete>),
Product(Arc<Incomplete>, Arc<Incomplete>),
Final(Arc<super::Final>),
}
impl DagLike for &'_ Incomplete {
type Node = Incomplete;
fn data(&self) -> &Incomplete {
self
}
fn as_dag_node(&self) -> Dag<Self> {
match *self {
Incomplete::Free(_) | Incomplete::Cycle | Incomplete::Final(_) => Dag::Nullary,
Incomplete::Sum(ref left, ref right) | Incomplete::Product(ref left, ref right) => {
Dag::Binary(left, right)
}
}
}
}
impl fmt::Debug for Incomplete {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Incomplete {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut skip_next = false;
for data in self.verbose_pre_order_iter::<NoSharing>(None) {
if skip_next {
skip_next = false;
continue;
}
match (data.node, data.n_children_yielded) {
(Incomplete::Free(ref s), _) => f.write_str(s)?,
(Incomplete::Cycle, _) => f.write_str("<self-reference>")?,
(Incomplete::Sum(ref left, _), 0) if left.is_unit() => {
skip_next = true;
}
(Incomplete::Sum(ref left, _), 1) if left.is_unit() => {}
(Incomplete::Sum(ref left, _), 2) if left.is_unit() => {
f.write_str("?")?;
}
(Incomplete::Sum(..), 0) | (Incomplete::Product(..), 0) => {
if data.index > 0 {
f.write_str("(")?;
}
}
(Incomplete::Sum(..), 2) | (Incomplete::Product(..), 2) => {
if data.index > 0 {
f.write_str(")")?;
}
}
(Incomplete::Sum(..), _) => f.write_str(" + ")?,
(Incomplete::Product(..), _) => f.write_str(" × ")?,
(Incomplete::Final(ref fnl), _) => fnl.fmt(f)?,
}
}
Ok(())
}
}
impl Incomplete {
pub fn is_unit(&self) -> bool {
if let Incomplete::Final(ref fnl) = self {
fnl.is_unit()
} else {
false
}
}
pub(super) fn occurs_check<'brand>(
ctx: &Context<'brand>,
bound_ref: BoundRef<'brand>,
) -> Option<Arc<Self>> {
use std::collections::HashSet;
use super::context::OccursCheckId;
use super::BoundRef;
enum OccursCheckStack<'brand> {
Iterate(BoundRef<'brand>),
Complete(OccursCheckId<'brand>),
}
let mut stack = vec![OccursCheckStack::Iterate(bound_ref)];
let mut in_progress = HashSet::new();
let mut completed = HashSet::new();
while let Some(top) = stack.pop() {
let bound = match top {
OccursCheckStack::Complete(id) => {
in_progress.remove(&id);
completed.insert(id);
continue;
}
OccursCheckStack::Iterate(b) => b,
};
let id = bound.occurs_check_id();
if completed.contains(&id) {
continue;
}
if !in_progress.insert(id) {
return Some(Arc::new(Self::Cycle));
}
stack.push(OccursCheckStack::Complete(id));
if let Some((_, child)) = (ctx, bound.shallow_clone()).right_child() {
stack.push(OccursCheckStack::Iterate(child));
}
if let Some((_, child)) = (ctx, bound).left_child() {
stack.push(OccursCheckStack::Iterate(child));
}
}
None
}
pub(super) fn from_bound_ref<'brand>(
ctx: &Context<'brand>,
bound_ref: BoundRef<'brand>,
) -> Arc<Self> {
if let Some(err) = Self::occurs_check(ctx, bound_ref.shallow_clone()) {
return err;
}
let mut finalized = vec![];
for data in (ctx, bound_ref).post_order_iter::<NoSharing>() {
let bound_get = data.node.0.get(&data.node.1);
let final_data = match bound_get {
Bound::Free(s) => Incomplete::Free(s),
Bound::Complete(ref arc) => Incomplete::Final(Arc::clone(arc)),
Bound::Sum(..) => Incomplete::Sum(
Arc::clone(&finalized[data.left_index.unwrap()]),
Arc::clone(&finalized[data.right_index.unwrap()]),
),
Bound::Product(..) => Incomplete::Product(
Arc::clone(&finalized[data.left_index.unwrap()]),
Arc::clone(&finalized[data.right_index.unwrap()]),
),
};
finalized.push(Arc::new(final_data));
}
finalized.pop().unwrap()
}
}