use tatara_lisp::{Atom, Sexp};
use tatara_lisp_eval::Value;
#[must_use]
pub fn render_form(v: &Value) -> String {
let sexp = match v {
Value::Sexp(s, _) => s.clone(),
other => match value_to_sexp(other) {
Some(s) => s,
None => return "<unrenderable expression>".to_string(),
},
};
format(&sexp)
}
#[must_use]
pub fn render_value(v: &Value) -> String {
literal(v).unwrap_or_else(|| "<a value with no literal form>".to_string())
}
#[must_use]
pub fn literal(v: &Value) -> Option<String> {
Some(match value_literal(v)? {
Sexp::Atom(Atom::Symbol(s)) => s,
sexp => format(&sexp),
})
}
#[must_use]
pub fn format(sexp: &Sexp) -> String {
blue_lang_fmt::format_forms(std::slice::from_ref(sexp))
.trim_end()
.to_string()
}
#[must_use]
pub fn value_literal(v: &Value) -> Option<Sexp> {
Some(match v {
Value::List(items) => {
let mut out = vec![Sexp::Atom(Atom::Symbol("list".to_string()))];
for i in items.iter() {
out.push(value_literal(i)?);
}
Sexp::List(out)
}
Value::Map(m) => {
let mut pairs: Vec<(Sexp, Sexp)> = m
.iter()
.map(|(k, v)| Some((key_to_sexp(k), value_literal(v)?)))
.collect::<Option<Vec<_>>>()?;
pairs.sort_by_key(|(k, _)| k.to_string());
let mut items = vec![Sexp::Atom(Atom::Symbol(
blue_lang_syntax::LOWERED_MAP.to_string(),
))];
for (k, v) in pairs {
items.push(k);
items.push(v);
}
Sexp::List(items)
}
other => value_to_sexp(other)?,
})
}
#[must_use]
pub fn value_to_sexp(v: &Value) -> Option<Sexp> {
Some(match v {
Value::Nil => Sexp::Nil,
Value::Bool(b) => Sexp::Atom(Atom::Bool(*b)),
Value::Int(n) => Sexp::Atom(Atom::Int(*n)),
Value::Float(x) => Sexp::Atom(Atom::Float(*x)),
Value::Str(s) => Sexp::Atom(Atom::Str(s.to_string())),
Value::Symbol(s) => Sexp::Atom(Atom::Symbol(s.to_string())),
Value::Keyword(k) => Sexp::Atom(Atom::Keyword(k.to_string())),
Value::Sexp(s, _) => s.clone(),
Value::List(items) => Sexp::List(
items
.iter()
.map(value_to_sexp)
.collect::<Option<Vec<_>>>()?,
),
_ => return None,
})
}
fn key_to_sexp(k: &tatara_lisp_eval::value::MapKey) -> Sexp {
use tatara_lisp_eval::value::MapKey;
match k {
MapKey::Nil => Sexp::Nil,
MapKey::Bool(b) => Sexp::Atom(Atom::Bool(*b)),
MapKey::Int(n) => Sexp::Atom(Atom::Int(*n)),
MapKey::Float(bits) => Sexp::Atom(Atom::Float(f64::from_bits(*bits))),
MapKey::Str(s) => Sexp::Atom(Atom::Str(s.to_string())),
MapKey::Symbol(s) => Sexp::Atom(Atom::Symbol(s.to_string())),
MapKey::Keyword(s) => Sexp::Atom(Atom::Keyword(s.to_string())),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn values_render_as_the_literal_that_produces_them() {
let list = Value::List(Arc::new(
vec![
Value::Int(1),
Value::Str(Arc::from("a")),
Value::Keyword(Arc::from("k")),
]
.into(),
));
assert_eq!(render_value(&list), r#"[1, "a", :k]"#);
assert_eq!(render_value(&Value::Nil), "nil");
assert_eq!(
render_value(&Value::List(Arc::new(Vec::new().into()))),
"[]"
);
}
}