use crate::*;
use formally::support::*;
use derive_more::From;
use transitive::Transitive;
pub use rug::{Integer, Rational};
use std::fmt::Formatter;
use std::{
fmt::Display,
hash::{Hash, Hasher},
sync::{Arc, Mutex},
};
#[derive(Debug, Clone, Hash, PartialEq, Eq, Located, Locatable)]
#[non_exhaustive]
pub enum Constant {
Integer {
value: Arc<Integer>,
span: Option<Span>,
},
Rational {
value: Arc<Rational>,
span: Option<Span>,
},
}
impl From<Integer> for Constant {
fn from(value: Integer) -> Self {
Constant::Integer {
value: Arc::new(value),
span: None,
}
}
}
impl From<Rational> for Constant {
fn from(value: Rational) -> Self {
Constant::Rational {
value: Arc::new(value),
span: None,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Located, Locatable)]
pub struct BoundRef {
pub function: Function,
pub span: Option<Span>,
}
impl From<Function> for BoundRef {
fn from(function: Function) -> Self {
BoundRef {
function,
span: None,
}
}
}
impl Display for BoundRef {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.function.name())
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, From, Located, Locatable, Transitive)]
#[allow(clippy::duplicated_attributes)]
#[transitive(from(Function, BoundRef))]
#[transitive(from(Variable, Function))]
#[transitive(from(Primitive, Function))]
#[transitive(from(UserFunction, Function))]
#[transitive(from(Declared, UserFunction))]
#[transitive(from(Defined, UserFunction))]
pub enum FunctionRef {
Bound(BoundRef),
Unbound(Identifier<'static>),
}
impl Display for FunctionRef {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FunctionRef::Bound(bound) => bound.fmt(f),
FunctionRef::Unbound(unbound) => unbound.fmt(f),
}
}
}
impl FunctionRef {
pub fn name(&self) -> &Identifier<'static> {
match self {
FunctionRef::Bound(bound) => bound.function.name(),
FunctionRef::Unbound(unbound) => unbound,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Located, Locatable, Transitive)]
#[allow(clippy::duplicated_attributes)]
#[transitive(from(Identifier<'static>, FunctionRef))]
#[transitive(from(BoundRef, FunctionRef))]
#[transitive(from(Function, BoundRef))]
#[transitive(from(Variable, Function))]
#[transitive(from(Primitive, Function))]
#[transitive(from(UserFunction, Function))]
#[transitive(from(Declared, UserFunction))]
#[transitive(from(Defined, UserFunction))]
pub struct Atom {
pub head: FunctionRef,
pub arguments: Arc<[Term]>,
pub span: Option<Span>,
}
impl From<FunctionRef> for Atom {
fn from(head: FunctionRef) -> Self {
Atom {
head,
arguments: Arc::default(),
span: None,
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum Quantifier {
Forall,
Exists,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Located, Locatable)]
pub struct Quantified {
pub quantifier: Quantifier,
pub variables: Arc<[Variable]>,
pub body: Term,
pub span: Option<Span>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Located, Locatable)]
pub struct Binding<V: ToSort = Sort, T: ToTerm = Term> {
pub variable: Variable<V>,
pub def: T,
pub span: Option<Span>,
}
impl<T: TypeCheck + ToTerm> Binding<Infer, T> {
pub fn new(name: Identifier<'_>, def: T) -> Binding<Infer, T> {
let namespan = name.span();
Binding {
variable: Variable::new(name, Infer).over(namespan),
def,
span: None,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Located, Locatable)]
pub struct Let {
pub bindings: Arc<[Binding]>,
pub body: Term,
pub span: Option<Span>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, From, Located, Locatable)]
#[allow(clippy::duplicated_attributes)]
#[non_exhaustive]
pub enum TermKind {
Constant(Constant),
#[from(skip)]
Atom(Atom),
Quantified(Quantified),
Let(Let),
}
impl<T: Into<Atom>> From<T> for TermKind {
fn from(atom: T) -> Self {
TermKind::Atom(atom.into())
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Term(pub(crate) Nominal<Arc<TermInner>>);
#[derive(Debug)]
pub(crate) struct TermInner {
pub(crate) kind: TermKind,
pub(crate) sort: Mutex<Option<Sort>>,
pub(crate) resolved: bool,
}
impl TermInner {
pub(crate) fn new(kind: TermKind) -> TermInner {
let resolved = match &kind {
TermKind::Constant(_) => true,
TermKind::Atom(atom) => {
matches!(&atom.head, FunctionRef::Bound(_))
&& atom.arguments.iter().all(Term::is_resolved)
}
TermKind::Quantified(quant) => quant.body.is_resolved(),
TermKind::Let(let_) => let_.body.is_resolved(),
};
TermInner {
kind,
sort: Mutex::default(),
resolved,
}
}
}
impl Term {
pub fn is_resolved(&self) -> bool {
self.0.resolved
}
}
impl Hash for TermInner {
fn hash<H: Hasher>(&self, state: &mut H) {
self.kind.hash(state)
}
}
impl PartialEq for TermInner {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind
}
}
impl Eq for TermInner {}
impl Located for Term {
fn span(&self) -> Option<Span> {
self.0.kind.span()
}
}
impl From<bool> for TermKind {
fn from(value: bool) -> Self {
if value {
TermKind::from(theories::Core::True())
} else {
TermKind::from(theories::Core::False())
}
}
}
impl Term {
pub fn kind(&self) -> &TermKind {
&self.0.kind
}
}
impl PartialEq<bool> for Term {
fn eq(&self, other: &bool) -> bool {
if let TermKind::Atom(atom) = self.kind()
&& let Atom { head, .. } = atom
&& let FunctionRef::Bound(bound) = head
&& let BoundRef { function, .. } = bound
&& let Function::Primitive(prim) = function
{
(*other && *prim == theories::Core::True())
|| (!*other && *prim == theories::Core::False())
} else {
false
}
}
}
impl PartialEq<Integer> for Term {
fn eq(&self, other: &Integer) -> bool {
if let TermKind::Constant(cnst) = self.kind()
&& let Constant::Integer { value, .. } = cnst
{
**value == *other
} else {
false
}
}
}
impl PartialEq<Rational> for Term {
fn eq(&self, other: &Rational) -> bool {
if let TermKind::Constant(cnst) = self.kind()
&& let Constant::Rational { value, .. } = cnst
{
**value == *other
} else {
false
}
}
}