Better handling of global imports.

This commit is contained in:
Jesse Brault 2025-11-02 13:41:00 -06:00
parent 6bcef184eb
commit da05bb101b
8 changed files with 298 additions and 35 deletions

View File

@ -1,8 +1,7 @@
use crate::ast::node::{
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody
, Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration
, StarUseStatement, UseStatement,
UseStatementIdentifier, UseStatementPrefix,
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, Identifier,
IdentifierOrFqn, Module, ModuleLevelDeclaration, PlatformFunction, StarUseStatement,
UseStatement, UseStatementIdentifier, UseStatementPrefix,
};
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
@ -11,10 +10,9 @@ use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
use crate::name_analysis::symbol_table::SymbolTable;
use crate::name_analysis::util::{
handle_insert_error, join_fqn_parts,
};
use crate::name_analysis::util::{handle_insert_error, join_fqn_parts};
use std::cell::RefCell;
use std::process::id;
use std::rc::Rc;
pub fn na_p1_compilation_unit(
@ -26,13 +24,26 @@ pub fn na_p1_compilation_unit(
if let Some(namespace) = compilation_unit.namespace() {
match namespace.identifier_or_fqn() {
IdentifierOrFqn::Identifier(identifier) => {
symbol_table.set_current_fqn(&[identifier.name()])
symbol_table.set_current_fqn(&[identifier.name()]);
}
IdentifierOrFqn::FullyQualifiedName(fqn) => {
symbol_table
.set_current_fqn(&fqn.identifiers().map(Identifier::name).collect::<Vec<_>>());
}
}
let fqn_parts = match namespace.identifier_or_fqn() {
IdentifierOrFqn::Identifier(identifier) => {
vec![identifier.name()]
}
IdentifierOrFqn::FullyQualifiedName(fqn) => {
let mut parts = vec![];
for identifier in fqn.identifiers() {
parts.push(identifier.name());
}
parts
}
};
symbol_table.register_module(&fqn_parts);
} else {
symbol_table.set_current_fqn(&[]);
}
@ -187,7 +198,9 @@ fn na_p1_module_level_declaration(
.map(|function_symbol| ModuleLevelSymbol::Function(function_symbol))
}
ModuleLevelDeclaration::PlatformFunction(platform_function) => {
todo!()
na_p1_platform_function(platform_function, symbol_table, diagnostics).map(
|platform_function_symbol| ModuleLevelSymbol::Function(platform_function_symbol),
)
}
}
}
@ -264,7 +277,7 @@ fn na_p1_function(
if maybe_function_symbol.is_some() {
function.set_function_symbol(maybe_function_symbol.as_ref().unwrap().clone());
}
// create a scope for this function
symbol_table.push_scope(&format!("FunctionScope {}", function.identifier().name()));
function.set_scope_id(symbol_table.current_scope_id());
@ -272,3 +285,45 @@ fn na_p1_function(
maybe_function_symbol
}
fn na_p1_platform_function(
platform_function: &mut PlatformFunction,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<Rc<RefCell<FunctionSymbol>>> {
let to_insert = FunctionSymbol::new(
&symbol_table.resolve_fqn(Rc::from(platform_function.identifier().name())),
platform_function.is_public(),
true,
Some(SourceDefinition::from_identifier(
platform_function.identifier(),
)),
);
let maybe_function_symbol = match symbol_table.insert_function_symbol(to_insert) {
Ok(function_symbol) => Some(function_symbol),
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
platform_function.identifier().name(),
platform_function.identifier().file_id(),
platform_function.identifier().range(),
diagnostics,
);
None
}
};
if maybe_function_symbol.is_some() {
platform_function.set_function_symbol(maybe_function_symbol.as_ref().unwrap().clone());
}
// create a scope for later
symbol_table.push_scope(&format!(
"PlatformFunctionScope {}",
platform_function.identifier().name()
));
platform_function.set_scope_id(symbol_table.current_scope_id());
symbol_table.pop_scope();
maybe_function_symbol
}

View File

