Resolve local variables.

This commit is contained in:
Jesse Brault 2025-10-14 11:14:07 -05:00
parent dd249dd5bd
commit 34fae6ccca
3 changed files with 100 additions and 11 deletions

View File

@ -7,8 +7,8 @@ fn getWorlds() -> List<World> = [
] ]
fn findWorldByColor(worlds: List<World>, color: String) -> String fn findWorldByColor(worlds: List<World>, color: String) -> String
worlds.find { it.color == color } worlds.find { it -> it.color == color }
.map { it.name } .map { it -> it.name }
.expect "No world has the given color ${color}" .expect "No world has the given color ${color}"
end end

View File

@ -20,7 +20,7 @@ use crate::name_analysis::symbol::type_symbol::{
}; };
use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol}; use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
use crate::name_analysis::symbol::variable_symbol::VariableSymbol; use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
use crate::name_analysis::symbol_table::{SymbolInsertError, SymbolTable}; use crate::name_analysis::symbol_table::{SymbolInsertError, SymbolLookupError, SymbolTable};
use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::diagnostic::{Diagnostic, Label};
use std::range::Range; use std::range::Range;
@ -56,6 +56,29 @@ fn handle_insert_error(
} }
} }
fn handle_lookup_error(
err: SymbolLookupError,
error_symbol_name: &str,
error_file_id: usize,
error_range: Range<usize>,
symbol_types: &str,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match err {
SymbolLookupError::NoDefinition => {
let diagnostic = Diagnostic::error()
.with_message(format!(
"No such {} symbol '{}' in scope.",
symbol_types, error_symbol_name,
))
.with_label(
Label::primary(error_file_id, error_range).with_message("Symbol used here."),
);
diagnostics.push(diagnostic);
}
}
}
fn gather_node_children<'a>( fn gather_node_children<'a>(
node: &'a dyn AstNode<'a>, node: &'a dyn AstNode<'a>,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
@ -244,11 +267,8 @@ fn gather_node<'a>(
diagnostics, diagnostics,
); );
} }
AstNodeRef::LValue(l_value) => {
gather_node_children(l_value, symbol_table, fqn_context, scope_table, diagnostics);
}
AstNodeRef::VariableUse(variable_use) => { AstNodeRef::VariableUse(variable_use) => {
gather_variable_use(variable_use, symbol_table, scope_table); gather_variable_use(variable_use, symbol_table, diagnostics);
} }
AstNodeRef::TernaryExpression(ternary_expression) => { AstNodeRef::TernaryExpression(ternary_expression) => {
gather_ternary_expression( gather_ternary_expression(
@ -1234,9 +1254,21 @@ fn gather_for_statement<'a>(
fn gather_variable_use<'a>( fn gather_variable_use<'a>(
variable_use: &'a VariableUse, variable_use: &'a VariableUse,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
scope_table: &mut ScopeTable<'a>, diagnostics: &mut Vec<DmDiagnostic>,
) { ) {
scope_table.insert_variable_use_scope(variable_use, symbol_table.current_scope_id()); if let Err(lookup_error) = symbol_table.lookup_addressable_by_identifier(
variable_use.identifier().name(),
symbol_table.current_scope_id(),
) {
handle_lookup_error(
lookup_error,
variable_use.identifier().name(),
variable_use.identifier().file_id(),
variable_use.identifier().range(),
"Variable",
diagnostics,
)
}
} }
fn gather_ternary_expression<'a>( fn gather_ternary_expression<'a>(

View File

@ -45,8 +45,11 @@ impl SymbolTable {
pub fn push_scope(&mut self, debug_name: &str) { pub fn push_scope(&mut self, debug_name: &str) {
let id = self.scopes.len(); let id = self.scopes.len();
self.scopes self.scopes.push(Scope::new(
.push(Scope::new(Some(self.current_scope_id), id, debug_name.to_string())); Some(self.current_scope_id),
id,
debug_name.to_string(),
));
self.current_scope_id = id; self.current_scope_id = id;
} }
@ -316,7 +319,61 @@ impl SymbolTable {
Some(&self.scopes[parent_id]) Some(&self.scopes[parent_id])
} else { } else {
None None
};
}
Err(NoDefinition)
}
fn lookup_addressable_in_scope_by_identifier(
scope: &Scope,
identifier: &str,
) -> Option<Symbol> {
scope
.variable_symbols()
.get(identifier)
.map(|variable_symbol| Symbol::Variable(variable_symbol.clone()))
.or_else(|| {
scope
.parameter_symbols()
.get(identifier)
.map(|parameter_symbol| Symbol::Parameter(parameter_symbol.clone()))
})
.or_else(|| {
scope
.class_member_symbols()
.get(identifier)
.map(|class_member_symbol| Symbol::ClassMember(class_member_symbol.clone()))
})
.or_else(|| {
scope
.function_symbols()
.get(identifier)
.map(|function_symbol| Symbol::Function(function_symbol.clone()))
})
.or_else(|| {
scope
.type_symbols()
.get(identifier)
.map(|type_symbol| Symbol::Type(type_symbol.clone()))
})
}
pub fn lookup_addressable_by_identifier(
&self,
identifier: &str,
scope_id: usize,
) -> Result<Symbol, SymbolLookupError> {
let mut scope_opt = Some(&self.scopes[scope_id]);
while let Some(scope) = scope_opt {
if let Some(symbol) = Self::lookup_addressable_in_scope_by_identifier(scope, identifier)
{
return Ok(symbol);
} }
scope_opt = if let Some(parent_id) = scope.parent() {
Some(&self.scopes[parent_id])
} else {
None
};
} }
Err(NoDefinition) Err(NoDefinition)
} }