1
use std::{borrow::Borrow, cell::Ref};
2
use uniplate::Uniplate;
3

            
4
use super::{
5
    AbstractLiteral, DeclarationPtr, DomainPtr, Expression, Literal, Moo, Name,
6
    categories::{Category, CategoryOf},
7
    domains::HasDomain,
8
    records::RecordValue,
9
};
10
use derivative::Derivative;
11
use polyquine::Quine;
12
use serde::{Deserialize, Serialize};
13

            
14
/// An `Atom` is an indivisible expression, such as a literal or a reference.
15
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate, Derivative, Quine)]
16
#[derivative(Hash)]
17
#[uniplate()]
18
#[biplate(to=Literal)]
19
#[biplate(to=Expression)]
20
#[biplate(to=AbstractLiteral<Literal>)]
21
#[biplate(to=RecordValue<Literal>)]
22
#[biplate(to=DeclarationPtr)]
23
#[biplate(to=Name)]
24
#[biplate(to=super::Reference)]
25
#[path_prefix(conjure_cp::ast)]
26
pub enum Atom {
27
    Literal(Literal),
28
    #[polyquine_skip]
29
    Reference(super::Reference),
30
}
31

            
32
impl Atom {
33
    pub fn new_ref(decl: DeclarationPtr) -> Atom {
34
        Atom::Reference(super::Reference::new(decl))
35
    }
36

            
37
    pub fn into_declaration(self) -> DeclarationPtr {
38
        match self {
39
            Atom::Reference(reference) => reference.into_ptr(),
40
            _ => panic!("Called into_declaration on a non-reference Atom"),
41
        }
42
    }
43
}
44

            
45
impl CategoryOf for Atom {
46
    fn category_of(&self) -> Category {
47
        match self {
48
            Atom::Literal(_) => Category::Constant,
49
            Atom::Reference(reference) => reference.category_of(),
50
        }
51
    }
52
}
53

            
54
impl HasDomain for Atom {
55
    fn domain_of(&self) -> DomainPtr {
56
        match self {
57
            Atom::Literal(literal) => literal.domain_of(),
58
            Atom::Reference(reference) => reference.domain_of(),
59
        }
60
    }
61
}
62

            
63
impl std::fmt::Display for Atom {
64
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65
        match self {
66
            Atom::Literal(x) => x.fmt(f),
67
            Atom::Reference(x) => x.fmt(f),
68
        }
69
    }
70
}
71

            
72
impl From<Literal> for Atom {
73
    fn from(value: Literal) -> Self {
74
        Atom::Literal(value)
75
    }
76
}
77

            
78
impl From<DeclarationPtr> for Atom {
79
    fn from(value: DeclarationPtr) -> Self {
80
        Atom::Reference(super::Reference::new(value))
81
    }
82
}
83

            
84
impl From<super::Reference> for Atom {
85
    fn from(value: super::Reference) -> Self {
86
        Atom::Reference(value)
87
    }
88
}
89

            
90
impl From<i32> for Atom {
91
    fn from(value: i32) -> Self {
92
        Atom::Literal(value.into())
93
    }
94
}
95

            
96
impl From<bool> for Atom {
97
    fn from(value: bool) -> Self {
98
        Atom::Literal(value.into())
99
    }
100
}
101

            
102
impl TryFrom<Expression> for Atom {
103
    type Error = &'static str;
104

            
105
    fn try_from(value: Expression) -> Result<Self, Self::Error> {
106
        match value {
107
            Expression::Atomic(_, atom) => Ok(atom),
108
            _ => Err("Cannot convert non-atomic expression to Atom"),
109
        }
110
    }
111
}
112

            
113
impl TryFrom<Box<Expression>> for Atom {
114
    type Error = &'static str;
115

            
116
    fn try_from(value: Box<Expression>) -> Result<Self, Self::Error> {
117
        TryFrom::try_from(*value)
118
    }
119
}
120

            
121
impl TryFrom<Moo<Expression>> for Atom {
122
    type Error = &'static str;
123

            
124
    fn try_from(value: Moo<Expression>) -> Result<Self, Self::Error> {
125
        TryFrom::try_from(Moo::unwrap_or_clone(value))
126
    }
127
}
128
impl<'a> TryFrom<&'a Expression> for &'a Atom {
129
    type Error = &'static str;
130

            
131
    fn try_from(value: &'a Expression) -> Result<Self, Self::Error> {
132
        match value {
133
            Expression::Atomic(_, atom) => Ok(atom),
134
            _ => Err("Cannot convert non-atomic expression to Atom"),
135
        }
136
    }
