Compare commits

...

5 Commits

Author SHA1 Message Date
Jesse Brault
13330300c1 WIP fleshing out of block and expression name analysis. 2025-10-31 13:04:22 -05:00
Jesse Brault
12a3a61156 Work on functions and parameter name resolution. 2025-10-31 12:14:33 -05:00
Jesse Brault
9b83a531ca Remove un-needed test. 2025-10-29 12:14:52 -05:00
Jesse Brault
e5c5be6d95 Re-enable tests for name analysis. Currently failing. 2025-10-29 12:13:39 -05:00
Jesse Brault
d653d26e14 Work on star-use symbols. 2025-10-28 10:43:42 -05:00
20 changed files with 986 additions and 363 deletions

View File

@ -116,8 +116,7 @@ fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
use std::range::Range; use std::range::Range;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use crate::name_analysis::symbol::type_symbol::*; use crate::name_analysis::symbol::*;
use crate::name_analysis::symbol::use_symbol::*;
#(#types)* #(#types)*
}; };

View File

@ -79,9 +79,9 @@ pub mod build {
include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs")); include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs"));
pub fn build_ast(file_id: usize, parsed_pairs: &mut Pairs<Rule>) -> Box<CompilationUnit> { pub fn build_ast(file_id: usize, parsed_pairs: &mut Pairs<Rule>) -> CompilationUnit {
let compilation_unit_pair = parsed_pairs.next().unwrap(); let compilation_unit_pair = parsed_pairs.next().unwrap();
Box::new(build_compilation_unit(file_id, compilation_unit_pair)) build_compilation_unit(file_id, compilation_unit_pair)
} }
#[cfg(test)] #[cfg(test)]
@ -174,22 +174,6 @@ pub mod pretty_print {
pub mod ast_node { pub mod ast_node {
include!(concat!(env!("OUT_DIR"), "/src/ast/ast_node.rs")); include!(concat!(env!("OUT_DIR"), "/src/ast/ast_node.rs"));
#[cfg(test)]
mod tests {
use crate::ast::ast_node::AstNode;
use crate::ast::node::CompilationUnit;
fn get_cu() -> CompilationUnit {
todo!()
}
#[test]
fn simple() {
let cu = get_cu();
for child in cu.children() {}
}
}
} }
pub mod walk { pub mod walk {

View File

@ -3,40 +3,69 @@ use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use deimos::ast::build::build_ast; use deimos::ast::build::build_ast;
use deimos::name_analysis::analyze_names; use deimos::name_analysis::analyze_names;
use deimos::name_analysis::symbol_table::symbol_tree::SymbolTree;
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 deimos::std_core::add_std_core_symbols; use deimos::std_core::add_std_core_symbols;
use pest::Parser; use pest::Parser;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::path::PathBuf; use std::path::PathBuf;
pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> { struct ParseErrors {
let mut compilation_units = vec![]; errors: Vec<pest::error::Error<Rule>>,
let mut files: SimpleFiles<String, String> = SimpleFiles::new(); }
impl Debug for ParseErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Display for ParseErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "There were errors during parsing.")?;
for parse_error in &self.errors {
writeln!(f, "{}", parse_error)?;
}
Ok(())
}
}
impl std::error::Error for ParseErrors {}
pub fn name_analysis(paths: &[PathBuf]) -> Result<(), Box<dyn std::error::Error>> {
let mut paths_and_sources: HashMap<String, String> = HashMap::new();
for path in paths { 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); paths_and_sources.insert(path.display().to_string(), src);
let file_id = files.add(path.display().to_string(), src.clone()); // I don't love this clone }
let mut compilation_units = vec![];
let mut files: SimpleFiles<&str, &str> = SimpleFiles::new();
let mut parse_errors = vec![];
for (path, source) in &paths_and_sources {
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
match parse_result { match parse_result {
Ok(mut pairs) => { Ok(mut pairs) => {
let file_id = files.add(path, source);
let compilation_unit = build_ast(file_id, &mut pairs); let compilation_unit = build_ast(file_id, &mut pairs);
compilation_units.push(compilation_unit); compilation_units.push(compilation_unit);
Ok::<(), Box<dyn std::error::Error>>(())
} }
Err(e) => Err(e.into()), Err(error) => {
}?; parse_errors.push(error);
}
}
}
if !parse_errors.is_empty() {
return Err(Box::new(ParseErrors { errors: parse_errors }));
} }
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols."); add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
let diagnostics = analyze_names( let diagnostics = analyze_names(&mut compilation_units, &files, &mut symbol_table);
&mut compilation_units,
&files,
&mut symbol_table,
);
if diagnostics.is_empty() { if diagnostics.is_empty() {
println!("Name analysis complete."); println!("Name analysis complete.");
println!("{}", symbol_table); println!("{}", symbol_table);

View File

@ -1,8 +1,8 @@
use crate::ast::node::{ use crate::ast::node::{
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody,
GenericParameters, Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration, Parameters, GenericParameters, Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration, Parameter,
PrimitiveType, ReturnType, TypeUse, TypedArray, UseStatement, UseStatementIdentifier, Parameters, PrimitiveType, ReturnType, StarUseStatement, TypeUse, TypedArray, UseStatement,
UseStatementPrefix, UseStatementIdentifier, UseStatementPrefix,
}; };
use crate::diagnostic::DmDiagnostic; use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol; use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
@ -13,10 +13,13 @@ use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
use crate::name_analysis::symbol::primitive_type_symbol::PrimitiveTypeSymbol; use crate::name_analysis::symbol::primitive_type_symbol::PrimitiveTypeSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition; use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::type_symbol::TypeSymbol; use crate::name_analysis::symbol::type_symbol::TypeSymbol;
use crate::name_analysis::symbol::use_symbol::ConcreteUseSymbol; use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
use crate::name_analysis::symbol_table::SymbolTable; use crate::name_analysis::symbol_table::SymbolTable;
use crate::name_analysis::util::{format_fqn, handle_insert_error, handle_lookup_error}; use crate::name_analysis::util::{
format_fqn, handle_insert_error, handle_lookup_error, join_fqn_parts,
};
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::format;
use std::rc::Rc; use std::rc::Rc;
pub fn na_p1_compilation_unit( pub fn na_p1_compilation_unit(
@ -60,7 +63,7 @@ fn na_p1_use_statement(
na_p1_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics); na_p1_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics);
} }
UseStatement::StarUseStatement(star_use_statement) => { UseStatement::StarUseStatement(star_use_statement) => {
todo!() na_p1_star_use_statement(star_use_statement, symbol_table, diagnostics);
} }
} }
} }
@ -100,7 +103,7 @@ fn na_p1_concrete_use_statement(
} }
fn handle_concrete_use_statement_identifier( fn handle_concrete_use_statement_identifier(
prefixes: &Vec<Rc<str>>, prefixes: &[Rc<str>],
use_statement_identifier: &mut UseStatementIdentifier, use_statement_identifier: &mut UseStatementIdentifier,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
@ -134,6 +137,40 @@ fn handle_concrete_use_statement_identifier(
} }
} }
fn na_p1_star_use_statement(
star_use_statement: &mut StarUseStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let fqn_parts = star_use_statement
.prefixes()
.map(UseStatementPrefix::identifier)
.map(Identifier::name)
.map(|name| Rc::from(name))
.collect::<Vec<Rc<str>>>();
let to_insert = StarUseSymbol::new(
&fqn_parts,
Some(SourceDefinition::from_star_use_statement(
star_use_statement,
)),
);
match symbol_table.insert_star_use_symbol(to_insert) {
Ok(star_use_symbol) => {
star_use_statement.set_symbol(star_use_symbol);
}
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
&join_fqn_parts(&fqn_parts),
star_use_statement.file_id(),
star_use_statement.range(),
diagnostics,
);
}
}
}
fn na_p1_module_level_declaration( fn na_p1_module_level_declaration(
module_level_declaration: &mut ModuleLevelDeclaration, module_level_declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
@ -207,8 +244,6 @@ fn na_p1_function(
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<Rc<RefCell<FunctionSymbol>>> { ) -> Option<Rc<RefCell<FunctionSymbol>>> {
symbol_table.push_scope(&format!("FunctionScope {}", function.identifier().name()));
let to_insert = FunctionSymbol::new( let to_insert = FunctionSymbol::new(
&symbol_table.resolve_fqn(Rc::from(function.identifier().name())), &symbol_table.resolve_fqn(Rc::from(function.identifier().name())),
function.is_public(), function.is_public(),
@ -216,28 +251,7 @@ fn na_p1_function(
Some(SourceDefinition::from_identifier(function.identifier())), Some(SourceDefinition::from_identifier(function.identifier())),
); );
let function_symbol = match symbol_table.insert_function_symbol(to_insert) { let function_symbol = match symbol_table.insert_function_symbol(to_insert) {
Ok(function_symbol) => { Ok(function_symbol) => Some(function_symbol),
{
let mut as_ref_mut = function_symbol.borrow_mut();
// generics
na_p1_generic_parameters(function.generics_mut(), symbol_table, diagnostics);
// parameters
as_ref_mut.set_parameter_symbols(na_p1_parameters(
function.parameters_mut(),
symbol_table,
diagnostics,
));
// return type
let return_type =
na_p1_return_type(function.return_type_mut(), symbol_table, diagnostics);
if let Some(type_symbol) = return_type {
as_ref_mut.set_return_type(type_symbol);
}
}
Some(function_symbol)
}
Err(symbol_insert_error) => { Err(symbol_insert_error) => {
handle_insert_error( handle_insert_error(
symbol_insert_error, symbol_insert_error,
@ -250,7 +264,42 @@ fn na_p1_function(
} }
}; };
symbol_table.pop_scope(); if function_symbol.is_some() {
let mut as_ref_mut = function_symbol.as_ref().unwrap().borrow_mut();
// push a scope for this function
symbol_table.push_scope(&format!("FunctionScope {}", function.identifier().name()));
// generics
na_p1_generic_parameters(function.generics_mut(), symbol_table, diagnostics);
// parameters
as_ref_mut.set_parameter_symbols(na_p1_parameters(
function.parameters_mut(),
symbol_table,
diagnostics,
));
// return type
let return_type = na_p1_return_type(function.return_type_mut(), symbol_table, diagnostics);
if let Some(type_symbol) = return_type {
as_ref_mut.set_return_type(type_symbol);
}
symbol_table.push_scope(&format!(
"FunctionBodyScope {}",
function.identifier().name()
));
na_p1_function_body(
function.function_body_mut(),
symbol_table,
diagnostics
);
symbol_table.pop_scope();
symbol_table.pop_scope();
}
function_symbol function_symbol
} }
@ -260,7 +309,38 @@ fn na_p1_parameters(
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
) -> Vec<Rc<RefCell<ParameterSymbol>>> { ) -> Vec<Rc<RefCell<ParameterSymbol>>> {
todo!() parameters
.parameters_mut()
.map(|parameter| na_p1_parameter(parameter, symbol_table, diagnostics))
.filter(Option::is_some)
.map(Option::unwrap)
.collect()
}
fn na_p1_parameter(
parameter: &mut Parameter,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<Rc<RefCell<ParameterSymbol>>> {
let parameter_type_symbol = na_p1_type_use(parameter.type_use_mut(), symbol_table, diagnostics);
let to_insert = ParameterSymbol::new(
parameter.identifier().name(),
Some(SourceDefinition::from_identifier(parameter.identifier())),
parameter_type_symbol,
);
match symbol_table.insert_parameter_symbol(to_insert) {
Ok(parameter_symbol) => Some(parameter_symbol),
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
parameter.identifier().name(),
parameter.identifier().file_id(),
parameter.identifier().range(),
diagnostics,
);
None
}
}
} }
fn na_p1_return_type( fn na_p1_return_type(
@ -394,5 +474,22 @@ fn na_p1_function_body(
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
) { ) {
todo!() match function_body {
FunctionBody::FunctionAliasBody(alias_body) => {
// set scope id for pass 2; see below
alias_body.set_scope_id(symbol_table.current_scope_id());
}
FunctionBody::FunctionEqualsBody(equals_body) => {
// see below
equals_body.set_scope_id(symbol_table.current_scope_id());
}
FunctionBody::FunctionBlockBody(block_body) => {
// we need to do all insertion/resolution in pass 2, because we
// might call functions/use classes/etc from the same compilation
// unit which haven't been defined yet. So the strategy is to set
// the scope id for the body and then in pass 2, set the symbol
// table's current scope to that id.
block_body.set_scope_id(symbol_table.current_scope_id());
}
}
} }

View File

@ -36,8 +36,11 @@ pub mod symbol;
pub mod symbol_table; pub mod symbol_table;
mod util; mod util;
pub fn analyze_names<'a, F: Files<'a, FileId = usize, Name = String>>( pub fn analyze_names<
compilation_units: &mut Vec<Box<CompilationUnit>>, 'a,
F: Files<'a, FileId = usize, Name = &'a str, Source = &'a str> + ?Sized,
>(
compilation_units: &mut Vec<CompilationUnit>,
files: &'a F, files: &'a F,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Vec<DmDiagnostic> { ) -> Vec<DmDiagnostic> {
@ -46,7 +49,11 @@ pub fn analyze_names<'a, F: Files<'a, FileId = usize, Name = String>>(
// gather symbols // gather symbols
for compilation_unit in compilation_units.iter_mut() { for compilation_unit in compilation_units.iter_mut() {
let file_name = files.name(compilation_unit.file_id()).unwrap(); let file_name = files.name(compilation_unit.file_id()).unwrap();
na_p1_compilation_unit(&file_name, compilation_unit, symbol_table, &mut diagnostics); na_p1_compilation_unit(file_name, compilation_unit, symbol_table, &mut diagnostics);
}
if !diagnostics.is_empty() {
return diagnostics;
} }
// resolve symbols // resolve symbols
@ -54,221 +61,160 @@ pub fn analyze_names<'a, F: Files<'a, FileId = usize, Name = String>>(
na_p2_compilation_unit(compilation_unit, symbol_table, &mut diagnostics); na_p2_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
} }
diagnostics.into() diagnostics
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use super::*; use super::*;
// use crate::ast::build::build_ast; use crate::ast::build::build_ast;
// use crate::ast::children::NodeRef; use crate::parser::{DeimosParser, Rule};
// use crate::ast::walk::walk_depth_first; use crate::std_core::add_std_core_symbols;
// use crate::parser::{DeimosParser, Rule}; use codespan_reporting::files::SimpleFiles;
// use crate::std_core::add_std_core_symbols; use codespan_reporting::term;
// use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
// use codespan_reporting::term; use pest::Parser;
// use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use std::collections::HashMap;
// use indoc::indoc; use std::rc::Rc;
// use pest::Parser; use crate::name_analysis::symbol::use_symbol::StarUseSymbol;
// use std::collections::HashMap;
// fn parse_compilation_units<'a>(
// fn assert_number_of_diagnostics( files: &mut SimpleFiles<&'a str, &'a str>,
// sources: HashMap<&str, &str>, sources: HashMap<&'a str, &'a str>,
// symbol_table: &mut SymbolTable, ) -> Vec<CompilationUnit> {
// n_diagnostics: usize, let mut compilation_units: Vec<CompilationUnit> = vec![];
// ) -> Vec<CompilationUnit> {
// let mut files = SimpleFiles::new(); for (file_name, source) in sources {
// let mut compilation_units = vec![]; let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
// if let Err(err) = &parse_result {
// for (file_name, source) in sources { panic!("{}", err);
// let file_id = files.add(file_name, source); }
// let parse_result = DeimosParser::parse(Rule::CompilationUnit, source); let mut pairs = parse_result.unwrap();
// if let Err(err) = &parse_result { if pairs.as_str().trim() != source.trim() {
// panic!("{}", err); panic!("Parsing did not consume entire input.");
// } }
// let mut pairs = parse_result.unwrap(); let file_id = files.add(file_name, source);
// if pairs.as_str().trim() != source.trim() { compilation_units.push(build_ast(file_id, &mut pairs));
// panic!("Parsing did not consume entire input."); }
// }
// compilation_units
// compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap())); }
// }
// fn assert_number_of_diagnostics<'a>(
// let diagnostics = analyze_names(&mut compilation_units, symbol_table); sources: HashMap<&'a str, &'a str>,
// symbol_table: &mut SymbolTable,
// if diagnostics.len() != n_diagnostics { number_of_diagnostics: usize,
// let writer = StandardStream::stderr(ColorChoice::Always); ) -> Vec<CompilationUnit> {
// let config = term::Config::default(); let mut files = SimpleFiles::<&'a str, &'a str>::new();
// let mut compilation_units = parse_compilation_units(&mut files, sources);
// for diagnostic in &diagnostics {
// term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap(); let diagnostics = analyze_names(&mut compilation_units, &files, symbol_table);
// }
// if diagnostics.len() != number_of_diagnostics {
// eprintln!("{}", symbol_table); let writer = StandardStream::stderr(ColorChoice::Always);
// } let config = term::Config::default();
//
// assert_eq!(n_diagnostics, diagnostics.len()); for diagnostic in &diagnostics {
// term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
// compilation_units }
// }
// eprintln!("{}", symbol_table);
// fn assert_no_diagnostics( }
// sources: HashMap<&str, &str>,
// symbol_table: &mut SymbolTable, assert_eq!(number_of_diagnostics, diagnostics.len());
// ) -> Vec<CompilationUnit> {
// assert_number_of_diagnostics(sources, symbol_table, 0) compilation_units
// } }
//
// fn assert_saved_symbols(compilation_unit: &CompilationUnit) { fn assert_no_diagnostics(
// walk_depth_first(compilation_unit, &mut |node_ref| match node_ref { sources: HashMap<&str, &str>,
// NodeRef::Identifier(identifier) => { symbol_table: &mut SymbolTable,
// if identifier.saved_symbol().is_none() { ) -> Vec<CompilationUnit> {
// panic!("{:?} does not have a saved symbol.", identifier) assert_number_of_diagnostics(sources, symbol_table, 0)
// } }
// }
// NodeRef::FullyQualifiedName(fqn) => { #[test]
// if fqn.saved_symbol().is_none() { fn params_seen() {
// panic!("{:?} does not have a saved symbol.", fqn) let sources = HashMap::from([(
// } "main.dm",
// } "
// NodeRef::UseStatement(use_statement) => match use_statement { fn main(args: Array<String>)
// _ => todo!(), let x = args
// }, end
// _ => {} ",
// }) )]);
// }
// assert_no_diagnostics(sources, &mut SymbolTable::new());
// fn assert_resolved_symbols(compilation_unit: &CompilationUnit) { }
// walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
// NodeRef::UseStatement(use_statement) => match use_statement { #[test]
// _ => todo!(), fn two_files() {
// }, let sources = HashMap::from([
// _ => {} (
// }) "main.dm",
// } "
// use test::Greeter
// #[test] ",
// fn params_seen() { ),
// let sources: HashMap<&str, &str> = HashMap::from([( (
// "main.dm", "deps.dm",
// indoc! {" "
// fn main(args: Array<String>) { ns test
// let x = args;
// }"}, pub class Greeter end
// )]); ",
// ),
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new()); ]);
// for ref cu in cus { assert_no_diagnostics(sources, &mut SymbolTable::new());
// assert_saved_symbols(cu); }
// }
// } #[test]
// fn sees_std_core_println() {
// #[test] let sources: HashMap<&str, &str> = HashMap::from([(
// fn two_files() { "main.dm",
// let sources: HashMap<&str, &str> = HashMap::from([ "
// ( fn main(args: Array<String>)
// "main.dm", std::core::println args
// indoc! {" end
// use test::Greeter; ",
// "}, )]);
// ),
// ( let mut symbol_table = SymbolTable::new();
// "deps.dm", add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
// indoc! {" assert_no_diagnostics(sources, &mut symbol_table);
// ns test; }
//
// pub class Greeter {} #[test]
// "}, fn sees_duplicate_fn() {
// ), let sources: HashMap<&str, &str> = HashMap::from([(
// ]); "main.dm",
// "
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new()); fn main(args: Array<String>) end
// for ref cu in cus { fn main(args: Array<String>) end
// assert_saved_symbols(cu); ",
// assert_resolved_symbols(cu); )]);
// } assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
// } }
//
// #[test] #[test]
// fn sees_std_core_println() { fn sees_println() {
// let sources: HashMap<&str, &str> = HashMap::from([( let sources = HashMap::from([(
// "main.dm", "main.dm",
// indoc! {" "
// fn main(args: Array<String>) { fn main(args: Array<String>)
// println(args) println args
// } end
// "}, "
// )]); )]);
// let mut symbol_table = SymbolTable::new();
// let mut symbol_table = SymbolTable::new(); let global_std_core_use = StarUseSymbol::new(
// add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols."); &[Rc::from("std"), Rc::from("core")],
// let cus = assert_no_diagnostics(sources, &mut symbol_table); None
// for ref cu in cus { );
// assert_saved_symbols(cu); symbol_table.insert_star_use_symbol(global_std_core_use)
// assert_resolved_symbols(cu); .expect("Failed to insert star use symbol.");
// }
// } add_std_core_symbols(&mut symbol_table).expect("Failed to add std/core symbols.");
// assert_no_diagnostics(sources, &mut symbol_table);
// #[test] }
// fn sees_duplicate_fn() { }
// let sources: HashMap<&str, &str> = HashMap::from([(
// "main.dm",
// indoc! {"
// fn main(args: Array<String>) {}
// fn main(args: Array<String>) {}
// "},
// )]);
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
// }
//
// #[test]
// fn use_class_from_other_file() {
// let sources: HashMap<&str, &str> = HashMap::from([
// (
// "main.dm",
// indoc! {"
// use greeter::Greeter;
//
// fn test(greeter: Greeter) {}
// "},
// ),
// (
// "greeter.dm",
// indoc! {"
// ns greeter;
//
// class Greeter {}
// "},
// ),
// ]);
// let mut symbol_table = SymbolTable::new();
// let cus = assert_no_diagnostics(sources, &mut symbol_table);
// for ref cu in cus {
// assert_saved_symbols(cu);
// assert_resolved_symbols(cu);
// }
// }
//
// #[test]
// fn shadow_import() {
// let sources: HashMap<&str, &str> = HashMap::from([
// (
// "main.dm",
// indoc! {"
// use greeter::Greeter;
//
// class Greeter {}
// "},
// ),
// (
// "greeter.dm",
// indoc! {"
// ns greeter;
//
// class Greeter {}
// "},
// ),
// ]);
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
// }
// }

View File

@ -1,27 +1,27 @@
use crate::ast::node::{ use crate::ast::node::{AssignmentStatement, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Expression, ExpressionStatement, Function, FunctionAliasBody, FunctionBlockBody, FunctionBody, Identifier, LValue, ModuleLevelDeclaration, StarUseStatement, Statement, TypeUse, UseStatement, UseStatementIdentifier, UseStatementPrefix, VariableDeclaration, VariableUse};
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Identifier, UseStatement,
UseStatementIdentifier, UseStatementPrefix,
};
use crate::diagnostic::DmDiagnostic; use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable}; use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable};
use crate::name_analysis::util::handle_lookup_error; use crate::name_analysis::util::{handle_insert_error, handle_lookup_error, join_fqn_parts};
use std::rc::Rc;
pub fn na_p2_compilation_unit( pub fn na_p2_compilation_unit(
compilation_unit: &mut CompilationUnit, compilation_unit: &mut CompilationUnit,
symbol_table: &SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
) { ) {
// TODO: check namespace for proper file name
for use_statement in compilation_unit.use_statements_mut() { for use_statement in compilation_unit.use_statements_mut() {
na_p2_use_statement(use_statement, symbol_table, diagnostics); na_p2_use_statement(use_statement, symbol_table, diagnostics);
} }
for declaration in compilation_unit.module_level_declarations_mut() {
// TODO: declarations na_p2_module_level_declaration(declaration, symbol_table, diagnostics);
}
} }
fn na_p2_use_statement( fn na_p2_use_statement(
use_statement: &mut UseStatement, use_statement: &mut UseStatement,
symbol_table: &SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
) { ) {
match use_statement { match use_statement {
@ -29,22 +29,22 @@ fn na_p2_use_statement(
na_p2_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics); na_p2_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics);
} }
UseStatement::StarUseStatement(star_use_statement) => { UseStatement::StarUseStatement(star_use_statement) => {
todo!() na_p2_star_use_statement(star_use_statement, symbol_table, diagnostics);
} }
} }
} }
fn na_p2_concrete_use_statement( fn na_p2_concrete_use_statement(
concrete_use_statement: &mut ConcreteUseStatement, concrete_use_statement: &mut ConcreteUseStatement,
symbol_table: &SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
) { ) {
let base_fqn_parts: Vec<String> = concrete_use_statement let base_fqn_parts = concrete_use_statement
.prefixes() .prefixes()
.map(UseStatementPrefix::identifier) .map(UseStatementPrefix::identifier)
.map(Identifier::name) .map(Identifier::name)
.map(ToString::to_string) .map(Rc::from)
.collect(); .collect::<Vec<Rc<str>>>();
match concrete_use_statement.suffix_mut() { match concrete_use_statement.suffix_mut() {
ConcreteUseStatementSuffix::UseStatementIdentifier(use_statement_identifier) => { ConcreteUseStatementSuffix::UseStatementIdentifier(use_statement_identifier) => {
@ -69,17 +69,14 @@ fn na_p2_concrete_use_statement(
} }
fn handle_concrete_use_statement_identifier( fn handle_concrete_use_statement_identifier(
base_fqn_parts: &[String], base_fqn_parts: &[Rc<str>],
use_statement_identifier: &mut UseStatementIdentifier, use_statement_identifier: &mut UseStatementIdentifier,
symbol_table: &SymbolTable, symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>, diagnostics: &mut Vec<DmDiagnostic>,
) { ) {
let fqn_parts = { let fqn_parts = {
let mut all_parts: Vec<&str> = vec![]; let mut all_parts = base_fqn_parts.to_vec();
for part in base_fqn_parts { all_parts.push(Rc::from(use_statement_identifier.identifier().name()));
all_parts.push(part);
}
all_parts.push(use_statement_identifier.identifier().name());
all_parts all_parts
}; };
@ -93,10 +90,305 @@ fn handle_concrete_use_statement_identifier(
} else { } else {
handle_lookup_error( handle_lookup_error(
SymbolLookupError::NoDefinition, SymbolLookupError::NoDefinition,
&fqn_parts.join("::"), &join_fqn_parts(&fqn_parts),
use_statement_identifier.identifier().file_id(), use_statement_identifier.identifier().file_id(),
use_statement_identifier.identifier().range(), use_statement_identifier.identifier().range(),
diagnostics, diagnostics,
); );
} }
} }
fn na_p2_star_use_statement(
star_use_statement: &mut StarUseStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let mut symbol_ref_mut = star_use_statement.symbol().unwrap().borrow_mut();
match symbol_table.find_usable_symbols_by_base_fqn(symbol_ref_mut.fqn_parts()) {
Ok(usable_symbols) => {
symbol_ref_mut.set_resolved_symbols(usable_symbols);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
&join_fqn_parts(symbol_ref_mut.fqn_parts()),
star_use_statement.file_id(),
star_use_statement.range(),
diagnostics,
);
}
}
}
fn na_p2_module_level_declaration(
module_level_declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match module_level_declaration {
ModuleLevelDeclaration::Module(module_declaration) => {
todo!()
}
ModuleLevelDeclaration::Interface(interface) => {
todo!()
}
ModuleLevelDeclaration::Class(class) => {
todo!()
}
ModuleLevelDeclaration::Function(function) => {
na_p2_function(function, symbol_table, diagnostics);
}
ModuleLevelDeclaration::PlatformFunction(platform_function) => {
todo!()
}
}
}
fn na_p2_function(
function: &mut Function,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_function_body(function.function_body_mut(), symbol_table, diagnostics);
}
fn na_p2_function_body(
function_body: &mut FunctionBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match function_body {
FunctionBody::FunctionAliasBody(alias_body) => {
na_p2_function_alias_body(alias_body, symbol_table, diagnostics);
}
FunctionBody::FunctionEqualsBody(equals_body) => {}
FunctionBody::FunctionBlockBody(block_body) => {
na_p2_function_block_body(block_body, symbol_table, diagnostics);
}
}
}
fn na_p2_function_alias_body(
function_alias_body: &mut FunctionAliasBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let maybe_function_symbol = symbol_table.lookup_function_symbol(
function_alias_body.identifier().name(),
*function_alias_body.scope_id().unwrap(),
);
match maybe_function_symbol {
Ok(function_symbol) => {
function_alias_body.set_resolved_function_symbol(function_symbol);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
function_alias_body.identifier().name(),
function_alias_body.identifier().file_id(),
function_alias_body.identifier().range(),
diagnostics,
);
}
}
}
fn na_p2_function_block_body(
function_block_body: &mut FunctionBlockBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
symbol_table.set_current_scope(*function_block_body.scope_id().unwrap());
for statement in function_block_body.statements_mut() {
na_p2_statement(statement, symbol_table, diagnostics);
}
}
fn na_p2_statement(
statement: &mut Statement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match statement {
Statement::VariableDeclaration(variable_declaration) => {
na_p2_variable_declaration(variable_declaration, symbol_table, diagnostics);
}
Statement::AssignmentStatement(assignment_statement) => {
na_p2_assignment_statement(assignment_statement, symbol_table, diagnostics);
}
Statement::ExpressionStatement(expression_statement) => {
na_p2_expression_statement(expression_statement, symbol_table, diagnostics);
}
Statement::UseStatement(use_statement) => {
todo!()
}
Statement::IfStatement(if_statement) => {
todo!()
}
Statement::WhileStatement(while_statement) => {
todo!()
}
Statement::ForStatement(for_statement) => {
todo!()
}
}
}
fn na_p2_variable_declaration(
variable_declaration: &mut VariableDeclaration,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
// handle variable itself
let to_insert = VariableSymbol::new(
variable_declaration.identifier().name(),
variable_declaration.is_mut(),
Some(SourceDefinition::from_identifier(
variable_declaration.identifier(),
)),
);
match symbol_table.insert_variable_symbol(to_insert) {
Ok(variable_symbol) => {
variable_declaration.set_variable_symbol(variable_symbol);
}
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
variable_declaration.identifier().name(),
variable_declaration.identifier().file_id(),
variable_declaration.identifier().range(),
diagnostics,
);
}
}
// type-use
if let Some(type_use) = variable_declaration.type_use_mut() {
na_p2_type_use(type_use, symbol_table, diagnostics);
}
// initializer
if let Some(expression) = variable_declaration.expression_mut() {
na_p2_expression(expression, symbol_table, diagnostics);
}
}
fn na_p2_assignment_statement(
assignment_statement: &mut AssignmentStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_l_value(assignment_statement.l_value_mut(), symbol_table, diagnostics);
na_p2_expression(assignment_statement.expression_mut(), symbol_table, diagnostics);
}
fn na_p2_l_value(
l_value: &mut LValue,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_variable_use(l_value.variable_use_mut(), symbol_table, diagnostics);
// TODO: suffixes
}
fn na_p2_expression_statement(
expression_statement: &mut ExpressionStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_expression(expression_statement.expression_mut(), symbol_table, diagnostics);
}
fn na_p2_variable_use(
variable_use: &mut VariableUse,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match symbol_table.lookup_lv_symbol(variable_use.identifier().name()) {
Ok(lv_symbol) => {
variable_use.set_lv_symbol(lv_symbol);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
variable_use.identifier().name(),
variable_use.identifier().file_id(),
variable_use.identifier().range(),
diagnostics,
);
}
}
}
fn na_p2_expression(
expression: &mut Expression,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match expression {
Expression::Ternary(ternary) => {
todo!()
}
Expression::Or(or) => {
todo!()
}
Expression::And(and) => {
todo!()
}
Expression::Comparison(comparison) => {
todo!()
}
Expression::Shift(shift) => {
todo!()
}
Expression::Additive(additive) => {
todo!()
}
Expression::Multiplicative(multiplicative) => {
todo!()
}
Expression::Prefix(prefix) => {
todo!()
}
Expression::Suffix(suffix) => {
todo!()
}
Expression::Literal(literal) => {
todo!()
}
Expression::VariableUse(variable_use) => {
na_p2_variable_use(variable_use, symbol_table, diagnostics);
}
Expression::Fqn(fqn) => {
todo!()
}
Expression::Closure(closure) => {
todo!()
}
Expression::List(list) => {
todo!()
}
}
}
fn na_p2_type_use(
type_use: &mut TypeUse,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match type_use {
TypeUse::PrimitiveType(primitive_type_use) => {
todo!()
}
TypeUse::InterfaceOrClassTypeUse(interface_or_class_type_use) => {
todo!()
}
TypeUse::TupleTypeUse(tuple_type_use) => {
todo!()
}
TypeUse::FunctionTypeUse(function_type_use) => {
todo!()
}
}
}

