use itertools::Itertools;
use smtlib_lowlevel::{
ast::{self, Identifier},
lexicon::{Numeral, Symbol},
Storage,
};
use crate::terms::{self, qual_ident, STerm};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Sort<'st> {
Static(ast::Sort<'st>),
Dynamic {
st: &'st Storage,
name: &'st str,
index: &'st [Index<'st>],
parameters: &'st [Sort<'st>],
},
}
impl std::fmt::Display for Sort<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.ast().fmt(f)
}
}
impl<'st> From<ast::Sort<'st>> for Sort<'st> {
fn from(sort: ast::Sort<'st>) -> Self {
Sort::Static(sort)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Index<'st> {
Numeral(usize),
Symbol(&'st str),
}
impl<'st> Index<'st> {
fn ast(&self) -> ast::Index<'st> {
match self {
Index::Numeral(n) => ast::Index::Numeral(Numeral::from_usize(*n)),
Index::Symbol(s) => ast::Index::Symbol(Symbol(s)),
}
}
}
pub(crate) fn is_built_in_sort(name: &str) -> bool {
matches!(name, "Int" | "Bool" | "Array" | "BitVec")
}
impl<'st> Sort<'st> {
pub fn new(st: &'st Storage, name: impl Into<String>) -> Self {
let mut name = name.into();
if !is_built_in_sort(&name) {
name += "_xxx";
}
let name = st.alloc_str(&name);
Self::Dynamic {
st,
name,
index: &[],
parameters: &[],
}
}
pub fn new_parametric(
st: &'st Storage,
name: impl Into<String>,
parameters: &[Sort<'st>],
) -> Self {
Self::Dynamic {
st,
name: st.alloc_str(&name.into()),
index: &[],
parameters: st.alloc_slice(parameters),
}
}
pub fn new_indexed(st: &'st Storage, name: impl Into<String>, index: &[Index<'st>]) -> Self {
Self::Dynamic {
st,
name: st.alloc_str(&name.into()),
index: st.alloc_slice(index),
parameters: &[],
}
}
pub fn ast(self) -> ast::Sort<'st> {
match self {
Sort::Static(sort) => sort,
Sort::Dynamic {
st,
name,
index,
parameters,
} => {
let ident = if index.is_empty() {
Identifier::Simple(Symbol(name))
} else {
Identifier::Indexed(
Symbol(name),
st.alloc_slice(&index.iter().map(|idx| idx.ast()).collect_vec()),
)
};
if parameters.is_empty() {
ast::Sort::Sort(ident)
} else {
ast::Sort::Parametric(
ident,
st.alloc_slice(¶meters.iter().map(|param| param.ast()).collect_vec()),
)
}
}
}
}
pub fn new_const(
&self,
st: &'st Storage,
name: impl Into<String>,
) -> terms::Const<'st, terms::Dynamic<'st>> {
let name = st.alloc_str(&name.into());
terms::Const(
name,
terms::Dynamic::from_term_sort(
STerm::new(
st,
ast::Term::Identifier(qual_ident(name, Some(self.ast()))),
),
*self,
),
)
}
}