1
use std::borrow::Borrow;
2
use uniplate::Uniplate;
3

            
4
use crate::ast::enumerated::EnumVariant;
5

            
6
use super::{
7
    AbstractLiteral, DeclarationPtr, DomainPtr, Expression, Literal, Moo, Name,
8
    categories::{Category, CategoryOf},
9
    domains::HasDomain,
10
    records::Field,
11
};
12
use derivative::Derivative;
13
use parking_lot::MappedRwLockReadGuard;
14
use polyquine::Quine;
15
use serde::{Deserialize, Serialize};
16

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

            
35
impl Atom {
36
82687
    pub fn new_ref(decl: DeclarationPtr) -> Atom {
37
82687
        Atom::Reference(super::Reference::new(decl))
38
82687
    }
39

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

            
48
impl CategoryOf for Atom {
49
13804740
    fn category_of(&self) -> Category {
50
13804740
        match self {
51
3729395
            Atom::Literal(_) => Category::Constant,
52
10075345
            Atom::Reference(reference) => reference.category_of(),
53
        }
54
13804740
    }
55
}
56

            
57
impl HasDomain for Atom {
58
302136830
    fn domain_of(&self) -> DomainPtr {
59
302136830
        match self {
60
37015517
            Atom::Literal(literal) => literal.domain_of(),
61
265121313
            Atom::Reference(reference) => reference.domain_of(),
62
        }
63
302136830
    }
64
}
65

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

            
75
impl From<Literal> for Atom {
76
10438583
    fn from(value: Literal) -> Self {
77
10438583
        Atom::Literal(value)
78
10438583
    }
79
}
80

            
81
impl From<DeclarationPtr> for Atom {
82
    fn from(value: DeclarationPtr) -> Self {
83
        Atom::Reference(super::Reference::new(value))
84
    }
85
}
86

            
87
impl From<super::Reference> for Atom {
88
179392816
    fn from(value: super::Reference) -> Self {
89
179392816
        Atom::Reference(value)
90
179392816
    }
91
}
92

            
93
impl From<i32> for Atom {
94
240
    fn from(value: i32) -> Self {
95
240
        Atom::Literal(value.into())
96
240
    }
97
}
98

            
99
impl From<bool> for Atom {
100
2580209
    fn from(value: bool) -> Self {
101
2580209
        Atom::Literal(value.into())
102
2580209
    }
103
}
104

            
105
impl TryFrom<Expression> for Atom {
106
    type Error = &'static str;
107

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

            
116
impl TryFrom<Box<Expression>> for Atom {
117
    type Error = &'static str;
118

            
119
    fn try_from(value: Box<Expression>) -> Result<Self, Self::Error> {
120
        TryFrom::try_from(*value)
121
    }
122
}
123

            
124
impl TryFrom<Moo<Expression>> for Atom {
125
    type Error = &'static str;
126

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

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

            
142
impl<'a> TryFrom<&'a Box<Expression>> for &'a Atom {
143
    type Error = &'static str;
144

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

            
151
impl<'a> TryFrom<&'a Moo<Expression>> for &'a Atom {
152
    type Error = &'static str;
153

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

            
160
impl TryFrom<Atom> for Literal {
161
    type Error = &'static str;
162

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

            
171
impl<'a> TryFrom<&'a Atom> for &'a Literal {
172
    type Error = &'static str;
173

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

            
182
impl TryFrom<Atom> for Name {
183
    type Error = &'static str;
184

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

            
193
impl<'a> TryFrom<&'a Atom> for MappedRwLockReadGuard<'a, Name> {
194
    type Error = &'static str;
195

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

            
204
impl TryFrom<Atom> for i32 {
205
    type Error = &'static str;
206

            
207
11919
    fn try_from(value: Atom) -> Result<Self, Self::Error> {
208
11919
        let lit: Literal = value.try_into()?;
209
        lit.try_into()
210
11919
    }
211
}
212

            
213
impl TryFrom<&Box<Atom>> for i32 {
214
    type Error = &'static str;
215

            
216
    fn try_from(value: &Box<Atom>) -> Result<Self, Self::Error> {
217
        TryFrom::<&Atom>::try_from(value.as_ref())
218
    }
219
}
220

            
221
impl TryFrom<Box<Atom>> for i32 {
222
    type Error = &'static str;
223

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

            
230
impl TryFrom<&Moo<Atom>> for i32 {
231
    type Error = &'static str;
232

            
233
1106427
    fn try_from(value: &Moo<Atom>) -> Result<Self, Self::Error> {
234
1106427
        TryFrom::<&Atom>::try_from(value.as_ref())
235
1106427
    }
236
}
237

            
238
impl TryFrom<Moo<Atom>> for i32 {
239
    type Error = &'static str;
240

            
241
    fn try_from(value: Moo<Atom>) -> Result<Self, Self::Error> {
242
        TryFrom::<&Atom>::try_from(value.as_ref())
243
    }
244
}
245

            
246
impl TryFrom<&Atom> for i32 {
247
    type Error = &'static str;
248

            
249
4034459
    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
250
4034459
        match value {
251
1694939
            Atom::Literal(lit) => lit.try_into(),
252
            Atom::Reference(reference) => {
253
2339520
                let lit = reference
254
2339520
                    .resolve_constant()
255
2339520
                    .ok_or("Cannot convert non-constant reference atom to literal")?;
256
1045586
                lit.try_into()
257
                    .map_err(|_| "Cannot convert non-int reference atom to i32")
258
            }
259
        }
260
4034459
    }
261
}
262

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

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

            
272
impl TryFrom<&Atom> for bool {
273
    type Error = &'static str;
274

            
275
1077
    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
276
1077
        match value {
277
            Atom::Literal(lit) => lit.try_into(),
278
            Atom::Reference(reference) => {
279
1077
                let lit = reference
280
1077
                    .resolve_constant()
281
1077
                    .ok_or("Cannot convert non-constant reference atom to literal")?;
282
                lit.try_into()
283
                    .map_err(|_| "Cannot convert non-bool reference atom to bool")
284
            }
285
        }
286
1077
    }
287
}
288

            
289
impl TryFrom<Atom> for EnumVariant {
290
    type Error = &'static str;
291

            
292
    fn try_from(value: Atom) -> Result<Self, Self::Error> {
293
        let lit: Literal = value.try_into()?;
294
        lit.try_into()
295
    }
296
}
297

            
298
impl TryFrom<&Atom> for EnumVariant {
299
    type Error = &'static str;
300

            
301
    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
302
        match value {
303
            Atom::Literal(lit) => lit.try_into(),
304
            Atom::Reference(reference) => {
305
                let lit = reference
306
                    .resolve_constant()
307
                    .ok_or("Cannot convert non-constant reference atom to literal")?;
308
                lit.try_into()
309
                    .map_err(|_| "Cannot convert non-bool reference atom to bool")
310
            }
311
        }
312
    }
313
}