Skip to main content

conjure_cp_core/ast/
atom.rs

1use std::borrow::Borrow;
2use uniplate::Uniplate;
3
4use crate::ast::enumerated::EnumVariant;
5
6use super::{
7    AbstractLiteral, DeclarationPtr, DomainPtr, Expression, Literal, Moo, Name,
8    categories::{Category, CategoryOf},
9    domains::HasDomain,
10    records::Field,
11};
12use derivative::Derivative;
13use parking_lot::MappedRwLockReadGuard;
14use polyquine::Quine;
15use 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)]
29pub enum Atom {
30    Literal(Literal),
31    #[polyquine_skip]
32    Reference(super::Reference),
33}
34
35impl Atom {
36    pub fn new_ref(decl: DeclarationPtr) -> Atom {
37        Atom::Reference(super::Reference::new(decl))
38    }
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
48impl CategoryOf for Atom {
49    fn category_of(&self) -> Category {
50        match self {
51            Atom::Literal(_) => Category::Constant,
52            Atom::Reference(reference) => reference.category_of(),
53        }
54    }
55}
56
57impl HasDomain for Atom {
58    fn domain_of(&self) -> DomainPtr {
59        match self {
60            Atom::Literal(literal) => literal.domain_of(),
61            Atom::Reference(reference) => reference.domain_of(),
62        }
63    }
64}
65
66impl std::fmt::Display for Atom {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        match self {
69            Atom::Literal(x) => x.fmt(f),
70            Atom::Reference(x) => x.fmt(f),
71        }
72    }
73}
74
75impl From<Literal> for Atom {
76    fn from(value: Literal) -> Self {
77        Atom::Literal(value)
78    }
79}
80
81impl From<DeclarationPtr> for Atom {
82    fn from(value: DeclarationPtr) -> Self {
83        Atom::Reference(super::Reference::new(value))
84    }
85}
86
87impl From<super::Reference> for Atom {
88    fn from(value: super::Reference) -> Self {
89        Atom::Reference(value)
90    }
91}
92
93impl From<i32> for Atom {
94    fn from(value: i32) -> Self {
95        Atom::Literal(value.into())
96    }
97}
98
99impl From<bool> for Atom {
100    fn from(value: bool) -> Self {
101        Atom::Literal(value.into())
102    }
103}
104
105impl TryFrom<Expression> for Atom {
106    type Error = &'static str;
107
108    fn try_from(value: Expression) -> Result<Self, Self::Error> {
109        match value {
110            Expression::Atomic(_, atom) => Ok(atom),
111            _ => Err("Cannot convert non-atomic expression to Atom"),
112        }
113    }
114}
115
116impl 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
124impl TryFrom<Moo<Expression>> for Atom {
125    type Error = &'static str;
126
127    fn try_from(value: Moo<Expression>) -> Result<Self, Self::Error> {
128        TryFrom::try_from(Moo::unwrap_or_clone(value))
129    }
130}
131impl<'a> TryFrom<&'a Expression> for &'a Atom {
132    type Error = &'static str;
133
134    fn try_from(value: &'a Expression) -> Result<Self, Self::Error> {
135        match value {
136            Expression::Atomic(_, atom) => Ok(atom),
137            _ => Err("Cannot convert non-atomic expression to Atom"),
138        }
139    }
140}
141
142impl<'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
151impl<'a> TryFrom<&'a Moo<Expression>> for &'a Atom {
152    type Error = &'static str;
153
154    fn try_from(value: &'a Moo<Expression>) -> Result<Self, Self::Error> {
155        let expr: &'a Expression = value.borrow();
156        expr.try_into()
157    }
158}
159
160impl TryFrom<Atom> for Literal {
161    type Error = &'static str;
162
163    fn try_from(value: Atom) -> Result<Self, Self::Error> {
164        match value {
165            Atom::Literal(l) => Ok(l),
166            _ => Err("Cannot convert non-literal atom to Literal"),
167        }
168    }
169}
170
171impl<'a> TryFrom<&'a Atom> for &'a Literal {
172    type Error = &'static str;
173
174    fn try_from(value: &'a Atom) -> Result<Self, Self::Error> {
175        match value {
176            Atom::Literal(l) => Ok(l),
177            _ => Err("Cannot convert non-literal atom to Literal"),
178        }
179    }
180}
181
182impl 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
193impl<'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
204impl TryFrom<Atom> for i32 {
205    type Error = &'static str;
206
207    fn try_from(value: Atom) -> Result<Self, Self::Error> {
208        let lit: Literal = value.try_into()?;
209        lit.try_into()
210    }
211}
212
213impl 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
221impl 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
230impl TryFrom<&Moo<Atom>> for i32 {
231    type Error = &'static str;
232
233    fn try_from(value: &Moo<Atom>) -> Result<Self, Self::Error> {
234        TryFrom::<&Atom>::try_from(value.as_ref())
235    }
236}
237
238impl 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
246impl TryFrom<&Atom> for i32 {
247    type Error = &'static str;
248
249    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
250        match value {
251            Atom::Literal(lit) => lit.try_into(),
252            Atom::Reference(reference) => {
253                let lit = reference
254                    .resolve_constant()
255                    .ok_or("Cannot convert non-constant reference atom to literal")?;
256                lit.try_into()
257                    .map_err(|_| "Cannot convert non-int reference atom to i32")
258            }
259        }
260    }
261}
262
263impl 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
272impl TryFrom<&Atom> for bool {
273    type Error = &'static str;
274
275    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
276        match value {
277            Atom::Literal(lit) => lit.try_into(),
278            Atom::Reference(reference) => {
279                let lit = reference
280                    .resolve_constant()
281                    .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    }
287}
288
289impl 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
298impl 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}