#![deny(missing_docs)]
#![deny(unsafe_code)]
use std::str::FromStr;
use std::fmt::{self, Display, Debug, Formatter};
use std::ops::Deref;
pub type StringValue = Value<String>;
#[derive(PartialEq, Clone, PartialOrd)]
pub enum Value<Sym> {
Data(Box<Value<Sym>>),
Code(Box<Value<Sym>>),
Str(String),
Symbol(Sym),
Int(i64),
Float(f64),
Cons(Box<Value<Sym>>, Box<Value<Sym>>),
Nil,
}
impl<Sym: FromStr> Value<Sym> {
pub fn auto<T>(value: T) -> Value<Sym> where T: AutoValue<Sym> {
value.auto()
}
pub fn symbol(s: &str) -> Option<Value<Sym>> {
let sym = Sym::from_str(s);
match sym {
Ok(sym) => Some(Value::Symbol(sym)),
Err(_) => None,
}
}
pub fn string<S: Into<String>>(s: S) -> Value<Sym> {
Value::Str(s.into())
}
pub fn int<I: Into<i64>>(i: I) -> Value<Sym> {
Value::Int(i.into())
}
pub fn float<F: Into<f64>>(f: F) -> Value<Sym> {
Value::Float(f.into())
}
pub fn list<V: Into<Value<Sym>>>(mut source_vec: Vec<V>) -> Value<Sym> {
let mut result = Value::Nil;
while let Some(value) = source_vec.pop() {
result = Value::cons(value.into(), result)
}
result
}
pub fn into_list<A, V: Into<Value<Sym>>, F>(source_vec: Vec<A>, map: F) -> Value<Sym>
where F: Fn(&A) -> V {
let converted = source_vec.iter().map(map).collect();
Value::list(converted)
}
pub fn cons<V: Into<Value<Sym>>>(left: V, right: V) -> Value<Sym> {
Value::Cons(Box::new(left.into()), Box::new(right.into()))
}
pub fn final_cons<V: Into<Value<Sym>>>(left: V) -> Value<Sym> {
Value::cons(left.into(), Value::Nil)
}
pub fn nil() -> Value<Sym> {
Value::Nil
}
pub fn is_nil(&self) -> bool {
match *self {
Value::Nil => true,
_ => false,
}
}
pub fn code<V: Into<Value<Sym>>>(value: V) -> Value<Sym> {
Value::Code(Box::new(value.into()))
}
pub fn data<V: Into<Value<Sym>>>(value: V) -> Value<Sym> {
Value::Data(Box::new(value.into()))
}
pub fn is_cons(&self) -> bool {
match *self {
Value::Cons(_, _) => true,
_ => false,
}
}
pub fn is_list(&self) -> bool {
match *self {
Value::Nil => true,
Value::Cons(_, ref right) => right.is_list(),
_ => false,
}
}
pub fn is_data(&self) -> bool {
match *self {
Value::Data(_) => true,
_ => false,
}
}
pub fn is_code(&self) -> bool {
match *self {
Value::Code(_) => true,
_ => false,
}
}
pub fn unwrap(self) -> Value<Sym> {
match self {
Value::Code(code) => code.unwrap(),
Value::Data(data) => data.unwrap(),
other => other,
}
}
pub fn unwrap_full(self) -> Value<Sym> {
match self {
Value::Code(code) => code.unwrap_full(),
Value::Data(data) => data.unwrap_full(),
Value::Cons(left, right) =>
Value::cons(
left.unwrap_full(),
right.unwrap_full()
),
_ => self,
}
}
pub fn is_multimode(&self) -> bool {
match *self {
Value::Data(ref contents) => contents.code_children(),
_ => false
}
}
fn code_children(&self) -> bool {
match *self {
Value::Code(_) => true,
Value::Data(ref child) =>
child.code_children(),
Value::Cons(ref left, ref right) =>
left.code_children() || right.code_children(),
_ => false
}
}
}
impl<Sym> AsRef<Value<Sym>> for Value<Sym> {
fn as_ref(&self) -> &Self {
match *self {
Value::Data(ref data) => data,
Value::Code(ref code) => code,
_ => self
}
}
}
impl<Sym> Display for Value<Sym> where Sym: ToString + FromStr {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match *self {
Value::Cons(ref left, ref right) => display_cons(left, right, true, f),
Value::Str(ref text) => write!(f, "\"{}\"", escape_string(&text)),
Value::Symbol(ref sym) => write!(f, "{}", escape_symbol(&sym.to_string())),
Value::Int(ref i) => write!(f, "{}", i),
Value::Float(ref fl) => format_float(f, *fl),
Value::Nil => write!(f, "()"),
Value::Data(ref data) =>
if self.is_multimode() {
write!(f, "{}", MultiMode::new(self))
} else {
write!(f, "'{}", data)
},
Value::Code(ref code) => write!(f, "{}", code),
}
}
}
impl<Sym> Debug for Value<Sym> where Sym: ToString + FromStr {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self)
}
}
fn display_cons<Sym>(left: &Value<Sym>, right: &Value<Sym>, root: bool, f: &mut Formatter)
-> Result<(), fmt::Error> where Sym: ToString + FromStr {
if root {
try!(write!(f, "("));
}
try!(write!(f, "{}", left));
match *right {
Value::Nil => write!(f, ")"),
Value::Cons(ref left, ref right) => {
try!(write!(f, " "));
display_cons(left, right, false, f)
}
_ => write!(f, " . {})", right),
}
}
fn escape_string(text: &AsRef<str>) -> String {
text.as_ref().chars().map(
|c| -> String {
match c {
'\t' => "\\t".to_string(),
'\n' => "\\n".to_string(),
'\r' => "\\r".to_string(),
'\'' => "\\\'".to_string(),
'\"' => "\\\"".to_string(),
'\\' => "\\\\".to_string(),
_ => c.to_string(),
}
}
).collect()
}
fn escape_symbol(text: &AsRef<str>) -> String {
escape_string(text).chars().map(
|c| -> String {
match c {
' ' => "\\ ".to_string(),
'(' => "\\(".to_string(),
')' => "\\)".to_string(),
'#' => "\\#".to_string(),
';' => "\\;".to_string(),
'`' => "\\`".to_string(),
',' => "\\,".to_string(),
_ => c.to_string(),
}
}
).collect()
}
fn format_float<F: Into<f64>>(f: &mut Formatter, fl: F) -> Result<(), fmt::Error> {
let float = fl.into();
if float.fract() == 0f64 {
write!(f, "{:.1}", float)
} else {
write!(f, "{}", float)
}
}
#[derive(PartialEq, PartialOrd)]
struct MultiMode<'a, Sym: 'a + ToString + FromStr> {
value: &'a Value<Sym>,
is_data: bool,
declare_mode: bool,
}
impl<'a, Sym: 'a + FromStr + ToString> MultiMode<'a, Sym> {
fn new(data: &'a Value<Sym>) -> Self {
match *data {
Value::Data(_) => MultiMode {
value: data,
is_data: true,
declare_mode: true
},
_ => panic!("Multimode::new only to be used on Value::Data"),
}
}
fn wrap_child<'b>(&self, child: &'b Value<Sym>) -> MultiMode<'b, Sym> {
let mode_flip = match *child {
Value::Data(_) => !self.is_data,
Value::Code(_) => self.is_data,
_ => false,
};
MultiMode {
value: child,
is_data: self.is_data ^ mode_flip,
declare_mode: mode_flip,
}
}
}
impl<'a, Sym: 'a + ToString + FromStr> Display for MultiMode<'a, Sym> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
if self.declare_mode {
match *self.value {
Value::Data(ref data) =>
write!(f, "`{}", self.wrap_child(data)),
Value::Code(ref code) =>
write!(f, ",{}", self.wrap_child(code)),
_ => unreachable!(),
}
} else if let Value::Cons(ref left, ref right) = *self.value {
display_cons_multimode(
self.wrap_child(left),
self.wrap_child(right),
true,
f
)
} else {
match *self.value {
Value::Data(ref data) =>
write!(f, "{}", self.wrap_child(data)),
Value::Code(ref code) =>
write!(f, "{}", self.wrap_child(code)),
_ => write!(f, "{}", self.value),
}
}
}
}
impl<'a, Sym: 'a + ToString + FromStr> Debug for MultiMode<'a, Sym> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self)
}
}
fn display_cons_multimode<Sym>(left: MultiMode<Sym>, right: MultiMode<Sym>, root: bool, f: &mut Formatter)
-> Result<(), fmt::Error> where Sym: ToString + FromStr {
if root {
try!(write!(f, "("));
}
try!(write!(f, "{}", left));
match *right {
Value::Nil => write!(f, ")"),
Value::Cons(ref i_left, ref i_right) => {
try!(write!(f, " "));
display_cons_multimode(
right.wrap_child(i_left),
right.wrap_child(i_right),
false,
f
)
}
_ => write!(f, " . {})", right),
}
}
impl<'a, Sym: FromStr + ToString + Sized> Deref for MultiMode<'a, Sym> {
type Target = Value<Sym>;
fn deref(&self) -> &Value<Sym> {
self.value
}
}
pub trait AutoValue<Sym> {
fn auto(self) -> Value<Sym>;
}
impl<Sym: FromStr> AutoValue<Sym> for Value<Sym> {
fn auto(self) -> Value<Sym> {
self
}
}
impl<Sym: FromStr> AutoValue<Sym> for i64 {
fn auto(self) -> Value<Sym> {
Value::int(self)
}
}
impl<Sym: FromStr> AutoValue<Sym> for f64 {
fn auto(self) -> Value<Sym> {
Value::float(self)
}
}
impl<'a, Sym: FromStr> AutoValue<Sym> for &'a str {
fn auto(self) -> Value<Sym> {
Value::string(self)
}
}
impl<Sym: FromStr> AutoValue<Sym> for String {
fn auto(self) -> Value<Sym> {
Value::string(self)
}
}
#[macro_export]
macro_rules! s_tree {
($t:ident: ()) => {
$t::nil();
};
($t:ident: ($l:tt . $r:tt)) => {
$t::cons(s_tree!($t: $l), s_tree!($t: $r))
};
($t:ident: ($($i:tt)*)) => {
$t::list(vec![
$(s_tree!($t: $i)),*
])
};
($t:ident: [d:$v:tt]) => {
$t::data(s_tree!($t: $v))
};
($t:ident: [c:$v:tt]) => {
$t::code(s_tree!($t: $v))
};
($t:ident: [s:$v:tt]) => {
$t::symbol($v).unwrap()
};
($t:ident: [$v:tt]) => {
$t::symbol(stringify!($v)).unwrap()
};
($t:ident: [$k:ident:$v:expr]) => {
$t::$k($v)
};
($t:ident: $v:expr) => {
$t::auto($v)
};
}
#[test]
fn value_fmt_test() {
assert_eq!(format!("{:?}", Value::<String>::int(13)), "13");
assert_eq!(format!("{:?}", Value::<String>::int(-13)), "-13");
assert_eq!(format!("{:?}", Value::<String>::float(13.0)), "13.0");
assert_eq!(format!("{:?}", Value::<String>::float(13.125)), "13.125");
assert_eq!(format!("{:?}", Value::<String>::float(13.333)), "13.333");
assert_eq!(format!("{:?}", Value::<String>::float(-13.333)), "-13.333");
assert_eq!(format!("{:?}", Value::<String>::string("text")), "\"text\"");
assert_eq!(format!("{:?}", Value::<String>::string("hello\tthere\nfriend")), "\"hello\\tthere\\nfriend\"");
assert_eq!(format!("{:?}", Value::<String>::symbol("text").unwrap()), "text");
assert_eq!(format!("{:?}", Value::<String>::symbol("hello\tthere\nfriend").unwrap()), "hello\\tthere\\nfriend");
assert_eq!(
format!("{:?}", s_tree!(StringValue: (13 13.333 "text" [symbol]))),
"(13 13.333 \"text\" symbol)"
);
assert_eq!(
format!("{:?}", s_tree!(StringValue: (13 . (13.333 . ("text" . [symbol]))))),
"(13 13.333 \"text\" . symbol)"
);
assert_eq!(format!("{:?}", Value::<String>::cons(
Value::int(13),
Value::cons(
Value::float(13.333),
Value::cons(
Value::string("text"),
Value::symbol("symbol").unwrap()
)
)
)), "(13 13.333 \"text\" . symbol)");
}
#[test]
fn render_multimode() {
assert_eq!(format!(
"{:?}",
StringValue::code(StringValue::list(vec![
StringValue::data(StringValue::symbol("test").unwrap()),
StringValue::data(StringValue::list(vec![
StringValue::symbol("this").unwrap(),
StringValue::symbol("is").unwrap(),
StringValue::code(StringValue::symbol("not").unwrap()),
StringValue::symbol("data").unwrap(),
])),
StringValue::symbol("code?").unwrap(),
]))
), "('test `(this is ,not data) code?)");
assert_eq!(format!(
"{:?}",
s_tree!(StringValue: (
[d:[test]]
[d:([this] [is] [c:[not]] [data] [int:61])]
[s:"code"]
))
), "('test `(this is ,not data 61) code)");
}