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, 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        self.ptr().as_value_letting().map(|expr| expr.clone())
66    }
67
68    /// Evaluates this reference to a literal if it resolves to a constant.
69    pub fn resolve_constant(&self) -> Option<Literal> {
70        self.resolve_expression()
71            .and_then(|expr| super::eval::eval_constant(&expr))
72    }
73
74    /// Resolves this reference to an atomic expression, if possible.
75    pub fn resolve_atomic(&self) -> Option<Atom> {
76        self.resolve_expression().and_then(|expr| match expr {
77            Expression::Atomic(_, atom) => Some(atom),
78            _ => None,
79        })
80    }
81}
82
83impl From<Reference> for Expression {
84    fn from(value: Reference) -> Self {
85        Expression::Atomic(Metadata::new(), value.into())
86    }
87}
88
89impl From<DeclarationPtr> for Reference {
90    fn from(ptr: DeclarationPtr) -> Self {
91        Reference::new(ptr)
92    }
93}
94
95impl CategoryOf for Reference {
96    fn category_of(&self) -> Category {
97        self.ptr.category_of()
98    }
99}
100
101impl HasDomain for Reference {
102    fn domain_of(&self) -> DomainPtr {
103        self.ptr.domain().unwrap_or_else(|| {
104            bug!(
105                "reference ({name}) should have a domain",
106                name = self.ptr.name()
107            )
108        })
109    }
110}
111
112impl Display for Reference {
113    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114        self.ptr.name().fmt(f)
115    }
116}