View File

@ -1,4 +1,5 @@
use crate::name_analysis::symbol::source_definition::SourceDefinition; use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::Symbol;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
#[derive(Clone)] #[derive(Clone)]
@ -23,11 +24,11 @@ impl ClassMemberSymbol {
source_definition, source_definition,
} }
} }
pub fn is_public(&self) -> bool { pub fn is_public(&self) -> bool {
self.is_public self.is_public
} }
pub fn is_mut(&self) -> bool { pub fn is_mut(&self) -> bool {
self.is_mut self.is_mut
} }
@ -35,8 +36,10 @@ impl ClassMemberSymbol {
pub fn declared_name(&self) -> &str { pub fn declared_name(&self) -> &str {
&self.declared_name &self.declared_name
} }
}
pub fn source_definition(&self) -> Option<&SourceDefinition> { impl Symbol for ClassMemberSymbol {
fn source_definition(&self) -> Option<&SourceDefinition> {
self.source_definition.as_ref() self.source_definition.as_ref()
} }
} }

View File

@ -0,0 +1,28 @@
use std::cell::RefCell;
use std::rc::Rc;
use crate::name_analysis::symbol::class_member_symbol::ClassMemberSymbol;
use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
use crate::name_analysis::symbol::Symbol;
use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
pub enum LVSymbol {
ClassMember(Rc<RefCell<ClassMemberSymbol>>),
Parameter(Rc<RefCell<ParameterSymbol>>),
Variable(Rc<RefCell<VariableSymbol>>),
}
impl LVSymbol {
pub fn to_symbol(self) -> Rc<RefCell<dyn Symbol>> {
match self {
LVSymbol::ClassMember(class_member_symbol) => {
class_member_symbol as Rc<RefCell<dyn Symbol>>
}
LVSymbol::Parameter(parameter_symbol) => {
parameter_symbol as Rc<RefCell<dyn Symbol>>
}
LVSymbol::Variable(variable_symbol) => {
variable_symbol as Rc<RefCell<dyn Symbol>>
}
}
}
}

