deimos-lang/src/name_analysis/mod.rs
2025-05-18 16:18:19 -05:00

151 lines
4.0 KiB
Rust

use crate::ast::named::Named;
use crate::ast::CompilationUnit;
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::gather::gather_compilation_unit;
use crate::name_analysis::resolve::resolve_compilation_unit;
use crate::name_analysis::symbol_table::SymbolTable;
mod fqn_context;
mod gather;
mod resolve;
pub mod symbol;
pub mod symbol_table;
pub(self) struct DiagnosticsContainer {
diagnostics: Vec<DmDiagnostic>,
}
impl DiagnosticsContainer {
pub fn new() -> DiagnosticsContainer {
DiagnosticsContainer {
diagnostics: vec![],
}
}
pub fn add(&mut self, diagnostic: DmDiagnostic) {
self.diagnostics.push(diagnostic);
}
}
impl Into<Vec<DmDiagnostic>> for DiagnosticsContainer {
fn into(self) -> Vec<DmDiagnostic> {
self.diagnostics
}
}
pub fn analyze_names(
compilation_units: &mut Vec<CompilationUnit>,
symbol_table: &mut SymbolTable,
) -> Vec<DmDiagnostic> {
let mut diagnostics = DiagnosticsContainer::new();
// gather symbols
for compilation_unit in compilation_units.iter_mut() {
gather_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
}
// resolve symbols
for compilation_unit in compilation_units.iter_mut() {
resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
}
diagnostics.into()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::build::build_ast;
use crate::parser::{DeimosParser, Rule};
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use indoc::indoc;
use pest::Parser;
use std::collections::HashMap;
fn assert_no_diagnostics(sources: HashMap<&str, &str>) -> SymbolTable {
let mut files = SimpleFiles::new();
let mut compilation_units = vec![];
for (file_name, source) in sources {
let file_id = files.add(file_name, source);
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
if let Err(err) = &parse_result {
panic!("{}", err);
}
let mut pairs = parse_result.unwrap();
if pairs.as_str().trim() != source.trim() {
panic!("Parsing did not consume entire input.");
}
compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()))
}
let mut symbol_table = SymbolTable::new();
let diagnostics = analyze_names(&mut compilation_units, &mut symbol_table);
if !diagnostics.is_empty() {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
for diagnostic in &diagnostics {
term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
}
eprintln!("{}", symbol_table);
panic!("Diagnostics was not empty!");
}
for compilation_unit in &compilation_units {
dbg!(compilation_unit);
}
symbol_table
}
#[test]
fn params_seen() {
let sources: HashMap<&str, &str> = HashMap::from([(
"main.dm",
indoc! {"
fn main(args: Array<String>) {
let x = args;
}"},
)]);
assert_no_diagnostics(sources);
}
#[test]
fn two_files() {
let sources: HashMap<&str, &str> = HashMap::from([
(
"main.dm",
indoc! {"
use std::core::{Array, String, println};
fn main(args: Array<String>) {
println(\"Hello, World!\");
}
"},
),
(
"deps.dm",
indoc! {"
ns std::core;
pub class String {}
pub class Array {}
pub platform fn println(msg: String) -> Void;
"},
),
]);
assert_no_diagnostics(sources);
}
}