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;
#[derive(Finalize, Trace)]
pub enum Value<C: 'static + Context> {
AtomicWord(AtomicIsize, C::ValueMeta),
BuiltinFunction(Symbol, C::BuiltinFunction, C::ValueMeta),
Byte(u8, C::ValueMeta),
Bytes(Gc<Vec<u8>>, C::ValueMeta),
Cons(Gc<Value<C>>, Gc<Value<C>>, C::ValueMeta),
Fixnum(isize, C::ValueMeta),
Func(Option<Symbol>, Gc<Args<C>>, C::UserFunction, C::ValueMeta),
Object(C::ObjectVtable, Vec<Gc<Value<C>>>, C::ValueMeta),
Nil(C::ValueMeta),
String(Gc<String>, C::ValueMeta),
Symbol(Symbol, C::ValueMeta),
Vector(Vec<Gc<Value<C>>>, C::ValueMeta),
}
impl<C: 'static + Context> Value<C> {
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()))
}
}
pub fn improper_list<II: IntoIterator<Item = Gc<Value<C>>>>(
iter: II,
last: Gc<Value<C>>,
meta: C::ValueMeta,
) -> Gc<Value<C>> {
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)
}
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)
}
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(),
}
}
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()))
}
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))
})
}
}
}
pub fn transmute_data<C2: 'static + Context>(&self) -> Option<Value<C2>> {
fn default_meta<T, U: Default>(_: &T) -> Option<U> {
Some(U::default())
}
self.transmute_with(&default_meta)
}
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,
}
}
}