//! Quellen bleiben auch nach Include-Expansion einem Modul und einer Datei zugeordnet. use crate::{ast, lexer, parser, Diagnostic, SourcePos}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct SourceFile { pub module: u16, pub path: String, } #[derive(Debug, Clone, PartialEq)] pub struct SourceSegment { pub file: String, pub first_line: u32, pub text: String, } #[derive(Debug, Clone, PartialEq)] pub struct SourceUnit { pub name: String, pub segments: Vec, } impl SourceUnit { pub fn new(name: &str, file: &str, text: &str) -> Self { Self { name: name.into(), segments: vec![SourceSegment { file: file.into(), first_line: 1, text: text.into(), }], } } pub fn parse( &self, module: u16, sources: &mut Vec, ) -> (ast::Module, Vec) { let mut text = String::new(); let mut origins = Vec::new(); for segment in &self.segments { let file = SourceFile { module, path: segment.file.clone(), }; let source = sources.iter().position(|s| s == &file).unwrap_or_else(|| { sources.push(file); sources.len() - 1 }) as u32; for (line, part) in segment.text.split_inclusive('\n').enumerate() { origins.push(SourcePos { source, line: segment.first_line + line as u32, column: 1, }); text.push_str(part); if !part.ends_with('\n') { text.push('\n'); } } } let map = |pos: &mut SourcePos| { let origin = origins .get(pos.line.saturating_sub(1) as usize) .copied() .or_else(|| { origins.last().map(|p| SourcePos { line: p.line + 1, ..*p }) }) .unwrap_or_default(); pos.source = origin.source; pos.line = origin.line; }; let mut lexed = lexer::lex(&text); for token in &mut lexed.tokens { map(&mut token.pos); } for diagnostic in &mut lexed.diagnostics { map(&mut diagnostic.pos); } let parsed = parser::parse(&self.name, &lexed.tokens); lexed.diagnostics.extend(parsed.diagnostics); (parsed.module, lexed.diagnostics) } } pub fn locate_diagnostics(diagnostics: &mut [Diagnostic], sources: &[SourceFile]) { for diagnostic in diagnostics { diagnostic.file = sources .get(diagnostic.pos.source as usize) .map(|s| s.path.clone()); } }