1个不稳定版本
0.1.0 | 2023年7月12日 |
---|
#29 在 #miden
217 每月下载量
在 8 个crate中使用 (通过 miden-diagnostics)
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,可以用来从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(())
}
}
}
}
依赖项
~1.5MB
~35K SLoC