oftlisp 0.1.3

A compiler and interpreter for OftLisp, in Rust.
Documentation
use std::cmp::Ordering as CmpOrdering;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult, Write};
use std::sync::atomic::{AtomicIsize, Ordering as AOrdering};

use gc::Gc;
use itertools::Itertools;

use ast::Args;
use context::Context;
use symbol::Symbol;

/// A single OftLisp value.
// XXX: (Almost?) all of the built-in derives give incorrect trait impls (they
// constrain `C` instead of `C`'s constituents), so you need to write the trait
// impls by hand.
//
// This is true for the builtin traits, and also Trace.
// For Trace, see https://github.com/Manishearth/rust-gc/issues/67
#[derive(Finalize, Trace)]
pub enum Value<C: 'static + Context> {
    /// A mutable, signed, machine-sized integer.
    AtomicWord(AtomicIsize, C::ValueMeta),

    /// A built-in function.
    BuiltinFunction(Symbol, C::BuiltinFunction, C::ValueMeta),

    /// An unsigned 8-bit integer.
    Byte(u8, C::ValueMeta),

    /// A vector of arbitrary bytes, not subject to the UTF-8 condition of
    /// Strings.
    Bytes(Gc<Vec<u8>>, C::ValueMeta),

    /// A cons cell, used to create lists, trees, and other data structures.
    Cons(Gc<Value<C>>, Gc<Value<C>>, C::ValueMeta),

    /// A signed machine-sized integer.
    Fixnum(isize, C::ValueMeta),

    /// A user-defined function.
    Func(Option<Symbol>, Gc<Args<C>>, C::UserFunction, C::ValueMeta),

    /// The object type, containing a vtable and values.
    Object(C::ObjectVtable, Vec<Gc<Value<C>>>, C::ValueMeta),

    /// The nil literal, used to terminate lists and signal failure (or the
    /// lack of interesting results).
    Nil(C::ValueMeta),

    /// A UTF-8 string.
    String(Gc<String>, C::ValueMeta),

    /// An interned symbol, which may be compared in O(1).
    Symbol(Symbol, C::ValueMeta),

    /// A fixed-length collection with O(1) lookup.
    Vector(Vec<Gc<Value<C>>>, C::ValueMeta),
}

