//! 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, } 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()) } }