Skip to main content

conjure_cp_core/ast/
reference.rs

1use crate::ast::serde::{AsId, HasId};
2use crate::{ast::DeclarationPtr, bug};
3use derivative::Derivative;
4use parking_lot::MappedRwLockReadGuard;
5use serde::{Deserialize, Serialize};
6use serde_with::serde_as;
7use std::fmt::{Display, Formatter};
8use uniplate::Uniplate;
9
10use super::{
11    Atom, DeclarationKind, DomainPtr, Expression, GroundDomain, Literal, Metadata, Moo, Name,
12    categories::{Category, CategoryOf},
13    domains::HasDomain,
14};
15
16/// A reference to a declaration (variable, parameter, etc.)
17///
18/// This is a thin wrapper around [`DeclarationPtr`] with two main purposes:
19/// 1. Encapsulate the serde pragmas (e.g., serializing as IDs rather than full objects)
20/// 2. Enable type-directed traversals of references via uniplate
21#[serde_as]
22#[derive(
23    Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Uniplate, Derivative,
24)]
25#[derivative(Hash)]
26#[uniplate()]
27#[biplate(to=DeclarationPtr)]
28#[biplate(to=Name)]
29pub struct Reference {
30    #[serde_as(as = "AsId")]
31    pub ptr: DeclarationPtr,
32}
33
34impl Reference {
35    pub fn new(ptr: DeclarationPtr) -> Self {
36        Reference { ptr }
37    }
38
39    pub fn ptr(&self) -> &DeclarationPtr {
40        &self.ptr
41    }
42
43    pub fn into_ptr(self) -> DeclarationPtr {
44        self.ptr
45    }
46
47    pub fn name(&self) -> MappedRwLockReadGuard<'_, Name> {
48        self.ptr.name()
49    }
50
51    pub fn id(&self) -> crate::ast::serde::ObjId {
52        self.ptr.id()
53    }
54
55    pub fn domain(&self) -> Option<DomainPtr> {
56        self.ptr.domain()
57    }
58
59    pub fn resolved_domain(&self) -> Option<Moo<GroundDomain>> {
60        self.domain()?.resolve()
61    }
62
63    /// Returns the expression behind a value-letting reference, if this is one.
64    pub fn resolve_expression(&self) -> Option<Expression> {
65        if let Some(expr) = self.ptr().as_value_letting() {
66            return Some(expr.clone());
67        }
68
69        let generator = {
70            let kind = self.ptr.kind();
71            if let DeclarationKind::Quantified(inner) = &*kind {
72                inner.generator().cloned()
73            } else {
74                None
75            }
76        };
77
78        if let Some(generator) = generator
79            && let Some(expr) = generator.as_value_letting()
80        {
81            return Some(expr.clone());
82        }
83
84        None
85    }
86
87    /// Evaluates this reference to a literal if it resolves to a constant.
88    pub fn resolve_constant(&self) -> Option<Literal> {
89        self.resolve_expression()
90            .and_then(|expr| super::eval::eval_constant(&expr))
91    }
92
93    /// Resolves this reference to an atomic expression, if possible.
94    pub fn resolve_atomic(&self) -> Option<Atom> {
95        self.resolve_expression().and_then(|expr| match expr {
96            Expression::Atomic(_, atom) => Some(atom),
97            _ => None,
98        })
99    }
100}
101
102impl From<Reference> for Expression {
103    fn from(value: Reference) -> Self {
104        Expression::Atomic(Metadata::new(), value.into())
105    }
106}
107
108impl From<DeclarationPtr> for Reference {
109    fn from(ptr: DeclarationPtr) -> Self {
110        Reference::new(ptr)
111    }
112}
113
114impl CategoryOf for Reference {
115    fn category_of(&self) -> Category {
116        self.ptr.category_of()
117    }
118}
119
120impl HasDomain for Reference {
121    fn domain_of(&self) -> DomainPtr {
122        self.ptr.domain().unwrap_or_else(|| {
123            bug!(
124                "reference ({name}) should have a domain",
125                name = self.ptr.name()
126            )
127        })
128    }
129}
130
131impl Display for Reference {
132    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
133        self.ptr.name().fmt(f)
134    }
135}