1
use crate::ast::serde::{AsId, HasId};
2
use crate::{ast::DeclarationPtr, bug};
3
use derivative::Derivative;
4
use parking_lot::MappedRwLockReadGuard;
5
use serde::{Deserialize, Serialize};
6
use serde_with::serde_as;
7
use std::fmt::{Display, Formatter};
8
use uniplate::Uniplate;
9

            
10
use 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)]
29
pub struct Reference {
30
    #[serde_as(as = "AsId")]
31
    pub ptr: DeclarationPtr,
32
}
33

            
34
impl Reference {
35
55142
    pub fn new(ptr: DeclarationPtr) -> Self {
36
55142
        Reference { ptr }
37
55142
    }
38

            
39
1133200
    pub fn ptr(&self) -> &DeclarationPtr {
40
1133200
        &self.ptr
41
1133200
    }
42

            
43
260
    pub fn into_ptr(self) -> DeclarationPtr {
44
260
        self.ptr
45
260
    }
46

            
47
298700
    pub fn name(&self) -> MappedRwLockReadGuard<'_, Name> {
48
298700
        self.ptr.name()
49
298700
    }
50

            
51
5980
    pub fn id(&self) -> crate::ast::serde::ObjId {
52
5980
        self.ptr.id()
53
5980
    }
54

            
55
9580
    pub fn domain(&self) -> Option<DomainPtr> {
56
9580
        self.ptr.domain()
57
9580
    }
58

            
59
7620
    pub fn resolved_domain(&self) -> Option<Moo<GroundDomain>> {
60
7620
        self.domain()?.resolve()
61
7620
    }
62

            
63
    /// Returns the expression behind a value-letting reference, if this is one.
64
969200
    pub fn resolve_expression(&self) -> Option<Expression> {
65
969200
        self.ptr().as_value_letting().map(|expr| expr.clone())
66
969200
    }
67

            
68
    /// Evaluates this reference to a literal if it resolves to a constant.
69
936860
    pub fn resolve_constant(&self) -> Option<Literal> {
70
936860
        self.resolve_expression()
71
936860
            .and_then(|expr| super::eval::eval_constant(&expr))
72
936860
    }
73

            
74
    /// Resolves this reference to an atomic expression, if possible.
75
32340
    pub fn resolve_atomic(&self) -> Option<Atom> {
76
32340
        self.resolve_expression().and_then(|expr| match expr {
77
            Expression::Atomic(_, atom) => Some(atom),
78
            _ => None,
79
        })
80
32340
    }
81
}
82

            
83
impl From<Reference> for Expression {
84
    fn from(value: Reference) -> Self {
85
        Expression::Atomic(Metadata::new(), value.into())
86
    }
87
}
88

            
89
impl From<DeclarationPtr> for Reference {
90
    fn from(ptr: DeclarationPtr) -> Self {
91
        Reference::new(ptr)
92
    }
93
}
94

            
95
impl CategoryOf for Reference {
96
85900
    fn category_of(&self) -> Category {
97
85900
        self.ptr.category_of()
98
85900
    }
99
}
100

            
101
impl HasDomain for Reference {
102
124400
    fn domain_of(&self) -> DomainPtr {
103
124400
        self.ptr.domain().unwrap_or_else(|| {
104
            bug!(
105
                "reference ({name}) should have a domain",
106
                name = self.ptr.name()
107
            )
108
        })
109
124400
    }
110
}
111

            
112
impl Display for Reference {
113
573560
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114
573560
        self.ptr.name().fmt(f)
115
573560
    }
116
}