#miden #compiler #diagnostics #spans #miden-diagnostics

miden-diagnostics-macros

miden-diagnostics的宏支持包

1个不稳定版本

0.1.0 2023年7月12日

#29#miden

Download history 29/week @ 2024-04-20 27/week @ 2024-04-27 23/week @ 2024-05-04 23/week @ 2024-05-11 27/week @ 2024-05-18 18/week @ 2024-05-25 21/week @ 2024-06-01 14/week @ 2024-06-08 22/week @ 2024-06-15 27/week @ 2024-06-22 4/week @ 2024-06-29 2/week @ 2024-07-06 26/week @ 2024-07-13 28/week @ 2024-07-20 57/week @ 2024-07-27 106/week @ 2024-08-03

217 每月下载量
8 个crate中使用 (通过 miden-diagnostics)

MIT 许可证

27KB
430

miden-diagnostics

此crate提供了有用的编译器诊断基础设施,旨在在执行某些类型编译的各个Miden组件之间共享/重用,例如AirScript、(开发中的) Miden IR和Miden汇编。

有关构建在低级工具之上的解析实用程序的更多信息,请参阅 miden-parsing。 完整的编译器前端预计将在该crate的 Scanner 类型之上构建一个词法分析器,并实现其 Parser 特性。 有关生成带源跨度AST的许多细节都由该crate处理,但完全可以创建自己的替代方案。

组件

此crate提供了针对两个不同的、但相互关联的使用案例的功能

  • 跟踪编译器源代码、这些源代码中的位置/跨度,以及支持将跨度关联到Rust数据结构的基础设施,例如 #[derive(Spanned)]
  • 构造、发出以及显示/捕获编译器诊断,可选地用源跨度装饰,以便渲染类似 rustc 的消息/警告/错误。

源级调试信息

我们在codespan crate提供的某些原语的基础上构建,以提供有关跟踪源、位置和跨度的丰富功能集。目标是确保在编译器结构中装饰源位置的成本尽可能低,同时保留轻松获取有关这些结构的有用信息的能力,例如给定对象是从哪个文件/行/列派生的。

以下是一些支持此用例的关键功能

  • SourceId 是对已加载到内存中的特定源文件的紧凑引用
  • SourceIndex 是对某些源文件中特定位置的紧凑引用
  • SourceSpan 是一个紧凑的结构,它引用了某些源文件中特定范围的定位。此类型是您将与之交互的最常见的值类型,用于生成指向源文件中特定字符范围的漂亮诊断,这些诊断与相关的诊断有关。
  • Span<T> 是一种类型,用于无侵入地关联一个 SourceSpan 与类型 T;解引用到 T,并实现了各种其他特质,以中继方式委托给 T,例如 PartialEq
  • Spanned 是一个可以由类型实现以在请求时生成 SourceSpan 的特质。类型 Span<T> 实现了这个特质,并且当 T: Spanned 时,它自动为所有 Box<T> 实现。
  • CodeMap 是一个线程安全的数据结构,由编译器驱动器构建一次并共享给所有子线程。它存储读入内存的文件,通过源文件名(无论是否真实)进行去重。它提供了API,可以用来从 SourceIdSourceIndexSourceSpan 获取有用的高级信息,例如文件名、行号和列号;以及获取原始源内容的一个切片。

诊断

我们在 codespan_reporting 包提供的某些实用工具的基础上构建,以提供更丰富的生成、显示和/或捕获编译器诊断的功能。

以下是一些支持此用例的关键功能

  • Diagnostic 是一种类型,代表一个编译器诊断,包含严重性、消息和一个(可选的)标签/注释集合。
  • ToDiagnostic 是一个代表从类型生成 Diagnostic 能力的特质。实际上,这用于将错误转换为诊断,当编译应继续进行时,因为错误是非致命的。例如,在解析/语义分析期间,通常希望捕获尽可能多的错误,然后再使编译任务失败,而不是在遇到第一个错误时退出。
  • Severity 表示诊断是错误、警告、故障还是简单注释。
  • Label 用于将源位置与诊断关联起来,并带有一些描述性文本。标签有主/次口味,这会影响标签在显示时的排序/渲染方式。
  • InFlightDiagnostic 提供了一个流畅的、构建器模式API,用于即时构建和发出诊断,请参见下面的示例。
  • Emitter 是一个可以用来控制如何发出诊断的特质。这可以用来做有用的事情,例如禁用诊断、捕获它们以进行测试或控制它们的显示方式;内置的 NullEmitterCaptureEmitterDefaultEmitter 类型执行相应的功能。
  • DiagnosticsHandler 是一个线程安全的类型,由编译器驱动器构建一次,然后共享给所有执行线程。它可以配置为使用特定的 Emitter 实现类型,控制发出诊断的最小严重性,将警告转换为错误等。

示例

抽象语法树

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

#[derive(Clone, PartialEq, Eq, Hash, Spanned)]
pub struct Ident(#[span] Span<String>);

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

#[derive(Spanned)]
pub struct Let {
    pub span: SourceSpan,
    pub var: Ident,
    pub body: Box<Expr>,
}

#[derive(Spanned)]
pub struct BinaryExpr {
    pub span: SourceSpan,
    pub op: BinaryOp,
    pub lhs: Box<Expr>,
    pub rhs: Box<Expr>,
}

#[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<Expr>,
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum UnaryOp {
    Neg,
}

诊断

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(())
            }
        }
    }
}

依赖项

~1.5MB
~35K SLoC