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)
|
f(file_id, pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_ast(file_id: usize, compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
|
pub fn build_ast(file_name: &str, file_id: usize, compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
|
||||||
build_compilation_unit(file_id, compilation_unit_pair)
|
build_compilation_unit(file_name, file_id, compilation_unit_pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_identifier(file_id: usize, identifier_pair: Pair<Rule>) -> Identifier {
|
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 namespace = None;
|
||||||
let mut declarations = vec![];
|
let mut declarations = vec![];
|
||||||
|
|
||||||
@ -311,6 +311,8 @@ fn build_compilation_unit(file_id: usize, compilation_unit_pair: Pair<Rule>) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
CompilationUnit {
|
CompilationUnit {
|
||||||
|
file_name: file_name.to_string(),
|
||||||
|
file_id,
|
||||||
namespace,
|
namespace,
|
||||||
declarations,
|
declarations,
|
||||||
}
|
}
|
||||||
@ -1558,7 +1560,7 @@ mod tests {
|
|||||||
if pairs.as_str().trim() != src.trim() {
|
if pairs.as_str().trim() != src.trim() {
|
||||||
panic!("Parsing did not consume entire input.");
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct CompilationUnit {
|
pub struct CompilationUnit {
|
||||||
|
pub file_name: String,
|
||||||
|
pub file_id: usize,
|
||||||
pub namespace: Option<FullyQualifiedName>,
|
pub namespace: Option<FullyQualifiedName>,
|
||||||
pub declarations: Vec<ModuleLevelDeclaration>,
|
pub declarations: Vec<ModuleLevelDeclaration>,
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::NameAnalysis { paths } => {
|
Commands::NameAnalysis { paths } => {
|
||||||
for path in paths {
|
let result = name_analysis(&paths);
|
||||||
let result = name_analysis(&path);
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,33 +6,41 @@ use deimos::name_analysis::analyze_names;
|
|||||||
use deimos::name_analysis::symbol_table::SymbolTable;
|
use deimos::name_analysis::symbol_table::SymbolTable;
|
||||||
use deimos::parser::{DeimosParser, Rule};
|
use deimos::parser::{DeimosParser, Rule};
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use std::path::Path;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn name_analysis(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut compilation_units = vec![];
|
||||||
|
let mut files = SimpleFiles::new();
|
||||||
|
|
||||||
|
for path in paths {
|
||||||
let src = std::fs::read_to_string(path).unwrap();
|
let src = std::fs::read_to_string(path).unwrap();
|
||||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
||||||
let mut files = SimpleFiles::new();
|
let file_id = files.add(path.display().to_string(), src.clone()); // I don't love this clone
|
||||||
let file_id = files.add(path.display().to_string(), &src);
|
|
||||||
|
|
||||||
match parse_result {
|
match parse_result {
|
||||||
Ok(mut pairs) => {
|
Ok(mut pairs) => {
|
||||||
let compilation_unit_pair = pairs.next().unwrap();
|
let compilation_unit_pair = pairs.next().unwrap();
|
||||||
let mut compilation_unit = build_ast(file_id, compilation_unit_pair);
|
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()),
|
||||||
|
}?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
let mut symbol_table = SymbolTable::new();
|
||||||
let diagnostics = analyze_names(file_id, &mut compilation_unit, &mut symbol_table);
|
|
||||||
|
let diagnostics = analyze_names(&mut compilation_units, &mut symbol_table);
|
||||||
if diagnostics.is_empty() {
|
if diagnostics.is_empty() {
|
||||||
println!("Name analysis complete.");
|
println!("Name analysis complete.");
|
||||||
println!("Symbol table\n-------\n{}", symbol_table);
|
println!("Symbol table\n-------\n{}", symbol_table);
|
||||||
Ok(())
|
|
||||||
} else {
|
} else {
|
||||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||||
let config = term::Config::default();
|
let config = term::Config::default();
|
||||||
for diagnostic in diagnostics {
|
for diagnostic in diagnostics {
|
||||||
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
|
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => Err(e.into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ pub fn pretty_print_parse(path: &PathBuf) {
|
|||||||
match parse_result {
|
match parse_result {
|
||||||
Ok(mut pairs) => {
|
Ok(mut pairs) => {
|
||||||
let compilation_unit_pair = pairs.next().unwrap();
|
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()));
|
let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
|
||||||
compilation_unit
|
compilation_unit
|
||||||
.pretty_print(&mut indent_writer)
|
.pretty_print(&mut indent_writer)
|
||||||
|
@ -11,7 +11,7 @@ pub fn unparse(path: &PathBuf) {
|
|||||||
match parse_result {
|
match parse_result {
|
||||||
Ok(mut pairs) => {
|
Ok(mut pairs) => {
|
||||||
let compilation_unit_pair = pairs.next().unwrap();
|
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()));
|
let mut writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
|
||||||
compilation_unit
|
compilation_unit
|
||||||
.unparse(&mut writer)
|
.unparse(&mut writer)
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use crate::ast::named::Named;
|
use crate::ast::named::Named;
|
||||||
use crate::ast::{CompilationUnit, Identifier};
|
use crate::ast::CompilationUnit;
|
||||||
use crate::diagnostic::DmDiagnostic;
|
use crate::diagnostic::DmDiagnostic;
|
||||||
use crate::name_analysis::fqn_context::FqnContext;
|
use crate::name_analysis::fqn_context::FqnContext;
|
||||||
use crate::name_analysis::gather::gather_module_level_declaration;
|
use crate::name_analysis::gather::gather_module_level_declaration;
|
||||||
use crate::name_analysis::resolve::resolve_module_level_declaration;
|
use crate::name_analysis::resolve::resolve_module_level_declaration;
|
||||||
use crate::name_analysis::symbol_table::SymbolTable;
|
use crate::name_analysis::symbol_table::SymbolTable;
|
||||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
mod fqn_context;
|
mod fqn_context;
|
||||||
mod gather;
|
mod gather;
|
||||||
@ -15,14 +13,12 @@ pub mod symbol;
|
|||||||
pub mod symbol_table;
|
pub mod symbol_table;
|
||||||
|
|
||||||
pub(self) struct DiagnosticsContainer {
|
pub(self) struct DiagnosticsContainer {
|
||||||
file_id: usize,
|
|
||||||
diagnostics: Vec<DmDiagnostic>,
|
diagnostics: Vec<DmDiagnostic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiagnosticsContainer {
|
impl DiagnosticsContainer {
|
||||||
pub fn new(file_id: usize) -> DiagnosticsContainer {
|
pub fn new() -> DiagnosticsContainer {
|
||||||
DiagnosticsContainer {
|
DiagnosticsContainer {
|
||||||
file_id,
|
|
||||||
diagnostics: vec![],
|
diagnostics: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,14 +26,6 @@ impl DiagnosticsContainer {
|
|||||||
pub fn add(&mut self, diagnostic: DmDiagnostic) {
|
pub fn add(&mut self, diagnostic: DmDiagnostic) {
|
||||||
self.diagnostics.push(diagnostic);
|
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 {
|
impl Into<Vec<DmDiagnostic>> for DiagnosticsContainer {
|
||||||
@ -47,17 +35,19 @@ impl Into<Vec<DmDiagnostic>> for DiagnosticsContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyze_names(
|
pub fn analyze_names(
|
||||||
file_id: usize,
|
compilation_units: &mut Vec<CompilationUnit>,
|
||||||
compilation_unit: &mut CompilationUnit,
|
|
||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
) -> Vec<DmDiagnostic> {
|
) -> Vec<DmDiagnostic> {
|
||||||
let mut diagnostics = DiagnosticsContainer::new(file_id);
|
let mut diagnostics = DiagnosticsContainer::new();
|
||||||
|
|
||||||
|
// gather symbols
|
||||||
|
for compilation_unit in compilation_units.iter_mut() {
|
||||||
let mut fqn_context = FqnContext::new();
|
let mut fqn_context = FqnContext::new();
|
||||||
if let Some(namespace) = &compilation_unit.namespace {
|
if let Some(namespace) = &compilation_unit.namespace {
|
||||||
fqn_context.push(namespace.name().to_string());
|
fqn_context.push(namespace.name().to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
symbol_table.push_scope(&format!("FileScope({})", compilation_unit.file_name));
|
||||||
for declaration in &mut compilation_unit.declarations {
|
for declaration in &mut compilation_unit.declarations {
|
||||||
gather_module_level_declaration(
|
gather_module_level_declaration(
|
||||||
declaration,
|
declaration,
|
||||||
@ -66,12 +56,16 @@ pub fn analyze_names(
|
|||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
symbol_table.pop_scope();
|
||||||
assert_eq!(symbol_table.current_scope_id(), 0);
|
assert_eq!(symbol_table.current_scope_id(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve symbols
|
||||||
|
for compilation_unit in compilation_units.iter_mut() {
|
||||||
for declaration in &mut compilation_unit.declarations {
|
for declaration in &mut compilation_unit.declarations {
|
||||||
resolve_module_level_declaration(declaration, symbol_table, &mut diagnostics);
|
resolve_module_level_declaration(declaration, symbol_table, &mut diagnostics);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diagnostics.into()
|
diagnostics.into()
|
||||||
}
|
}
|
||||||
@ -84,23 +78,31 @@ mod tests {
|
|||||||
use codespan_reporting::files::SimpleFiles;
|
use codespan_reporting::files::SimpleFiles;
|
||||||
use codespan_reporting::term;
|
use codespan_reporting::term;
|
||||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||||
|
use indoc::indoc;
|
||||||
use pest::Parser;
|
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 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);
|
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 {
|
if let Err(err) = &parse_result {
|
||||||
panic!("{:?}", err);
|
panic!("{:?}", err);
|
||||||
}
|
}
|
||||||
|
let mut pairs = parse_result.unwrap();
|
||||||
|
if pairs.as_str().trim() != source.trim() {
|
||||||
|
panic!("Parsing did not consume entire input.");
|
||||||
|
}
|
||||||
|
|
||||||
let compilation_unit_pair = parse_result.unwrap().next().unwrap();
|
compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()))
|
||||||
let mut ast = build_ast(test_file_id, compilation_unit_pair);
|
}
|
||||||
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
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() {
|
if !diagnostics.is_empty() {
|
||||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||||
@ -117,12 +119,40 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn params_seen() {
|
fn params_seen() {
|
||||||
assert_no_diagnostics(
|
let sources: HashMap<&str, &str> = HashMap::from([(
|
||||||
r#"
|
"main.dm",
|
||||||
|
indoc! {"
|
||||||
fn main(args: Array<String>) {
|
fn main(args: Array<String>) {
|
||||||
let x = args;
|
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