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};