impl<C: 'static + Context> Value<C> {
    /// A pseudo-constructor that converts a `bool` to a `Value`.
    pub fn bool(b: bool) -> Gc<Value<C>> {
        if b {
            Gc::new(Value::Symbol(Symbol::from("true"), Default::default()))
        } else {
            Gc::new(Value::Nil(Default::default()))
        }
    }

    /// Converts an `Iterator` and a last element to an improper cons-list.
    pub fn improper_list<II: IntoIterator<Item = Gc<Value<C>>>>(
        iter: II,
        last: Gc<Value<C>>,
        meta: C::ValueMeta,
    ) -> Gc<Value<C>> {
        // TODO Turn this into a loop.
        pub fn f<C: 'static + Context, I: Iterator<Item = Gc<Value<C>>>>(
            mut iter: I,
            last: Gc<Value<C>>,
            meta: C::ValueMeta,
        ) -> Gc<Value<C>> {
            let next = iter.next();
            match next {
                None => last,
                Some(head) => {
                    let tail = f(iter, last, meta.clone());
                    Gc::new(Value::Cons(head, tail, meta))
                }
            }
        }

        f(iter.into_iter(), last, meta)
    }

    /// Converts an `Iterator` to a cons-list.
    pub fn list<II: IntoIterator<Item = Gc<Value<C>>>>(
        iter: II,
        meta: C::ValueMeta,
    ) -> Gc<Value<C>> {
        let tail = Gc::new(Value::Nil(meta.clone()));
        Value::improper_list(iter, tail, meta)
    }

    /// Extracts the metadata from a `Value`.
    pub fn meta(&self) -> C::ValueMeta {
        match *self {
            Value::AtomicWord(_, ref m) => m.clone(),
            Value::BuiltinFunction(_, _, ref m) => m.clone(),
            Value::Byte(_, ref m) => m.clone(),
            Value::Bytes(_, ref m) => m.clone(),
            Value::Cons(_, _, ref m) => m.clone(),
            Value::Fixnum(_, ref m) => m.clone(),
            Value::Func(_, _, _, ref m) => m.clone(),
            Value::Object(_, _, ref m) => m.clone(),
            Value::Nil(ref m) => m.clone(),
            Value::String(_, ref m) => m.clone(),
            Value::Symbol(_, ref m) => m.clone(),
            Value::Vector(_, ref m) => m.clone(),
        }
    }

    /// Converts an ordering to a `Value`.
    pub fn ordering(o: Option<CmpOrdering>) -> Gc<Value<C>> {
        let sym = match o {
            Some(CmpOrdering::Less) => "<",
            Some(CmpOrdering::Equal) => "=",
            Some(CmpOrdering::Greater) => ">",
            None => "<>",
        };
        Gc::new(Value::Symbol(sym.into(), Default::default()))
    }

    /// Converts a `Value` between `Context`s.
    ///
    /// Will fail to transmute functions and objects.
    pub fn transmute_with<C2, F>(&self, f: &F) -> Option<Value<C2>>
    where
        C2: 'static + Context,
        F: Fn(&C::ValueMeta) -> Option<C2::ValueMeta>,
    {
        match *self {
            Value::AtomicWord(ref n, ref m) => {
                f(m).map(|m| {
                    let n = AtomicIsize::new(n.load(AOrdering::SeqCst));
                    Value::AtomicWord(n, m)
                })
            }
            Value::BuiltinFunction(..) => None,
            Value::Byte(n, ref m) => f(m).map(|m| Value::Byte(n, m)),
            Value::Bytes(ref bs, ref m) => f(m).map(|m| Value::Bytes(bs.clone(), m)),
            Value::Cons(ref h, ref t, ref m) => {
                f(m).and_then(|m| {
                    h.transmute_with(f).and_then(|h| {
                        t.transmute_with(f).map(|t| {
                            Value::Cons(Gc::new(h), Gc::new(t), m)
                        })
                    })
                })
            }
            Value::Fixnum(n, ref m) => f(m).map(|m| Value::Fixnum(n, m)),
            Value::Func(..) => None,
            Value::Object(..) => None,
            Value::Nil(ref m) => f(m).map(Value::Nil),
            Value::String(ref s, ref m) => f(m).map(|m| Value::String(s.clone(), m)),
            Value::Symbol(s, ref m) => f(m).map(|m| Value::Symbol(s, m)),
            Value::Vector(ref v, ref m) => {
                f(m).and_then(|m| {
                    v.iter()
                        .map(|v| (*v).transmute_data().map(Gc::new))
                        .collect::<Option<_>>()
                        .map(|v| Value::Vector(v, m))
                })
            }
        }
    }

    /// Converts a `Value` between `Context`s.
    ///
    /// Will fail to transmute functions and objects. Any metadata will be
    /// discarded, with the default for the destination being used. To preserve
    /// metadata, use `transmute_with` instead.
    pub fn transmute_data<C2: 'static + Context>(&self) -> Option<Value<C2>> {
        // I think the issues with just using a simple closure here are
        // bug-report-worthy.
        fn default_meta<T, U: Default>(_: &T) -> Option<U> {
            Some(U::default())
        }
        self.transmute_with(&default_meta)
    }

    /// Converts an `Iterator` to a vector.
    pub fn vector<II: IntoIterator<Item = Gc<Value<C>>>>(iter: II) -> Gc<Value<C>> {
        Gc::new(Value::Vector(
            iter.into_iter().collect(),
            Default::default(),
        ))
    }
}

