aterm 0.20.0

Implementation of the Annotated Terms data structure
Documentation
use interface::{ATerm as ATermT, ATermFactory as ATermFactoryT, TermPlaceholder};
use bad_idea::BrokenF32;
use print::ATermWrite;

use std::rc::Rc;
use std::marker::PhantomData;
use std::hash::BuildHasher;
use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt;

use string_share::{StringShare, InternedString};

pub mod shared;

/// The basic term type, without annotations
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum Term<'s, B> {
    /// An 32bit signed integer
    Int(i32),
    /// A 64bit signed integer. This is an optional component in the spec, but it's easy to support.
    Long(i64),
    /// An 32bit floating point number, but with a broken equality so a coherent implementation of
    /// Hash could be given (if NaN != NaN, then what Hash should it get?). You shouldn't directly
    /// check equality of floating point numbers anyways because of rounding.
    Real(BrokenF32),
    /// The main thing: Application of a constructor, using a string for the constructor name.
    Application(InternedString<'s>, Box<[Rc<ATerm<'s, B>>]>),
    /// A list of ATerms.
    List(Box<[Rc<ATerm<'s, B>>]>),
    /// A placeholder for pattern matching. Only here for compatibility with the spec, use Rust's
    /// pattern matching facilities instead!
    Placeholder(TermPlaceholder<Rc<ATerm<'s, B>>>),
    /// A Binary Large OBject. Here used to embed any other type. Note it will need to implement
    /// some traits if you still want to parse or print it.
    Blob(B),
}

impl<'s, B> ATermWrite for Term<'s, B>
where
    B: ATermWrite,
{
    fn to_ascii<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
        use self::Term::*;
        use bad_idea::BrokenF32;
        use std::borrow::Borrow;
        match *self {
            Int(i) => write!(writer, "{}", i),
            Long(l) => write!(writer, "{}", l),
            Real(BrokenF32(r)) => write!(writer, "{}", r),
            Application(cons, ref children) => {
                writer.write_str(cons.borrow())?;
                if !children.is_empty() {
                    write!(writer, "(")?;
                    children.to_ascii(writer)?;
                    write!(writer, ")")?;
                }
                Ok(())
            }
            List(ref l) => {
                write!(writer, "[")?;
                l.to_ascii(writer)?;
                write!(writer, "]")
            }
            Placeholder(ref tp) => tp.to_ascii(writer),
            Blob(ref b) => b.to_ascii(writer),
        }
    }
}

pub type T<'s, A: ATermT<'s>> = Term<'s, <A as ATermT<'s>>::Blob>;

/// The annotated term. This only combines the term with the annotations.
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ATerm<'s, B> {
    /// The actual term.
    pub term: Term<'s, B>,
    /// The annotations on the term. The spec says this is an ATerm list of pairs of ATerms. We
    /// extend this to a general list of aterms so we can support more systems (e.g. Stratego/XT).
    pub annotations: Box<[Rc<ATerm<'s, B>>]>,
}

impl<'s, B> ATermWrite for ATerm<'s, B>
where
    B: ATermWrite,
{
    fn to_ascii<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
        let annotations = &self.annotations;
        self.term.to_ascii(writer)?;
        if !annotations.is_empty() {
            write!(writer, "{{")?;
            annotations.to_ascii(writer)?;
            write!(writer, "}}")?;
        }
        Ok(())
    }
}

impl<'s, B: ATermWrite> fmt::Display for ATerm<'s, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.to_ascii(f)
    }
}

impl<'s, B> ATerm<'s, B> {
    pub fn no_annos(term: Term<'s, B>) -> Self {
        ATerm {
            term: term,
            annotations: Box::new([]),
        }
    }

    pub fn with_annos<A>(term: Term<'s, B>, annos: A) -> Self
    where
        A: IntoIterator<Item = Rc<ATerm<'s, B>>>,
    {
        ATerm {
            term: term,
            annotations: annos.into_iter().collect::<Vec<_>>().into_boxed_slice(),
        }
    }
}

impl<'s, B> ATermT<'s> for ATerm<'s, B> {
    type Rec = Rc<ATerm<'s, B>>;
    type Blob = B;

    fn get_int(&self) -> Option<i32> {
        match self.term {
            Term::Int(i) => Some(i),
            _ => None,
        }
    }

    fn get_long(&self) -> Option<i64> {
        match self.term {
            Term::Long(l) => Some(l),
            _ => None,
        }
    }