View File

@ -3,6 +3,7 @@ pub mod class_symbol;
pub mod function_symbol; pub mod function_symbol;
pub mod generic_type_symbol; pub mod generic_type_symbol;
pub mod interface_symbol; pub mod interface_symbol;
pub mod lv_symbol;
pub mod module_level_symbol; pub mod module_level_symbol;
pub mod module_symbol; pub mod module_symbol;
pub mod parameter_symbol; pub mod parameter_symbol;
@ -16,6 +17,13 @@ pub mod variable_symbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition; use crate::name_analysis::symbol::source_definition::SourceDefinition;
use std::fmt::Debug; use std::fmt::Debug;
pub use self::{
class_member_symbol::*, class_symbol::*, function_symbol::*, function_symbol::*,
generic_type_symbol::*, interface_symbol::*, lv_symbol::*, module_level_symbol::*,
module_symbol::*, parameter_symbol::*, primitive_type_symbol::*, type_symbol::*,
usable_symbol::*, use_symbol::*, variable_symbol::*,
};
pub trait Symbol: Debug { pub trait Symbol: Debug {
fn source_definition(&self) -> Option<&SourceDefinition>; fn source_definition(&self) -> Option<&SourceDefinition>;
} }

View File

@ -1,9 +1,11 @@
use crate::name_analysis::symbol::source_definition::SourceDefinition; use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::type_symbol::TypeSymbol; use crate::name_analysis::symbol::type_symbol::TypeSymbol;
use crate::name_analysis::symbol::Symbol;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::rc::Rc;
pub struct ParameterSymbol { pub struct ParameterSymbol {
declared_name: String, declared_name: Rc<str>,
source_definition: Option<SourceDefinition>, source_definition: Option<SourceDefinition>,
type_symbol: Option<TypeSymbol>, type_symbol: Option<TypeSymbol>,
} }
@ -14,8 +16,8 @@ impl ParameterSymbol {
source_definition: Option<SourceDefinition>, source_definition: Option<SourceDefinition>,
type_symbol: Option<TypeSymbol>, type_symbol: Option<TypeSymbol>,
) -> Self { ) -> Self {
ParameterSymbol { Self {
declared_name: declared_name.to_string(), declared_name: Rc::from(declared_name),
source_definition, source_definition,
type_symbol, type_symbol,
} }
@ -25,7 +27,13 @@ impl ParameterSymbol {
&self.declared_name &self.declared_name
} }
pub fn source_definition(&self) -> Option<&SourceDefinition> { pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
}
impl Symbol for ParameterSymbol {
fn source_definition(&self) -> Option<&SourceDefinition> {
self.source_definition.as_ref() self.source_definition.as_ref()
} }
} }

