use crate::{
Command, CoreOp, Ctx, FunctionDec, IConst, ICoreOp, IIndex, ILet, IMatch, IOp, IQuantifier,
ISort, ISymbol, IVar, Let, Logic, Quantifier, Sorted, Term, Void, IUF, UF,
};
pub use amzn_smt_ir_derive::Fold;
mod compose;
pub use compose::{Compose, TryComposed};
mod intra;
pub use intra::IntraLogicFolder;
mod inter;
pub use inter::InterLogicFolder;
use smallvec::SmallVec;
use smt2parser::Numeral;
pub trait Folder<T: Logic, M = ()>: Compose {
type Output;
type Error;
fn context(&self) -> Option<&Ctx> {
None
}
fn context_mut(&mut self) -> Option<&mut Ctx> {
debug_assert!(
self.context().is_none(),
"context() and context_mut() should be implemented together"
);
None
}
fn fold_term(&mut self, term: Term<T>) -> Result<Self::Output, Self::Error> {
term.super_fold_with(self)
}
fn fold_const(&mut self, constant: IConst) -> Result<Self::Output, Self::Error>;
fn fold_var(&mut self, var: IVar<T::Var>) -> Result<Self::Output, Self::Error>;
fn fold_core_op(&mut self, op: ICoreOp<T>) -> Result<Self::Output, Self::Error>;
fn fold_theory_op(&mut self, op: IOp<T>) -> Result<Self::Output, Self::Error>;
fn fold_uninterpreted_func(&mut self, func: IUF<T>) -> Result<Self::Output, Self::Error>;
fn fold_let(&mut self, l: ILet<T>) -> Result<Self::Output, Self::Error>;
fn fold_match(&mut self, m: IMatch<T>) -> Result<Self::Output, Self::Error>;
fn fold_quantifier(&mut self, quantifier: IQuantifier<T>) -> Result<Self::Output, Self::Error>;
fn fold_set_logic(&mut self, logic: ISymbol) -> Result<Command<Self::Output>, Self::Error> {
Ok(Command::SetLogic { symbol: logic })
}
fn fold_assert(&mut self, asserted: Term<T>) -> Result<Command<Self::Output>, Self::Error> {
self.fold_term(asserted)
.map(|term| Command::Assert { term })
}
fn fold_declare_const(
&mut self,
symbol: ISymbol,
sort: ISort,
) -> Result<Command<Self::Output>, Self::Error> {
Ok(Command::DeclareConst { symbol, sort })
}
fn fold_declare_fun(
&mut self,
symbol: ISymbol,
parameters: Vec<ISort>,
sort: ISort,
) -> Result<Command<Self::Output>, Self::Error> {
Ok(Command::DeclareFun {
symbol,
parameters,
sort,
})
}
fn fold_declare_sort(
&mut self,
symbol: ISymbol,
arity: Numeral,
) -> Result<Command<Self::Output>, Self::Error> {
Ok(Command::DeclareSort { symbol, arity })
}
fn fold_define_fun(
&mut self,
sig: FunctionDec,
body: Term<T>,
) -> Result<Command<Self::Output>, Self::Error> {
let term = body.fold_with(self)?;
Ok(Command::DefineFun { sig, term })
}
fn fold_define_fun_rec(
&mut self,
sig: FunctionDec,
body: Term<T>,
) -> Result<Command<Self::Output>, Self::Error> {
let term = body.fold_with(self)?;
Ok(Command::DefineFunRec { sig, term })
}
fn fold_define_funs_rec(
&mut self,
funs: Vec<(FunctionDec, Term<T>)>,
) -> Result<Command<Self::Output>, Self::Error> {
let funs = funs
.into_iter()
.map(|(sig, body)| Ok((sig, body.fold_with(self)?)))
.collect::<Result<_, _>>()?;
Ok(Command::DefineFunsRec { funs })
}
fn fold_define_sort(
&mut self,
symbol: ISymbol,
parameters: Vec<ISymbol>,
sort: ISort,
) -> Result<Command<Self::Output>, Self::Error> {
Ok(Command::DefineSort {
symbol,
parameters,
sort,
})
}
fn fold_get_value(
&mut self,
terms: Vec<Term<T>>,
) -> Result<Command<Self::Output>, Self::Error> {
let terms = terms.fold_with(self)?;
Ok(Command::GetValue { terms })
}
}
pub trait Fold<T: Logic, Out> {
type Output;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>;
}
pub trait SuperFold<T: Logic, Out> {
type Output;
fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>;
}
impl<T: Logic, Out> Fold<T, Out> for Term<T> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
folder.fold_term(self)
}
}
impl<T: Logic, Out> Fold<T, Out> for &Term<T> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
folder.fold_term(self.clone())
}
}
impl<T: Logic, Out> SuperFold<T, Out> for Term<T> {
type Output = Out;
fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
match self {
Self::Constant(c) => c.fold_with(folder),
Self::Variable(v) => v.fold_with(folder),
Self::CoreOp(op) => op.fold_with(folder),
Self::OtherOp(op) => op.fold_with(folder),
Self::UF(uf) => uf.fold_with(folder),
Self::Let(l) => l.fold_with(folder),
Self::Match(m) => m.fold_with(folder),
Self::Quantifier(q) => q.fold_with(folder),
}
}
}
impl<T: Logic, Out> SuperFold<T, Out> for &Term<T> {
type Output = Out;
fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
self.clone().super_fold_with(folder)
}
}
impl<T: Logic, Out> Fold<T, Out> for IConst {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
folder.fold_const(self)
}
}
impl<T: Logic, Out> Fold<T, Out> for IVar<T::Var> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
folder.fold_var(self)
}
}
impl<T: Logic, Out> Fold<T, Out> for ICoreOp<T> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
folder.fold_core_op(self)
}
}
impl<T: Logic, Inner: Fold<T, Out>, Out> SuperFold<T, Out> for CoreOp<Inner> {
type Output = CoreOp<Inner::Output>;
fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
use CoreOp::*;
const STACK_RED_ZONE: usize = 32 * 1024;
const NEW_STACK_SIZE: usize = 1024 * 1024;
let op = match self {
True => True,
False => False,
Not(t) => Not(t.fold_with(folder)?),
And(args) => And(stacker::maybe_grow(STACK_RED_ZONE, NEW_STACK_SIZE, || {
args.fold_with(folder)
})?),
Or(args) => Or(stacker::maybe_grow(STACK_RED_ZONE, NEW_STACK_SIZE, || {
args.fold_with(folder)
})?),
Xor(args) => Xor(args.fold_with(folder)?),
Imp(args) => Imp(args.fold_with(folder)?),
Eq(args) => Eq(args.fold_with(folder)?),
Distinct(args) => Distinct(args.fold_with(folder)?),
Ite(i, t, e) => Ite(
i.fold_with(folder)?,
t.fold_with(folder)?,
e.fold_with(folder)?,
),
};
Ok(op)
}
}
impl<L: Logic, Out> Fold<L, Out> for IOp<L> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<L, M, Output = Out>,
{
folder.fold_theory_op(self)
}
}
impl<T: Logic, Out> Fold<T, Out> for IUF<T> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
folder.fold_uninterpreted_func(self)
}
}
impl<T: Logic, Inner, Out> SuperFold<T, Out> for UF<Inner>
where
Inner: Fold<T, Out>,
{
type Output = UF<Inner::Output>;
fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
let (func, args) = (self.func.fold_with(folder)?, self.args.fold_with(folder)?);
Ok(UF { func, args })
}
}
impl<T: Logic, Out> Fold<T, Out> for IQuantifier<T> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
folder.fold_quantifier(self)
}
}
impl<T: Logic, Inner, Out> SuperFold<T, Out> for Quantifier<Inner>
where
Inner: Fold<T, Out>,
{
type Output = Quantifier<Inner::Output>;
fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
use Quantifier::*;
Ok(match self {
Forall(vars, t) => Forall(vars.fold_with(folder)?, t.fold_with(folder)?),
Exists(vars, t) => Exists(vars.fold_with(folder)?, t.fold_with(folder)?),
})
}
}
impl<T: Logic, Out> Fold<T, Out> for ILet<T> {
type Output = Out;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
let old_bindings = if let Some(ctx) = folder.context_mut() {
self.bindings
.iter()
.map(|(sym, t)| {
let sort = t.sort(ctx).ok();
let old_sort = ctx.local.bind_var(sym.clone(), sort);
(sym.clone(), old_sort)
})
.collect()
} else {
vec![]
};
let res = folder.fold_let(self);
if let Some(ctx) = folder.context_mut() {
for (sym, old_sort) in old_bindings {
match old_sort {
None => ctx.local.unbind_var(&sym),
Some(sort) => {
ctx.local.bind_var(sym, sort);
}
}
}
}
res
}
}
impl<T: Logic, Inner: Fold<T, Out>, Out> SuperFold<T, Out> for Let<Inner> {
type Output = Let<Inner::Output>;
fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
Ok(Let {
bindings: self.bindings.fold_with(folder)?,
term: self.term.fold_with(folder)?,
})
}
}
impl<T: Logic, Out> Fold<T, Out> for Void {
type Output = Out;
fn fold_with<F: Folder<T, M>, M>(self, _folder: &mut F) -> Result<Self::Output, F::Error> {
match self {}
}
}
impl<T: Logic, Out> SuperFold<T, Out> for Void {
type Output = Void;
fn super_fold_with<F: Folder<T, M>, M>(
self,
_folder: &mut F,
) -> Result<Self::Output, F::Error> {
match self {}
}
}
impl<T: Logic, Out> Fold<T, Out> for ISymbol {
type Output = Self;
fn fold_with<F, M>(self, _: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
Ok(self)
}
}
impl<T: Logic, Out> Fold<T, Out> for ISort {
type Output = Self;
fn fold_with<F, M>(self, _: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
Ok(self)
}
}
impl<T: Logic, Out, Inner: Fold<T, Out>> Fold<T, Out> for Vec<Inner> {
type Output = Vec<<Inner as Fold<T, Out>>::Output>;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
self.into_iter().map(|t| t.fold_with(folder)).collect()
}
}
impl<T: Logic, Out, Inner: Fold<T, Out>, const N: usize> Fold<T, Out> for SmallVec<[Inner; N]>
where
[Inner; N]: smallvec::Array<Item = Inner>,
[Inner::Output; N]: smallvec::Array<Item = Inner::Output>,
{
type Output = SmallVec<[Inner::Output; N]>;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
self.into_iter().map(|t| t.fold_with(folder)).collect()
}
}
impl<'a, T: Logic, Out, Inner, Output, const N: usize> Fold<T, Out> for &'a SmallVec<[Inner; N]>
where
&'a Inner: Fold<T, Out, Output = Output>,
[Inner; N]: smallvec::Array<Item = Inner>,
[Output; N]: smallvec::Array<Item = Output>,
{
type Output = SmallVec<[Output; N]>;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
self.iter().map(|t| t.fold_with(folder)).collect()
}
}
impl<T: Logic, Out, Inner: Fold<T, Out>> Fold<T, Out> for Box<[Inner]> {
type Output = Box<[<Inner as Fold<T, Out>>::Output]>;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
self.into_vec()
.into_iter()
.map(|t| t.fold_with(folder))
.collect()
}
}
impl<T: Logic, Out, const N: usize> Fold<T, Out> for [IIndex; N] {
type Output = Self;
fn fold_with<F, M>(self, _: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
Ok(self)
}
}
impl<T: Logic, Out, Inner, Output> Fold<T, Out> for &[Inner]
where
for<'a> &'a Inner: Fold<T, Out, Output = Output>,
{
type Output = Vec<Output>;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
self.iter().map(|t| t.fold_with(folder)).collect()
}
}
impl<T: Logic, Out, Inner, Output> Fold<T, Out> for &Vec<Inner>
where
for<'a> &'a Inner: Fold<T, Out, Output = Output>,
{
type Output = Vec<Output>;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
self.iter().map(|t| t.fold_with(folder)).collect()
}
}
macro_rules! impl_fold_tuple {
($($x:ident),*) => {
impl<T: Logic, Out, $($x),*> Fold<T, Out> for ($($x),*)
where
$($x: Fold<T, Out>),*
{
type Output = ($($x::Output),*);
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<T, M, Output = Out>,
{
#[allow(non_snake_case)]
let ($($x),*) = self;
Ok(($($x.fold_with(folder)?),*))
}
}
};
}
impl_fold_tuple!(A, B);
impl_fold_tuple!(A, B, C);
impl_fold_tuple!(A, B, C, D);
impl_fold_tuple!(A, B, C, D, E);
impl<L: Logic, Out> Fold<L, Out> for Command<Term<L>> {
type Output = Command<Out>;
fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
where
F: Folder<L, M, Output = Out>,
{
use smt2parser::concrete::Command::*;
if let Some(ctx) = folder.context_mut() {
ctx.process(&self);
}
let command = match self {
Assert { term } => folder.fold_assert(term)?,
CheckSat => CheckSat,
CheckSatAssuming { literals } => CheckSatAssuming { literals },
DeclareConst { symbol, sort } => folder.fold_declare_const(symbol, sort)?,
DeclareDatatype { symbol, datatype } => DeclareDatatype { symbol, datatype },
DeclareDatatypes { datatypes } => DeclareDatatypes { datatypes },
DeclareFun {
symbol,
parameters,
sort,
} => folder.fold_declare_fun(symbol, parameters, sort)?,
DeclareSort { symbol, arity } => folder.fold_declare_sort(symbol, arity)?,
DefineFun { sig, term } => folder.fold_define_fun(sig, term)?,
DefineFunRec { sig, term } => folder.fold_define_fun_rec(sig, term)?,
DefineFunsRec { funs } => folder.fold_define_funs_rec(funs)?,
DefineSort {
symbol,
parameters,
sort,
} => folder.fold_define_sort(symbol, parameters, sort)?,
Echo { message } => Echo { message },
Exit => Exit,
GetAssertions => GetAssertions,
GetAssignment => GetAssignment,
GetInfo { flag } => GetInfo { flag },
GetModel => GetModel,
GetOption { keyword } => GetOption { keyword },
GetProof => GetProof,
GetUnsatAssumptions => GetUnsatAssumptions,
GetUnsatCore => GetUnsatCore,
GetValue { terms } => folder.fold_get_value(terms)?,
Pop { level } => Pop { level },
Push { level } => Push { level },
Reset => Reset,
ResetAssertions => ResetAssertions,
SetInfo { keyword, value } => SetInfo { keyword, value },
SetLogic { symbol } => folder.fold_set_logic(symbol)?,
SetOption { keyword, value } => SetOption { keyword, value },
};
if let Some(ctx) = folder.context_mut() {
ctx.process(&command);
}
Ok(command)
}
}