1 个不稳定版本
0.1.0 | 2023年7月12日 |
---|
在 编程语言 中排名 408
每月下载量 199
用于 7 个crate(6个直接使用)
65KB
1K SLoC
miden-diagnostics
此crate提供有用的编译器诊断基础设施,旨在在执行某种类型编译的各种Miden组件之间共享/重用,例如AirScript、(开发中)的Miden IR和Miden汇编。
请参阅 miden-parsing 以获取构建在所提供低级工具之上的解析实用工具。完整的编译器前端应在该crate的 Scanner
类型之上构建一个词法分析器,并实现它的 Parser
特性。许多涉及生成包含源span的AST的细节都由该crate处理,但也可以自行构建替代方案。
组件
此crate提供两个不同但相关的用例的功能
- 跟踪编译器源,这些源中的位置/范围,以及支持将范围关联到Rust数据结构的基础设施,例如
#[derive(Spanned)]
- 构建、生成和显示/捕获编译器诊断,可选地装饰有源span以渲染类似rustc的消息/警告/错误。
源级调试信息
我们基于codespan crate提供的某些原语,以尽可能高效的方式提供跟踪源、位置和范围的丰富功能。意图是确保装饰编译器结构以包含源位置的成本尽可能低,同时保留轻松获取有关这些结构的有用信息的能力,例如给定对象是从哪个文件/行/列派生出来的。
以下支持此用例的关键特性
SourceId
是指向加载到内存中的特定源文件的紧凑引用SourceIndex
是对源文件中某个特定位置的紧凑引用。SourceSpan
是一个紧凑的结构体,它引用源文件中某个特定范围的多个位置。此类型是您将与之交互的最常见的值类型,用于生成指向源文件中特定字符范围的漂亮诊断信息。Span<T>
是一种类型,用于非侵入性地将SourceSpan
与类型T
关联;解引用到T
,并实现一系列其他特质,这些特质以传递方式委托给T
,例如PartialEq
Spanned
是一个特质,类型可以实施以在请求时生成SourceSpan
。类型Span<T>
实现了这个特质,并且对于所有Box<T>
,其中T: Spanned
,这个特质将被自动实现。CodeMap
是一个线程安全的数据结构,由编译器驱动程序构建一次,并在所有子线程之间共享。它存储读取到内存中的文件,通过源文件名称(无论是否真实或合成)进行去重。它提供了API,可用于从SourceId
、SourceIndex
或SourceSpan
获取有用的高级信息,例如文件名、行和列号;以及获取原始源内容的一个切片。
诊断
我们利用由 codespan_reporting 包提供的某些实用程序,提供更丰富的功能集来生成、显示和/或捕获编译器诊断。
以下支持此用例的关键特性
Diagnostic
是一个类型,表示编译器诊断,包括严重性、消息以及一个(可选的)标签/注释集合。ToDiagnostic
是一个特质,它表示从类型生成Diagnostic
的能力。在实际应用中,这通常用于将错误转换为诊断,当编译应该继续进行,因为错误是非致命的时候。例如,在解析/语义分析期间,您通常希望在编译任务失败之前捕获尽可能多的错误,而不是在遇到第一个错误时退出。Severity
表示诊断是错误、警告、缺陷还是简单的备注。Label
用于将源位置与包含描述性文本的诊断相关联。标签有主/次两种口味,这会影响在显示时标签的顺序/渲染方式。InFlightDiagnostic
提供了一个流畅的、构建器模式API,用于动态构建和发出诊断,下面有示例。Emitter
是一个特质,可以用来控制如何发出诊断。这可以用来做有用的事情,比如禁用诊断、捕获它们用于测试,或控制它们的显示方式;内置的NullEmitter
、CaptureEmitter
和DefaultEmitter
类型执行相应的功能。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(())
}
}
}
}
依赖项
~3–10MB
~99K SLoC