    fn get_real(&self) -> Option<f32> {
        match self.term {
            Term::Real(BrokenF32(r)) => Some(r),
            _ => None,
        }
    }

    fn get_application(&self) -> Option<(InternedString<'s>, &[Self::Rec])> {
        match self.term {
            Term::Application(c, ref r) => Some((c, r)),
            _ => None,
        }
    }

    fn get_list(&self) -> Option<Vec<Self::Rec>> {
        match self.term {
            Term::List(ref r) => Some(Vec::from(&**r)),
            _ => None,
        }
    }

    fn get_placeholder(&self) -> Option<&TermPlaceholder<Self::Rec>> {
        match self.term {
            Term::Placeholder(ref tp) => Some(tp),
            _ => None,
        }
    }

    fn get_blob(&self) -> Option<&Self::Blob> {
        match self.term {
            Term::Blob(ref b) => Some(b),
            _ => None,
        }
    }

    fn get_annotations(&self) -> &[Self::Rec] {
        &*self.annotations
    }
}

pub struct ATermFactory<B, H: BuildHasher> {
    string_cache: RefCell<StringShare<H>>,
    _nothing: PhantomData<B>,
}

impl<'s, B, H: BuildHasher + Default> ATermFactory<B, H> {
    pub fn new() -> Self {
        ATermFactory {
            string_cache: RefCell::new(StringShare::new()),
            _nothing: PhantomData,
        }
    }
}

impl<'s, B, H: BuildHasher + Default> Default for ATermFactory<B, H> {
    fn default() -> Self {
        Self::new()
    }
}

impl<'s, B, H> ATermFactoryT<'s> for ATermFactory<B, H>
where
    B: 's + Clone,
    H: 's + BuildHasher + Default,
{
    type ATerm = ATerm<'s, B>;
    type ATermRef = Rc<ATerm<'s, B>>;

    fn application<I, S>(&'s self, constructor: S, children: I) -> Self::ATermRef
    where
        I: IntoIterator<Item = Self::ATermRef>,
        S: Into<Cow<'s, str>>,
    {
        use self::Term::Application;
        use utils::extend_lifetime_mut;

        // I'm not sure why I even need to extend the lifetime here, so this could be totally wrong!
        // But if we add something to the string cache, and then use a reference to it, that
        // *should* be fine right?
        let mut string_cache = unsafe { extend_lifetime_mut(&mut self.string_cache.borrow_mut()) };
        let cons = string_cache.insert(constructor.into().as_ref());
        Rc::new(ATerm::no_annos(Application(
            cons,
            children.into_iter().collect::<Vec<_>>().into_boxed_slice(),
        )))
    }

    fn with_annos<A>(&'s self, term: Self::ATermRef, annos: A) -> Self::ATermRef
    where
        A: IntoIterator<Item = Self::ATermRef>,
    {
        Rc::new(ATerm::with_annos(term.term.clone(), annos))
    }

    fn int(&'s self, value: i32) -> Self::ATermRef {
        Rc::new(ATerm::no_annos(Term::Int(value)))
    }

    fn string<S>(&'s self, value: S) -> Self::ATermRef
    where
        S: Into<Cow<'s, str>>,
    {
        self.application(::utils::string_escape(&value.into()), ::std::iter::empty())
    }

    fn tuple<I>(&'s self, children: I) -> Self::ATermRef
    where
        I: IntoIterator<Item = Self::ATermRef>,
    {
        self.application(String::new(), children)
    }

    fn real(&'s self, value: f32) -> Self::ATermRef {
        Rc::new(ATerm::no_annos(Term::Real(BrokenF32(value))))
    }

    fn list<I>(&'s self, value: I) -> Self::ATermRef
    where
        I: IntoIterator<Item = Self::ATermRef>,
    {
        Rc::new(ATerm::no_annos(Term::List(
            value.into_iter().collect::<Vec<_>>().into_boxed_slice(),
        )))
    }

    fn placeholder(&'s self, value: TermPlaceholder<Self::ATermRef>) -> Self::ATermRef {
        Rc::new(ATerm::no_annos(Term::Placeholder(value)))
    }

    fn blob(&'s self, value: <Self::ATerm as ATermT<'s>>::Blob) -> Self::ATermRef {
        Rc::new(ATerm::no_annos(Term::Blob(value)))
    }

    fn long(&'s self, value: i64) -> Self::ATermRef {
        Rc::new(ATerm::no_annos(Term::Long(value)))
    }
}