@ -46,7 +46,9 @@ pub fn analyze_names<
) -> Vec<DmDiagnostic> {
let mut diagnostics = vec![];
// gather symbols
// first pass
// generally, gather symbols from declarations
// don't go into functions
for compilation_unit in compilation_units.iter_mut() {
let file_name = files.name(compilation_unit.file_id()).unwrap();
na_p1_compilation_unit(file_name, compilation_unit, symbol_table, &mut diagnostics);
@ -56,7 +58,11 @@ pub fn analyze_names<
return diagnostics;
}
// resolve symbols
// resolve global imports
symbol_table.resolve_global_imports();
// second pass
// resolve referenced symbols and go into functions
for compilation_unit in compilation_units.iter_mut() {
na_p2_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
}
@ -318,12 +324,39 @@ mod tests {
add_std_core_symbols(&mut symbol_table).expect("Failed to add std/core symbols.");
let resolved_std_core_symbols = symbol_table
.find_usable_symbols_by_base_fqn(&str_slice_to_rcs(&["std", "core"]))
.unwrap();
global_std_core_star_use
.borrow_mut()
.set_resolved_symbols(resolved_std_core_symbols);
assert_no_diagnostics(sources, &mut symbol_table);
}
#[test]
fn with_std_core_source() {
let sources = HashMap::from([
(
"std/core/print.dm",
"
ns std::core
pub platform fn println(msg: Any) -> Void
pub platform fn print(msg: Any) -> Void
",
),
(
"main.dm",
"
fn main()
println('A line')
print('Not a line')
end
",
),
]);
let mut symbol_table = SymbolTable::new();
let global_star_use = symbol_table
.insert_star_use_symbol(StarUseSymbol::new(
&str_slice_to_rcs(&["std", "core"]),
None,
))
.expect("Failed to insert star use symbol.");
assert_no_diagnostics(sources, &mut symbol_table);
}

View File

@ -1,11 +1,11 @@
use crate::ast::node::{
AssignmentStatement, Call, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix,
Expression, ExpressionList, ExpressionStatement, Function, FunctionAliasBody,
FunctionBlockBody, FunctionBody, FunctionEqualsBody, GenericParameters, Identifier,
IdentifierExpression, IdentifierOrFqn, LValue, LValueSuffix, ModuleLevelDeclaration,
ObjectIndex, Parameter, Parameters, PrimitiveType, ReturnType, StarUseStatement, Statement,
SuffixExpression, SuffixOperator, TypeUse, TypedArray, UseStatement, UseStatementIdentifier,
UseStatementPrefix, VariableDeclaration,
AssignmentStatement, BacktickString, Call, CompilationUnit, ConcreteUseStatement,
ConcreteUseStatementSuffix, DString, Expression, ExpressionList, ExpressionStatement, Function,
FunctionAliasBody, FunctionBlockBody, FunctionBody, FunctionEqualsBody, GenericParameters,
Identifier, IdentifierExpression, IdentifierOrFqn, LValue, LValueSuffix, Literal,
ModuleLevelDeclaration, ObjectIndex, Parameter, Parameters, PlatformFunction, PrimitiveType,
ReturnType, StarUseStatement, Statement, SuffixExpression, SuffixOperator, TypeUse, TypedArray,
UseStatement, UseStatementIdentifier, UseStatementPrefix, VariableDeclaration,
};
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
@ -153,7 +153,7 @@ fn na_p2_module_level_declaration(
na_p2_function(function, symbol_table, diagnostics);
}
ModuleLevelDeclaration::PlatformFunction(platform_function) => {
todo!()
na_p2_platform_function(platform_function, symbol_table, diagnostics);
}
}
}
@ -197,6 +197,44 @@ fn na_p2_function(
symbol_table.pop_scope();
}
fn na_p2_platform_function(
platform_function: &mut PlatformFunction,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
// change to this function's scope
symbol_table.set_current_scope(*platform_function.scope_id().unwrap());
// generics
na_p2_generic_parameters(platform_function.generics_mut(), symbol_table, diagnostics);
// parameters
let parameter_symbols = na_p2_parameters(
platform_function.parameters_mut(),
symbol_table,
diagnostics,
);
platform_function
.function_symbol_mut()
.unwrap()
.borrow_mut()
.set_parameter_symbols(parameter_symbols);
// return_type
let maybe_return_type_symbol = na_p2_return_type(
platform_function.return_type_mut(),
symbol_table,
diagnostics,
);
if let Some(return_type_symbol) = maybe_return_type_symbol {
platform_function
.function_symbol_mut()
.unwrap()
.borrow_mut()
.set_return_type(return_type_symbol);
}
}
fn na_p2_generic_parameters(
generic_parameters: &mut GenericParameters,
symbol_table: &mut SymbolTable,
@ -602,7 +640,7 @@ fn na_p2_expression(
na_p2_suffix_expression(suffix, symbol_table, diagnostics);
}
Expression::Literal(literal) => {
todo!()
na_p2_literal(literal, symbol_table, diagnostics);
}
Expression::Identifier(identifier_expression) => {
na_p2_identifier_expression(identifier_expression, symbol_table, diagnostics);
@ -726,3 +764,49 @@ fn na_p2_expression_list(
na_p2_expression(expression, symbol_table, diagnostics);
}
}
fn na_p2_literal(
literal: &mut Literal,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match literal {
Literal::DString(d_string) => {
na_p2_d_string(d_string, symbol_table, diagnostics);
}
Literal::BacktickString(backtick_string) => {
na_p2_backtick_string(backtick_string, symbol_table, diagnostics);
}
_ => {
// no-op, nothing to check.
}
}
}
fn na_p2_d_string(
d_string: &mut DString,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for d_string_expression in d_string.expressions_mut() {
na_p2_expression(
d_string_expression.expression_mut(),
symbol_table,
diagnostics,
);
}
}
fn na_p2_backtick_string(
backtick_string: &mut BacktickString,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for d_string_expression in backtick_string.expressions_mut() {
na_p2_expression(
d_string_expression.expression_mut(),
symbol_table,
diagnostics,
);
}
}

View File

@ -10,6 +10,7 @@ use crate::name_analysis::symbol::{ExpressibleSymbol, LVSymbol, Symbol};
use crate::name_analysis::symbol_table::fqn_context::FqnContext;
use crate::name_analysis::symbol_table::symbol_tree::SymbolTree;
use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined;
use crate::name_analysis::util::join_fqn_parts;
use scope::Scope;
use std::cell::RefCell;
use std::fmt::Display;
@ -132,6 +133,45 @@ impl SymbolTable {
as_rc
}
pub fn resolve_global_imports(&mut self) {
let global_scope = &self.scopes[0];
for concrete_use_symbol in global_scope.concrete_use_symbols() {
match self
.symbol_tree
.find_usable_symbol(concrete_use_symbol.borrow().fqn_parts())
{
Ok(usable_symbol) => {
concrete_use_symbol.borrow_mut().set_resolved_symbol(usable_symbol);
}
Err(symbol_lookup_error) => {
// panic because this is definitely a compiler/configuration error
panic!(
"{:?}: Cannot resolve global import {}",
symbol_lookup_error,
join_fqn_parts(concrete_use_symbol.borrow().fqn_parts())
);
}
}
}
for star_use_symbol in global_scope.star_use_symbols() {
let mut star_use_symbol_ref_mut = star_use_symbol.borrow_mut();
match self.symbol_tree.find_all_by_base_fqn(star_use_symbol_ref_mut.fqn_parts()) {
Ok(usable_symbols) => {
star_use_symbol_ref_mut.set_resolved_symbols(usable_symbols);
}
Err(symbol_lookup_error) => {
// again, panic because this is a compiler/configuration error
panic!(
"{:?}: Cannot resolve global star import {}::*",
symbol_lookup_error,
join_fqn_parts(star_use_symbol.borrow().fqn_parts())
);
}
}
}
}
pub fn find_usable_symbols_by_base_fqn(
&self,
fqn_parts: &[Rc<str>],

View File

@ -69,6 +69,14 @@ impl Scope {
pub fn id(&self) -> usize {
self.id
}
pub fn concrete_use_symbols(&self) -> impl Iterator<Item = &Rc<RefCell<ConcreteUseSymbol>>> {
self.concrete_use_symbols.values()
}
pub fn star_use_symbols(&self) -> impl Iterator<Item = &Rc<RefCell<StarUseSymbol>>> {
self.star_use_symbols.values()
}
pub fn insert_concrete_use_symbol(
&mut self,

View File

@ -81,8 +81,10 @@ impl SymbolTree {
panic!("Unable to register module fqn with no parts.")
}
if fqn_parts.len() == 1 {
self.children
.insert(fqn_parts[0].clone(), SymbolTree::new(fqn_parts[0].as_ref()));
if !self.children.contains_key(&fqn_parts[0]) {
self.children
.insert(fqn_parts[0].clone(), SymbolTree::new(fqn_parts[0].as_ref()));
}
} else {
if self.children.contains_key(fqn_parts[0].as_ref()) {
let child = self.children.get_mut(fqn_parts[0].as_ref()).unwrap();
@ -154,12 +156,44 @@ impl SymbolTree {
}
}
}
pub fn find_usable_symbol(
&self,
fqn_parts: &[Rc<str>],
) -> Result<UsableSymbol, SymbolLookupError> {
match fqn_parts.len() {
0 => panic!("Cannot resolve a usable symbol with no fqn part."),
1 => self
.find_interface(fqn_parts)
.map(|interface_symbol| UsableSymbol::Interface(interface_symbol.clone()))
.or_else(|| {
self.find_class(fqn_parts)
.map(|class_symbol| UsableSymbol::Class(class_symbol.clone()))
})
.or_else(|| {
self.find_function(fqn_parts)
.map(|function_symbol| UsableSymbol::Function(function_symbol))
})
.map_or_else(
|| Err(SymbolLookupError::NoDefinition),
|usable_symbol| Ok(usable_symbol),
),
_ => {
if self.children.contains_key(fqn_parts[0].as_ref()) {
let child = self.children.get(fqn_parts[0].as_ref()).unwrap();
child.find_usable_symbol(&fqn_parts[1..])
} else {
Err(SymbolLookupError::NoSuchNamespace)
}
}
}
}
}
enum FormatAction<'a> {
PrintSymbolTree(&'a SymbolTree),
IncreaseIndent,
DecreaseIndent
DecreaseIndent,
}
impl Display for SymbolTree {
@ -168,7 +202,7 @@ impl Display for SymbolTree {
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
let mut stack: Vec<FormatAction> = vec![];
stack.push(FormatAction::PrintSymbolTree(self));
while let Some(format_action) = stack.pop() {
match format_action {
FormatAction::PrintSymbolTree(symbol_tree) => {
@ -181,7 +215,10 @@ impl Display for SymbolTree {
// increase for the first child
stack.push(FormatAction::IncreaseIndent);
indent_writer.writeln_indented(&format!("SymbolTree(debug_name = {})", symbol_tree.debug_name))?;
indent_writer.writeln_indented(&format!(
"SymbolTree(debug_name = {})",
symbol_tree.debug_name
))?;
indent_writer.increase_indent();
for interface in symbol_tree.interfaces.values() {
@ -203,7 +240,7 @@ impl Display for SymbolTree {
}
}
}
writeln!(f, "{}", acc)
}
}

View File

@ -503,6 +503,12 @@ PlatformFunction:
- identifier
- parameters
- return_type
fields:
- function_symbol:
kind: FunctionSymbol
wrap: rc_ref_cell
- scope_id:
kind: usize
PlatformOperatorFunction:
struct:
children:

View File

@ -188,8 +188,8 @@ FullyQualifiedName = {
}
IdentifierOrFqn = {
Identifier
| FullyQualifiedName
FullyQualifiedName
| Identifier
}
// Common lists