openqasm 0.1.2

Parser and translator for OpenQASM 2.0
Documentation
//! 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, Symbol};
pub use parser::{Parser, SourceCache};
pub use translate::{ExprVisitor, GateWriter, Linearize, ProgramVisitor, Value};