Files
TerminalBasic/crates/tb-frontend/src/source.rs

97 lines
2.8 KiB
Rust

//! 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<SourceSegment>,
}
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<SourceFile>,
) -> (ast::Module, Vec<Diagnostic>) {
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());
}
}