1use std::sync::Arc;
2
3#[cfg(not(feature = "python"))]
4use optipy::strip_pyo3;
5#[cfg(feature = "stubs")]
6use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyclass_complex_enum, gen_stub_pymethods};
7
8use super::MemoryReference;
9use crate::{
10 pickleable_new,
11 quil::{Quil, ToQuilError},
12};
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
16#[cfg_attr(
17 feature = "python",
18 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
19)]
20pub struct Label {
21 pub target: Target,
22}
23
24pickleable_new! {
25 impl Label {
26 pub fn new(target: Target);
27 }
28}
29
30impl Quil for Label {
31 fn write(
32 &self,
33 writer: &mut impl std::fmt::Write,
34 fall_back_to_debug: bool,
35 ) -> crate::quil::ToQuilResult<()> {
36 write!(writer, "LABEL ")?;
37 self.target.write(writer, fall_back_to_debug)
38 }
39}
40
41#[derive(Clone, Debug, PartialEq, Eq, Hash, strum::EnumTryAs)]
42#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
43#[cfg_attr(
44 feature = "python",
45 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash)
46)]
47pub enum Target {
48 Fixed(String),
49 Placeholder(TargetPlaceholder),
50}
51
52impl Target {
53 pub(crate) fn resolve_placeholder<R>(&mut self, resolver: R)
54 where
55 R: Fn(&TargetPlaceholder) -> Option<String>,
56 {
57 if let Target::Placeholder(placeholder) = self {
58 if let Some(resolved) = resolver(placeholder) {
59 *self = Target::Fixed(resolved);
60 }
61 }
62 }
63}
64
65impl Quil for Target {
66 fn write(
67 &self,
68 writer: &mut impl std::fmt::Write,
69 fall_back_to_debug: bool,
70 ) -> crate::quil::ToQuilResult<()> {
71 match self {
72 Target::Fixed(label) => write!(writer, "@{label}").map_err(Into::into),
73 Target::Placeholder(_) => {
74 if fall_back_to_debug {
75 write!(writer, "@{self:?}").map_err(Into::into)
76 } else {
77 Err(ToQuilError::UnresolvedLabelPlaceholder)
78 }
79 }
80 }
81 }
82}
83
84type TargetPlaceholderInner = Arc<String>;
85
86#[derive(Clone, Debug, Eq)]
89#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
90#[cfg_attr(
91 feature = "python",
92 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, ord, subclass)
93)]
94pub struct TargetPlaceholder(TargetPlaceholderInner);
95
96#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
97#[cfg_attr(feature = "python", pyo3::pymethods)]
98#[cfg_attr(not(feature = "python"), strip_pyo3)]
99impl TargetPlaceholder {
100 #[new]
101 pub fn new(base_label: String) -> Self {
102 Self(Arc::new(base_label))
103 }
104
105 #[getter(base_label)]
106 pub fn as_inner(&self) -> &str {
107 &self.0
108 }
109}
110
111impl TargetPlaceholder {
112 fn address(&self) -> usize {
113 &*self.0 as *const _ as usize
114 }
115}
116
117impl std::hash::Hash for TargetPlaceholder {
118 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
119 self.address().hash(state);
120 }
121}
122
123impl PartialOrd for TargetPlaceholder {
124 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
125 Some(self.cmp(other))
126 }
127}
128
129impl Ord for TargetPlaceholder {
130 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
131 self.address().cmp(&other.address())
132 }
133}
134
135impl PartialEq for TargetPlaceholder {
136 fn eq(&self, other: &Self) -> bool {
137 Arc::<std::string::String>::ptr_eq(&self.0, &other.0)
138 }
139}
140
141#[derive(Clone, Debug, PartialEq, Eq)]
142#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
143#[cfg_attr(
144 feature = "python",
145 pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
146)]
147pub struct Jump {
148 pub target: Target,
149}
150
151impl Quil for Jump {
152 fn write(
153 &self,
154 writer: &mut impl std::fmt::Write,
155 fall_back_to_debug: bool,
156 ) -> Result<(), crate::quil::ToQuilError> {
157 write!(writer, "JUMP ")?;
158 self.target.write(writer, fall_back_to_debug)?;
159 Ok(())
160 }
161}
162
163pickleable_new! {
164 impl Jump {
165 pub fn new(target: Target);
166 }
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
170#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
171#[cfg_attr(
172 feature = "python",
173 pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
174)]
175pub struct JumpWhen {
176 pub target: Target,
177 pub condition: MemoryReference,
178}
179
180pickleable_new! {
181 impl JumpWhen {
182 pub fn new(target: Target, condition: MemoryReference);
183 }
184}
185
186impl Quil for JumpWhen {
187 fn write(
188 &self,
189 writer: &mut impl std::fmt::Write,
190 fall_back_to_debug: bool,
191 ) -> Result<(), crate::quil::ToQuilError> {
192 write!(writer, "JUMP-WHEN ")?;
193 self.target.write(writer, fall_back_to_debug)?;
194 write!(writer, " {}", self.condition)?;
195 Ok(())
196 }
197}
198
199#[derive(Clone, Debug, PartialEq, Eq)]
200#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
201#[cfg_attr(
202 feature = "python",
203 pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
204)]
205pub struct JumpUnless {
206 pub target: Target,
207 pub condition: MemoryReference,
208}
209
210pickleable_new! {
211 impl JumpUnless {
212 pub fn new(target: Target, condition: MemoryReference);
213 }
214}
215
216impl Quil for JumpUnless {
217 fn write(
218 &self,
219 writer: &mut impl std::fmt::Write,
220 fall_back_to_debug: bool,
221 ) -> Result<(), crate::quil::ToQuilError> {
222 write!(writer, "JUMP-UNLESS ")?;
223 self.target.write(writer, fall_back_to_debug)?;
224 write!(writer, " {}", self.condition)?;
225 Ok(())
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use rstest::rstest;
233
234 #[test]
235 fn resolve_placeholder() {
236 let mut label = Target::Placeholder(TargetPlaceholder::new("base".to_string()));
237 label.resolve_placeholder(|_| Some("test".to_string()));
238 assert_eq!(label, Target::Fixed("test".to_string()))
239 }
240
241 #[rstest]
242 #[case(Target::Fixed(String::from("test")), Ok("@test"), "@test")]
243 #[case(
244 Target::Placeholder(TargetPlaceholder::new(String::from("test-placeholder"))),
245 Err(ToQuilError::UnresolvedLabelPlaceholder),
246 "@Placeholder(TargetPlaceholder(\"test-placeholder\"))"
247 )]
248 fn quil_format(
249 #[case] input: Target,
250 #[case] expected_quil: crate::quil::ToQuilResult<&str>,
251 #[case] expected_debug: &str,
252 ) {
253 assert_eq!(input.to_quil(), expected_quil.map(|s| s.to_string()));
254 assert_eq!(input.to_quil_or_debug(), expected_debug);
255 }
256}