quil_rs/instruction/
control_flow.rs

1use 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/// An opaque placeholder for a label whose index may be assigned
69/// at a later time.
70#[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}