Refactor name-analysis for multiple files; set up (failing) test case for multiple files.
This commit is contained in:
parent
3026d22750
commit
0c18b976d7
@ -15,8 +15,8 @@ fn expect_and_use<T>(
|
||||
f(file_id, pair)
|
||||
}
|
||||
|
||||
pub fn build_ast(file_id: usize, compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
|
||||
build_compilation_unit(file_id, compilation_unit_pair)
|
||||
pub fn build_ast(file_name: &str, file_id: usize, compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
|
||||
build_compilation_unit(file_name, file_id, compilation_unit_pair)
|
||||
}
|
||||
|
||||
fn build_identifier(file_id: usize, identifier_pair: Pair<Rule>) -> Identifier {
|
||||
@ -293,7 +293,7 @@ fn build_references(file_id: usize, ref_list_pair: Pair<Rule>) -> References {
|
||||
)
|
||||
}
|
||||
|
||||
fn build_compilation_unit(file_id: usize, compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
|
||||
fn build_compilation_unit(file_name: &str, file_id: usize, compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
|
||||
let mut namespace = None;
|
||||
let mut declarations = vec![];
|
||||
|
||||
@ -311,6 +311,8 @@ fn build_compilation_unit(file_id: usize, compilation_unit_pair: Pair<Rule>) ->
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,6 +287,8 @@ impl Default for References {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompilationUnit {
|
||||
pub file_name: String,
|
||||
pub file_id: usize,
|
||||
pub namespace: Option<FullyQualifiedName>,
|
||||
pub declarations: Vec<ModuleLevelDeclaration>,
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn std::error::Error>> {
|
||||
let src = std::fs::read_to_string(path).unwrap();
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
||||
pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
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<dyn std::error::Error>>(())
|
||||
}
|
||||
}
|
||||
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(())
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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<DmDiagnostic>,
|
||||
}
|
||||
|
||||
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<Vec<DmDiagnostic>> for DiagnosticsContainer {
|
||||
@ -47,30 +35,36 @@ impl Into<Vec<DmDiagnostic>> for DiagnosticsContainer {
|
||||
}
|
||||
|
||||
pub fn analyze_names(
|
||||
file_id: usize,
|
||||
compilation_unit: &mut CompilationUnit,
|
||||
compilation_units: &mut Vec<CompilationUnit>,
|
||||
symbol_table: &mut SymbolTable,
|
||||
) -> Vec<DmDiagnostic> {
|
||||
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<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::println;
|
||||
|
||||
fn main(args: Array<String>) {
|
||||
println(\"Hello, World!\");
|
||||
}
|
||||
"},
|
||||
),
|
||||
(
|
||||
"print.dm",
|
||||
indoc! {"
|
||||
ns std::core;
|
||||
|
||||
declare platform fn println(msg: String) -> Void
|
||||
"},
|
||||
),
|
||||
]);
|
||||
|
||||
assert_no_diagnostics(sources);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user