1
use crate::ast::declaration::serde::DeclarationPtrAsId;
2
use crate::ast::serde::HasId;
3
use crate::{ast::DeclarationPtr, bug};
4
use derivative::Derivative;
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
    DomainPtr, Expression, GroundDomain, 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(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate, Derivative)]
23
#[derivative(Hash)]
24
#[uniplate()]
25
#[biplate(to=DeclarationPtr)]
26
#[biplate(to=Name)]
27
pub struct Reference {
28
    #[serde_as(as = "DeclarationPtrAsId")]
29
    pub ptr: DeclarationPtr,
30
}
31

            
32
impl Reference {
33
    pub fn new(ptr: DeclarationPtr) -> Self {
34
        Reference { ptr }
35
    }
36

            
37
    pub fn ptr(&self) -> &DeclarationPtr {
38
        &self.ptr
39
    }
40

            
41
    pub fn into_ptr(self) -> DeclarationPtr {
42
        self.ptr
43
    }
44

            
45
    pub fn name(&self) -> std::cell::Ref<'_, Name> {
46
        self.ptr.name()
47
    }
48

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

            
53
    pub fn domain(&self) -> Option<DomainPtr> {
54
        self.ptr.domain()
55
    }
56

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

            
62
impl From<Reference> for Expression {
63
    fn from(value: Reference) -> Self {
64
        Expression::Atomic(Metadata::new(), value.into())
65
    }
66
}
67

            
68
impl From<DeclarationPtr> for Reference {
69
    fn from(ptr: DeclarationPtr) -> Self {
70
        Reference::new(ptr)
71
    }
72
}
73

            
74
impl CategoryOf for Reference {
75
    fn category_of(&self) -> Category {
76
        self.ptr.category_of()
77
    }
78
}
79

            
80
impl HasDomain for Reference {
81
    fn domain_of(&self) -> DomainPtr {
82
        self.ptr.domain().unwrap_or_else(|| {
83
            bug!(
84
                "reference ({name}) should have a domain",
85
                name = self.ptr.name()
86
            )
87
        })
88
    }
89
}
90

            
91
impl Display for Reference {
92
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93
        self.ptr.name().fmt(f)
94
    }
95
}