View File

@ -1,4 +1,4 @@
use crate::ast::node::{Identifier, Operator}; use crate::ast::node::{Identifier, Operator, StarUseStatement};
use std::range::Range; use std::range::Range;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -14,7 +14,7 @@ impl SourceDefinition {
range: identifier.range(), range: identifier.range(),
} }
} }
pub fn from_operator(operator: &Operator) -> Self { pub fn from_operator(operator: &Operator) -> Self {
Self { Self {
file_id: operator.file_id(), file_id: operator.file_id(),
@ -22,6 +22,13 @@ impl SourceDefinition {
} }
} }
pub fn from_star_use_statement(star_use_statement: &StarUseStatement) -> Self {
Self {
file_id: star_use_statement.file_id(),
range: star_use_statement.range(),
}
}
pub fn file_id(&self) -> usize { pub fn file_id(&self) -> usize {
self.file_id self.file_id
} }

View File

@ -5,7 +5,7 @@ use crate::name_analysis::symbol::Symbol;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum UsableSymbol { pub enum UsableSymbol {
Interface(Rc<RefCell<InterfaceSymbol>>), Interface(Rc<RefCell<InterfaceSymbol>>),
Class(Rc<RefCell<ClassSymbol>>), Class(Rc<RefCell<ClassSymbol>>),

View File

@ -76,6 +76,7 @@ impl Debug for ConcreteUseSymbol {
pub struct StarUseSymbol { pub struct StarUseSymbol {
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
source_definition: Option<SourceDefinition>, source_definition: Option<SourceDefinition>,
resolved_symbols: Vec<UsableSymbol>,
} }
impl StarUseSymbol { impl StarUseSymbol {
@ -83,12 +84,21 @@ impl StarUseSymbol {
Self { Self {
fqn_parts: fqn_parts.to_vec(), fqn_parts: fqn_parts.to_vec(),
source_definition, source_definition,
resolved_symbols: vec![],
} }
} }
pub fn fqn_parts(&self) -> &[Rc<str>] { pub fn fqn_parts(&self) -> &[Rc<str>] {
&self.fqn_parts &self.fqn_parts
} }
pub fn resolved_symbols(&self) -> &[UsableSymbol] {
&self.resolved_symbols
}
pub fn set_resolved_symbols(&mut self, symbols: Vec<UsableSymbol>) {
self.resolved_symbols = symbols;
}
} }
impl Symbol for StarUseSymbol { impl Symbol for StarUseSymbol {

View File

@ -1,17 +1,23 @@
use crate::name_analysis::symbol::source_definition::SourceDefinition; use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::Symbol;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::rc::Rc;
#[derive(Clone)] #[derive(Clone)]
pub struct VariableSymbol { pub struct VariableSymbol {
declared_name: String, declared_name: Rc<str>,
is_mutable: bool, is_mutable: bool,
source_definition: Option<SourceDefinition>, source_definition: Option<SourceDefinition>,
} }
impl VariableSymbol { impl VariableSymbol {
pub fn new(declared_name: &str, is_mutable: bool, source_definition: Option<SourceDefinition>) -> Self { pub fn new(
declared_name: &str,
is_mutable: bool,
source_definition: Option<SourceDefinition>,
) -> Self {
VariableSymbol { VariableSymbol {
declared_name: declared_name.to_string(), declared_name: Rc::from(declared_name),
is_mutable, is_mutable,
source_definition, source_definition,
} }
@ -21,11 +27,17 @@ impl VariableSymbol {
&self.declared_name &self.declared_name
} }
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn is_mutable(&self) -> bool { pub fn is_mutable(&self) -> bool {
self.is_mutable self.is_mutable
} }
}
pub fn source_definition(&self) -> Option<&SourceDefinition> {
impl Symbol for VariableSymbol {
fn source_definition(&self) -> Option<&SourceDefinition> {
self.source_definition.as_ref() self.source_definition.as_ref()
} }
} }

View File

@ -1,9 +1,11 @@
use crate::name_analysis::symbol::function_symbol::FunctionSymbol; use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
use crate::name_analysis::symbol::generic_type_symbol::GenericTypeSymbol;
use crate::name_analysis::symbol::module_symbol::ModuleSymbol; use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
use crate::name_analysis::symbol::type_symbol::TypeSymbol; use crate::name_analysis::symbol::type_symbol::TypeSymbol;
use crate::name_analysis::symbol::usable_symbol::UsableSymbol; use crate::name_analysis::symbol::usable_symbol::UsableSymbol;
use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol}; use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
use crate::name_analysis::symbol::Symbol; use crate::name_analysis::symbol::{LVSymbol, Symbol};
use crate::name_analysis::symbol_table::fqn_context::FqnContext; use crate::name_analysis::symbol_table::fqn_context::FqnContext;
use crate::name_analysis::symbol_table::symbol_tree::SymbolTree; use crate::name_analysis::symbol_table::symbol_tree::SymbolTree;
use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined; use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined;
@ -12,7 +14,7 @@ use std::cell::RefCell;
use std::fmt::Display; use std::fmt::Display;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
use crate::name_analysis::symbol::generic_type_symbol::GenericTypeSymbol; use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
pub(self) mod fqn_context; pub(self) mod fqn_context;
mod scope; mod scope;
@ -25,6 +27,7 @@ pub enum SymbolInsertError {
pub enum SymbolLookupError { pub enum SymbolLookupError {
NoDefinition, NoDefinition,
NoSuchNamespace,
} }
#[derive(Debug)] #[derive(Debug)]
@ -65,6 +68,10 @@ impl SymbolTable {
self.current_scope_id = parent_id; self.current_scope_id = parent_id;
} }
} }
pub fn set_current_scope(&mut self, id: usize) {
self.current_scope_id = id;
}
pub fn set_current_fqn(&mut self, names: &[&str]) { pub fn set_current_fqn(&mut self, names: &[&str]) {
self.fqn_context = Box::new(FqnContext::new()); self.fqn_context = Box::new(FqnContext::new());
@ -95,7 +102,7 @@ impl SymbolTable {
parts parts
} }
pub fn find_usable_symbol(&self, fqn_parts: &[&str]) -> Option<UsableSymbol> { pub fn find_usable_symbol(&self, fqn_parts: &[Rc<str>]) -> Option<UsableSymbol> {
self.symbol_tree self.symbol_tree
.find_interface(fqn_parts) .find_interface(fqn_parts)
.map(|interface_symbol| UsableSymbol::Interface(interface_symbol)) .map(|interface_symbol| UsableSymbol::Interface(interface_symbol))
@ -111,6 +118,17 @@ impl SymbolTable {
}) })
} }
pub fn register_module(&mut self, fqn_parts: &[&str]) {
self.symbol_tree.register_module_by_fqn_parts(fqn_parts);
}
pub fn find_usable_symbols_by_base_fqn(
&self,
fqn_parts: &[Rc<str>],
) -> Result<Vec<UsableSymbol>, SymbolLookupError> {
self.symbol_tree.find_all_by_base_fqn(fqn_parts)
}
fn current_scope(&self) -> &Scope { fn current_scope(&self) -> &Scope {
&self.scopes[self.current_scope_id] &self.scopes[self.current_scope_id]
} }
@ -179,15 +197,54 @@ impl SymbolTable {
Ok(inserted) Ok(inserted)
} }
} }
pub fn insert_generic_type_symbol( pub fn insert_generic_type_symbol(
&mut self, &mut self,
generic_type_symbol: GenericTypeSymbol, generic_type_symbol: GenericTypeSymbol,
) -> Result<Rc<RefCell<GenericTypeSymbol>>, SymbolInsertError> { ) -> Result<Rc<RefCell<GenericTypeSymbol>>, SymbolInsertError> {
if let Some(defined_symbol) = self.current_scope().find_type_symbol(generic_type_symbol.declared_name()) { if let Some(defined_symbol) = self
.current_scope()
.find_type_symbol(generic_type_symbol.declared_name())
{
Err(SymbolAlreadyDefined(defined_symbol.to_symbol())) Err(SymbolAlreadyDefined(defined_symbol.to_symbol()))
} else { } else {
let inserted = self.current_scope_mut().insert_generic_type_symbol(generic_type_symbol); let inserted = self
.current_scope_mut()
.insert_generic_type_symbol(generic_type_symbol);
Ok(inserted)
}
}
pub fn insert_parameter_symbol(
&mut self,
parameter_symbol: ParameterSymbol,
) -> Result<Rc<RefCell<ParameterSymbol>>, SymbolInsertError> {
if let Some(defined_symbol) = self
.current_scope()
.find_lv_symbol(parameter_symbol.declared_name())
{
Err(SymbolAlreadyDefined(defined_symbol.to_symbol()))
} else {
let inserted = self
.current_scope_mut()
.insert_parameter_symbol(parameter_symbol);
Ok(inserted)
}
}
pub fn insert_variable_symbol(
&mut self,
variable_symbol: VariableSymbol,
) -> Result<Rc<RefCell<VariableSymbol>>, SymbolInsertError> {
if let Some(defined_symbol) = self
.current_scope()
.find_lv_symbol(variable_symbol.declared_name())
{
Err(SymbolAlreadyDefined(defined_symbol.to_symbol()))
} else {
let inserted = self
.current_scope_mut()
.insert_variable_symbol(variable_symbol);
Ok(inserted) Ok(inserted)
} }
} }
@ -205,10 +262,38 @@ impl SymbolTable {
} }
Err(SymbolLookupError::NoDefinition) Err(SymbolLookupError::NoDefinition)
} }
pub fn lookup_type_by_fqn(&self, fqn_parts: &[&str]) -> Result<TypeSymbol, SymbolLookupError> { pub fn lookup_type_by_fqn(&self, fqn_parts: &[&str]) -> Result<TypeSymbol, SymbolLookupError> {
todo!() todo!()
} }
pub fn lookup_function_symbol(&self, declared_name: &str, scope_id: usize) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolLookupError> {
let mut current_scope: Option<&Scope> = self.scopes.get(scope_id);
while let Some(scope) = current_scope.take() {
if let Some(function_symbol) = scope.find_function_symbol(declared_name) {
return Ok(function_symbol.clone());
} else {
current_scope = scope
.parent()
.and_then(|parent_id| self.scopes.get(parent_id));
}
}
Err(SymbolLookupError::NoDefinition)
}
pub fn lookup_lv_symbol(&self, declared_name: &str) -> Result<LVSymbol, SymbolLookupError> {
let mut current_scope: Option<&Scope> = Some(self.current_scope());
while let Some(scope) = current_scope.take() {
if let Some(lv_symbol) = scope.find_lv_symbol(declared_name) {
return Ok(lv_symbol)
} else {
current_scope = scope
.parent()
.and_then(|parent_id| self.scopes.get(parent_id));
}
}
Err(SymbolLookupError::NoDefinition)
}
} }
impl Display for SymbolTable { impl Display for SymbolTable {

View File

@ -3,6 +3,7 @@ use crate::name_analysis::symbol::class_symbol::ClassSymbol;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol; use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
use crate::name_analysis::symbol::generic_type_symbol::GenericTypeSymbol; use crate::name_analysis::symbol::generic_type_symbol::GenericTypeSymbol;
use crate::name_analysis::symbol::interface_symbol::InterfaceSymbol; use crate::name_analysis::symbol::interface_symbol::InterfaceSymbol;
use crate::name_analysis::symbol::lv_symbol::LVSymbol;
use crate::name_analysis::symbol::module_level_symbol::ModuleLevelSymbol; use crate::name_analysis::symbol::module_level_symbol::ModuleLevelSymbol;
use crate::name_analysis::symbol::module_symbol::ModuleSymbol; use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol; use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
@ -26,9 +27,9 @@ pub struct Scope {
class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>, class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>,
generic_symbols: HashMap<Rc<str>, Rc<RefCell<GenericTypeSymbol>>>, generic_symbols: HashMap<Rc<str>, Rc<RefCell<GenericTypeSymbol>>>,
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>, function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
parameter_symbols: HashMap<Rc<str>, ParameterSymbol>, parameter_symbols: HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>>,
variable_symbols: HashMap<Rc<str>, VariableSymbol>, variable_symbols: HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>>,
class_member_symbols: HashMap<Rc<str>, ClassMemberSymbol>, class_member_symbols: HashMap<Rc<str>, Rc<RefCell<ClassMemberSymbol>>>,
debug_name: String, debug_name: String,
} }
@ -125,12 +126,31 @@ impl Scope {
pub fn get_class_symbol(&self, declared_name: &str) -> Option<&Rc<RefCell<ClassSymbol>>> { pub fn get_class_symbol(&self, declared_name: &str) -> Option<&Rc<RefCell<ClassSymbol>>> {
self.class_symbols.get(declared_name) self.class_symbols.get(declared_name)
} }
pub fn insert_generic_type_symbol(&mut self, symbol: GenericTypeSymbol) -> Rc<RefCell<GenericTypeSymbol>> { pub fn insert_generic_type_symbol(
&mut self,
symbol: GenericTypeSymbol,
) -> Rc<RefCell<GenericTypeSymbol>> {
let key = symbol.declared_name_owned(); let key = symbol.declared_name_owned();
insert_symbol!(self.generic_symbols, symbol, key) insert_symbol!(self.generic_symbols, symbol, key)
} }
pub fn insert_parameter_symbol(
&mut self,
symbol: ParameterSymbol,
) -> Rc<RefCell<ParameterSymbol>> {
let key = symbol.declared_name_owned();
insert_symbol!(self.parameter_symbols, symbol, key)
}
pub fn insert_variable_symbol(
&mut self,
symbol: VariableSymbol,
) -> Rc<RefCell<VariableSymbol>> {
let key = symbol.declared_name_owned();
insert_symbol!(self.variable_symbols, symbol, key)
}
pub fn find_module_level_symbol(&self, declared_name: &str) -> Option<ModuleLevelSymbol> { pub fn find_module_level_symbol(&self, declared_name: &str) -> Option<ModuleLevelSymbol> {
self.module_symbols self.module_symbols
.get(declared_name) .get(declared_name)
@ -168,6 +188,26 @@ impl Scope {
}) })
} }
pub fn find_lv_symbol(&self, declared_name: &str) -> Option<LVSymbol> {
self.class_member_symbols
.get(declared_name)
.map(|class_member_symbol| LVSymbol::ClassMember(class_member_symbol.clone()))
.or_else(|| {
self.parameter_symbols
.get(declared_name)
.map(|parameter_symbol| LVSymbol::Parameter(parameter_symbol.clone()))
})
.or_else(|| {
self.variable_symbols
.get(declared_name)
.map(|variable_symbol| LVSymbol::Variable(variable_symbol.clone()))
})
}
pub fn find_function_symbol(&self, declared_name: &str) -> Option<&Rc<RefCell<FunctionSymbol>>> {
self.function_symbols.get(declared_name)
}
pub fn debug_name(&self) -> &str { pub fn debug_name(&self) -> &str {
&self.debug_name &self.debug_name
} }