impl<C: 'static + Context> Clone for Value<C> {
    fn clone(&self) -> Self {
        match *self {
            Value::AtomicWord(ref n, ref m) => {
                let n = AtomicIsize::new(n.load(AOrdering::SeqCst));
                Value::AtomicWord(n, m.clone())
            }
            Value::BuiltinFunction(n, ref bf, ref m) => {
                Value::BuiltinFunction(n, bf.clone(), m.clone())
            }
            Value::Byte(n, ref m) => Value::Byte(n, m.clone()),
            Value::Bytes(ref bs, ref m) => Value::Bytes(bs.clone(), m.clone()),
            Value::Cons(ref h, ref t, ref m) => Value::Cons(h.clone(), t.clone(), m.clone()),
            Value::Fixnum(n, ref m) => Value::Fixnum(n, m.clone()),
            Value::Func(name, ref args, ref func, ref m) => {
                Value::Func(name, args.clone(), func.clone(), m.clone())
            }
            Value::Object(ref vtbl, ref vals, ref m) => {
                Value::Object(vtbl.clone(), vals.clone(), m.clone())
            }
            Value::Nil(ref m) => Value::Nil(m.clone()),
            Value::String(ref str, ref m) => Value::String(str.clone(), m.clone()),
            Value::Symbol(sym, ref m) => Value::Symbol(sym, m.clone()),
            Value::Vector(ref vec, ref m) => Value::Vector(vec.clone(), m.clone()),
        }
    }
}

impl<C: 'static + Context> Debug for Value<C> {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        match *self {
            Value::AtomicWord(ref n, ref m) => {
                fmt.debug_tuple("AtomicWord").field(n).field(m).finish()
            }
            Value::BuiltinFunction(n, ref bf, ref m) => {
                fmt.debug_tuple("BuiltinFunction")
                    .field(&n)
                    .field(bf)
                    .field(m)
                    .finish()
            }
            Value::Byte(n, ref m) => fmt.debug_tuple("Byte").field(&n).field(m).finish(),
            Value::Bytes(ref bs, ref m) => fmt.debug_tuple("Bytes").field(bs).field(m).finish(),
            Value::Cons(ref h, ref t, ref m) => {
                fmt.debug_tuple("Cons").field(h).field(t).field(m).finish()
            }
            Value::Fixnum(n, ref m) => fmt.debug_tuple("Fixnum").field(&n).field(m).finish(),
            Value::Func(name, ref args, ref func, ref m) => {
                fmt.debug_tuple("Func")
                    .field(&name)
                    .field(args)
                    .field(func)
                    .field(m)
                    .finish()
            }
            Value::Nil(ref m) => fmt.debug_tuple("Nil").field(m).finish(),
            Value::Object(ref vtbl, ref vals, ref m) => {
                fmt.debug_tuple("Object")
                    .field(vtbl)
                    .field(vals)
                    .field(m)
                    .finish()
            }
            Value::String(ref str, ref m) => fmt.debug_tuple("String").field(str).field(m).finish(),
            Value::Symbol(sym, ref m) => fmt.debug_tuple("Symbol").field(&sym).field(m).finish(),
            Value::Vector(ref vec, ref m) => {
                fmt.debug_tuple("Vector").field(&vec).field(m).finish()
            }
        }
    }
}

