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