1
use std::borrow::Borrow;
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 parking_lot::MappedRwLockReadGuard;
12
use polyquine::Quine;
13
use serde::{Deserialize, Serialize};
14

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
247
146900
    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
248
146900
        match value {
249
47900
            Atom::Literal(lit) => lit.try_into(),
250
99000
            Atom::Reference(reference) => {
251
99000
                let lit = reference
252
99000
                    .resolve_constant()
253
99000
                    .ok_or("Cannot convert non-constant reference atom to literal")?;
254
140
                lit.try_into()
255
140
                    .map_err(|_| "Cannot convert non-int reference atom to i32")
256
            }
257
        }
258
146900
    }
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
}
269

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

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