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