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