137
}
138

            
139
impl<'a> TryFrom<&'a Box<Expression>> for &'a Atom {
140
    type Error = &'static str;
141

            
142
    fn try_from(value: &'a Box<Expression>) -> Result<Self, Self::Error> {
143
        let expr: &'a Expression = value.borrow();
144
        expr.try_into()
145
    }
146
}
147

            
148
impl<'a> TryFrom<&'a Moo<Expression>> for &'a Atom {
149
    type Error = &'static str;
150

            
151
    fn try_from(value: &'a Moo<Expression>) -> Result<Self, Self::Error> {
152
        let expr: &'a Expression = value.borrow();
153
        expr.try_into()
154
    }
155
}
156

            
157
impl TryFrom<Atom> for Literal {
158
    type Error = &'static str;
159

            
160
    fn try_from(value: Atom) -> Result<Self, Self::Error> {
161
        match value {
162
            Atom::Literal(l) => Ok(l),
163
            _ => Err("Cannot convert non-literal atom to Literal"),
164
        }
165
    }
166
}
167

            
168
impl<'a> TryFrom<&'a Atom> for &'a Literal {
169
    type Error = &'static str;
170

            
171
    fn try_from(value: &'a Atom) -> Result<Self, Self::Error> {
172
        match value {
173
            Atom::Literal(l) => Ok(l),
174
            _ => Err("Cannot convert non-literal atom to Literal"),
175
        }
176
    }
177
}
178

            
179
impl TryFrom<Atom> for Name {
180
    type Error = &'static str;
181

            
182
    fn try_from(value: Atom) -> Result<Self, Self::Error> {
183
        match value {
184
            Atom::Reference(x) => Ok(x.ptr().name().clone()),
185
            _ => Err("Cannot convert non-reference atom to Name"),
186
        }
187
    }
188
}
189

            
190
impl<'a> TryFrom<&'a Atom> for Ref<'a, Name> {
191
    type Error = &'static str;
192

            
193
    fn try_from(value: &'a Atom) -> Result<Self, Self::Error> {
194
        match value {
195
            Atom::Reference(x) => Ok(x.ptr().name()),
196
            _ => Err("Cannot convert non-reference atom to Name"),
197
        }
198
    }
199
}
200

            
201
impl TryFrom<Atom> for i32 {
202
    type Error = &'static str;
203

            
204
    fn try_from(value: Atom) -> Result<Self, Self::Error> {
205
        let lit: Literal = value.try_into()?;
206
        lit.try_into()
207
    }
208
}
209

            
210
impl TryFrom<&Box<Atom>> for i32 {
211
    type Error = &'static str;
212

            
213
    fn try_from(value: &Box<Atom>) -> Result<Self, Self::Error> {
214
        TryFrom::<&Atom>::try_from(value.as_ref())
215
    }
216
}
217

            
218
impl TryFrom<Box<Atom>> for i32 {
219
    type Error = &'static str;
220

            
221
    fn try_from(value: Box<Atom>) -> Result<Self, Self::Error> {
222
        let lit: Literal = (*value).try_into()?;
223
        lit.try_into()
224
    }
225
}
226

            
227
impl TryFrom<&Moo<Atom>> for i32 {
228
    type Error = &'static str;
229

            
230
    fn try_from(value: &Moo<Atom>) -> Result<Self, Self::Error> {
231
        TryFrom::<&Atom>::try_from(value.as_ref())
232
    }
233
}
234

            
235
impl TryFrom<Moo<Atom>> for i32 {
236
    type Error = &'static str;
237

            
238
    fn try_from(value: Moo<Atom>) -> Result<Self, Self::Error> {
239
        TryFrom::<&Atom>::try_from(value.as_ref())
240
    }
241
}
242

            
243
impl TryFrom<&Atom> for i32 {
244
    type Error = &'static str;
245

            
246
    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
247
        let lit: &Literal = value.try_into()?;
248
        lit.try_into()
249
    }
250
}
251

            
252
impl TryFrom<Atom> for bool {
253
    type Error = &'static str;
254

            
255
    fn try_from(value: Atom) -> Result<Self, Self::Error> {
256
        let lit: Literal = value.try_into()?;
257
        lit.try_into()
258
    }
259
}
260

            
261
impl TryFrom<&Atom> for bool {
262
    type Error = &'static str;
263

            
264
    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
265
        let lit: &Literal = value.try_into()?;
266
        lit.try_into()
267
    }
268
}