conjure_cp_core/ast/
atom.rs

1use crate::{ast::declaration::serde::DeclarationPtrAsId, bug};
2use std::{borrow::Borrow, cell::Ref};
3use uniplate::Uniplate;
4
5use super::{
6    AbstractLiteral, DeclarationPtr, Domain, Expression, Literal, Moo, Name,
7    categories::{Category, CategoryOf},
8    domains::HasDomain,
9    records::RecordValue,
10};
11use derivative::Derivative;
12use serde::{Deserialize, Serialize};
13use serde_with::serde_as;
14
15/// An `Atom` is an indivisible expression, such as a literal or a reference.
16#[serde_as]
17#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate, Derivative)]
18#[derivative(Hash)]
19#[uniplate()]
20#[biplate(to=Literal)]
21#[biplate(to=Expression)]
22#[biplate(to=AbstractLiteral<Literal>)]
23#[biplate(to=RecordValue<Literal>)]
24#[biplate(to=DeclarationPtr)]
25#[biplate(to=Name)]
26pub enum Atom {
27    Literal(Literal),
28    // FIXME: check if these are the hashing semantics we want.
29    Reference(#[serde_as(as = "DeclarationPtrAsId")] DeclarationPtr),
30}
31
32impl Atom {
33    pub fn new_ref(decl: DeclarationPtr) -> Atom {
34        Atom::Reference(decl)
35    }
36
37    pub fn into_declaration(self) -> DeclarationPtr {
38        match self {
39            Atom::Reference(decl) => decl,
40            _ => panic!("Called into_declaration on a non-reference Atom"),
41        }
42    }
43
44    /// Shorthand to create an integer literal.
45    pub fn new_ilit(value: i32) -> Atom {
46        Atom::Literal(Literal::Int(value))
47    }
48
49    /// Shorthand to create a boolean literal.
50    pub fn new_blit(value: bool) -> Atom {
51        Atom::Literal(Literal::Bool(value))
52    }
53}
54
55impl CategoryOf for Atom {
56    fn category_of(&self) -> Category {
57        match self {
58            Atom::Literal(_) => Category::Constant,
59            Atom::Reference(declaration_ptr) => declaration_ptr.category_of(),
60        }
61    }
62}
63
64impl HasDomain for Atom {
65    fn domain_of(&self) -> Domain {
66        match self {
67            Atom::Literal(literal) => literal.domain_of(),
68            Atom::Reference(ptr) => ptr.domain().unwrap_or_else(|| {
69                bug!("reference ({name}) should have a domain", name = ptr.name())
70            }),
71        }
72    }
73}
74
75impl std::fmt::Display for Atom {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        match self {
78            Atom::Literal(x) => x.fmt(f),
79            Atom::Reference(x) => x.name().fmt(f),
80        }
81    }
82}
83
84impl From<Literal> for Atom {
85    fn from(value: Literal) -> Self {
86        Atom::Literal(value)
87    }
88}
89
90impl From<DeclarationPtr> for Atom {
91    fn from(value: DeclarationPtr) -> Self {
92        Atom::Reference(value)
93    }
94}
95
96impl From<i32> for Atom {
97    fn from(value: i32) -> Self {
98        Atom::Literal(value.into())
99    }
100}
101
102impl From<bool> for Atom {
103    fn from(value: bool) -> Self {
104        Atom::Literal(value.into())
105    }
106}
107
108impl TryFrom<Expression> for Atom {
109    type Error = &'static str;
110
111    fn try_from(value: Expression) -> Result<Self, Self::Error> {
112        match value {
113            Expression::Atomic(_, atom) => Ok(atom),
114            _ => Err("Cannot convert non-atomic expression to Atom"),
115        }
116    }
117}
118
119impl TryFrom<Box<Expression>> for Atom {
120    type Error = &'static str;
121
122    fn try_from(value: Box<Expression>) -> Result<Self, Self::Error> {
123        TryFrom::try_from(*value)
124    }
125}
126
127impl TryFrom<Moo<Expression>> for Atom {
128    type Error = &'static str;
129
130    fn try_from(value: Moo<Expression>) -> Result<Self, Self::Error> {
131        TryFrom::try_from(Moo::unwrap_or_clone(value))
132    }
133}
134impl<'a> TryFrom<&'a Expression> for &'a Atom {
135    type Error = &'static str;
136
137    fn try_from(value: &'a Expression) -> Result<Self, Self::Error> {
138        match value {
139            Expression::Atomic(_, atom) => Ok(atom),
140            _ => Err("Cannot convert non-atomic expression to Atom"),
141        }
142    }
143}
144
145impl<'a> TryFrom<&'a Box<Expression>> for &'a Atom {
146    type Error = &'static str;
147
148    fn try_from(value: &'a Box<Expression>) -> Result<Self, Self::Error> {
149        let expr: &'a Expression = value.borrow();
150        expr.try_into()
151    }
152}
153
154impl<'a> TryFrom<&'a Moo<Expression>> for &'a Atom {
155    type Error = &'static str;
156
157    fn try_from(value: &'a Moo<Expression>) -> Result<Self, Self::Error> {
158        let expr: &'a Expression = value.borrow();
159        expr.try_into()
160    }
161}
162
163impl TryFrom<Atom> for Literal {
164    type Error = &'static str;
165
166    fn try_from(value: Atom) -> Result<Self, Self::Error> {
167        match value {
168            Atom::Literal(l) => Ok(l),
169            _ => Err("Cannot convert non-literal atom to Literal"),
170        }
171    }
172}
173
174impl<'a> TryFrom<&'a Atom> for &'a Literal {
175    type Error = &'static str;
176
177    fn try_from(value: &'a Atom) -> Result<Self, Self::Error> {
178        match value {
179            Atom::Literal(l) => Ok(l),
180            _ => Err("Cannot convert non-literal atom to Literal"),
181        }
182    }
183}
184
185impl TryFrom<Atom> for Name {
186    type Error = &'static str;
187
188    fn try_from(value: Atom) -> Result<Self, Self::Error> {
189        match value {
190            Atom::Reference(x) => Ok(x.name().clone()),
191            _ => Err("Cannot convert non-reference atom to Name"),
192        }
193    }
194}
195
196impl<'a> TryFrom<&'a Atom> for Ref<'a, Name> {
197    type Error = &'static str;
198
199    fn try_from(value: &'a Atom) -> Result<Self, Self::Error> {
200        match value {
201            Atom::Reference(x) => Ok(x.name()),
202            _ => Err("Cannot convert non-reference atom to Name"),
203        }
204    }
205}
206
207impl TryFrom<Atom> for i32 {
208    type Error = &'static str;
209
210    fn try_from(value: Atom) -> Result<Self, Self::Error> {
211        let lit: Literal = value.try_into()?;
212        lit.try_into()
213    }
214}
215
216impl TryFrom<&Box<Atom>> for i32 {
217    type Error = &'static str;
218
219    fn try_from(value: &Box<Atom>) -> Result<Self, Self::Error> {
220        TryFrom::<&Atom>::try_from(value.as_ref())
221    }
222}
223
224impl TryFrom<Box<Atom>> for i32 {
225    type Error = &'static str;
226
227    fn try_from(value: Box<Atom>) -> Result<Self, Self::Error> {
228        let lit: Literal = (*value).try_into()?;
229        lit.try_into()
230    }
231}
232
233impl TryFrom<&Moo<Atom>> for i32 {
234    type Error = &'static str;
235
236    fn try_from(value: &Moo<Atom>) -> Result<Self, Self::Error> {
237        TryFrom::<&Atom>::try_from(value.as_ref())
238    }
239}
240
241impl TryFrom<Moo<Atom>> for i32 {
242    type Error = &'static str;
243
244    fn try_from(value: Moo<Atom>) -> Result<Self, Self::Error> {
245        TryFrom::<&Atom>::try_from(value.as_ref())
246    }
247}
248
249impl TryFrom<&Atom> for i32 {
250    type Error = &'static str;
251
252    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
253        let lit: &Literal = value.try_into()?;
254        lit.try_into()
255    }
256}
257
258impl TryFrom<Atom> for bool {
259    type Error = &'static str;
260
261    fn try_from(value: Atom) -> Result<Self, Self::Error> {
262        let lit: Literal = value.try_into()?;
263        lit.try_into()
264    }
265}
266
267impl TryFrom<&Atom> for bool {
268    type Error = &'static str;
269
270    fn try_from(value: &Atom) -> Result<Self, Self::Error> {
271        let lit: &Literal = value.try_into()?;
272        lit.try_into()
273    }
274}