1use std::{collections::HashSet, str::FromStr};
9
10use indexmap::IndexMap;
11use nom_locate::LocatedSpan;
12use num_complex::Complex64;
13
14#[cfg(feature = "stubs")]
15use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyclass_complex_enum};
16
17use crate::{
18 expression::format_complex,
19 floating_point_eq,
20 parser::lex,
21 pickleable_new,
22 program::{disallow_leftover, MemoryAccesses, MemoryRegion, SyntaxError},
23 quil::Quil,
24 validation::identifier::{validate_user_identifier, IdentifierValidationError},
25};
26
27use super::{
28 Instruction, MemoryReference, Pragma, PragmaArgument, ScalarType, Vector,
29 RESERVED_PRAGMA_EXTERN,
30};
31
32#[derive(Clone, Debug, PartialEq, Hash, Eq)]
34#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
35#[cfg_attr(
36 feature = "python",
37 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash)
38)]
39pub enum ExternParameterType {
40 Scalar(ScalarType),
44 FixedLengthVector(Vector),
49 VariableLengthVector(ScalarType),
54}
55
56impl Quil for ExternParameterType {
57 fn write(
58 &self,
59 f: &mut impl std::fmt::Write,
60 fall_back_to_debug: bool,
61 ) -> crate::quil::ToQuilResult<()> {
62 match self {
63 ExternParameterType::Scalar(value) => value.write(f, fall_back_to_debug),
64 ExternParameterType::FixedLengthVector(value) => value.write(f, fall_back_to_debug),
65 ExternParameterType::VariableLengthVector(value) => {
66 value.write(f, fall_back_to_debug)?;
67 Ok(write!(f, "[]")?)
68 }
69 }
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Hash)]
75#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
76#[cfg_attr(
77 feature = "python",
78 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
79)]
80pub struct ExternParameter {
81 pub(crate) name: String,
83 pub(crate) mutable: bool,
85 pub(crate) data_type: ExternParameterType,
87}
88
89pickleable_new! {
90 impl ExternParameter {
91 pub fn try_new(
94 name: String,
95 mutable: bool,
96 data_type: ExternParameterType,
97 ) -> Result<ExternParameter, ExternError> {
98 validate_user_identifier(name.as_str()).map_err(ExternError::from_boxed)?;
99 Ok(ExternParameter {
100 name,
101 mutable,
102 data_type,
103 })
104 }
105 }
106}
107
108impl ExternParameter {
109 pub fn name(&self) -> &str {
110 self.name.as_str()
111 }
112
113 pub fn mutable(&self) -> bool {
114 self.mutable
115 }
116
117 pub fn data_type(&self) -> &ExternParameterType {
118 &self.data_type
119 }
120}
121
122impl Quil for ExternParameter {
123 fn write(
124 &self,
125 writer: &mut impl std::fmt::Write,
126 fall_back_to_debug: bool,
127 ) -> Result<(), crate::quil::ToQuilError> {
128 write!(writer, "{} : ", self.name)?;
129 if self.mutable {
130 write!(writer, "mut ")?;
131 }
132 self.data_type.write(writer, fall_back_to_debug)
133 }
134}
135
136#[derive(Clone, Debug, PartialEq, Eq, Hash)]
143#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
144#[cfg_attr(
145 feature = "python",
146 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
147)]
148pub struct ExternSignature {
149 pub(crate) return_type: Option<ScalarType>,
151 pub(crate) parameters: Vec<ExternParameter>,
153}
154
155pickleable_new! {
156 impl ExternSignature {
157 pub fn new(return_type: Option<ScalarType>, parameters: Vec<ExternParameter>);
159 }
160}
161
162impl ExternSignature {
163 pub fn return_type(&self) -> Option<&ScalarType> {
164 self.return_type.as_ref()
165 }
166
167 pub fn parameters(&self) -> &[ExternParameter] {
168 self.parameters.as_slice()
169 }
170}
171
172const EXPECTED_PRAGMA_EXTERN_STRUCTURE: &str = "PRAGMA EXTERN {name} \"{scalar type}? (\\(({parameter name} : mut? {parameter type}) (, {parameter name} : mut? {parameter type})*\\))?\"";
173
174#[derive(Debug, thiserror::Error, PartialEq, Clone)]
176pub enum ExternError {
177 #[error(
179 "invalid extern signature syntax: {0:?} (expected `{EXPECTED_PRAGMA_EXTERN_STRUCTURE}`)"
180 )]
181 Syntax(Box<SyntaxError<ExternSignature>>),
182 #[error(
184 "failed to lex extern signature: {0:?} (expected `{EXPECTED_PRAGMA_EXTERN_STRUCTURE}`)"
185 )]
186 Lex(Box<crate::parser::LexError>),
187 #[error("`PRAGMA EXTERN` must have a single argument representing the extern name")]
189 InvalidPragmaArguments,
190 #[error("`PRAGMA EXTERN` instruction has no signature")]
192 NoSignature,
193 #[error("`PRAGMA EXTERN` instruction has no name")]
195 NoName,
196 #[error("ExternPragmaMap contained a pragma that was not EXTERN")]
198 PragmaIsNotExtern,
199 #[error("extern definition has a signature but it has neither a return nor parameters")]
201 NoReturnOrParameters,
202 #[error("invalid identifier: {0:?}")]
204 Name(#[from] Box<IdentifierValidationError>),
205}
206
207impl ExternError {
208 fn from_boxed<T>(value: T) -> Self
209 where
210 ExternError: From<Box<T>>,
211 {
212 ExternError::from(Box::new(value))
213 }
214}
215
216impl FromStr for ExternSignature {
217 type Err = ExternError;
218
219 fn from_str(s: &str) -> Result<Self, Self::Err> {
220 let signature_input = LocatedSpan::new(s);
221 let signature_tokens = lex(signature_input)
222 .map_err(Box::new)
223 .map_err(ExternError::Lex)?;
224 let signature = disallow_leftover(
225 crate::parser::pragma_extern::parse_extern_signature(signature_tokens.as_slice())
226 .map_err(crate::parser::ParseError::from_nom_internal_err),
227 )
228 .map_err(Box::new)
229 .map_err(ExternError::Syntax)?;
230 if signature.return_type.is_none() && signature.parameters.is_empty() {
231 return Err(ExternError::NoReturnOrParameters);
232 }
233 for parameter in &signature.parameters {
234 validate_user_identifier(parameter.name.as_str()).map_err(ExternError::from_boxed)?;
235 }
236 Ok(signature)
237 }
238}
239
240impl Quil for ExternSignature {
241 fn write(
242 &self,
243 writer: &mut impl std::fmt::Write,
244 fall_back_to_debug: bool,
245 ) -> Result<(), crate::quil::ToQuilError> {
246 if let Some(return_type) = &self.return_type {
247 return_type.write(writer, fall_back_to_debug)?;
248 if !self.parameters.is_empty() {
249 write!(writer, " ")?;
250 }
251 }
252 if self.parameters.is_empty() {
253 return Ok(());
254 }
255 write!(writer, "(")?;
256 for (i, parameter) in self.parameters.iter().enumerate() {
257 if i > 0 {
258 write!(writer, ", ")?;
259 }
260 parameter.write(writer, fall_back_to_debug)?;
261 }
262 write!(writer, ")").map_err(Into::into)
263 }
264}
265
266impl TryFrom<Pragma> for ExternSignature {
267 type Error = ExternError;
268
269 fn try_from(value: Pragma) -> Result<Self, ExternError> {
270 if value.name != RESERVED_PRAGMA_EXTERN {
271 return Err(ExternError::PragmaIsNotExtern);
272 }
273 if value.arguments.is_empty()
274 || !matches!(value.arguments[0], PragmaArgument::Identifier(_))
275 {
276 return Err(ExternError::NoName);
277 }
278 if value.arguments.len() > 1 {
279 return Err(ExternError::InvalidPragmaArguments);
280 }
281
282 match value.data {
283 Some(data) => ExternSignature::from_str(data.as_str()),
284 None => Err(ExternError::NoSignature),
285 }
286 }
287}
288
289#[derive(Clone, Debug, PartialEq, Default)]
293#[cfg_attr(feature = "python", derive(pyo3::IntoPyObject))]
294pub struct ExternPragmaMap(IndexMap<Option<String>, Pragma>);
295
296impl ExternPragmaMap {
297 pub(crate) fn len(&self) -> usize {
298 self.0.len()
299 }
300
301 pub(crate) fn into_instructions(self) -> Vec<Instruction> {
302 self.0.into_values().map(Instruction::Pragma).collect()
303 }
304
305 pub fn to_instructions(&self) -> Vec<Instruction> {
307 self.0.values().cloned().map(Instruction::Pragma).collect()
308 }
309
310 pub(crate) fn insert(&mut self, pragma: Pragma) -> Option<Pragma> {
318 self.0.insert(
319 match pragma.arguments.first() {
320 Some(PragmaArgument::Identifier(name)) => Some(name.clone()),
321 _ => None,
322 },
323 pragma,
324 )
325 }
326
327 pub fn extend(&mut self, other: Self) {
333 self.0.extend(other.0);
334 }
335
336 pub(crate) fn retain<F>(&mut self, f: F)
337 where
338 F: FnMut(&Option<String>, &mut Pragma) -> bool,
339 {
340 self.0.retain(f)
341 }
342}
343
344impl std::iter::IntoIterator for ExternPragmaMap {
345 type Item = (Option<String>, Pragma);
346 type IntoIter = indexmap::map::IntoIter<Option<String>, Pragma>;
347
348 fn into_iter(self) -> Self::IntoIter {
349 self.0.into_iter()
350 }
351}
352
353#[derive(Clone, Debug, PartialEq, Default)]
356pub struct ExternSignatureMap(IndexMap<String, ExternSignature>);
357
358impl TryFrom<ExternPragmaMap> for ExternSignatureMap {
359 type Error = (Pragma, ExternError);
362
363 fn try_from(value: ExternPragmaMap) -> Result<Self, Self::Error> {
364 Ok(ExternSignatureMap(
365 value
366 .0
367 .into_iter()
368 .map(|(key, value)| -> Result<_, Self::Error> {
369 match key {
370 Some(name) => {
371 validate_user_identifier(name.as_str())
372 .map_err(ExternError::from_boxed)
373 .map_err(|error| (value.clone(), error))?;
374 let signature = ExternSignature::try_from(value.clone())
375 .map_err(|error| (value, error))?;
376 Ok((name, signature))
377 }
378 _ => Err((value, ExternError::NoName)),
379 }
380 })
381 .collect::<Result<_, Self::Error>>()?,
382 ))
383 }
384}
385
386impl ExternSignatureMap {
387 #[inline]
388 pub fn len(&self) -> usize {
389 self.0.len()
390 }
391
392 #[inline]
393 pub fn is_empty(&self) -> bool {
394 self.0.is_empty()
395 }
396
397 #[inline]
398 pub fn iter(&self) -> impl Iterator<Item = (&String, &ExternSignature)> {
399 self.0.iter()
400 }
401}
402
403#[derive(Clone, Debug, thiserror::Error, PartialEq)]
405pub enum CallArgumentResolutionError {
406 #[error("undeclared memory reference {0}")]
408 UndeclaredMemoryReference(String),
409 #[error("mismatched vector: expected {expected:?}, found {found:?}")]
411 MismatchedVector { expected: Vector, found: Vector },
412 #[error("mismatched scalar: expected {expected:?}, found {found:?}")]
414 MismatchedScalar {
415 expected: ScalarType,
416 found: ScalarType,
417 },
418 #[error("vector parameters must be passed as an identifier, found {0:?}")]
420 InvalidVectorArgument(UnresolvedCallArgument),
421 #[error("return argument must be a memory reference or identifier, found {found:?}")]
423 ReturnArgument { found: UnresolvedCallArgument },
424 #[error("immediate arguments cannot be specified for mutable parameter {0}")]
426 ImmediateArgumentForMutable(String),
427}
428
429#[derive(Clone, Debug)]
433#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
434#[cfg_attr(
435 feature = "python",
436 pyo3::pyclass(name = "CallArgument", module = "quil.instructions", eq, frozen, hash)
437)]
438pub enum UnresolvedCallArgument {
439 Identifier(String),
442 MemoryReference(MemoryReference),
444 Immediate(Complex64),
446}
447
448impl PartialEq for UnresolvedCallArgument {
449 fn eq(&self, other: &Self) -> bool {
450 match (self, other) {
451 (Self::Identifier(this), Self::Identifier(that)) => this == that,
452 (Self::MemoryReference(this), Self::MemoryReference(that)) => this == that,
453 (Self::Immediate(this), Self::Immediate(that)) => {
454 floating_point_eq::complex64::eq(*this, *that)
455 }
456 (Self::Identifier(_) | Self::MemoryReference(_) | Self::Immediate(_), _) => false,
459 }
460 }
461}
462
463impl Eq for UnresolvedCallArgument {}
464
465impl std::hash::Hash for UnresolvedCallArgument {
466 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
467 match self {
468 UnresolvedCallArgument::Identifier(value) => {
469 "Identifier".hash(state);
470 value.hash(state);
471 }
472 UnresolvedCallArgument::MemoryReference(value) => {
473 "MemoryReference".hash(state);
474 value.hash(state);
475 }
476 UnresolvedCallArgument::Immediate(value) => {
477 "Immediate".hash(state);
478 floating_point_eq::complex64::hash(*value, state);
479 }
480 }
481 }
482}
483
484impl UnresolvedCallArgument {
485 fn resolve(
488 &self,
489 memory_regions: &IndexMap<String, MemoryRegion>,
490 extern_parameter: &ExternParameter,
491 ) -> Result<ResolvedCallArgument, CallArgumentResolutionError> {
492 match self {
493 UnresolvedCallArgument::Identifier(value) => {
494 let expected_vector = match &extern_parameter.data_type {
495 ExternParameterType::Scalar(_) => {
496 return UnresolvedCallArgument::MemoryReference(MemoryReference::new(
497 value.clone(),
498 0,
499 ))
500 .resolve(memory_regions, extern_parameter);
501 }
502 ExternParameterType::FixedLengthVector(expected_vector) => {
503 let memory_region =
504 memory_regions.get(value.as_str()).ok_or_else(|| {
505 CallArgumentResolutionError::UndeclaredMemoryReference(
506 value.clone(),
507 )
508 })?;
509 if &memory_region.size != expected_vector {
510 return Err(CallArgumentResolutionError::MismatchedVector {
511 expected: expected_vector.clone(),
512 found: memory_region.size.clone(),
513 });
514 }
515
516 Ok(expected_vector.clone())
517 }
518 ExternParameterType::VariableLengthVector(scalar_type) => {
519 let memory_region =
520 memory_regions.get(value.as_str()).ok_or_else(|| {
521 CallArgumentResolutionError::UndeclaredMemoryReference(
522 value.clone(),
523 )
524 })?;
525 if &memory_region.size.data_type != scalar_type {
526 return Err(CallArgumentResolutionError::MismatchedScalar {
527 expected: *scalar_type,
528 found: memory_region.size.data_type,
529 });
530 }
531 Ok(memory_region.size.clone())
532 }
533 }?;
534 Ok(ResolvedCallArgument::Vector {
535 memory_region_name: value.clone(),
536 vector: expected_vector,
537 mutable: extern_parameter.mutable,
538 })
539 }
540 UnresolvedCallArgument::MemoryReference(value) => {
541 let expected_scalar = match extern_parameter.data_type {
542 ExternParameterType::Scalar(ref scalar) => Ok(scalar),
543 ExternParameterType::FixedLengthVector(_)
544 | ExternParameterType::VariableLengthVector(_) => {
545 Err(CallArgumentResolutionError::InvalidVectorArgument(
546 Self::MemoryReference(value.clone()),
547 ))
548 }
549 }?;
550 let memory_region = memory_regions.get(value.name.as_str()).ok_or_else(|| {
551 CallArgumentResolutionError::UndeclaredMemoryReference(value.name.clone())
552 })?;
553 if memory_region.size.data_type != *expected_scalar {
554 return Err(CallArgumentResolutionError::MismatchedScalar {
555 expected: *expected_scalar,
556 found: memory_region.size.data_type,
557 });
558 }
559 Ok(ResolvedCallArgument::MemoryReference {
560 memory_reference: value.clone(),
561 scalar_type: *expected_scalar,
562 mutable: extern_parameter.mutable,
563 })
564 }
565 UnresolvedCallArgument::Immediate(value) => {
566 if extern_parameter.mutable {
567 return Err(CallArgumentResolutionError::ImmediateArgumentForMutable(
568 extern_parameter.name.clone(),
569 ));
570 }
571 let expected_scalar = match extern_parameter.data_type {
572 ExternParameterType::Scalar(ref scalar) => Ok(scalar),
573 ExternParameterType::FixedLengthVector(_)
574 | ExternParameterType::VariableLengthVector(_) => Err(
575 CallArgumentResolutionError::InvalidVectorArgument(self.clone()),
576 ),
577 }?;
578 Ok(ResolvedCallArgument::Immediate {
579 value: *value,
580 scalar_type: *expected_scalar,
581 })
582 }
583 }
584 }
585
586 fn resolve_return(
589 &self,
590 memory_regions: &IndexMap<String, MemoryRegion>,
591 return_type: ScalarType,
592 ) -> Result<ResolvedCallArgument, CallArgumentResolutionError> {
593 let memory_reference = match self {
594 UnresolvedCallArgument::MemoryReference(memory_reference) => {
595 Ok(memory_reference.clone())
596 }
597 UnresolvedCallArgument::Identifier(identifier) => {
598 Ok(MemoryReference::new(identifier.clone(), 0))
599 }
600 _ => Err(CallArgumentResolutionError::ReturnArgument {
601 found: self.clone(),
602 }),
603 }?;
604 let memory_region = memory_regions
605 .get(memory_reference.name.as_str())
606 .ok_or_else(|| {
607 CallArgumentResolutionError::UndeclaredMemoryReference(
608 memory_reference.name.clone(),
609 )
610 })?;
611 if memory_region.size.data_type != return_type {
612 return Err(CallArgumentResolutionError::MismatchedScalar {
613 expected: return_type,
614 found: memory_region.size.data_type,
615 });
616 }
617 Ok(ResolvedCallArgument::MemoryReference {
618 memory_reference: memory_reference.clone(),
619 scalar_type: return_type,
620 mutable: true,
621 })
622 }
623}
624
625impl Quil for UnresolvedCallArgument {
626 fn write(
627 &self,
628 f: &mut impl std::fmt::Write,
629 fall_back_to_debug: bool,
630 ) -> crate::quil::ToQuilResult<()> {
631 match &self {
632 UnresolvedCallArgument::Identifier(value) => write!(f, "{value}",).map_err(Into::into),
633 UnresolvedCallArgument::MemoryReference(value) => value.write(f, fall_back_to_debug),
634 UnresolvedCallArgument::Immediate(value) => {
635 write!(f, "{}", format_complex(value)).map_err(Into::into)
636 }
637 }
638 }
639}
640
641#[derive(Clone, Debug)]
645pub enum ResolvedCallArgument {
646 Vector {
648 memory_region_name: String,
649 vector: Vector,
650 mutable: bool,
651 },
652 MemoryReference {
654 memory_reference: MemoryReference,
655 scalar_type: ScalarType,
656 mutable: bool,
657 },
658 Immediate {
660 value: Complex64,
661 scalar_type: ScalarType,
662 },
663}
664
665impl PartialEq for ResolvedCallArgument {
666 fn eq(&self, other: &Self) -> bool {
667 match (self, other) {
668 (
669 Self::Vector {
670 memory_region_name: this_memory_region_name,
671 vector: this_vector,
672 mutable: this_mutable,
673 },
674 Self::Vector {
675 memory_region_name: that_memory_region_name,
676 vector: that_vector,
677 mutable: that_mutable,
678 },
679 ) => {
680 this_memory_region_name == that_memory_region_name
681 && this_vector == that_vector
682 && this_mutable == that_mutable
683 }
684
685 (
686 Self::MemoryReference {
687 memory_reference: this_memory_reference,
688 scalar_type: this_scalar_type,
689 mutable: this_mutable,
690 },
691 Self::MemoryReference {
692 memory_reference: that_memory_reference,
693 scalar_type: that_scalar_type,
694 mutable: that_mutable,
695 },
696 ) => {
697 this_memory_reference == that_memory_reference
698 && this_scalar_type == that_scalar_type
699 && this_mutable == that_mutable
700 }
701
702 (
703 Self::Immediate {
704 value: this_value,
705 scalar_type: this_scalar_type,
706 },
707 Self::Immediate {
708 value: that_value,
709 scalar_type: that_scalar_type,
710 },
711 ) => {
712 floating_point_eq::complex64::eq(*this_value, *that_value)
713 && this_scalar_type == that_scalar_type
714 }
715
716 (Self::Vector { .. } | Self::MemoryReference { .. } | Self::Immediate { .. }, _) => {
719 false
720 }
721 }
722 }
723}
724
725impl From<ResolvedCallArgument> for UnresolvedCallArgument {
726 fn from(value: ResolvedCallArgument) -> Self {
727 match value {
728 ResolvedCallArgument::Vector {
729 memory_region_name, ..
730 } => UnresolvedCallArgument::Identifier(memory_region_name),
731 ResolvedCallArgument::MemoryReference {
732 memory_reference, ..
733 } => UnresolvedCallArgument::MemoryReference(memory_reference),
734 ResolvedCallArgument::Immediate { value, .. } => {
735 UnresolvedCallArgument::Immediate(value)
736 }
737 }
738 }
739}
740
741impl Eq for ResolvedCallArgument {}
742
743impl std::hash::Hash for ResolvedCallArgument {
744 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
745 match self {
746 ResolvedCallArgument::Vector {
747 memory_region_name,
748 vector,
749 mutable,
750 } => {
751 "Vector".hash(state);
752 memory_region_name.hash(state);
753 vector.hash(state);
754 mutable.hash(state);
755 }
756 ResolvedCallArgument::MemoryReference {
757 memory_reference,
758 scalar_type,
759 mutable,
760 } => {
761 "MemoryReference".hash(state);
762 memory_reference.hash(state);
763 scalar_type.hash(state);
764 mutable.hash(state);
765 }
766 ResolvedCallArgument::Immediate { value, scalar_type } => {
767 "Immediate".hash(state);
768 floating_point_eq::complex64::hash(*value, state);
769 scalar_type.hash(state);
770 }
771 }
772 }
773}
774
775#[derive(Clone, Debug, PartialEq, thiserror::Error, Eq)]
777pub enum CallError {
778 #[error(transparent)]
780 Name(#[from] IdentifierValidationError),
781}
782
783#[derive(Clone, Debug, PartialEq, Hash, Eq)]
796#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
797#[cfg_attr(
798 feature = "python",
799 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, subclass, get_all)
800)]
801pub struct Call {
802 pub name: String,
804 pub arguments: Vec<UnresolvedCallArgument>,
806}
807
808pickleable_new! {
809 impl Call {
810 pub fn try_new(
813 name: String,
814 arguments: Vec<UnresolvedCallArgument>,
815 ) -> Result<Call, CallError> {
816 validate_user_identifier(name.as_str()).map_err(CallError::Name)?;
817
818 Ok(Self { name, arguments })
819 }
820 }
821}
822
823impl Call {
824 pub fn arguments(&self) -> &[UnresolvedCallArgument] {
825 self.arguments.as_slice()
826 }
827}
828
829#[derive(Clone, Debug, thiserror::Error, PartialEq)]
831pub enum CallArgumentError {
832 #[error("error resolving return argument: {0:?}")]
834 Return(CallArgumentResolutionError),
835 #[error("error resolving argument {index}: {error:?}")]
837 Argument {
838 index: usize,
839 error: CallArgumentResolutionError,
840 },
841}
842
843#[derive(Debug, thiserror::Error, PartialEq, Clone)]
846pub enum CallSignatureError {
847 #[error("expected {expected} arguments, found {found}")]
848 ParameterCount { expected: usize, found: usize },
849 #[error("error resolving arguments: {0:?}")]
850 Arguments(Vec<CallArgumentError>),
851}
852
853#[derive(Debug, thiserror::Error, PartialEq, Clone)]
856pub enum CallResolutionError {
857 #[error("call found matching extern instruction for {name}, but signature validation failed: {error:?}")]
859 Signature {
860 name: String,
861 error: CallSignatureError,
862 },
863 #[error("no extern instruction found with name {0}")]
865 NoMatchingExternInstruction(String),
866 #[error(transparent)]
868 ExternSignature(#[from] ExternError),
869}
870
871#[allow(clippy::manual_try_fold)]
872fn convert_unresolved_to_resolved_call_arguments(
873 arguments: &[UnresolvedCallArgument],
874 signature: &ExternSignature,
875 memory_regions: &IndexMap<String, MemoryRegion>,
876) -> Result<Vec<ResolvedCallArgument>, CallSignatureError> {
877 arguments
878 .iter()
879 .enumerate()
880 .map(|(i, argument)| {
881 if i == 0 {
882 if let Some(return_type) = signature.return_type {
883 return argument
884 .resolve_return(memory_regions, return_type)
885 .map_err(CallArgumentError::Return);
886 }
887 }
888 let parameter_index = if signature.return_type.is_some() {
889 i - 1
890 } else {
891 i
892 };
893 let parameter = &signature.parameters[parameter_index];
894 argument
895 .resolve(memory_regions, parameter)
896 .map_err(|error| CallArgumentError::Argument {
897 index: parameter_index,
898 error,
899 })
900 })
901 .fold(
902 Ok(Vec::new()),
903 |acc: Result<Vec<ResolvedCallArgument>, Vec<CallArgumentError>>,
904 result: Result<ResolvedCallArgument, CallArgumentError>| {
905 match (acc, result) {
906 (Ok(mut acc), Ok(resolved)) => {
907 acc.push(resolved);
908 Ok(acc)
909 }
910 (Ok(_), Err(error)) => Err(vec![error]),
911 (Err(errors), Ok(_)) => Err(errors),
912 (Err(mut errors), Err(error)) => {
913 errors.push(error);
914 Err(errors)
915 }
916 }
917 },
918 )
919 .map_err(CallSignatureError::Arguments)
920}
921
922impl Call {
923 fn resolve_to_signature(
925 &self,
926 signature: &ExternSignature,
927 memory_regions: &IndexMap<String, MemoryRegion>,
928 ) -> Result<Vec<ResolvedCallArgument>, CallSignatureError> {
929 let mut expected_parameter_count = signature.parameters.len();
930 if signature.return_type.is_some() {
931 expected_parameter_count += 1;
932 }
933
934 if self.arguments.len() != expected_parameter_count {
935 return Err(CallSignatureError::ParameterCount {
936 expected: expected_parameter_count,
937 found: self.arguments.len(),
938 });
939 }
940
941 let resolved_call_arguments = convert_unresolved_to_resolved_call_arguments(
942 &self.arguments,
943 signature,
944 memory_regions,
945 )?;
946
947 Ok(resolved_call_arguments)
948 }
949
950 pub fn resolve_arguments(
953 &self,
954 memory_regions: &IndexMap<String, MemoryRegion>,
955 extern_signature_map: &ExternSignatureMap,
956 ) -> Result<Vec<ResolvedCallArgument>, CallResolutionError> {
957 let extern_signature = extern_signature_map
958 .0
959 .get(self.name.as_str())
960 .ok_or_else(|| CallResolutionError::NoMatchingExternInstruction(self.name.clone()))?;
961
962 self.resolve_to_signature(extern_signature, memory_regions)
963 .map_err(|error| CallResolutionError::Signature {
964 name: self.name.clone(),
965 error,
966 })
967 }
968
969 pub(crate) fn get_memory_accesses(
972 &self,
973 extern_signatures: &ExternSignatureMap,
974 ) -> Result<MemoryAccesses, CallResolutionError> {
975 let extern_signature = extern_signatures
976 .0
977 .get(self.name.as_str())
978 .ok_or_else(|| CallResolutionError::NoMatchingExternInstruction(self.name.clone()))?;
979
980 let mut reads = HashSet::new();
981 let mut writes = HashSet::new();
982 let mut arguments = self.arguments.iter();
983 if extern_signature.return_type.is_some() {
984 if let Some(argument) = self.arguments.first() {
985 arguments.next();
986 match argument {
987 UnresolvedCallArgument::MemoryReference(memory_reference) => {
988 reads.insert(memory_reference.name.clone());
989 writes.insert(memory_reference.name.clone());
990 }
991 UnresolvedCallArgument::Identifier(identifier) => {
992 reads.insert(identifier.clone());
993 writes.insert(identifier.clone());
994 }
995 _ => {}
996 }
997 }
998 }
999 for (argument, parameter) in std::iter::zip(arguments, extern_signature.parameters.iter()) {
1000 match argument {
1001 UnresolvedCallArgument::MemoryReference(memory_reference) => {
1002 reads.insert(memory_reference.name.clone());
1003 if parameter.mutable {
1004 writes.insert(memory_reference.name.clone());
1005 }
1006 }
1007 UnresolvedCallArgument::Identifier(identifier) => {
1008 reads.insert(identifier.clone());
1009 if parameter.mutable {
1010 writes.insert(identifier.clone());
1011 }
1012 }
1013 _ => {}
1014 }
1015 }
1016 Ok(MemoryAccesses {
1017 reads,
1018 writes,
1019 captures: HashSet::new(),
1020 })
1021 }
1022}
1023
1024impl Quil for Call {
1025 fn write(
1026 &self,
1027 f: &mut impl std::fmt::Write,
1028 fall_back_to_debug: bool,
1029 ) -> crate::quil::ToQuilResult<()> {
1030 write!(f, "CALL {}", self.name)?;
1031 for argument in self.arguments.as_slice() {
1032 write!(f, " ")?;
1033 argument.write(f, fall_back_to_debug)?;
1034 }
1035 Ok(())
1036 }
1037}
1038
1039#[cfg(test)]
1040mod tests {
1041 use super::*;
1042 use crate::instruction::PragmaArgument;
1043 use rstest::*;
1044
1045 struct ExternSignatureQuilTestCase {
1047 signature: ExternSignature,
1049 expected: &'static str,
1051 }
1052
1053 impl ExternSignatureQuilTestCase {
1054 fn case_01() -> Self {
1056 Self {
1057 signature: ExternSignature {
1058 return_type: Some(ScalarType::Integer),
1059 parameters: vec![
1060 ExternParameter {
1061 name: "bar".to_string(),
1062 mutable: false,
1063 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1064 },
1065 ExternParameter {
1066 name: "baz".to_string(),
1067 mutable: true,
1068 data_type: ExternParameterType::FixedLengthVector(Vector {
1069 data_type: ScalarType::Bit,
1070 length: 2,
1071 }),
1072 },
1073 ],
1074 },
1075 expected: "INTEGER (bar : INTEGER, baz : mut BIT[2])",
1076 }
1077 }
1078
1079 fn case_02() -> Self {
1081 let signature = ExternSignature {
1082 return_type: None,
1083 parameters: vec![
1084 ExternParameter {
1085 name: "bar".to_string(),
1086 mutable: false,
1087 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1088 },
1089 ExternParameter {
1090 name: "baz".to_string(),
1091 mutable: true,
1092 data_type: ExternParameterType::FixedLengthVector(Vector {
1093 data_type: ScalarType::Bit,
1094 length: 2,
1095 }),
1096 },
1097 ],
1098 };
1099 Self {
1100 signature,
1101 expected: "(bar : INTEGER, baz : mut BIT[2])",
1102 }
1103 }
1104
1105 fn case_03() -> Self {
1107 let signature = ExternSignature {
1108 return_type: Some(ScalarType::Integer),
1109 parameters: vec![],
1110 };
1111 Self {
1112 signature,
1113 expected: "INTEGER",
1114 }
1115 }
1116
1117 fn case_04() -> Self {
1119 let signature = ExternSignature {
1120 return_type: None,
1121 parameters: vec![],
1122 };
1123 Self {
1124 signature,
1125 expected: "",
1126 }
1127 }
1128
1129 fn case_05() -> Self {
1131 let signature = ExternSignature {
1132 return_type: None,
1133 parameters: vec![ExternParameter {
1134 name: "bar".to_string(),
1135 mutable: false,
1136 data_type: ExternParameterType::VariableLengthVector(ScalarType::Integer),
1137 }],
1138 };
1139 Self {
1140 signature,
1141 expected: "(bar : INTEGER[])",
1142 }
1143 }
1144 }
1145
1146 #[rstest]
1148 #[case(ExternSignatureQuilTestCase::case_01())]
1149 #[case(ExternSignatureQuilTestCase::case_02())]
1150 #[case(ExternSignatureQuilTestCase::case_03())]
1151 #[case(ExternSignatureQuilTestCase::case_04())]
1152 #[case(ExternSignatureQuilTestCase::case_05())]
1153 #[case(ExternSignatureQuilTestCase::case_05())]
1154 fn test_extern_signature_quil(#[case] test_case: ExternSignatureQuilTestCase) {
1155 assert_eq!(
1156 test_case
1157 .signature
1158 .to_quil()
1159 .expect("must be able to call to quil"),
1160 test_case.expected.to_string()
1161 );
1162 }
1163
1164 struct CallQuilTestCase {
1166 call: Call,
1168 expected: &'static str,
1170 }
1171
1172 impl CallQuilTestCase {
1173 fn case_01() -> Self {
1174 let call = Call {
1175 name: "foo".to_string(),
1176 arguments: vec![
1177 UnresolvedCallArgument::MemoryReference(MemoryReference {
1178 name: "bar".to_string(),
1179 index: 0,
1180 }),
1181 UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1182 UnresolvedCallArgument::Identifier("baz".to_string()),
1183 ],
1184 };
1185 Self {
1186 call,
1187 expected: "CALL foo bar[0] 2 baz",
1188 }
1189 }
1190
1191 fn case_02() -> Self {
1192 let call = Call {
1193 name: "foo".to_string(),
1194 arguments: vec![
1195 UnresolvedCallArgument::MemoryReference(MemoryReference {
1196 name: "bar".to_string(),
1197 index: 0,
1198 }),
1199 UnresolvedCallArgument::Identifier("baz".to_string()),
1200 ],
1201 };
1202 Self {
1203 call,
1204 expected: "CALL foo bar[0] baz",
1205 }
1206 }
1207
1208 fn case_03() -> Self {
1209 let call = Call {
1210 name: "foo".to_string(),
1211 arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
1212 name: "bar".to_string(),
1213 index: 0,
1214 })],
1215 };
1216 Self {
1217 call,
1218 expected: "CALL foo bar[0]",
1219 }
1220 }
1221
1222 fn case_04() -> Self {
1224 let call = Call {
1225 name: "foo".to_string(),
1226 arguments: vec![],
1227 };
1228
1229 Self {
1230 call,
1231 expected: "CALL foo",
1232 }
1233 }
1234 }
1235
1236 #[rstest]
1238 #[case(CallQuilTestCase::case_01())]
1239 #[case(CallQuilTestCase::case_02())]
1240 #[case(CallQuilTestCase::case_03())]
1241 #[case(CallQuilTestCase::case_04())]
1242 fn test_call_quil(#[case] test_case: CallQuilTestCase) {
1243 assert_eq!(
1244 test_case
1245 .call
1246 .to_quil()
1247 .expect("must be able to call to quil"),
1248 test_case.expected.to_string()
1249 );
1250 }
1251
1252 fn build_declarations() -> IndexMap<String, MemoryRegion> {
1254 [
1255 ("integer", Vector::new(ScalarType::Integer, 3)),
1256 ("real", Vector::new(ScalarType::Real, 3)),
1257 ("bit", Vector::new(ScalarType::Bit, 3)),
1258 ("octet", Vector::new(ScalarType::Octet, 3)),
1259 ]
1260 .into_iter()
1261 .map(|(name, vector)| (name.to_string(), MemoryRegion::new(vector, None)))
1262 .collect()
1263 }
1264
1265 struct ArgumentResolutionTestCase {
1267 call_argument: UnresolvedCallArgument,
1268 extern_parameter: ExternParameter,
1269 expected: Result<ResolvedCallArgument, CallArgumentResolutionError>,
1270 }
1271
1272 impl ArgumentResolutionTestCase {
1273 fn case_01() -> Self {
1275 ArgumentResolutionTestCase {
1276 call_argument: UnresolvedCallArgument::MemoryReference(MemoryReference {
1277 name: "integer".to_string(),
1278 index: 0,
1279 }),
1280 extern_parameter: ExternParameter {
1281 name: "bar".to_string(),
1282 mutable: false,
1283 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1284 },
1285 expected: Ok(ResolvedCallArgument::MemoryReference {
1286 memory_reference: MemoryReference {
1287 name: "integer".to_string(),
1288 index: 0,
1289 },
1290 scalar_type: ScalarType::Integer,
1291 mutable: false,
1292 }),
1293 }
1294 }
1295
1296 fn case_02() -> Self {
1298 ArgumentResolutionTestCase {
1299 call_argument: UnresolvedCallArgument::Identifier("real".to_string()),
1300 extern_parameter: ExternParameter {
1301 name: "bar".to_string(),
1302 mutable: false,
1303 data_type: ExternParameterType::FixedLengthVector(Vector {
1304 data_type: ScalarType::Real,
1305 length: 3,
1306 }),
1307 },
1308 expected: Ok(ResolvedCallArgument::Vector {
1309 memory_region_name: "real".to_string(),
1310 vector: Vector {
1311 data_type: ScalarType::Real,
1312 length: 3,
1313 },
1314 mutable: false,
1315 }),
1316 }
1317 }
1318
1319 fn case_03() -> Self {
1321 ArgumentResolutionTestCase {
1322 call_argument: UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1323 extern_parameter: ExternParameter {
1324 name: "bar".to_string(),
1325 mutable: false,
1326 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1327 },
1328 expected: Ok(ResolvedCallArgument::Immediate {
1329 value: Complex64::new(2.0, 0.0),
1330 scalar_type: ScalarType::Integer,
1331 }),
1332 }
1333 }
1334
1335 fn case_04() -> Self {
1337 ArgumentResolutionTestCase {
1338 call_argument: UnresolvedCallArgument::Identifier("undeclared".to_string()),
1339 extern_parameter: ExternParameter {
1340 name: "bar".to_string(),
1341 mutable: false,
1342 data_type: ExternParameterType::FixedLengthVector(Vector {
1343 data_type: ScalarType::Real,
1344 length: 3,
1345 }),
1346 },
1347 expected: Err(CallArgumentResolutionError::UndeclaredMemoryReference(
1348 "undeclared".to_string(),
1349 )),
1350 }
1351 }
1352
1353 fn case_05() -> Self {
1355 ArgumentResolutionTestCase {
1356 call_argument: UnresolvedCallArgument::MemoryReference(MemoryReference {
1357 name: "undeclared".to_string(),
1358 index: 0,
1359 }),
1360 extern_parameter: ExternParameter {
1361 name: "bar".to_string(),
1362 mutable: false,
1363 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1364 },
1365 expected: Err(CallArgumentResolutionError::UndeclaredMemoryReference(
1366 "undeclared".to_string(),
1367 )),
1368 }
1369 }
1370
1371 fn case_06() -> Self {
1373 ArgumentResolutionTestCase {
1374 call_argument: UnresolvedCallArgument::Identifier("integer".to_string()),
1375 extern_parameter: ExternParameter {
1376 name: "bar".to_string(),
1377 mutable: false,
1378 data_type: ExternParameterType::FixedLengthVector(Vector {
1379 data_type: ScalarType::Real,
1380 length: 3,
1381 }),
1382 },
1383 expected: Err(CallArgumentResolutionError::MismatchedVector {
1384 expected: Vector {
1385 data_type: ScalarType::Real,
1386 length: 3,
1387 },
1388 found: Vector {
1389 data_type: ScalarType::Integer,
1390 length: 3,
1391 },
1392 }),
1393 }
1394 }
1395
1396 fn case_07() -> Self {
1398 ArgumentResolutionTestCase {
1399 call_argument: UnresolvedCallArgument::Identifier("integer".to_string()),
1400 extern_parameter: ExternParameter {
1401 name: "bar".to_string(),
1402 mutable: false,
1403 data_type: ExternParameterType::FixedLengthVector(Vector {
1404 data_type: ScalarType::Integer,
1405 length: 4,
1406 }),
1407 },
1408 expected: Err(CallArgumentResolutionError::MismatchedVector {
1409 expected: Vector {
1410 data_type: ScalarType::Integer,
1411 length: 4,
1412 },
1413 found: Vector {
1414 data_type: ScalarType::Integer,
1415 length: 3,
1416 },
1417 }),
1418 }
1419 }
1420
1421 fn case_08() -> Self {
1423 ArgumentResolutionTestCase {
1424 call_argument: UnresolvedCallArgument::MemoryReference(MemoryReference {
1425 name: "octet".to_string(),
1426 index: 0,
1427 }),
1428 extern_parameter: ExternParameter {
1429 name: "bar".to_string(),
1430 mutable: false,
1431 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1432 },
1433 expected: Err(CallArgumentResolutionError::MismatchedScalar {
1434 expected: ScalarType::Integer,
1435 found: ScalarType::Octet,
1436 }),
1437 }
1438 }
1439
1440 fn case_09() -> Self {
1443 let call_argument = UnresolvedCallArgument::Identifier("integer".to_string());
1444 ArgumentResolutionTestCase {
1445 call_argument: call_argument.clone(),
1446 extern_parameter: ExternParameter {
1447 name: "bar".to_string(),
1448 mutable: false,
1449 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1450 },
1451 expected: Ok(ResolvedCallArgument::MemoryReference {
1452 memory_reference: MemoryReference::new("integer".to_string(), 0),
1453 scalar_type: ScalarType::Integer,
1454 mutable: false,
1455 }),
1456 }
1457 }
1458
1459 fn case_10() -> Self {
1461 let call_argument = UnresolvedCallArgument::MemoryReference(MemoryReference {
1462 name: "integer".to_string(),
1463 index: 0,
1464 });
1465 ArgumentResolutionTestCase {
1466 call_argument: call_argument.clone(),
1467 extern_parameter: ExternParameter {
1468 name: "bar".to_string(),
1469 mutable: false,
1470 data_type: ExternParameterType::FixedLengthVector(Vector {
1471 data_type: ScalarType::Integer,
1472 length: 3,
1473 }),
1474 },
1475 expected: Err(CallArgumentResolutionError::InvalidVectorArgument(
1476 call_argument,
1477 )),
1478 }
1479 }
1480
1481 fn case_11() -> Self {
1483 let call_argument = UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0));
1484 ArgumentResolutionTestCase {
1485 call_argument: call_argument.clone(),
1486 extern_parameter: ExternParameter {
1487 name: "bar".to_string(),
1488 mutable: false,
1489 data_type: ExternParameterType::FixedLengthVector(Vector {
1490 data_type: ScalarType::Integer,
1491 length: 3,
1492 }),
1493 },
1494 expected: Err(CallArgumentResolutionError::InvalidVectorArgument(
1495 call_argument,
1496 )),
1497 }
1498 }
1499
1500 fn case_12() -> Self {
1503 let call_argument = UnresolvedCallArgument::Identifier("integer".to_string());
1504 ArgumentResolutionTestCase {
1505 call_argument: call_argument.clone(),
1506 extern_parameter: ExternParameter {
1507 name: "bar".to_string(),
1508 mutable: false,
1509 data_type: ExternParameterType::VariableLengthVector(ScalarType::Integer),
1510 },
1511 expected: Ok(ResolvedCallArgument::Vector {
1512 memory_region_name: "integer".to_string(),
1513 mutable: false,
1514 vector: Vector {
1515 data_type: ScalarType::Integer,
1516 length: 3,
1517 },
1518 }),
1519 }
1520 }
1521
1522 fn case_13() -> Self {
1524 let call_argument = UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0));
1525 ArgumentResolutionTestCase {
1526 call_argument: call_argument.clone(),
1527 extern_parameter: ExternParameter {
1528 name: "bar".to_string(),
1529 mutable: true,
1530 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1531 },
1532 expected: Err(CallArgumentResolutionError::ImmediateArgumentForMutable(
1533 "bar".to_string(),
1534 )),
1535 }
1536 }
1537 }
1538
1539 #[rstest]
1541 #[case(ArgumentResolutionTestCase::case_01())]
1542 #[case(ArgumentResolutionTestCase::case_02())]
1543 #[case(ArgumentResolutionTestCase::case_03())]
1544 #[case(ArgumentResolutionTestCase::case_04())]
1545 #[case(ArgumentResolutionTestCase::case_05())]
1546 #[case(ArgumentResolutionTestCase::case_06())]
1547 #[case(ArgumentResolutionTestCase::case_07())]
1548 #[case(ArgumentResolutionTestCase::case_08())]
1549 #[case(ArgumentResolutionTestCase::case_09())]
1550 #[case(ArgumentResolutionTestCase::case_10())]
1551 #[case(ArgumentResolutionTestCase::case_11())]
1552 #[case(ArgumentResolutionTestCase::case_12())]
1553 #[case(ArgumentResolutionTestCase::case_13())]
1554 fn test_argument_resolution(#[case] test_case: ArgumentResolutionTestCase) {
1555 let memory_regions = build_declarations();
1556 let found = test_case
1557 .call_argument
1558 .resolve(&memory_regions, &test_case.extern_parameter);
1559 match (test_case.expected, found) {
1560 (Ok(expected), Ok(found)) => assert_eq!(expected, found),
1561 (Ok(expected), Err(found)) => {
1562 panic!("expected resolution {expected:?}, found err {found:?}")
1563 }
1564 (Err(expected), Ok(found)) => {
1565 panic!("expected err {expected:?}, found resolution {found:?}")
1566 }
1567 (Err(expected), Err(found)) => assert_eq!(expected, found),
1568 }
1569 }
1570
1571 struct ReturnArgumentResolutionTestCase {
1573 call_argument: UnresolvedCallArgument,
1575 return_type: ScalarType,
1577 expected: Result<ResolvedCallArgument, CallArgumentResolutionError>,
1579 }
1580
1581 impl ReturnArgumentResolutionTestCase {
1582 fn case_01() -> Self {
1584 let call_argument = UnresolvedCallArgument::MemoryReference(MemoryReference {
1585 name: "integer".to_string(),
1586 index: 0,
1587 });
1588 let expected = Ok(ResolvedCallArgument::MemoryReference {
1589 memory_reference: MemoryReference {
1590 name: "integer".to_string(),
1591 index: 0,
1592 },
1593 scalar_type: ScalarType::Integer,
1594 mutable: true,
1595 });
1596 Self {
1597 call_argument,
1598 return_type: ScalarType::Integer,
1599 expected,
1600 }
1601 }
1602
1603 fn case_02() -> Self {
1605 let call_argument = UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0));
1606 let expected = Err(CallArgumentResolutionError::ReturnArgument {
1607 found: call_argument.clone(),
1608 });
1609 Self {
1610 call_argument,
1611 return_type: ScalarType::Integer,
1612 expected,
1613 }
1614 }
1615
1616 fn case_03() -> Self {
1618 let call_argument = UnresolvedCallArgument::Identifier("integer".to_string());
1619 let expected = Ok(ResolvedCallArgument::MemoryReference {
1620 memory_reference: MemoryReference::new("integer".to_string(), 0),
1621 scalar_type: ScalarType::Integer,
1622 mutable: true,
1623 });
1624 Self {
1625 call_argument,
1626 return_type: ScalarType::Integer,
1627 expected,
1628 }
1629 }
1630 }
1631
1632 #[rstest]
1634 #[case(ReturnArgumentResolutionTestCase::case_01())]
1635 #[case(ReturnArgumentResolutionTestCase::case_02())]
1636 #[case(ReturnArgumentResolutionTestCase::case_03())]
1637 fn test_return_argument_resolution(#[case] test_case: ReturnArgumentResolutionTestCase) {
1638 let memory_regions = build_declarations();
1639
1640 let found = test_case
1641 .call_argument
1642 .resolve_return(&memory_regions, test_case.return_type);
1643 match (test_case.expected, found) {
1644 (Ok(expected), Ok(found)) => assert_eq!(expected, found),
1645 (Ok(expected), Err(found)) => {
1646 panic!("expected resolution {expected:?}, found err {found:?}")
1647 }
1648 (Err(expected), Ok(found)) => {
1649 panic!("expected err {expected:?}, found resolution {found:?}")
1650 }
1651 (Err(expected), Err(found)) => assert_eq!(expected, found),
1652 }
1653 }
1654
1655 struct ResolveToSignatureTestCase {
1657 call: Call,
1659 signature: ExternSignature,
1661 expected: Result<Vec<ResolvedCallArgument>, CallSignatureError>,
1663 }
1664
1665 impl ResolveToSignatureTestCase {
1666 fn case_01() -> Self {
1668 let call = Call {
1669 name: "foo".to_string(),
1670 arguments: vec![
1671 UnresolvedCallArgument::MemoryReference(MemoryReference {
1672 name: "integer".to_string(),
1673 index: 0,
1674 }),
1675 UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1676 UnresolvedCallArgument::Identifier("bit".to_string()),
1677 ],
1678 };
1679 let signature = ExternSignature {
1680 return_type: Some(ScalarType::Integer),
1681 parameters: vec![
1682 ExternParameter {
1683 name: "bar".to_string(),
1684 mutable: false,
1685 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1686 },
1687 ExternParameter {
1688 name: "baz".to_string(),
1689 mutable: true,
1690 data_type: ExternParameterType::FixedLengthVector(Vector {
1691 data_type: ScalarType::Bit,
1692 length: 3,
1693 }),
1694 },
1695 ],
1696 };
1697 let resolved = vec![
1698 ResolvedCallArgument::MemoryReference {
1699 memory_reference: MemoryReference {
1700 name: "integer".to_string(),
1701 index: 0,
1702 },
1703 scalar_type: ScalarType::Integer,
1704 mutable: true,
1705 },
1706 ResolvedCallArgument::Immediate {
1707 value: Complex64::new(2.0, 0.0),
1708 scalar_type: ScalarType::Integer,
1709 },
1710 ResolvedCallArgument::Vector {
1711 memory_region_name: "bit".to_string(),
1712 vector: Vector {
1713 data_type: ScalarType::Bit,
1714 length: 3,
1715 },
1716 mutable: true,
1717 },
1718 ];
1719 Self {
1720 call,
1721 signature,
1722 expected: Ok(resolved),
1723 }
1724 }
1725
1726 fn case_02() -> Self {
1728 let call = Call {
1729 name: "foo".to_string(),
1730 arguments: vec![
1731 UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1732 UnresolvedCallArgument::Identifier("bit".to_string()),
1733 ],
1734 };
1735 let signature = ExternSignature {
1736 return_type: None,
1737 parameters: vec![
1738 ExternParameter {
1739 name: "bar".to_string(),
1740 mutable: false,
1741 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1742 },
1743 ExternParameter {
1744 name: "baz".to_string(),
1745 mutable: true,
1746 data_type: ExternParameterType::FixedLengthVector(Vector {
1747 data_type: ScalarType::Bit,
1748 length: 3,
1749 }),
1750 },
1751 ],
1752 };
1753 let resolved = vec![
1754 ResolvedCallArgument::Immediate {
1755 value: Complex64::new(2.0, 0.0),
1756 scalar_type: ScalarType::Integer,
1757 },
1758 ResolvedCallArgument::Vector {
1759 memory_region_name: "bit".to_string(),
1760 vector: Vector {
1761 data_type: ScalarType::Bit,
1762 length: 3,
1763 },
1764 mutable: true,
1765 },
1766 ];
1767 Self {
1768 call,
1769 signature,
1770 expected: Ok(resolved),
1771 }
1772 }
1773
1774 fn case_03() -> Self {
1776 let call = Call {
1777 name: "foo".to_string(),
1778 arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
1779 name: "integer".to_string(),
1780 index: 0,
1781 })],
1782 };
1783 let signature = ExternSignature {
1784 return_type: Some(ScalarType::Integer),
1785 parameters: vec![],
1786 };
1787 let resolved = vec![ResolvedCallArgument::MemoryReference {
1788 memory_reference: MemoryReference {
1789 name: "integer".to_string(),
1790 index: 0,
1791 },
1792 scalar_type: ScalarType::Integer,
1793 mutable: true,
1794 }];
1795 Self {
1796 call,
1797 signature,
1798 expected: Ok(resolved),
1799 }
1800 }
1801
1802 fn case_04() -> Self {
1804 let call = Call {
1805 name: "foo".to_string(),
1806 arguments: vec![
1807 UnresolvedCallArgument::MemoryReference(MemoryReference {
1808 name: "integer".to_string(),
1809 index: 0,
1810 }),
1811 UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1812 UnresolvedCallArgument::Identifier("bit".to_string()),
1813 ],
1814 };
1815 let signature = ExternSignature {
1816 return_type: Some(ScalarType::Integer),
1817 parameters: vec![ExternParameter {
1818 name: "bar".to_string(),
1819 mutable: false,
1820 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1821 }],
1822 };
1823
1824 Self {
1825 call,
1826 signature,
1827 expected: Err(CallSignatureError::ParameterCount {
1828 expected: 2,
1829 found: 3,
1830 }),
1831 }
1832 }
1833
1834 fn case_05() -> Self {
1836 let call = Call {
1837 name: "foo".to_string(),
1838 arguments: vec![
1839 UnresolvedCallArgument::MemoryReference(MemoryReference {
1840 name: "integer".to_string(),
1841 index: 0,
1842 }),
1843 UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1844 ],
1845 };
1846 let signature = ExternSignature {
1847 return_type: Some(ScalarType::Integer),
1848 parameters: vec![],
1849 };
1850
1851 Self {
1852 call,
1853 signature,
1854 expected: Err(CallSignatureError::ParameterCount {
1855 expected: 1,
1856 found: 2,
1857 }),
1858 }
1859 }
1860
1861 fn case_06() -> Self {
1863 let call = Call {
1864 name: "foo".to_string(),
1865 arguments: vec![
1866 UnresolvedCallArgument::MemoryReference(MemoryReference {
1867 name: "integer".to_string(),
1868 index: 0,
1869 }),
1870 UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1871 UnresolvedCallArgument::Identifier("bit".to_string()),
1872 ],
1873 };
1874 let signature = ExternSignature {
1875 return_type: None,
1876 parameters: vec![ExternParameter {
1877 name: "bar".to_string(),
1878 mutable: false,
1879 data_type: ExternParameterType::Scalar(ScalarType::Integer),
1880 }],
1881 };
1882
1883 Self {
1884 call,
1885 signature,
1886 expected: Err(CallSignatureError::ParameterCount {
1887 expected: 1,
1888 found: 3,
1889 }),
1890 }
1891 }
1892
1893 fn case_07() -> Self {
1895 let call = Call {
1896 name: "foo".to_string(),
1897 arguments: vec![
1898 UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1899 UnresolvedCallArgument::Identifier("bit".to_string()),
1900 ],
1901 };
1902 let signature = ExternSignature {
1903 return_type: Some(ScalarType::Integer),
1904 parameters: vec![ExternParameter {
1905 name: "bar".to_string(),
1906 mutable: false,
1907 data_type: ExternParameterType::Scalar(ScalarType::Real),
1908 }],
1909 };
1910
1911 Self {
1912 call,
1913 signature,
1914 expected: Err(CallSignatureError::Arguments(vec![
1915 CallArgumentError::Return(CallArgumentResolutionError::ReturnArgument {
1916 found: UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1917 }),
1918 CallArgumentError::Argument {
1919 index: 0,
1920 error: CallArgumentResolutionError::MismatchedScalar {
1921 expected: ScalarType::Real,
1922 found: ScalarType::Bit,
1923 },
1924 },
1925 ])),
1926 }
1927 }
1928 }
1929
1930 #[rstest]
1932 #[case(ResolveToSignatureTestCase::case_01())]
1933 #[case(ResolveToSignatureTestCase::case_02())]
1934 #[case(ResolveToSignatureTestCase::case_03())]
1935 #[case(ResolveToSignatureTestCase::case_04())]
1936 #[case(ResolveToSignatureTestCase::case_05())]
1937 #[case(ResolveToSignatureTestCase::case_06())]
1938 #[case(ResolveToSignatureTestCase::case_07())]
1939 fn test_assert_matching_signature(#[case] test_case: ResolveToSignatureTestCase) {
1940 let memory_regions = build_declarations();
1941 let found = test_case
1942 .call
1943 .resolve_to_signature(&test_case.signature, &memory_regions);
1944 match (test_case.expected, found) {
1945 (Ok(_), Ok(_)) => {}
1946 (Ok(expected), Err(found)) => {
1947 panic!("expected resolution {expected:?}, found err {found:?}")
1948 }
1949 (Err(expected), Ok(found)) => {
1950 panic!("expected err {expected:?}, found resolution {found:?}")
1951 }
1952 (Err(expected), Err(found)) => assert_eq!(expected, found),
1953 }
1954 }
1955
1956 struct CallResolutionTestCase {
1958 call: Call,
1960 extern_signature_map: ExternSignatureMap,
1962 expected: Result<Vec<ResolvedCallArgument>, CallResolutionError>,
1964 }
1965
1966 impl CallResolutionTestCase {
1967 fn case_01() -> Self {
1969 let call = Call {
1970 name: "foo".to_string(),
1971 arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
1972 name: "integer".to_string(),
1973 index: 0,
1974 })],
1975 };
1976 let signature = ExternSignature {
1977 return_type: Some(ScalarType::Integer),
1978 parameters: vec![],
1979 };
1980 let resolved = vec![ResolvedCallArgument::MemoryReference {
1981 memory_reference: MemoryReference {
1982 name: "integer".to_string(),
1983 index: 0,
1984 },
1985 scalar_type: ScalarType::Integer,
1986 mutable: true,
1987 }];
1988 Self {
1989 call,
1990 extern_signature_map: ExternSignatureMap(
1991 [("foo".to_string(), signature)].iter().cloned().collect(),
1992 ),
1993 expected: Ok(resolved),
1994 }
1995 }
1996
1997 fn case_02() -> Self {
1999 let call = Call {
2000 name: "foo".to_string(),
2001 arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
2002 name: "integer".to_string(),
2003 index: 0,
2004 })],
2005 };
2006 let signature = ExternSignature {
2007 return_type: Some(ScalarType::Real),
2008 parameters: vec![],
2009 };
2010 Self {
2011 call,
2012 extern_signature_map: ExternSignatureMap(
2013 [("foo".to_string(), signature)].iter().cloned().collect(),
2014 ),
2015 expected: Err(CallResolutionError::Signature {
2016 name: "foo".to_string(),
2017 error: CallSignatureError::Arguments(vec![CallArgumentError::Return(
2018 CallArgumentResolutionError::MismatchedScalar {
2019 expected: ScalarType::Real,
2020 found: ScalarType::Integer,
2021 },
2022 )]),
2023 }),
2024 }
2025 }
2026
2027 fn case_03() -> Self {
2029 let call = Call {
2030 name: "undeclared".to_string(),
2031 arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
2032 name: "integer".to_string(),
2033 index: 0,
2034 })],
2035 };
2036 let signature = ExternSignature {
2037 return_type: Some(ScalarType::Real),
2038 parameters: vec![],
2039 };
2040 Self {
2041 call,
2042 extern_signature_map: ExternSignatureMap(
2043 [("foo".to_string(), signature)].iter().cloned().collect(),
2044 ),
2045 expected: Err(CallResolutionError::NoMatchingExternInstruction(
2046 "undeclared".to_string(),
2047 )),
2048 }
2049 }
2050 }
2051
2052 #[rstest]
2054 #[case(CallResolutionTestCase::case_01())]
2055 #[case(CallResolutionTestCase::case_02())]
2056 #[case(CallResolutionTestCase::case_03())]
2057 fn test_call_resolution(#[case] test_case: CallResolutionTestCase) {
2058 let memory_regions = build_declarations();
2059 let found = test_case
2060 .call
2061 .resolve_arguments(&memory_regions, &test_case.extern_signature_map);
2062 match (test_case.expected, found) {
2063 (Ok(expected), Ok(found)) => {
2064 assert_eq!(expected, found);
2065 }
2066 (Ok(expected), Err(found)) => {
2067 panic!("expected resolution {expected:?}, found err {found:?}")
2068 }
2069 (Err(expected), Ok(_)) => {
2070 panic!(
2071 "expected err {:?}, found resolution {:?}",
2072 expected, test_case.call.arguments
2073 )
2074 }
2075 (Err(expected), Err(found)) => assert_eq!(expected, found),
2076 }
2077 }
2078
2079 struct ExternPragmaMapConverstionTestCase {
2081 extern_pragma_map: ExternPragmaMap,
2083 expected: Result<ExternSignatureMap, ExternError>,
2085 }
2086
2087 impl ExternPragmaMapConverstionTestCase {
2088 fn case_01() -> Self {
2090 let pragma1 = Pragma {
2091 name: RESERVED_PRAGMA_EXTERN.to_string(),
2092 arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2093 data: Some("(bar : INTEGER)".to_string()),
2094 };
2095 let signature1 = ExternSignature {
2096 return_type: None,
2097 parameters: vec![ExternParameter {
2098 name: "bar".to_string(),
2099 mutable: false,
2100 data_type: ExternParameterType::Scalar(ScalarType::Integer),
2101 }],
2102 };
2103 let pragma2 = Pragma {
2104 name: RESERVED_PRAGMA_EXTERN.to_string(),
2105 arguments: vec![PragmaArgument::Identifier("baz".to_string())],
2106 data: Some("REAL (biz : REAL)".to_string()),
2107 };
2108 let signature2 = ExternSignature {
2109 return_type: Some(ScalarType::Real),
2110 parameters: vec![ExternParameter {
2111 name: "biz".to_string(),
2112 mutable: false,
2113 data_type: ExternParameterType::Scalar(ScalarType::Real),
2114 }],
2115 };
2116 let pragma3 = Pragma {
2117 name: RESERVED_PRAGMA_EXTERN.to_string(),
2118 arguments: vec![PragmaArgument::Identifier("buzz".to_string())],
2119 data: Some("OCTET".to_string()),
2120 };
2121 let signature3 = ExternSignature {
2122 return_type: Some(ScalarType::Octet),
2123 parameters: vec![],
2124 };
2125 Self {
2126 extern_pragma_map: ExternPragmaMap(
2127 [("foo", pragma1), ("baz", pragma2), ("buzz", pragma3)]
2128 .into_iter()
2129 .map(|(name, pragma)| (Some(name.to_string()), pragma))
2130 .collect(),
2131 ),
2132 expected: Ok(ExternSignatureMap(
2133 [
2134 ("foo", signature1),
2135 ("baz", signature2),
2136 ("buzz", signature3),
2137 ]
2138 .into_iter()
2139 .map(|(name, signature)| (name.to_string(), signature))
2140 .collect(),
2141 )),
2142 }
2143 }
2144
2145 fn case_02() -> Self {
2147 let pragma = Pragma {
2148 name: RESERVED_PRAGMA_EXTERN.to_string(),
2149 arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2150 data: None,
2151 };
2152 let expected = Err(ExternError::NoSignature);
2153 Self {
2154 extern_pragma_map: ExternPragmaMap(
2155 [(Some("foo".to_string()), pragma)].into_iter().collect(),
2156 ),
2157 expected,
2158 }
2159 }
2160
2161 fn case_03() -> Self {
2163 let pragma = Pragma {
2164 name: RESERVED_PRAGMA_EXTERN.to_string(),
2165 arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2166 data: Some("()".to_string()),
2167 };
2168 let expected = Err(ExternError::NoReturnOrParameters);
2169 Self {
2170 extern_pragma_map: ExternPragmaMap(
2171 [(Some("foo".to_string()), pragma)].into_iter().collect(),
2172 ),
2173 expected,
2174 }
2175 }
2176
2177 fn case_04() -> Self {
2179 let pragma = Pragma {
2180 name: RESERVED_PRAGMA_EXTERN.to_string(),
2181 arguments: vec![],
2182 data: Some("(bar : REAL)".to_string()),
2183 };
2184 let expected = Err(ExternError::NoName);
2185 Self {
2186 extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2187 expected,
2188 }
2189 }
2190
2191 fn case_05() -> Self {
2193 let pragma = Pragma {
2194 name: "NOTEXTERN".to_string(),
2195 arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2196 data: Some("(bar : REAL)".to_string()),
2197 };
2198 let expected = Err(ExternError::NoName);
2199 Self {
2200 extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2201 expected,
2202 }
2203 }
2204
2205 fn case_06() -> Self {
2207 let pragma = Pragma {
2208 name: RESERVED_PRAGMA_EXTERN.to_string(),
2209 arguments: vec![
2210 PragmaArgument::Identifier("foo".to_string()),
2211 PragmaArgument::Identifier("bar".to_string()),
2212 ],
2213 data: Some("OCTET".to_string()),
2214 };
2215 let expected = Err(ExternError::NoName);
2216 Self {
2217 extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2218 expected,
2219 }
2220 }
2221
2222 fn case_07() -> Self {
2224 let pragma = Pragma {
2225 name: RESERVED_PRAGMA_EXTERN.to_string(),
2226 arguments: vec![PragmaArgument::Integer(0)],
2227 data: Some("OCTET".to_string()),
2228 };
2229 let expected = Err(ExternError::NoName);
2230 Self {
2231 extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2232 expected,
2233 }
2234 }
2235
2236 fn case_08() -> Self {
2238 let pragma = Pragma {
2239 name: RESERVED_PRAGMA_EXTERN.to_string(),
2240 arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2241 data: Some("OCTET (ㆆ _ ㆆ)".to_string()),
2242 };
2243 let expected = Err(ExternSignature::from_str("OCTET (ㆆ _ ㆆ)").unwrap_err());
2244 Self {
2245 extern_pragma_map: ExternPragmaMap(
2246 [(Some("foo".to_string()), pragma)].into_iter().collect(),
2247 ),
2248 expected,
2249 }
2250 }
2251
2252 fn case_09() -> Self {
2254 let pragma = Pragma {
2255 name: RESERVED_PRAGMA_EXTERN.to_string(),
2256 arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2257 data: Some("OCTET (bar : INTEGER".to_string()),
2258 };
2259 let expected = Err(ExternSignature::from_str("OCTET (bar : INTEGER").unwrap_err());
2260 Self {
2261 extern_pragma_map: ExternPragmaMap(
2262 [(Some("foo".to_string()), pragma)].into_iter().collect(),
2263 ),
2264 expected,
2265 }
2266 }
2267 }
2268
2269 #[rstest]
2271 #[case(ExternPragmaMapConverstionTestCase::case_01())]
2272 #[case(ExternPragmaMapConverstionTestCase::case_02())]
2273 #[case(ExternPragmaMapConverstionTestCase::case_03())]
2274 #[case(ExternPragmaMapConverstionTestCase::case_04())]
2275 #[case(ExternPragmaMapConverstionTestCase::case_05())]
2276 #[case(ExternPragmaMapConverstionTestCase::case_06())]
2277 #[case(ExternPragmaMapConverstionTestCase::case_07())]
2278 #[case(ExternPragmaMapConverstionTestCase::case_08())]
2279 #[case(ExternPragmaMapConverstionTestCase::case_09())]
2280 fn test_extern_signature_map_validation(#[case] test_case: ExternPragmaMapConverstionTestCase) {
2281 let found = ExternSignatureMap::try_from(test_case.extern_pragma_map);
2282 match (test_case.expected, found) {
2283 (Ok(expected), Ok(found)) => {
2284 assert_eq!(expected, found);
2285 }
2286 (Ok(_), Err(found)) => {
2287 panic!("expected valid, found err {found:?}")
2288 }
2289 (Err(expected), Ok(_)) => {
2290 panic!("expected err {expected:?}, found valid")
2291 }
2292 (Err(expected), Err((_, found))) => assert_eq!(expected, found),
2293 }
2294 }
2295
2296 struct ExternSignatureFromStrTestCase {
2298 input: &'static str,
2300 expected: Result<ExternSignature, ExternError>,
2302 }
2303
2304 impl ExternSignatureFromStrTestCase {
2305 fn case_01() -> Self {
2307 Self {
2308 input: "",
2309 expected: Err(ExternError::NoReturnOrParameters),
2310 }
2311 }
2312
2313 fn case_02() -> Self {
2315 Self {
2316 input: "()",
2317 expected: Err(ExternError::NoReturnOrParameters),
2318 }
2319 }
2320
2321 fn case_03() -> Self {
2323 Self {
2324 input: "INTEGER",
2325 expected: Ok(crate::instruction::ExternSignature {
2326 return_type: Some(ScalarType::Integer),
2327 parameters: vec![],
2328 }),
2329 }
2330 }
2331
2332 fn case_04() -> Self {
2334 Self {
2335 input: "INTEGER ()",
2336 expected: Ok(crate::instruction::ExternSignature {
2337 return_type: Some(ScalarType::Integer),
2338 parameters: vec![],
2339 }),
2340 }
2341 }
2342
2343 fn case_05() -> Self {
2345 Self {
2346 input: "INTEGER (bar: REAL, baz: BIT[10], biz: mut OCTET)",
2347 expected: Ok(crate::instruction::ExternSignature {
2348 return_type: Some(ScalarType::Integer),
2349 parameters: vec![
2350 ExternParameter {
2351 name: "bar".to_string(),
2352 mutable: false,
2353 data_type: ExternParameterType::Scalar(ScalarType::Real),
2354 },
2355 ExternParameter {
2356 name: "baz".to_string(),
2357 mutable: false,
2358 data_type: ExternParameterType::FixedLengthVector(Vector {
2359 data_type: ScalarType::Bit,
2360 length: 10,
2361 }),
2362 },
2363 ExternParameter {
2364 name: "biz".to_string(),
2365 mutable: true,
2366 data_type: ExternParameterType::Scalar(ScalarType::Octet),
2367 },
2368 ],
2369 }),
2370 }
2371 }
2372
2373 fn case_06() -> Self {
2375 Self {
2376 input: "(bar: REAL, baz: BIT[10], biz : mut OCTET)",
2377 expected: Ok(crate::instruction::ExternSignature {
2378 return_type: None,
2379 parameters: vec![
2380 ExternParameter {
2381 name: "bar".to_string(),
2382 mutable: false,
2383 data_type: ExternParameterType::Scalar(ScalarType::Real),
2384 },
2385 ExternParameter {
2386 name: "baz".to_string(),
2387 mutable: false,
2388 data_type: ExternParameterType::FixedLengthVector(Vector {
2389 data_type: ScalarType::Bit,
2390 length: 10,
2391 }),
2392 },
2393 ExternParameter {
2394 name: "biz".to_string(),
2395 mutable: true,
2396 data_type: ExternParameterType::Scalar(ScalarType::Octet),
2397 },
2398 ],
2399 }),
2400 }
2401 }
2402
2403 fn case_07() -> Self {
2405 Self {
2406 input: "(bar : mut REAL[])",
2407 expected: Ok(crate::instruction::ExternSignature {
2408 return_type: None,
2409 parameters: vec![ExternParameter {
2410 name: "bar".to_string(),
2411 mutable: true,
2412 data_type: ExternParameterType::VariableLengthVector(ScalarType::Real),
2413 }],
2414 }),
2415 }
2416 }
2417 }
2418
2419 #[rstest]
2421 #[case(ExternSignatureFromStrTestCase::case_01())]
2422 #[case(ExternSignatureFromStrTestCase::case_02())]
2423 #[case(ExternSignatureFromStrTestCase::case_03())]
2424 #[case(ExternSignatureFromStrTestCase::case_04())]
2425 #[case(ExternSignatureFromStrTestCase::case_05())]
2426 #[case(ExternSignatureFromStrTestCase::case_06())]
2427 #[case(ExternSignatureFromStrTestCase::case_07())]
2428 fn test_parse_reserved_pragma_extern(#[case] test_case: ExternSignatureFromStrTestCase) {
2429 match (
2430 test_case.expected,
2431 ExternSignature::from_str(test_case.input),
2432 ) {
2433 (Ok(expected), Ok(parsed)) => {
2434 assert_eq!(expected, parsed);
2435 }
2436 (Ok(expected), Err(e)) => {
2437 panic!("Expected {expected:?}, got error: {e:?}");
2438 }
2439 (Err(expected), Ok(parsed)) => {
2440 panic!("Expected error: {expected:?}, got {parsed:?}");
2441 }
2442 (Err(expected), Err(found)) => {
2443 let expected = format!("{expected:?}");
2444 let found = format!("{found:?}");
2445 assert!(found.contains(&expected), "`{expected}` not in `{found}`");
2446 }
2447 }
2448 }
2449}