diff --git a/src/ast/build.rs b/src/ast/build.rs index 7fb2503..69f1a66 100644 --- a/src/ast/build.rs +++ b/src/ast/build.rs @@ -15,8 +15,8 @@ fn expect_and_use( f(file_id, pair) } -pub fn build_ast(file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { - build_compilation_unit(file_id, compilation_unit_pair) +pub fn build_ast(file_name: &str, file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { + build_compilation_unit(file_name, file_id, compilation_unit_pair) } fn build_identifier(file_id: usize, identifier_pair: Pair) -> Identifier { @@ -293,7 +293,7 @@ fn build_references(file_id: usize, ref_list_pair: Pair) -> References { ) } -fn build_compilation_unit(file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { +fn build_compilation_unit(file_name: &str, file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { let mut namespace = None; let mut declarations = vec![]; @@ -311,6 +311,8 @@ fn build_compilation_unit(file_id: usize, compilation_unit_pair: Pair) -> } CompilationUnit { + file_name: file_name.to_string(), + file_id, namespace, declarations, } @@ -1558,7 +1560,7 @@ mod tests { if pairs.as_str().trim() != src.trim() { panic!("Parsing did not consume entire input."); } - let ast = build_ast(0, pairs.next().unwrap()); + let ast = build_ast("test.dm", 0, pairs.next().unwrap()); } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 572e430..42313e5 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -287,6 +287,8 @@ impl Default for References { #[derive(Debug)] pub struct CompilationUnit { + pub file_name: String, + pub file_id: usize, pub namespace: Option, pub declarations: Vec, } diff --git a/src/bin/dmc/main.rs b/src/bin/dmc/main.rs index ea350bf..45ad77f 100644 --- a/src/bin/dmc/main.rs +++ b/src/bin/dmc/main.rs @@ -45,11 +45,9 @@ fn main() { } } Commands::NameAnalysis { paths } => { - for path in paths { - let result = name_analysis(&path); - if let Err(e) = result { - eprintln!("{}", e); - } + let result = name_analysis(&paths); + if let Err(e) = result { + eprintln!("{}", e) } } } diff --git a/src/bin/dmc/name_analysis.rs b/src/bin/dmc/name_analysis.rs index db046e9..f7a4d61 100644 --- a/src/bin/dmc/name_analysis.rs +++ b/src/bin/dmc/name_analysis.rs @@ -6,33 +6,41 @@ use deimos::name_analysis::analyze_names; use deimos::name_analysis::symbol_table::SymbolTable; use deimos::parser::{DeimosParser, Rule}; use pest::Parser; -use std::path::Path; +use std::path::PathBuf; -pub fn name_analysis(path: &Path) -> Result<(), Box> { - let src = std::fs::read_to_string(path).unwrap(); - let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src); +pub fn name_analysis(paths: &Vec) -> Result<(), Box> { + let mut compilation_units = vec![]; let mut files = SimpleFiles::new(); - let file_id = files.add(path.display().to_string(), &src); + + for path in paths { + let src = std::fs::read_to_string(path).unwrap(); + let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src); + let file_id = files.add(path.display().to_string(), src.clone()); // I don't love this clone - match parse_result { - Ok(mut pairs) => { - let compilation_unit_pair = pairs.next().unwrap(); - let mut compilation_unit = build_ast(file_id, compilation_unit_pair); - let mut symbol_table = SymbolTable::new(); - let diagnostics = analyze_names(file_id, &mut compilation_unit, &mut symbol_table); - if diagnostics.is_empty() { - println!("Name analysis complete."); - println!("Symbol table\n-------\n{}", symbol_table); - Ok(()) - } else { - let writer = StandardStream::stderr(ColorChoice::Always); - let config = term::Config::default(); - for diagnostic in diagnostics { - term::emit(&mut writer.lock(), &config, &files, &diagnostic)?; - } - Ok(()) + match parse_result { + Ok(mut pairs) => { + let compilation_unit_pair = pairs.next().unwrap(); + let compilation_unit = build_ast(&path.display().to_string(), file_id, compilation_unit_pair); + compilation_units.push(compilation_unit); + Ok::<(), Box>(()) } - } - Err(e) => Err(e.into()), + Err(e) => Err(e.into()), + }?; } + + let mut symbol_table = SymbolTable::new(); + + let diagnostics = analyze_names(&mut compilation_units, &mut symbol_table); + if diagnostics.is_empty() { + println!("Name analysis complete."); + println!("Symbol table\n-------\n{}", symbol_table); + } else { + let writer = StandardStream::stderr(ColorChoice::Always); + let config = term::Config::default(); + for diagnostic in diagnostics { + term::emit(&mut writer.lock(), &config, &files, &diagnostic)?; + } + } + + Ok(()) } diff --git a/src/bin/dmc/p3.rs b/src/bin/dmc/p3.rs index 36bd28a..1cd3777 100644 --- a/src/bin/dmc/p3.rs +++ b/src/bin/dmc/p3.rs @@ -11,7 +11,7 @@ pub fn pretty_print_parse(path: &PathBuf) { match parse_result { Ok(mut pairs) => { let compilation_unit_pair = pairs.next().unwrap(); - let compilation_unit = build_ast(0, compilation_unit_pair); + let compilation_unit = build_ast(&path.display().to_string(), 0, compilation_unit_pair); let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout())); compilation_unit .pretty_print(&mut indent_writer) diff --git a/src/bin/dmc/unparse.rs b/src/bin/dmc/unparse.rs index ec2851d..e6bf9aa 100644 --- a/src/bin/dmc/unparse.rs +++ b/src/bin/dmc/unparse.rs @@ -11,7 +11,7 @@ pub fn unparse(path: &PathBuf) { match parse_result { Ok(mut pairs) => { let compilation_unit_pair = pairs.next().unwrap(); - let compilation_unit = build_ast(0, compilation_unit_pair); + let compilation_unit = build_ast(&path.display().to_string(), 0, compilation_unit_pair); let mut writer = IndentWriter::new(0, " ", Box::new(std::io::stdout())); compilation_unit .unparse(&mut writer) diff --git a/src/name_analysis/mod.rs b/src/name_analysis/mod.rs index 663e00b..625f512 100644 --- a/src/name_analysis/mod.rs +++ b/src/name_analysis/mod.rs @@ -1,12 +1,10 @@ use crate::ast::named::Named; -use crate::ast::{CompilationUnit, Identifier}; +use crate::ast::CompilationUnit; use crate::diagnostic::DmDiagnostic; use crate::name_analysis::fqn_context::FqnContext; use crate::name_analysis::gather::gather_module_level_declaration; use crate::name_analysis::resolve::resolve_module_level_declaration; use crate::name_analysis::symbol_table::SymbolTable; -use codespan_reporting::diagnostic::{Diagnostic, Label}; -use log::debug; mod fqn_context; mod gather; @@ -15,14 +13,12 @@ pub mod symbol; pub mod symbol_table; pub(self) struct DiagnosticsContainer { - file_id: usize, diagnostics: Vec, } impl DiagnosticsContainer { - pub fn new(file_id: usize) -> DiagnosticsContainer { + pub fn new() -> DiagnosticsContainer { DiagnosticsContainer { - file_id, diagnostics: vec![], } } @@ -30,14 +26,6 @@ impl DiagnosticsContainer { pub fn add(&mut self, diagnostic: DmDiagnostic) { self.diagnostics.push(diagnostic); } - - pub fn add_error_at_identifier(&mut self, msg: &str, identifier: &Identifier) { - self.diagnostics.push( - Diagnostic::error() - .with_message(msg) - .with_label(Label::primary(self.file_id, identifier.range)), - ); - } } impl Into> for DiagnosticsContainer { @@ -47,30 +35,36 @@ impl Into> for DiagnosticsContainer { } pub fn analyze_names( - file_id: usize, - compilation_unit: &mut CompilationUnit, + compilation_units: &mut Vec, symbol_table: &mut SymbolTable, ) -> Vec { - let mut diagnostics = DiagnosticsContainer::new(file_id); + let mut diagnostics = DiagnosticsContainer::new(); - let mut fqn_context = FqnContext::new(); - if let Some(namespace) = &compilation_unit.namespace { - fqn_context.push(namespace.name().to_string()); + // gather symbols + for compilation_unit in compilation_units.iter_mut() { + let mut fqn_context = FqnContext::new(); + if let Some(namespace) = &compilation_unit.namespace { + fqn_context.push(namespace.name().to_string()); + } + + symbol_table.push_scope(&format!("FileScope({})", compilation_unit.file_name)); + for declaration in &mut compilation_unit.declarations { + gather_module_level_declaration( + declaration, + symbol_table, + &mut fqn_context, + &mut diagnostics, + ); + } + symbol_table.pop_scope(); + assert_eq!(symbol_table.current_scope_id(), 0); } - for declaration in &mut compilation_unit.declarations { - gather_module_level_declaration( - declaration, - symbol_table, - &mut fqn_context, - &mut diagnostics, - ); - } - - assert_eq!(symbol_table.current_scope_id(), 0); - - for declaration in &mut compilation_unit.declarations { - resolve_module_level_declaration(declaration, symbol_table, &mut diagnostics); + // resolve symbols + for compilation_unit in compilation_units.iter_mut() { + for declaration in &mut compilation_unit.declarations { + resolve_module_level_declaration(declaration, symbol_table, &mut diagnostics); + } } diagnostics.into() @@ -84,23 +78,31 @@ mod tests { 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(src: &str) { + fn assert_no_diagnostics(sources: HashMap<&str, &str>) { let mut files = SimpleFiles::new(); - let test_file_id = files.add("test.dm", src); + let mut compilation_units = vec![]; - let parse_result = DeimosParser::parse(Rule::CompilationUnit, src); - if let Err(err) = &parse_result { - panic!("{:?}", err); + 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 compilation_unit_pair = parse_result.unwrap().next().unwrap(); - let mut ast = build_ast(test_file_id, compilation_unit_pair); - let mut symbol_table = SymbolTable::new(); - let diagnostics = analyze_names(test_file_id, &mut ast, &mut symbol_table); + let diagnostics = analyze_names(&mut compilation_units, &mut symbol_table); if !diagnostics.is_empty() { let writer = StandardStream::stderr(ColorChoice::Always); @@ -117,12 +119,40 @@ mod tests { #[test] fn params_seen() { - assert_no_diagnostics( - r#" + let sources: HashMap<&str, &str> = HashMap::from([( + "main.dm", + indoc! {" fn main(args: Array) { let x = args; - } - "#, - ) + }"}, + )]); + + assert_no_diagnostics(sources) + } + + #[test] + fn two_files() { + let sources: HashMap<&str, &str> = HashMap::from([ + ( + "main.dm", + indoc! {" + use std::core::println; + + fn main(args: Array) { + println(\"Hello, World!\"); + } + "}, + ), + ( + "print.dm", + indoc! {" + ns std::core; + + declare platform fn println(msg: String) -> Void + "}, + ), + ]); + + assert_no_diagnostics(sources); } }