Files
endorphin-aster-vm/crates/frontend/src/source.rs
T

102 lines
4.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Reader 产生 SourceUnit,lexer/parser/compiler 借用其文本;错误在 annotate 时固化来源及行列。
use std::error::Error;
use std::fmt;
use std::path::PathBuf;
use crate::{Diagnostic, SourceLocation};
/// Source provenance, independent of filesystem layout or VM heap handles.
/// Endor revisions are opaque labels supplied by the host, not verified hashes.
#[derive(Debug, Clone, PartialEq, Eq)]
/// Reader 选择的来源身份,诊断和代码文件名显示使用;Endor revision 是宿主标签,不代表已验证哈希。
pub enum SourceOrigin {
// 内存文本的宿主显示名,不表示磁盘路径。
Text { name: String },
// 已读到 EOF 的字节流显示名,流本身不再存于快照。
Stream { name: String },
// 实际读取时保存的路径,不要求规范化为绝对路径。
File { path: PathBuf },
// 经宿主解析的 URI 与所选版本身份,前端不推断其命名空间或哈希算法。
Endor { uri: String, revision: String },
}
impl fmt::Display for SourceOrigin {
/// 诊断与生成代码文件名调用 Display,按来源种类生成可读身份文本。
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
// 文本/流显示名称,文件显示路径,Endor 来源附带版本标签。
match self {
Self::Text { name } | Self::Stream { name } => formatter.write_str(name),
Self::File { path } => write!(formatter, "{}", path.display()),
Self::Endor { uri, revision } => write!(formatter, "{uri} [revision {revision}]"),
}
}
}
/// An owned, immutable UTF-8 snapshot. Reader adapters preserve the text exactly
/// so token byte offsets still refer to the original decoded source.
#[derive(Debug, Clone, PartialEq, Eq)]
/// EndorReader 构造的拥有型 UTF-8 快照;原文不规范化,以保持 Token 和 AST 字节跨度对应原输入。
pub struct SourceUnit {
// 来源身份与文本一起保存,供错误和代码元数据使用。
origin: SourceOrigin,
// 已验证 UTF-8 的原始文本,解析阶段只借用它,不修改换行或空白。
text: String,
}
impl SourceUnit {
/// Reader 适配器完成解码后调用;将来源及文本移入快照,不再读取外部数据。
pub(crate) fn new(origin: SourceOrigin, text: String) -> Self {
Self { origin, text }
}
/// 编译器生成文件名、宿主检查来源时借用身份,保留 Endor 的已解析版本。
pub fn origin(&self) -> &SourceOrigin {
&self.origin
}
/// lexer/parser/compiler 及位置校验借用 UTF-8 文本,返回生命周期依附当前快照。
pub fn text(&self) -> &str {
&self.text
}
/// Bind a diagnostic to this snapshot before discarding its source buffer.
/// lex_source、parse_source 系列及 compile_source 在失败时调用,把字节诊断绑定到此来源和行列。
/// 返回值保存来源、位置及原诊断,不保存源码全文;后续无需再次读源即可显示基本错误。
pub fn annotate(&self, diagnostic: Diagnostic) -> SourceDiagnostic {
SourceDiagnostic {
location: diagnostic.location(&self.text),
origin: self.origin.clone(),
diagnostic: Box::new(diagnostic),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
/// 带来源及预计算位置的阶段错误,由 SourceUnit::annotate 创建,交给宿主显示或追查底层 Diagnostic。
pub struct SourceDiagnostic {
// 错误对应的来源,独立于原快照生命周期。
pub origin: SourceOrigin,
// annotate 当时根据原文计算的一基行列,后续 Display 不重新计算。
pub location: SourceLocation,
// 原始字节跨度、阶段、说明及期望项,保留给程序化处理与 Error::source。
pub diagnostic: Box<Diagnostic>,
}
impl fmt::Display for SourceDiagnostic {
/// 宿主显示错误时输出“来源:行:列:说明”,跨度及期望项仍可从 diagnostic 单独读取。
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{}:{}:{}: {}",
self.origin, self.location.line, self.location.column, self.diagnostic.message
)
}
}
impl Error for SourceDiagnostic {
/// 标准错误链使用者调用,暴露原始阶段诊断作为本错误的直接原因。
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self.diagnostic.as_ref())
}
}