use smol_str::SmolStr;
use super::bitvec::BitVec;
use super::ext::Ext;
use super::op::Op;
use super::term_type::TermType;
use super::type_abbrevs::*;
use std::{
collections::{BTreeMap, BTreeSet},
sync::Arc,
};
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub struct TermVar {
pub id: SmolStr,
pub ty: TermType,
}
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
#[allow(missing_docs)]
pub enum TermPrim {
Bool(bool),
Bitvec(BitVec),
String(SmolStr),
Entity(EntityUID),
Ext(Ext),
}
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
#[allow(missing_docs)]
pub enum Term {
Prim(TermPrim),
Var(TermVar),
None(TermType),
Some(Arc<Term>),
Set {
elts: Arc<BTreeSet<Term>>,
elts_ty: TermType,
},
Record(Arc<BTreeMap<Attr, Term>>),
App {
op: Op,
args: Arc<Vec<Term>>,
ret_ty: TermType,
},
}
impl From<bool> for Term {
fn from(b: bool) -> Self {
if b {
Term::Prim(TermPrim::Bool(true))
} else {
Term::Prim(TermPrim::Bool(false))
}
}
}
impl From<i64> for Term {
fn from(i: i64) -> Self {
#[allow(
clippy::expect_used,
reason = "Cannot panic because bitwidth passed in is non-zero."
)]
Term::Prim(TermPrim::Bitvec(
BitVec::of_int(64, i.into())
.expect("Cannot panic because bitwidth passed in is non-zero."),
))
}
}
impl From<BitVec> for Term {
fn from(bv: BitVec) -> Self {
Term::Prim(TermPrim::Bitvec(bv))
}
}
impl From<SmolStr> for Term {
fn from(s: SmolStr) -> Self {
Term::Prim(TermPrim::String(s))
}
}
impl From<EntityUID> for Term {
fn from(uid: EntityUID) -> Self {
Term::Prim(TermPrim::Entity(uid))
}
}
impl From<Ext> for Term {
fn from(ext: Ext) -> Self {
Term::Prim(TermPrim::Ext(ext))
}
}
impl From<TermVar> for Term {
fn from(v: TermVar) -> Self {
Term::Var(v)
}
}
impl TermPrim {
pub fn type_of(&self) -> TermType {
match self {
TermPrim::Bool(_) => TermType::Bool,
TermPrim::Bitvec(v) => TermType::Bitvec { n: v.width() },
TermPrim::String(_) => TermType::String,
TermPrim::Entity(e) => TermType::Entity {
ety: e.type_name().clone(),
},
TermPrim::Ext(Ext::Decimal { .. }) => TermType::Ext {
xty: ExtType::Decimal,
},
TermPrim::Ext(Ext::Ipaddr { .. }) => TermType::Ext {
xty: ExtType::IpAddr,
},
TermPrim::Ext(Ext::Duration { .. }) => TermType::Ext {
xty: ExtType::Duration,
},
TermPrim::Ext(Ext::Datetime { .. }) => TermType::Ext {
xty: ExtType::DateTime,
},
}
}
}
impl Term {
pub fn type_of(&self) -> TermType {
match self {
Term::Prim(l) => l.type_of(),
Term::Var(v) => v.ty.clone(),
Term::None(ty) => TermType::Option {
ty: Arc::new(ty.clone()),
},
Term::Some(t) => TermType::Option {
ty: Arc::new(t.type_of()),
},
Term::Set { elts_ty, .. } => TermType::Set {
ty: Arc::new(elts_ty.clone()),
},
Term::Record(m) => {
let rty = Arc::new(m.iter().map(|(k, v)| (k.clone(), v.type_of())).collect());
TermType::Record { rty }
}
Term::App { ret_ty, .. } => ret_ty.clone(),
}
}
pub fn is_literal(&self) -> bool {
match self {
Term::Prim(_) => true,
Term::None(_) => true,
Term::Some(t) => t.is_literal(),
Term::Set { elts, .. } => elts.iter().all(Term::is_literal),
Term::Record(m) => m.values().all(Term::is_literal),
_ => false,
}
}
}