impl<C: 'static + Context> Display for Value<C> {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        fn fmt_char(c: char, fmt: &mut Formatter) -> FmtResult {
            let n = c as u32;
            if n < 0x20 {
                fmt.write_str("\\x")?;
                fmt_hex_byte(n as u8, fmt)
            } else if n < 0x7f {
                fmt.write_char(c)
            } else if n < 0xff {
                fmt.write_str("\\x")?;
                fmt_hex_byte(n as u8, fmt)
            } else if n < 0xffff {
                fmt.write_str("\\u")?;
                fmt_hex_byte((n >> 8) as u8, fmt)?;
                fmt_hex_byte(n as u8, fmt)
            } else {
                fmt.write_str("\\U")?;
                fmt_hex_byte((n >> 24) as u8, fmt)?;
                fmt_hex_byte((n >> 16) as u8, fmt)?;
                fmt_hex_byte((n >> 8) as u8, fmt)?;
                fmt_hex_byte(n as u8, fmt)
            }
        }
        fn fmt_hex_byte(n: u8, fmt: &mut Formatter) -> FmtResult {
            write!(fmt, "{}{}", n / 0x10, n % 0x10)
        }
        fn display_list_helper<C: 'static + Context>(
            fmt: &mut Formatter,
            mut value: &Gc<Value<C>>,
        ) -> FmtResult {
            loop {
                match **value {
                    Value::Cons(ref car, ref cdr, _) => {
                        fmt.write_char(' ')?;
                        Display::fmt(car, fmt)?;
                        value = cdr;
                    }
                    Value::Nil(_) => return Ok(()),
                    _ => {
                        fmt.write_str(" \u{2022} ")?;
                        return Display::fmt(value, fmt);
                    }
                }
            }
        }

        match *self {
            Value::AtomicWord(ref n, _) => Display::fmt(&n.load(AOrdering::SeqCst), fmt),
            Value::BuiltinFunction(name, _, _) => write!(fmt, "#<function {}>", name),
            Value::Byte(ref n, _) => Display::fmt(n, fmt),
            Value::Bytes(ref bs, _) => {
                fmt.write_str("b\"")?;
                for b in bs.iter().cloned() {
                    fmt_char(b as char, fmt)?;
                }
                fmt.write_char('"')
            }
            Value::Cons(ref h, ref t, _) => {
                fmt.write_char('(')?;
                Display::fmt(h, fmt)?;
                display_list_helper(fmt, t)?;
                fmt.write_char(')')
            }
            Value::Fixnum(ref n, _) => Display::fmt(n, fmt),
            Value::Func(Some(s), _, _, _) => write!(fmt, "#<function {}>", s),
            Value::Func(None, _, _, _) => fmt.write_str("#<function>"),
            Value::Object(..) => fmt.write_str("#<object>"),
            Value::Nil(_) => fmt.write_str("()"),
            Value::String(ref s, _) => {
                fmt.write_char('"')?;
                for c in s.chars() {
                    fmt_char(c, fmt)?;
                }
                fmt.write_char('"')
            }
            Value::Symbol(ref s, _) => Display::fmt(s, fmt),
            Value::Vector(ref v, _) => write!(fmt, "[{}]", v.iter().join(" ")),
        }
    }
}

impl<C: 'static + Context> PartialEq for Value<C> {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Value::Byte(l, _), &Value::Byte(r, _)) => l == r,
            (&Value::Bytes(ref l, _), &Value::Bytes(ref r, _)) => l == r,
            (&Value::Cons(ref lh, ref lt, _), &Value::Cons(ref rh, ref rt, _)) => {
                lh == rh && lt == rt
            }
            (&Value::Fixnum(l, _), &Value::Fixnum(r, _)) => l == r,
            (&Value::Nil(_), &Value::Nil(_)) => true,
            (&Value::String(ref l, _), &Value::String(ref r, _)) => l == r,
            (&Value::Symbol(l, _), &Value::Symbol(r, _)) => l == r,
            (&Value::Vector(ref l, _), &Value::Vector(ref r, _)) => l == r,
            _ => false,
        }
    }
}

impl<C: 'static + Context> PartialOrd for Value<C> {
    fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
        match (self, other) {
            (&Value::Byte(ref l, _), &Value::Byte(ref r, _)) => l.partial_cmp(r),
            (&Value::Bytes(ref l, _), &Value::Bytes(ref r, _)) => l.partial_cmp(r),
            (&Value::Cons(ref lh, ref lt, _), &Value::Cons(ref rh, ref rt, _)) => {
                match lh.partial_cmp(rh) {
                    Some(CmpOrdering::Equal) |
                    None => lt.partial_cmp(rt),
                    o => o,
                }
            }
            (&Value::Fixnum(ref l, _), &Value::Fixnum(ref r, _)) => l.partial_cmp(r),
            (&Value::Nil(_), &Value::Nil(_)) => Some(CmpOrdering::Equal),
            (&Value::String(ref l, _), &Value::String(ref r, _)) => l.partial_cmp(r),
            (&Value::Symbol(ref l, _), &Value::Symbol(ref r, _)) => l.partial_cmp(r),
            (&Value::Vector(ref l, _), &Value::Vector(ref r, _)) => l.partial_cmp(r),
            _ => None,
        }
    }
}