miden-diagnostics

This crate provides useful infrastructure for compiler diagnostics, which are intended to be shared/reused across various Miden components that perform some type of compilation, e.g. AirScript, the (in development) Miden IR, and Miden Assembly.

See miden-parsing for parsing utilities that build on top of the low-level tools provided here. A complete compiler frontend is expected to build a lexer on top of that crate's Scanner type, and implement it's Parser trait. Many of the details involved in producing an AST with source spans are handled by that crate, but it is entirely possible to forge your own alternative.

Components

This crate provides functionality for two distinct, but inter-related use cases:

Source-Level Debugging Info

We build upon some of the primitives provided by the codespan crate to provide a rich set of functionality around tracking sources, locations, and spans in as efficient a way as possible. The intent is to ensure that decorating compiler structures with source locations is as cheap as possible, while preserving the ability to easily obtain useful information about those structures, such as what file/line/column a given object was derived from.

The following are the key features that support this use case:

Diagnostics

We build upon some utilities provided by the codespan_reporting crate to provide a richer set of features for generating, displaying and/or capturing compiler diagnostics.

The following are the key features that support this use case:

Examples

Abstract Syntax Tree

```rust use miden_diagnostics::{SourceSpan, Span, Spanned};

[derive(Clone, PartialEq, Eq, Hash, Spanned)]

pub struct Ident(#[span] Span);

[derive(Spanned)]

pub enum Expr { Var(#[span] Ident), Int(#[span] Span), Let(#[span] Let), Binary(#[span] BinaryExpr), Unary(#[span] UnaryExpr), }

[derive(Spanned)]

pub struct Let { pub span: SourceSpan, pub var: Ident, pub body: Box, }

[derive(Spanned)]

pub struct BinaryExpr { pub span: SourceSpan, pub op: BinaryOp, pub lhs: Box, pub rhs: Box, }

[derive(Copy, Clone, PartialEq, Eq)]

pub enum BinaryOp { Add, Sub, Mul, Div, }

[derived(Spanned)]

pub struct UnaryExpr { pub span: SourceSpan, pub op: UnaryOp, pub rhs: Box, }

[derive(Copy, Clone, PartialEq, Eq)]

pub enum UnaryOp { Neg, } ```

Diagnostics

```rust use std::sync::Arc;

use miden_diagnostics::*;

const INPUT_FILE = r#" let x = 42 in let y = x * 2 in x + y "#;

pub fn main() -> Result<(), ()> { // The codemap is where parsed inputs are stored and is the base for all source locations let codemap = Arc::new(CodeMap::new()); // The emitter defines how diagnostics will be emitted/displayed let emitter = Arc::new(DefaultEmitter::new(ColorChoice::Auto)); // The config provides some control over what diagnostics to display and how let config = DiagnosticsConfig::default(); // The diagnostics handler itself is used to emit diagnostics let diagnostics = Arc::new(DiagnosticsHandler::new(config, codemap.clone(), emitter));

// In our example, we're adding an input to the codemap, and then requesting the compiler compile it
codemap.add("nofile", INPUT_FILE.to_string());
compiler::compile(codemap, diagnostics, "nofile")

}

mod compiler { use miden_diagnostics::*;

pub fn compile<F: Into<FileName>>(codemap: Arc<CodeMap>, diagnostics: Arc<DiagnosticsHandler>, filename: F) -> Result<(), ()> {
    let filename = filename.into();
    let file = codemap.get_by_name(&filename).unwrap();

    // The details of parsing are left as an exercise for the reader, but it is expected
    // that for Miden projects that this crate will be combined with `miden-parsing` to
    // handle many of the details involved in producing a stream of tokens from raw sources
    //
    // In this case, we're parsing an Expr, or returning an error that has an associated source span
    match parser::parse(file.source(), &diagnostics)? {
        Ok(_expr) => Ok(()),
        Err(err) => {
            diagnostics.diagnostic(Severity::Error)
              .with_message("parsing failed")
              .with_primary_label(err.span(), err.to_string())
              .emit();
            Err(())
        }
    }
}

} ```