View File

@ -2,6 +2,8 @@ use crate::name_analysis::symbol::class_symbol::ClassSymbol;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol; use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
use crate::name_analysis::symbol::interface_symbol::InterfaceSymbol; use crate::name_analysis::symbol::interface_symbol::InterfaceSymbol;
use crate::name_analysis::symbol::module_symbol::ModuleSymbol; use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::usable_symbol::UsableSymbol;
use crate::name_analysis::symbol_table::SymbolLookupError;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -24,35 +26,35 @@ impl SymbolTree {
} }
} }
pub fn find_class(&self, fqn_parts: &[&str]) -> Option<Rc<RefCell<ClassSymbol>>> { pub fn find_class(&self, fqn_parts: &[Rc<str>]) -> Option<Rc<RefCell<ClassSymbol>>> {
match fqn_parts.len() { match fqn_parts.len() {
0 => None, 0 => None,
1 => self.classes.get(fqn_parts[0]).cloned(), 1 => self.classes.get(&fqn_parts[0]).cloned(),
_ => self _ => self
.children .children
.get(fqn_parts[0]) .get(&fqn_parts[0])
.and_then(|child_tree| child_tree.find_class(&fqn_parts[1..])), .and_then(|child_tree| child_tree.find_class(&fqn_parts[1..])),
} }
} }
pub fn find_interface(&self, fqn_parts: &[&str]) -> Option<Rc<RefCell<InterfaceSymbol>>> { pub fn find_interface(&self, fqn_parts: &[Rc<str>]) -> Option<Rc<RefCell<InterfaceSymbol>>> {
match fqn_parts.len() { match fqn_parts.len() {
0 => None, 0 => None,
1 => self.interfaces.get(fqn_parts[0]).cloned(), 1 => self.interfaces.get(&fqn_parts[0]).cloned(),
_ => self _ => self
.children .children
.get(fqn_parts[0]) .get(&fqn_parts[0])
.and_then(|child_tree| child_tree.find_interface(&fqn_parts[1..])), .and_then(|child_tree| child_tree.find_interface(&fqn_parts[1..])),
} }
} }
pub fn find_function(&self, fqn_parts: &[&str]) -> Option<Rc<RefCell<FunctionSymbol>>> { pub fn find_function(&self, fqn_parts: &[Rc<str>]) -> Option<Rc<RefCell<FunctionSymbol>>> {
match fqn_parts.len() { match fqn_parts.len() {
0 => None, 0 => None,
1 => self.functions.get(fqn_parts[0]).cloned(), 1 => self.functions.get(&fqn_parts[0]).cloned(),
_ => self _ => self
.children .children
.get(fqn_parts[0]) .get(&fqn_parts[0])
.and_then(|child_tree| child_tree.find_function(&fqn_parts[1..])), .and_then(|child_tree| child_tree.find_function(&fqn_parts[1..])),
} }
} }
@ -61,6 +63,15 @@ impl SymbolTree {
self.recurse_register_module(&module_symbol.borrow().fqn_parts()); self.recurse_register_module(&module_symbol.borrow().fqn_parts());
} }
pub fn register_module_by_fqn_parts(&mut self, fqn_parts: &[&str]) {
self.recurse_register_module(
&fqn_parts
.iter()
.map(|part| Rc::from(*part))
.collect::<Vec<Rc<str>>>(),
);
}
fn recurse_register_module(&mut self, fqn_parts: &[Rc<str>]) { fn recurse_register_module(&mut self, fqn_parts: &[Rc<str>]) {
if fqn_parts.len() == 0 { if fqn_parts.len() == 0 {
panic!("Unable to register module fqn with no parts.") panic!("Unable to register module fqn with no parts.")
@ -79,15 +90,19 @@ impl SymbolTree {
} }
} }
} }
pub fn register_function(&mut self, function_symbol: Rc<RefCell<FunctionSymbol>>) { pub fn register_function(&mut self, function_symbol: Rc<RefCell<FunctionSymbol>>) {
let fqn_parts = function_symbol.borrow().fqn_parts_owned(); let fqn_parts = function_symbol.borrow().fqn_parts_owned();
self.recurse_register_function(function_symbol, &fqn_parts); self.recurse_register_function(function_symbol, &fqn_parts);
} }
fn recurse_register_function(&mut self, function_symbol: Rc<RefCell<FunctionSymbol>>, fqn_parts: &[Rc<str>]) { fn recurse_register_function(
&mut self,
function_symbol: Rc<RefCell<FunctionSymbol>>,
fqn_parts: &[Rc<str>],
) {
if fqn_parts.len() == 0 { if fqn_parts.len() == 0 {
panic!("Unable to register function fqn with no parts.") panic!("Unable to register function fqn with no parts.");
} }
if fqn_parts.len() == 1 { if fqn_parts.len() == 1 {
self.functions.insert(fqn_parts[0].clone(), function_symbol); self.functions.insert(fqn_parts[0].clone(), function_symbol);
@ -100,5 +115,33 @@ impl SymbolTree {
} }
} }
} }
pub fn find_all_by_base_fqn(
&self,
fqn_parts: &[Rc<str>],
) -> Result<Vec<UsableSymbol>, SymbolLookupError> {
match fqn_parts.len() {
0 => {
let mut all_symbols: Vec<UsableSymbol> = vec![];
for interface_symbol in self.interfaces.values() {
all_symbols.push(UsableSymbol::Interface(interface_symbol.clone()));
}
for class_symbol in self.classes.values() {
all_symbols.push(UsableSymbol::Class(class_symbol.clone()));
}
for function_symbol in self.functions.values() {
all_symbols.push(UsableSymbol::Function(function_symbol.clone()));
}
Ok(all_symbols)
}
_ => {
if self.children.contains_key(fqn_parts[0].as_ref()) {
let child = self.children.get(fqn_parts[0].as_ref()).unwrap();
child.find_all_by_base_fqn(fqn_parts)
} else {
Err(SymbolLookupError::NoSuchNamespace)
}
}
}
}
} }

