use crate::ast::*;
use core::fmt;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::sync::Arc;
use itertools::Either;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use thiserror::Error;
#[derive(Debug, Clone, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(into = "Expr")]
#[serde(try_from = "Expr")]
pub enum Value {
Lit(Literal),
Set(Set),
Record(Arc<BTreeMap<SmolStr, Value>>),
ExtensionValue(Arc<ExtensionValueWithArgs>),
}
#[derive(Debug, Error)]
pub enum NotValue {
#[error("Not A Value")]
NotValue,
}
impl TryFrom<Expr> for Value {
type Error = NotValue;
fn try_from(value: Expr) -> Result<Self, Self::Error> {
match value.into_expr_kind() {
ExprKind::Lit(l) => Ok(Value::Lit(l)),
ExprKind::Unknown { .. } => Err(NotValue::NotValue),
ExprKind::Var(_) => Err(NotValue::NotValue),
ExprKind::Slot(_) => Err(NotValue::NotValue),
ExprKind::If { .. } => Err(NotValue::NotValue),
ExprKind::And { .. } => Err(NotValue::NotValue),
ExprKind::Or { .. } => Err(NotValue::NotValue),
ExprKind::UnaryApp { .. } => Err(NotValue::NotValue),
ExprKind::BinaryApp { .. } => Err(NotValue::NotValue),
ExprKind::ExtensionFunctionApp { .. } => Err(NotValue::NotValue),
ExprKind::GetAttr { .. } => Err(NotValue::NotValue),
ExprKind::HasAttr { .. } => Err(NotValue::NotValue),
ExprKind::Like { .. } => Err(NotValue::NotValue),
ExprKind::Set(members) => members
.iter()
.map(|e| e.clone().try_into())
.collect::<Result<Set, _>>()
.map(Value::Set),
ExprKind::Record { pairs } => pairs
.iter()
.map(|(k, v)| v.clone().try_into().map(|v: Value| (k.clone(), v)))
.collect::<Result<BTreeMap<SmolStr, Value>, _>>()
.map(|m| Value::Record(Arc::new(m))),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PartialValue {
Value(Value),
Residual(Expr),
}
impl<V: Into<Value>> From<V> for PartialValue {
fn from(into_v: V) -> Self {
PartialValue::Value(into_v.into())
}
}
impl From<Expr> for PartialValue {
fn from(e: Expr) -> Self {
debug_assert!(e.is_unknown());
PartialValue::Residual(e)
}
}
impl From<PartialValue> for Expr {
fn from(val: PartialValue) -> Self {
match val {
PartialValue::Value(v) => v.into(),
PartialValue::Residual(e) => e,
}
}
}
impl TryFrom<PartialValue> for Value {
type Error = NotValue;
fn try_from(value: PartialValue) -> Result<Self, Self::Error> {
match value {
PartialValue::Value(v) => Ok(v),
PartialValue::Residual(e) => e.try_into(),
}
}
}
impl fmt::Display for PartialValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PartialValue::Value(v) => write!(f, "{v}"),
PartialValue::Residual(r) => write!(f, "{r}"),
}
}
}
pub fn split<I>(i: I) -> Either<impl Iterator<Item = Value>, impl Iterator<Item = Expr>>
where
I: IntoIterator<Item = PartialValue>,
{
let mut values = vec![];
let mut residuals = vec![];
for item in i.into_iter() {
match item {
PartialValue::Value(a) => {
if residuals.is_empty() {
values.push(a)
} else {
residuals.push(a.into())
}
}
PartialValue::Residual(r) => {
residuals.push(r);
}
}
}
if residuals.is_empty() {
Either::Left(values.into_iter())
} else {
let mut exprs: Vec<Expr> = values.into_iter().map(|x| x.into()).collect();
exprs.append(&mut residuals);
Either::Right(exprs.into_iter())
}
}
#[derive(Debug, Clone)]
pub struct Set {
pub authoritative: Arc<BTreeSet<Value>>,
pub fast: Option<Arc<HashSet<Literal>>>,
}
impl Set {
pub fn len(&self) -> usize {
self.authoritative.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> impl Iterator<Item = &Value> {
self.authoritative.iter()
}
}
impl FromIterator<Value> for Set {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
let (literals, non_literals): (BTreeSet<_>, BTreeSet<_>) =
iter.into_iter().partition(|v| matches!(v, Value::Lit(_)));
if non_literals.is_empty() {
Self {
authoritative: Arc::new(literals.clone()), fast: Some(Arc::new(
literals
.into_iter()
.map(|v| match v {
Value::Lit(l) => l,
#[allow(clippy::unreachable)]
_ => unreachable!(),
})
.collect(),
)),
}
} else {
let mut all_items = non_literals;
let mut literals = literals;
all_items.append(&mut literals);
Self {
authoritative: Arc::new(all_items),
fast: None,
}
}
}
}
impl Value {
pub(crate) fn try_as_lit(&self) -> Option<&Literal> {
match self {
Self::Lit(lit) => Some(lit),
_ => None,
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Value) -> bool {
match (self, other) {
(Value::Lit(l1), Value::Lit(l2)) => l1 == l2,
(
Value::Set(Set {
fast: Some(rc1), ..
}),
Value::Set(Set {
fast: Some(rc2), ..
}),
) => rc1 == rc2,
(Value::Set(Set { fast: Some(_), .. }), Value::Set(Set { fast: None, .. })) => false, (Value::Set(Set { fast: None, .. }), Value::Set(Set { fast: Some(_), .. })) => false, (
Value::Set(Set {
authoritative: a1, ..
}),
Value::Set(Set {
authoritative: a2, ..
}),
) => a1 == a2,
(Value::Record(r1), Value::Record(r2)) => r1 == r2,
(Value::ExtensionValue(ev1), Value::ExtensionValue(ev2)) => ev1 == ev2,
(_, _) => false, }
}
}
impl Eq for Value {}
impl PartialEq for Set {
fn eq(&self, other: &Self) -> bool {
self.authoritative.as_ref() == other.authoritative.as_ref()
}
}
impl Eq for Set {}
impl PartialOrd<Set> for Set {
fn partial_cmp(&self, other: &Set) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Set {
fn cmp(&self, other: &Set) -> std::cmp::Ordering {
self.authoritative
.as_ref()
.cmp(other.authoritative.as_ref())
}
}
impl StaticallyTyped for Value {
fn type_of(&self) -> Type {
match self {
Self::Lit(lit) => lit.type_of(),
Self::Set(_) => Type::Set,
Self::Record(_) => Type::Record,
Self::ExtensionValue(ev) => ev.type_of(),
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Lit(lit) => write!(f, "{}", lit),
Self::Set(Set {
fast,
authoritative,
}) => {
let len = fast
.as_ref()
.map(|set| set.len())
.unwrap_or_else(|| authoritative.len());
match len {
0 => write!(f, "[]"),
1..=5 => {
write!(f, "[")?;
if let Some(rc) = fast {
for item in rc.as_ref() {
write!(f, "{item}, ")?;
}
} else {
for item in authoritative.as_ref() {
write!(f, "{item}, ")?;
}
}
write!(f, "]")?;
Ok(())
}
n => write!(f, "<set with {} elements>", n),
}
}
Self::Record(record) => write!(f, "<first-class record with {} fields>", record.len()),
Self::ExtensionValue(ev) => write!(f, "{}", ev),
}
}
}
impl<T: Into<Value>> From<Vec<T>> for Value {
fn from(v: Vec<T>) -> Self {
Self::set(v.into_iter().map(Into::into))
}
}
impl<S> From<BTreeMap<S, Value>> for Value
where
S: Into<SmolStr>,
{
fn from(map: BTreeMap<S, Value>) -> Self {
Self::Record(Arc::new(
map.into_iter().map(|(k, v)| (k.into(), v)).collect(),
))
}
}
impl<S> From<HashMap<S, Value>> for Value
where
S: Into<SmolStr>,
{
fn from(map: HashMap<S, Value>) -> Self {
Self::Record(Arc::new(
map.into_iter().map(|(k, v)| (k.into(), v)).collect(),
))
}
}
impl From<Vec<(SmolStr, Value)>> for Value {
fn from(v: Vec<(SmolStr, Value)>) -> Self {
Self::Record(Arc::new(v.into_iter().collect()))
}
}
impl<T: Into<Literal>> From<T> for Value {
fn from(lit: T) -> Self {
Self::Lit(lit.into())
}
}
impl Value {
pub fn empty_set() -> Self {
Self::Set(Set {
authoritative: Arc::new(BTreeSet::new()),
fast: Some(Arc::new(HashSet::new())),
})
}
pub fn empty_record() -> Self {
Self::Record(Arc::new(BTreeMap::new()))
}
pub fn set(vals: impl IntoIterator<Item = Value>) -> Self {
let authoritative: BTreeSet<Value> = vals.into_iter().collect();
let fast: Option<HashSet<Literal>> = authoritative
.iter()
.map(|v| v.try_as_lit().cloned())
.collect();
if let Some(fast) = fast {
Self::Set(Set {
authoritative: Arc::new(authoritative),
fast: Some(Arc::new(fast)),
})
} else {
Self::Set(Set {
authoritative: Arc::new(authoritative),
fast: None,
})
}
}
pub fn set_of_lits(lits: impl IntoIterator<Item = Literal>) -> Self {
let fast: HashSet<Literal> = lits.into_iter().collect();
let authoritative: BTreeSet<Value> =
fast.iter().map(|lit| Value::Lit(lit.clone())).collect();
Self::Set(Set {
authoritative: Arc::new(authoritative),
fast: Some(Arc::new(fast)),
})
}
}
#[allow(clippy::panic)]
#[cfg(test)]
mod test {
use super::*;
#[test]
fn values() {
assert_eq!(Value::from(true), Value::Lit(Literal::Bool(true)));
assert_eq!(Value::from(false), Value::Lit(Literal::Bool(false)));
assert_eq!(Value::from(23), Value::Lit(Literal::Long(23)));
assert_eq!(Value::from(-47), Value::Lit(Literal::Long(-47)));
assert_eq!(
Value::from("hello"),
Value::Lit(Literal::String("hello".into()))
);
assert_eq!(
Value::from("hello".to_owned()),
Value::Lit(Literal::String("hello".into()))
);
assert_eq!(
Value::from(String::new()),
Value::Lit(Literal::String(SmolStr::default()))
);
assert_eq!(
Value::from(""),
Value::Lit(Literal::String(SmolStr::default()))
);
assert_eq!(
Value::from(vec![2, -3, 40]),
Value::set(vec![Value::from(2), Value::from(-3), Value::from(40)])
);
assert_eq!(
Value::from(vec![Literal::from(false), Literal::from("eggs")]),
Value::set(vec!(Value::from(false), Value::from("eggs")))
);
assert_eq!(
Value::set(vec!(Value::from(false), Value::from("eggs"))),
Value::set_of_lits(vec!(Literal::from(false), Literal::from("eggs")))
);
let mut rec1: BTreeMap<SmolStr, Value> = BTreeMap::new();
rec1.insert("ham".into(), 3.into());
rec1.insert("eggs".into(), "hickory".into());
assert_eq!(Value::from(rec1.clone()), Value::Record(Arc::new(rec1)));
let mut rec2: BTreeMap<SmolStr, Value> = BTreeMap::new();
rec2.insert("hi".into(), "ham".into());
rec2.insert("eggs".into(), "hickory".into());
assert_eq!(
Value::from(vec![
("hi".into(), "ham".into()),
("eggs".into(), "hickory".into())
]),
Value::Record(Arc::new(rec2))
);
assert_eq!(
Value::from(EntityUID::with_eid("foo")),
Value::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid("foo"))))
);
}
#[test]
fn value_types() {
assert_eq!(Value::from(false).type_of(), Type::Bool);
assert_eq!(Value::from(23).type_of(), Type::Long);
assert_eq!(Value::from(-47).type_of(), Type::Long);
assert_eq!(Value::from("hello").type_of(), Type::String);
assert_eq!(Value::from(vec![2, -3, 40]).type_of(), Type::Set);
assert_eq!(Value::empty_set().type_of(), Type::Set);
assert_eq!(Value::empty_record().type_of(), Type::Record);
assert_eq!(
Value::from(vec![("hello".into(), Value::from("ham"))]).type_of(),
Type::Record
);
assert_eq!(
Value::from(EntityUID::with_eid("foo")).type_of(),
Type::entity_type(
Name::parse_unqualified_name("test_entity_type").expect("valid identifier")
)
);
}
#[test]
fn test_set_is_empty_for_empty_set() {
let set = Set {
authoritative: Arc::new(BTreeSet::new()),
fast: Some(Arc::new(HashSet::new())),
};
assert!(set.is_empty());
}
#[test]
fn test_set_is_not_empty_for_set_with_values() {
let set = Set {
authoritative: Arc::new(BTreeSet::from([Value::from("abc")])),
fast: None,
};
assert!(!set.is_empty());
}
#[test]
fn pretty_printer() {
assert_eq!(Value::from("abc").to_string(), r#""abc""#);
assert_eq!(Value::from("\t").to_string(), r#""\t""#);
assert_eq!(Value::from("🐈").to_string(), r#""🐈""#);
}
#[test]
fn set_collect() {
let v = vec![Value::Lit(1.into())];
let s: Set = v.into_iter().collect();
assert_eq!(s.len(), 1);
let v2 = vec![Value::Set(s)];
let s2: Set = v2.into_iter().collect();
assert_eq!(s2.len(), 1);
}
#[test]
fn split_values() {
let vs = [
PartialValue::Value(Value::Lit(1.into())),
PartialValue::Value(Value::Lit(2.into())),
];
match split(vs) {
Either::Left(vs) => assert_eq!(
vs.collect::<Vec<_>>(),
vec![Value::Lit(1.into()), Value::Lit(2.into())]
),
Either::Right(_) => panic!("Got residuals"),
}
}
#[test]
fn split_residuals() {
let rs = [
PartialValue::Value(Value::Lit(1.into())),
PartialValue::Residual(Expr::val(2)),
PartialValue::Value(Value::Lit(3.into())),
PartialValue::Residual(Expr::val(4)),
];
let expected = vec![Expr::val(1), Expr::val(2), Expr::val(3), Expr::val(4)];
match split(rs) {
Either::Left(_) => panic!("Got values"),
Either::Right(rs) => assert_eq!(rs.collect::<Vec<_>>(), expected),
}
}
#[test]
fn split_residuals2() {
let rs = [
PartialValue::Value(Value::Lit(1.into())),
PartialValue::Value(Value::Lit(2.into())),
PartialValue::Residual(Expr::val(3)),
PartialValue::Residual(Expr::val(4)),
];
let expected = vec![Expr::val(1), Expr::val(2), Expr::val(3), Expr::val(4)];
match split(rs) {
Either::Left(_) => panic!("Got values"),
Either::Right(rs) => assert_eq!(rs.collect::<Vec<_>>(), expected),
}
}
#[test]
fn split_residuals3() {
let rs = [
PartialValue::Residual(Expr::val(1)),
PartialValue::Residual(Expr::val(2)),
PartialValue::Value(Value::Lit(3.into())),
PartialValue::Value(Value::Lit(4.into())),
];
let expected = vec![Expr::val(1), Expr::val(2), Expr::val(3), Expr::val(4)];
match split(rs) {
Either::Left(_) => panic!("Got values"),
Either::Right(rs) => assert_eq!(rs.collect::<Vec<_>>(), expected),
}
}
}