openqasm/
lib.rs

1//! This crate implements a parser, type-checker and translator for OpenQASM 2.0.
2//!
3//! ### Parsing
4//!
5//! The main interface for parsing is the `parser::Parser` struct, and this
6//! produces either a `ast::Program` or a list of `parser::ParseError`s.
7//! Example Usage:
8//! ```ignore
9//! let mut cache = SourceCache::new();
10//! let mut parser = Parser::new(&mut cache);
11//! parser.parse_file("test.qasm");
12//! parser.parse_source("
13//!     OPENQASM 2.0;
14//!     qreg a;
15//!     creg b;
16//!     cx a, b;
17//! ");
18//!
19//! match parser.done().to_errors() {
20//!     Ok(program) => ..., // do something with this
21//!     Err(errors) => errors.print(&mut cache).unwrap()
22//! }
23//! ```
24//!
25//! ### Type-Checking
26//!
27//! Once you have a `Program`, you can type-check it with the
28//! `ast::Program::type_check` method. This detects many types of
29//! errors before translation.
30//!
31//! Example Usage:
32//! ```ignore
33//! let mut cache = SourceCache::new();
34//! let program: Program = ...; // obtain a program somehow
35//!
36//! if let Err(errors) = program.type_check().to_errors() {
37//!     errors.print(&mut cache).unwrap();
38//! }
39//! ```
40//!
41//! ### Translating
42//!
43//! There are three different interfaces for translating parsed
44//! programs into different formats:
45//!
46//! 1. `Linearize`/`GateWriter` is the high-level interface, and converts a
47//! program to a linear list of primitive and opaque gates, computing
48//! all the parameters and substituting arguments.
49//!
50//! 2. `ProgramVisitor`/`ExprVisitor` is the low-level interface, and just walks the
51//! AST, with user-defined callbacks for definitions, statements etc.
52//!
53//! Example Usage:
54//! ```ignore
55//! let program = ...; // acquire a program from somewhere.
56//! program.type_check().unwrap(); // make sure to type check.
57//! let mut l = Linearize::new(...); // linearize into into someoutput.
58//! l.visit_program(&program).unwrap();
59//! ```
60//!
61//! ### Error Handling
62//!
63//! Specific error types for each process (parsing, checking etc.)
64//! are provided. However, if you don't need to handle these individually,
65//! you can use `GenericError` trait and `GenericError::to_errors` to convert
66//! and `Result<_, SomeSpecificError>` into a generic `Result<_, Errors>` type.
67//! This can then be handled, printed or converted to a `Report` as detailed below.
68//!
69//! ### Features
70//!
71//! The `ariadne` feature is disabled by default and allows
72//! pretty-printing of errors using the `ariadne` crate by providing
73//! `to_report` functions on all error types, as well as `Errors::eprint`/`print`.
74//!
75//! The `pretty` feature is disabled by default and allows pretty-printing of
76//! AST objects using the `pretty` crate. This will implement the `pretty::Pretty`
77//! trait on these objects, and also provides the `to_pretty` method to easily render
78//! to a string.
79//!
80//! The `serde` feature is disabled by default and implements `serde::Serialize`
81//! and `serde::Deserialize` for all the AST types.
82//!
83
84#![deny(mutable_borrow_reservation_conflict)]
85#![cfg_attr(all(nightly_build, doc), feature(doc_auto_cfg))]
86
87#[macro_use]
88extern crate lalrpop_util;
89
90pub mod ast;
91pub mod parser;
92pub mod translate;
93pub mod typing;
94
95#[cfg(feature = "pretty")]
96mod pretty;
97
98use thiserror::Error;
99
100/// A generic error type for this crate.
101/// This collects all the error types used in this crate
102/// into one enum, for generic handling.
103#[derive(Debug, Error)]
104pub enum Error {
105    #[error(transparent)]
106    ParseError(#[from] parser::ParseError),
107    #[error(transparent)]
108    TypeError(#[from] typing::TypeError),
109    #[error(transparent)]
110    LinearizeError(#[from] translate::LinearizeError),
111}
112
113impl Error {
114    /// Convert this error to a `Report` for printing.
115    #[cfg(feature = "ariadne")]
116    pub fn to_report(&self) -> ast::Report {
117        match self {
118            Error::ParseError(e) => e.to_report(),
119            Error::TypeError(e) => e.to_report(),
120            Error::LinearizeError(e) => e.to_report(),
121        }
122    }
123}
124
125/// Represents a collection of generic errors.
126///
127/// This struct contains a vector of `Error` types
128/// that represent errors that have occured.
129///
130/// If `ariadne` is enabled, you can use `Errors::eprint`/`print`
131/// to print all the errors to standard output.
132#[derive(Debug, Error)]
133pub struct Errors {
134    pub errors: Vec<Error>,
135}
136
137impl std::fmt::Display for Errors {
138    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
139        for err in &self.errors {
140            write!(f, "{}, ", err)?;
141        }
142        Ok(())
143    }
144}
145
146#[cfg(feature = "ariadne")]
147impl Errors {
148    /// Convert all errors into `Report`s.
149    pub fn to_reports(&self) -> impl Iterator<Item = ast::Report> + '_ {
150        self.errors.iter().map(|err| err.to_report())
151    }
152
153    /// Print all the errors to stderr with `ariadne`.
154    pub fn eprint(&self, cache: &mut parser::SourceCache) -> std::io::Result<()> {
155        for report in self.to_reports() {
156            report.eprint(&mut *cache)?;
157            eprintln!();
158        }
159        Ok(())
160    }
161
162    /// Print all the errors to stdout with `ariadne`.
163    pub fn print(&self, cache: &mut parser::SourceCache) -> std::io::Result<()> {
164        for report in self.to_reports() {
165            report.print(&mut *cache)?;
166            println!();
167        }
168        Ok(())
169    }
170
171    /// Write all the errors to a `Write` implementation with `ariadne`.
172    pub fn write<W: std::io::Write>(
173        &self,
174        cache: &mut parser::SourceCache,
175        mut out: W,
176    ) -> std::io::Result<()> {
177        for report in self.to_reports() {
178            report.write(&mut *cache, &mut out)?;
179            writeln!(&mut out, "")?;
180        }
181        Ok(())
182    }
183}
184
185/// A trait to convert a specific result type into a generic one.
186/// This lets you convert `Result<T, E>` or `Result<T, Vec<E>>` into
187/// `Result<T, Errors>` for any error type `E` defined in this library.
188/// In this way you can ignore the specific type of error when you don't
189/// need to handle them explicitly (which is most of the time).
190///
191/// Example Usage:
192/// ```ignore
193/// fn test() -> Result<(), Errors> {
194///     ...
195///     let program = parser.done().to_errors()?;
196///     program.type_check().to_errors()?;
197/// }
198/// ```
199pub trait GenericError: Sized {
200    type Inner;
201
202    /// Convert any errors in this type to generic errors.
203    fn to_errors(self) -> Result<Self::Inner, Errors>;
204}
205
206impl<T, E> GenericError for std::result::Result<T, Vec<E>>
207where
208    Error: From<E>,
209{
210    type Inner = T;
211
212    fn to_errors(self) -> Result<T, Errors> {
213        self.map_err(|errs| Errors {
214            errors: errs.into_iter().map(Error::from).collect(),
215        })
216    }
217}
218
219impl<T, E> GenericError for std::result::Result<T, E>
220where
221    Error: From<E>,
222{
223    type Inner = T;
224
225    fn to_errors(self) -> Result<T, Errors> {
226        self.map_err(|e| Errors {
227            errors: vec![Error::from(e)],
228        })
229    }
230}
231
232pub use ast::{Decl, Expr, Program, Reg, Span, Stmt, Symbol};
233pub use parser::{Parser, SourceCache};
234pub use translate::{ExprVisitor, GateWriter, Linearize, ProgramVisitor, Value};