View File

@ -45,11 +45,19 @@ pub fn handle_lookup_error(
match err { match err {
SymbolLookupError::NoDefinition => { SymbolLookupError::NoDefinition => {
let diagnostic = Diagnostic::error() let diagnostic = Diagnostic::error()
.with_message(format!("No such symbol '{}' in scope.", error_symbol_name,)) .with_message(format!("No such symbol '{}' in scope.", error_symbol_name))
.with_label( .with_label(
Label::primary(error_file_id, error_range).with_message("Symbol used here."), Label::primary(error_file_id, error_range).with_message("Symbol used here."),
); );
diagnostics.push(diagnostic); diagnostics.push(diagnostic);
},
SymbolLookupError::NoSuchNamespace => {
let diagnostic = Diagnostic::error()
.with_message(format!("No such namespace '{}' found", error_symbol_name))
.with_label(
Label::primary(error_file_id, error_range).with_message("Namespace used here.")
);
diagnostics.push(diagnostic);
} }
} }
} }

View File

@ -60,17 +60,17 @@ FullyQualifiedName:
vec: vec:
rule: Identifier rule: Identifier
- file_id: - file_id:
special: special:
kind: file_id kind: file_id
- range: - range:
special: special:
kind: range kind: range
derive: derive:
- PartialEq - PartialEq
- Eq - Eq
- Hash - Hash
IdentifierOrFqn: IdentifierOrFqn:
tree_enum: tree_enum:
rules: rules:
- Identifier - Identifier
- FullyQualifiedName - FullyQualifiedName
@ -281,6 +281,10 @@ StarUseStatement:
- range: - range:
special: special:
kind: range kind: range
fields:
- symbol:
kind: StarUseSymbol
wrap: rc_ref_cell
UseStatementPrefix: UseStatementPrefix:
struct: struct:
children: children:
@ -605,6 +609,9 @@ FunctionEqualsBody:
struct: struct:
children: children:
- expression - expression
fields:
- scope_id:
kind: usize
FunctionAliasBody: FunctionAliasBody:
struct: struct:
children: children:
@ -612,6 +619,12 @@ FunctionAliasBody:
skip: skip:
rule: Alias rule: Alias
- identifier - identifier
fields:
- scope_id:
kind: usize
- resolved_function_symbol:
kind: FunctionSymbol
wrap: rc_ref_cell
FunctionBlockBody: FunctionBlockBody:
struct: struct:
children: children:
@ -621,6 +634,9 @@ FunctionBlockBody:
- end_kw: - end_kw:
skip: skip:
rule: End rule: End
fields:
- scope_id:
kind: usize
# Class constructs # Class constructs
ClassConstructor: ClassConstructor:
@ -677,6 +693,10 @@ VariableDeclaration:
- expression: - expression:
member: member:
optional: true optional: true
fields:
- variable_symbol:
kind: VariableSymbol
wrap: rc_ref_cell
AssignmentStatement: AssignmentStatement:
struct: struct:
children: children:
@ -782,10 +802,9 @@ VariableUse:
struct: struct:
children: children:
- identifier - identifier
derive: fields:
- PartialEq - lv_symbol:
- Eq kind: LVSymbol
- Hash
# Expressions # Expressions
Expression: Expression:

View File

@ -7,11 +7,16 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub fn add_std_core_symbols(symbol_table: &mut SymbolTable) -> Result<(), SymbolInsertError> { pub fn add_std_core_symbols(symbol_table: &mut SymbolTable) -> Result<(), SymbolInsertError> {
symbol_table.register_module(&["std"]);
symbol_table.register_module(&["std", "core"]);
symbol_table.set_current_fqn(&vec!["std", "core"]); symbol_table.set_current_fqn(&vec!["std", "core"]);
let println_msg_symbol = ParameterSymbol::new("msg", None, Some( let println_msg_symbol = ParameterSymbol::new(
TypeSymbol::Primitive(PrimitiveTypeSymbol::Any) "msg",
)); None,
Some(TypeSymbol::Primitive(PrimitiveTypeSymbol::Any)),
);
let println_symbol = FunctionSymbol::with_parameters_and_return_type( let println_symbol = FunctionSymbol::with_parameters_and_return_type(
&symbol_table.resolve_fqn(Rc::from("println")), &symbol_table.resolve_fqn(Rc::from("println")),