1use std::str::FromStr;
2
3use nom_locate::LocatedSpan;
4
5#[cfg(feature = "stubs")]
6use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyclass_enum};
7
8use crate::{
9 parser::{common::parse_memory_reference, lex, ParseError},
10 pickleable_new,
11 program::{disallow_leftover, SyntaxError},
12 quil::Quil,
13};
14
15use super::ArithmeticOperand;
16
17#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
18#[cfg_attr(feature = "stubs", gen_stub_pyclass_enum)]
19#[cfg_attr(
20 feature = "python",
21 pyo3::pyclass(
22 module = "quil.instructions",
23 eq,
24 frozen,
25 hash,
26 rename_all = "SCREAMING_SNAKE_CASE"
27 )
28)]
29pub enum ScalarType {
30 Bit,
31 Integer,
32 Octet,
33 Real,
34}
35
36impl Quil for ScalarType {
37 fn write(
38 &self,
39 f: &mut impl std::fmt::Write,
40 _fall_back_to_debug: bool,
41 ) -> crate::quil::ToQuilResult<()> {
42 use ScalarType::*;
43 write!(
44 f,
45 "{}",
46 match self {
47 Bit => "BIT",
48 Integer => "INTEGER",
49 Octet => "OCTET",
50 Real => "REAL",
51 }
52 )
53 .map_err(Into::into)
54 }
55}
56
57#[derive(Clone, Debug, Hash, PartialEq, Eq)]
58#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
59#[cfg_attr(
60 feature = "python",
61 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
62)]
63pub struct Vector {
64 pub data_type: ScalarType,
65 pub length: u64,
66}
67
68pickleable_new! {
69 impl Vector {
70 pub fn new(data_type: ScalarType, length: u64);
71 }
72}
73
74impl Quil for Vector {
75 fn write(
76 &self,
77 f: &mut impl std::fmt::Write,
78 fall_back_to_debug: bool,
79 ) -> crate::quil::ToQuilResult<()> {
80 self.data_type.write(f, fall_back_to_debug)?;
81 write!(f, "[{}]", self.length).map_err(Into::into)
82 }
83}
84
85#[derive(Clone, Debug, PartialEq, Eq, Hash)]
86#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
87#[cfg_attr(
88 feature = "python",
89 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, subclass)
90)]
91pub struct Sharing {
92 pub name: String,
93 pub offsets: Vec<Offset>,
94}
95
96pickleable_new! {
97 impl Sharing {
98 pub fn new(name: String, offsets: Vec<Offset>);
99 }
100}
101
102#[derive(Clone, Debug, PartialEq, Eq, Hash)]
103#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
104#[cfg_attr(
105 feature = "python",
106 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, subclass)
107)]
108pub struct Offset {
109 pub offset: u64,
110 pub data_type: ScalarType,
111}
112
113pickleable_new! {
114 impl Offset {
115 pub fn new(offset: u64, data_type: ScalarType);
116 }
117}
118
119impl Quil for Offset {
120 fn write(
121 &self,
122 f: &mut impl std::fmt::Write,
123 fall_back_to_debug: bool,
124 ) -> crate::quil::ToQuilResult<()> {
125 write!(f, "{} ", self.offset)?;
126 self.data_type.write(f, fall_back_to_debug)
127 }
128}
129
130#[derive(Clone, Debug, PartialEq, Eq, Hash)]
131#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
132#[cfg_attr(
133 feature = "python",
134 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
135)]
136pub struct Declaration {
137 pub name: String,
138 pub size: Vector,
139 pub sharing: Option<Sharing>,
140}
141
142pickleable_new! {
143 impl Declaration {
144 pub fn new(name: String, size: Vector, sharing: Option<Sharing>);
145 }
146}
147
148impl Quil for Declaration {
149 fn write(
150 &self,
151 f: &mut impl std::fmt::Write,
152 fall_back_to_debug: bool,
153 ) -> crate::quil::ToQuilResult<()> {
154 write!(f, "DECLARE {} ", self.name)?;
155 self.size.write(f, fall_back_to_debug)?;
156 if let Some(shared) = &self.sharing {
157 write!(f, " SHARING {}", shared.name)?;
158 if !shared.offsets.is_empty() {
159 write!(f, " OFFSET")?;
160 for offset in shared.offsets.iter() {
161 write!(f, " ")?;
162 offset.write(f, fall_back_to_debug)?;
163 }
164 }
165 }
166 Ok(())
167 }
168}
169
170#[cfg(test)]
171mod test_declaration {
172 use super::{Declaration, Offset, ScalarType, Sharing, Vector};
173 use crate::quil::Quil;
174 use insta::assert_snapshot;
175 use rstest::rstest;
176
177 #[rstest]
178 #[case(
179 "Basic Declaration",
180 Declaration{
181 name: "ro".to_string(),
182 size: Vector{data_type: ScalarType::Bit, length: 1},
183 sharing: None,
184
185 }
186 )]
187 #[case(
188 "Shared Declaration",
189 Declaration{
190 name: "ro".to_string(),
191 size: Vector{data_type: ScalarType::Integer, length: 2},
192 sharing: Some(Sharing{name: "foo".to_string(), offsets: vec![]})
193 }
194 )]
195 #[case(
196 "Shared Declaration with Offsets",
197 Declaration{
198 name: "ro".to_string(),
199 size: Vector{data_type: ScalarType::Real, length: 3},
200 sharing: Some(Sharing{
201 name: "bar".to_string(),
202 offsets: vec![
203 Offset{offset: 4, data_type: ScalarType::Bit},
204 Offset{offset: 5, data_type: ScalarType::Bit}
205 ]})
206 }
207 )]
208 fn test_display(#[case] description: &str, #[case] declaration: Declaration) {
209 insta::with_settings!({
210 snapshot_suffix => description,
211 }, {
212 assert_snapshot!(declaration.to_quil_or_debug())
213 })
214 }
215}
216
217#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
218#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
219#[cfg_attr(
220 feature = "python",
221 pyo3::pyclass(module = "quil.instructions", get_all, eq, frozen, hash, subclass)
222)]
223#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
224pub struct MemoryReference {
225 #[cfg_attr(
226 test,
227 proptest(strategy = "crate::expression::proptest_helpers::arb_name()")
228 )]
229 pub name: String,
230 pub index: u64,
231}
232
233pickleable_new! {
234 impl MemoryReference {
235 pub fn new(name: String, index: u64);
236 }
237}
238
239impl Quil for MemoryReference {
240 fn write(
241 &self,
242 f: &mut impl std::fmt::Write,
243 _fall_back_to_debug: bool,
244 ) -> crate::quil::ToQuilResult<()> {
245 write!(f, "{}[{}]", self.name, self.index).map_err(Into::into)
246 }
247}
248
249impl std::fmt::Display for MemoryReference {
250 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
251 write!(f, "{}[{}]", self.name, self.index)
252 }
253}
254
255impl FromStr for MemoryReference {
256 type Err = SyntaxError<Self>;
257
258 fn from_str(s: &str) -> Result<Self, Self::Err> {
259 let input = LocatedSpan::new(s);
260 let tokens = lex(input)?;
261 disallow_leftover(
262 parse_memory_reference(&tokens).map_err(ParseError::from_nom_internal_err),
263 )
264 }
265}
266
267#[derive(Clone, Debug, PartialEq, Eq, Hash)]
268#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
269#[cfg_attr(
270 feature = "python",
271 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
272)]
273pub struct Load {
274 pub destination: MemoryReference,
275 pub source: String,
276 pub offset: MemoryReference,
277}
278
279pickleable_new! {
280 impl Load {
281 pub fn new(destination: MemoryReference, source: String, offset: MemoryReference);
282 }
283}
284
285impl Quil for Load {
286 fn write(
287 &self,
288 f: &mut impl std::fmt::Write,
289 fall_back_to_debug: bool,
290 ) -> crate::quil::ToQuilResult<()> {
291 write!(f, "LOAD ")?;
292 self.destination.write(f, fall_back_to_debug)?;
293 write!(f, " {} ", self.source)?;
294 self.offset.write(f, fall_back_to_debug)?;
295 Ok(())
296 }
297}
298
299#[derive(Clone, Debug, PartialEq, Hash)]
300#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
301#[cfg_attr(
302 feature = "python",
303 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
304)]
305pub struct Store {
306 pub destination: String,
307 pub offset: MemoryReference,
308 pub source: ArithmeticOperand,
309}
310
311pickleable_new! {
312 impl Store {
313 pub fn new(destination: String, offset: MemoryReference, source: ArithmeticOperand);
314 }
315}
316
317impl Quil for Store {
318 fn write(
319 &self,
320 f: &mut impl std::fmt::Write,
321 fall_back_to_debug: bool,
322 ) -> crate::quil::ToQuilResult<()> {
323 write!(f, "STORE {} ", self.destination)?;
324 self.offset.write(f, fall_back_to_debug)?;
325 write!(f, " ")?;
326 self.source.write(f, fall_back_to_debug)?;
327 Ok(())
328 }
329}