diff --git a/examples/worlds.dm b/examples/worlds.dm index 8de9654..86dec34 100644 --- a/examples/worlds.dm +++ b/examples/worlds.dm @@ -7,8 +7,8 @@ fn getWorlds() -> List = [ ] fn findWorldByColor(worlds: List, color: String) -> String - worlds.find { it.color == color } - .map { it.name } + worlds.find { it -> it.color == color } + .map { it -> it.name } .expect "No world has the given color ${color}" end diff --git a/src/name_analysis/gather.rs b/src/name_analysis/gather.rs index 80c55dd..f3f12c5 100644 --- a/src/name_analysis/gather.rs +++ b/src/name_analysis/gather.rs @@ -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::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 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, + symbol_types: &str, + diagnostics: &mut Vec, +) { + 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>( node: &'a dyn AstNode<'a>, symbol_table: &mut SymbolTable, @@ -244,11 +267,8 @@ fn gather_node<'a>( diagnostics, ); } - AstNodeRef::LValue(l_value) => { - gather_node_children(l_value, symbol_table, fqn_context, scope_table, diagnostics); - } 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) => { gather_ternary_expression( @@ -1234,9 +1254,21 @@ fn gather_for_statement<'a>( fn gather_variable_use<'a>( variable_use: &'a VariableUse, symbol_table: &mut SymbolTable, - scope_table: &mut ScopeTable<'a>, + diagnostics: &mut Vec, ) { - 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>( diff --git a/src/name_analysis/symbol_table/mod.rs b/src/name_analysis/symbol_table/mod.rs index 78fd325..1033e98 100644 --- a/src/name_analysis/symbol_table/mod.rs +++ b/src/name_analysis/symbol_table/mod.rs @@ -45,8 +45,11 @@ impl SymbolTable { pub fn push_scope(&mut self, debug_name: &str) { let id = self.scopes.len(); - self.scopes - .push(Scope::new(Some(self.current_scope_id), id, debug_name.to_string())); + self.scopes.push(Scope::new( + Some(self.current_scope_id), + id, + debug_name.to_string(), + )); self.current_scope_id = id; } @@ -316,7 +319,61 @@ impl SymbolTable { Some(&self.scopes[parent_id]) } else { None + }; + } + Err(NoDefinition) + } + + fn lookup_addressable_in_scope_by_identifier( + scope: &Scope, + identifier: &str, + ) -> Option { + 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 { + 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) }