diff --git a/dm/src/repl.rs b/dm/src/repl.rs index 4c55226..2469aa9 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -6,8 +6,7 @@ use dmc_lib::diagnostic::Diagnostics; use dmc_lib::ir::variable_locations::VariableLocations; use dmc_lib::lexer::Lexer; use dmc_lib::parser::{parse_expression, parse_let_statement}; -use dmc_lib::semantic_analysis::AnalysisContext; -use dmc_lib::semantic_analysis::scope::{Scope, ScopeId}; +use dmc_lib::semantic_analysis::analysis_context::AnalysisContext; use dmc_lib::token::TokenKind; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; @@ -305,16 +304,8 @@ pub fn repl_2(read: &mut impl BufRead, register_count: usize) { let mut analysis_context = AnalysisContext::new(); analysis_context.push_scope("__repl_root_scope"); - - let root_scope_id = analysis_context.root_scope_id(); - analysis_context - .scopes_mut() - .push(Scope::new(Some(root_scope_id))); // function scope - let function_scope_id = analysis_context.scopes().len() - 1; - analysis_context - .scopes_mut() - .push(Scope::new(Some(function_scope_id))); // body scope - let body_scope_id = analysis_context.scopes().len() - 1; + analysis_context.push_scope("__repl_function_scope"); + analysis_context.push_scope("__repl_body_scope"); let fqn: Rc = Rc::from("__repl"); let mut variable_locations = VariableLocations::new(); @@ -353,7 +344,6 @@ pub fn repl_2(read: &mut impl BufRead, register_count: usize) { let compile_result = compile_let_statement_2( input, &mut analysis_context, - body_scope_id, &fqn, &mut variable_locations, register_count, @@ -378,7 +368,6 @@ pub fn repl_2(read: &mut impl BufRead, register_count: usize) { let compile_result = compile_expression_2( input, &mut analysis_context, - body_scope_id, &fqn, &mut variable_locations, register_count, @@ -438,7 +427,6 @@ pub fn repl_2(read: &mut impl BufRead, register_count: usize) { fn compile_expression_2( input: &str, analysis_context: &mut AnalysisContext, - function_scope_id: ScopeId, fqn: &Rc, variable_locations: &mut VariableLocations, register_count: usize, @@ -454,7 +442,6 @@ fn compile_expression_2( compile_statement_to_synthetic_function( &statement, analysis_context, - function_scope_id, fqn.clone(), variable_locations, register_count, @@ -465,7 +452,6 @@ fn compile_expression_2( fn compile_let_statement_2( input: &str, analysis_context: &mut AnalysisContext, - function_scope_id: ScopeId, fqn: &Rc, variable_locations: &mut VariableLocations, register_count: usize, @@ -478,7 +464,6 @@ fn compile_let_statement_2( compile_statement_to_synthetic_function( &Statement::Let(maybe_let_statement.unwrap()), analysis_context, - function_scope_id, fqn.clone(), variable_locations, register_count, diff --git a/dm/src/run.rs b/dm/src/run.rs index 368aff3..d42889d 100644 --- a/dm/src/run.rs +++ b/dm/src/run.rs @@ -6,7 +6,7 @@ use dm_std_lib::add_all_std_core; use dmc_lib::compile_compilation_unit; use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::{Diagnostic, Diagnostics}; -use dmc_lib::semantic_analysis::AnalysisContext; +use dmc_lib::semantic_analysis::analysis_context::AnalysisContext; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::{DvmContext, call}; use std::path::PathBuf; diff --git a/dmc-lib/src/ir/register_allocation.rs b/dmc-lib/src/ir/register_allocation.rs index 21a6bbd..68c3296 100644 --- a/dmc-lib/src/ir/register_allocation.rs +++ b/dmc-lib/src/ir/register_allocation.rs @@ -325,7 +325,7 @@ mod tests { use crate::diagnostic::Diagnostics; use crate::lowering::lower_to_ir_compilation_unit; use crate::parser::get_compilation_unit; - use crate::semantic_analysis::AnalysisContext; + use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::semantic_analysis::analyze_compilation_unit; fn line_graph() -> InterferenceGraph { diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index 8e91e87..d4acd1d 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -5,10 +5,9 @@ use crate::ir::compile_dvm_function; use crate::ir::variable_locations::VariableLocations; use crate::lowering::{lower_to_ir_compilation_unit, lower_to_ir_synthetic_function}; use crate::parser::parse_compilation_unit; -use crate::semantic_analysis::scope::ScopeId; use crate::semantic_analysis::{analyze_compilation_unit, analyze_statement}; use dvm_lib::vm::function::Function; -use semantic_analysis::AnalysisContext; +use semantic_analysis::analysis_context::AnalysisContext; use std::collections::HashMap; use std::rc::Rc; @@ -79,7 +78,6 @@ pub fn compile_compilation_unit( pub fn compile_statement_to_synthetic_function( statement: &Statement, analysis_context: &mut AnalysisContext, - function_scope_id: ScopeId, fqn: Rc, variable_locations: &mut VariableLocations, register_count: usize, diff --git a/dmc-lib/src/lowering/mod.rs b/dmc-lib/src/lowering/mod.rs index ebaa95b..c3af53c 100644 --- a/dmc-lib/src/lowering/mod.rs +++ b/dmc-lib/src/lowering/mod.rs @@ -23,7 +23,7 @@ use crate::ir::ir_statement::IrStatement; use crate::ir::ir_type_info::IrTypeInfo; use crate::ir::ir_variable::{IrVariable, IrVariableId}; use crate::lowering::util::{return_type_info_to_ir_type_info, to_ir_type_info}; -use crate::semantic_analysis::AnalysisContext; +use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::semantic_analysis::symbol::{Symbol, SymbolId}; use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; use std::collections::HashMap; diff --git a/dmc-lib/src/semantic_analysis/analysis_context.rs b/dmc-lib/src/semantic_analysis/analysis_context.rs new file mode 100644 index 0000000..5b8712c --- /dev/null +++ b/dmc-lib/src/semantic_analysis/analysis_context.rs @@ -0,0 +1,308 @@ +use crate::ast::NodeId; +use crate::diagnostic::Diagnostic; +use crate::diagnostic_factories::symbol_not_found; +use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared; +use crate::semantic_analysis::scope::{Scope, ScopeId}; +use crate::semantic_analysis::symbol::{Symbol, SymbolId}; +use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; +use crate::source_range::SourceRange; +use std::collections::HashMap; +use std::rc::Rc; + +pub struct AnalysisContext { + fqn_stack: Vec>, + scopes: Vec, + current_scope_id: Option, + nodes_to_scopes: HashMap, + symbols: Vec, + nodes_to_symbols: HashMap, + type_infos: Vec, + symbols_to_type_infos: HashMap, + nodes_to_type_infos: HashMap, +} + +impl AnalysisContext { + pub fn new() -> Self { + Self { + fqn_stack: Vec::new(), + scopes: Vec::new(), + current_scope_id: None, + nodes_to_scopes: HashMap::new(), + symbols: Vec::new(), + nodes_to_symbols: HashMap::new(), + type_infos: Vec::new(), + symbols_to_type_infos: HashMap::new(), + nodes_to_type_infos: HashMap::new(), + } + } + + pub fn commit(&mut self) { + todo!() + } + + pub fn rollback(&mut self) { + todo!() + } + + pub fn scopes(&self) -> &[Scope] { + &self.scopes + } + + pub fn scopes_mut(&mut self) -> &mut Vec { + &mut self.scopes + } + + pub fn symbols(&self) -> &[Symbol] { + &self.symbols + } + + pub fn nodes_to_symbols(&self) -> &HashMap { + &self.nodes_to_symbols + } + + pub fn type_infos(&self) -> &[TypeInfo] { + &self.type_infos + } + + pub fn symbols_to_type_infos(&self) -> &HashMap { + &self.symbols_to_type_infos + } + + pub fn nodes_to_type_infos(&self) -> &HashMap { + &self.nodes_to_type_infos + } + + pub fn push_fqn_part(&mut self, part: Rc) { + self.fqn_stack.push(part); + } + + pub fn pop_fqn_part(&mut self) { + self.fqn_stack.pop(); + } + + pub fn resolve_fqn(&self, suffix: &str) -> String { + let mut fqn = self.fqn_stack.clone(); + fqn.push(suffix.into()); + fqn.join("::") + } + + pub fn push_scope(&mut self, _debug_name: &str) { + let scope = Scope::new(self.current_scope_id); + self.scopes.push(scope); + self.current_scope_id = Some(self.scopes.len() - 1); + } + + fn get_scope(&self, scope_id: ScopeId) -> &Scope { + self.scopes.get(scope_id).expect(&format!( + "scope_id {} is not a valid index of self.scopes", + scope_id + )) + } + + fn get_scope_mut(&mut self, scope_id: ScopeId) -> &mut Scope { + self.scopes.get_mut(scope_id).expect(&format!( + "scope_id {} is not a valid index of self.scopes", + scope_id + )) + } + + fn get_current_scope(&self) -> &Scope { + self.get_scope(self.current_scope_id.expect("current_scope_id is None")) + } + + fn get_current_scope_mut(&mut self) -> &mut Scope { + self.get_scope_mut(self.current_scope_id.expect("current_scope_id is None")) + } + + pub fn pop_scope(&mut self) { + self.current_scope_id = self.get_current_scope().parent_id(); + } + + pub fn associate_node_to_current_scope(&mut self, node_id: NodeId) { + self.nodes_to_scopes.insert( + node_id, + self.current_scope_id.expect("current_scope_id is None"), + ); + } + + fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId { + // get declared name + let declared_name = symbol.declared_name_owned(); + + // push the symbol + self.symbols.push(symbol); + let symbol_id = self.symbols.len() - 1; + + // put it in scope + self.get_scope_mut(scope_id) + .insert_symbol(declared_name, symbol_id); + + symbol_id + } + + pub fn associate_node_to_symbol(&mut self, node_id: NodeId, symbol_id: SymbolId) { + self.nodes_to_symbols.insert(node_id, symbol_id); + } + + fn get_symbol_in_scope(&self, scope: &Scope, declared_name: &str) -> Option<&Symbol> { + scope.get_symbol_id(declared_name).map(|symbol_id| { + self.symbols.get(symbol_id).expect(&format!( + "symbol_id {} is not a valid index of self.symbols", + symbol_id + )) + }) + } + + /// Use this function only when there is no node associated with the symbol. Otherwise use + /// `try_insert_symbol_in_node_scope`. + pub fn try_insert_symbol_in_scope( + &mut self, + to_insert: Symbol, + scope_id: ScopeId, + ) -> Result<(), Diagnostic> { + let scope = self.get_scope(scope_id); + if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) { + Err(symbol_already_declared(already_declared, &to_insert)) + } else { + self.insert_symbol_in_scope(scope_id, to_insert); + // no associated node, so no need to update self.nodes_to_symbols + Ok(()) + } + } + + pub fn try_insert_associate_symbol_in_node_scope( + &mut self, + to_insert: Symbol, + owner_node_id: NodeId, + ) -> Result<(), Diagnostic> { + let node_scope_id = self.nodes_to_scopes.get(&owner_node_id).expect(&format!( + "owner_node_id {} is not in self.nodes_to_scopes", + owner_node_id + )); + let node_scope = self.get_scope(*node_scope_id); + if let Some(already_declared) = + self.get_symbol_in_scope(node_scope, to_insert.declared_name()) + { + Err(symbol_already_declared(already_declared, &to_insert)) + } else { + let symbol_id = self.insert_symbol_in_scope(*node_scope_id, to_insert); + self.associate_node_to_symbol(owner_node_id, symbol_id); + Ok(()) + } + } + + fn get_symbol_id_by_declared_name_in_node_scope( + &self, + node_id: NodeId, + declared_name: &str, + ) -> Option { + let scope_id = self + .nodes_to_scopes + .get(&node_id) + .expect(&format!("node_id {} has no associated scope", node_id)); + let scope = self.get_scope(*scope_id); + scope.get_symbol_id(declared_name) + } + + pub fn associate_node_with_self_symbol(&mut self, node_id: NodeId, declared_name: &str) { + let symbol_id = self + .get_symbol_id_by_declared_name_in_node_scope(node_id, declared_name) + .expect(&format!( + "could not get symbol_id for node_id {} and declared_name {}", + node_id, declared_name + )); + self.associate_node_to_symbol(node_id, symbol_id); + } + + pub fn lookup_and_associate_symbol_to_node( + &mut self, + node_id: NodeId, + declared_name: &str, + node_source_range: &SourceRange, + ) -> Result<(), Diagnostic> { + let scope_id = self + .nodes_to_scopes + .get(&node_id) + .expect(&format!("node_id {} has no associated scope", node_id)); + let mut maybe_scope = Some(self.get_scope(*scope_id)); + let mut found_symbol_id: Option = None; + while let Some(scope) = maybe_scope { + match scope.get_symbol_id(declared_name) { + None => { + maybe_scope = match scope.parent_id() { + None => None, + Some(parent_id) => Some(self.get_scope(parent_id)), + } + } + Some(symbol_id) => { + found_symbol_id = Some(symbol_id); + break; + } + } + } + + match found_symbol_id { + None => Err(symbol_not_found(declared_name, node_source_range)), + Some(symbol_id) => { + self.associate_node_to_symbol(node_id, symbol_id); + Ok(()) + } + } + } + + pub fn insert_type_info(&mut self, type_info: TypeInfo) -> TypeInfoId { + self.type_infos.push(type_info); + self.type_infos.len() - 1 + } + + pub fn insert_type_info_for_node(&mut self, type_info: TypeInfo, node_id: NodeId) { + self.type_infos.push(type_info); + self.nodes_to_type_infos + .insert(node_id, self.type_infos.len() - 1); + } + + pub fn associate_node_and_symbol_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) { + let symbol_id = self + .nodes_to_symbols + .get(&node_id) + .expect(&format!("node_id {} has no associated symbol", node_id)); + self.symbols_to_type_infos.insert(*symbol_id, type_info_id); + self.nodes_to_type_infos.insert(node_id, type_info_id); + } + + pub fn get_type_info_id_for_node(&self, node_id: NodeId) -> TypeInfoId { + *self + .nodes_to_type_infos + .get(&node_id) + .expect(&format!("node_id {} has no associated type", node_id)) + } + + pub fn get_type_info_for_node(&self, node_id: NodeId) -> &TypeInfo { + let type_info_id = self.get_type_info_id_for_node(node_id); + self.type_infos + .get(type_info_id) + .expect(&format!("invalid type_info_id {}", type_info_id)) + } + + pub fn get_type_info_by_id(&self, type_info_id: TypeInfoId) -> &TypeInfo { + self.type_infos + .get(type_info_id) + .expect(&format!("invalid type_info_id {}", type_info_id)) + } + + pub fn associate_node_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) { + self.nodes_to_type_infos.insert(node_id, type_info_id); + } + + pub fn lookup_type_for_node_via_symbol(&self, node_id: NodeId) -> TypeInfoId { + let symbol_id = self + .nodes_to_symbols + .get(&node_id) + .expect(&format!("node_id {} has no associated symbol", node_id)); + let symbol_type_info_id = self.symbols_to_type_infos.get(symbol_id).expect(&format!( + "symbol_id {} has no associated type_info", + symbol_id + )); + *symbol_type_info_id + } +} diff --git a/dmc-lib/src/semantic_analysis/collect_scopes.rs b/dmc-lib/src/semantic_analysis/collect_scopes.rs index 4040562..1b1c253 100644 --- a/dmc-lib/src/semantic_analysis/collect_scopes.rs +++ b/dmc-lib/src/semantic_analysis/collect_scopes.rs @@ -10,7 +10,7 @@ use crate::ast::identifier::Identifier; use crate::ast::let_statement::LetStatement; use crate::ast::negative_expression::NegativeExpression; use crate::ast::statement::Statement; -use crate::semantic_analysis::AnalysisContext; +use crate::semantic_analysis::analysis_context::AnalysisContext; /// N.b.: the parent_scope_id refers to the **parent** of the compilation unit scope, and must /// be a valid (non-panicking) index of `scopes`. diff --git a/dmc-lib/src/semantic_analysis/collect_symbols.rs b/dmc-lib/src/semantic_analysis/collect_symbols.rs index f30e373..2aa3cb9 100644 --- a/dmc-lib/src/semantic_analysis/collect_symbols.rs +++ b/dmc-lib/src/semantic_analysis/collect_symbols.rs @@ -3,7 +3,7 @@ use crate::ast::extern_function::ExternFunction; use crate::ast::function::Function; use crate::ast::parameter::Parameter; use crate::diagnostic::Diagnostics; -use crate::semantic_analysis::AnalysisContext; +use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol}; pub struct SymbolCollectionResult(pub Diagnostics); diff --git a/dmc-lib/src/semantic_analysis/collect_types.rs b/dmc-lib/src/semantic_analysis/collect_types.rs index 8e1cc1d..3e798a8 100644 --- a/dmc-lib/src/semantic_analysis/collect_types.rs +++ b/dmc-lib/src/semantic_analysis/collect_types.rs @@ -1,54 +1,21 @@ -use crate::ast::NodeId; use crate::ast::compilation_unit::CompilationUnit; use crate::ast::extern_function::ExternFunction; use crate::ast::function::Function; use crate::ast::parameter::Parameter; -use crate::semantic_analysis::symbol::SymbolId; +use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::semantic_analysis::type_info::{FunctionTypeInfo, TypeInfo, TypeInfoId}; -use std::collections::HashMap; -struct CollectTypesContext<'a> { - nodes_to_symbols: &'a HashMap, - type_infos: &'a mut Vec, - symbols_to_type_infos: &'a mut HashMap, -} - -impl<'a> CollectTypesContext<'a> { - pub fn new( - nodes_to_symbols: &'a HashMap, - type_infos: &'a mut Vec, - symbols_to_type_infos: &'a mut HashMap, - ) -> Self { - Self { - nodes_to_symbols, - type_infos, - symbols_to_type_infos, - } - } - - pub fn insert_type_info(&mut self, type_info: TypeInfo) -> TypeInfoId { - self.type_infos.push(type_info); - self.type_infos.len() - 1 - } -} - -pub fn collect_types( - compilation_unit: &CompilationUnit, - nodes_to_symbols: &HashMap, - type_infos: &mut Vec, - symbols_to_type_infos: &mut HashMap, -) { - let mut ctx = CollectTypesContext::new(nodes_to_symbols, type_infos, symbols_to_type_infos); +pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) { for function in compilation_unit.functions() { - collect_types_function(function, &mut ctx); + collect_types_function(function, ctx); } for extern_function in compilation_unit.extern_functions() { - collect_types_extern_function(extern_function, &mut ctx); + collect_types_extern_function(extern_function, ctx); } } -fn collect_types_function(function: &Function, ctx: &mut CollectTypesContext) { +fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) { let parameter_type_info_ids = collect_and_get_parameter_type_info_ids(function.parameters(), ctx); @@ -64,9 +31,7 @@ fn collect_types_function(function: &Function, ctx: &mut CollectTypesContext) { )); let function_type_info_id = ctx.insert_type_info(function_type_info); - let symbol_id = ctx.nodes_to_symbols[&function.node_id()]; - ctx.symbols_to_type_infos - .insert(symbol_id, function_type_info_id); + ctx.associate_node_and_symbol_to_type(function.node_id(), function_type_info_id); } fn declared_name_to_type_info(declared_name: &str) -> TypeInfo { @@ -82,7 +47,7 @@ fn declared_name_to_type_info(declared_name: &str) -> TypeInfo { fn collect_and_get_parameter_type_info_ids( parameters: &[Parameter], - ctx: &mut CollectTypesContext, + ctx: &mut AnalysisContext, ) -> Vec { let mut parameter_type_info_ids: Vec = Vec::new(); for parameter in parameters { @@ -91,13 +56,12 @@ fn collect_and_get_parameter_type_info_ids( parameter_type_info_ids.push(type_info_id); // also associate the parameter symbol with the type info - let symbol_id = ctx.nodes_to_symbols[¶meter.node_id()]; - ctx.symbols_to_type_infos.insert(symbol_id, type_info_id); + ctx.associate_node_and_symbol_to_type(parameter.node_id(), type_info_id); } parameter_type_info_ids } -fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut CollectTypesContext) { +fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut AnalysisContext) { let parameter_type_info_ids = collect_and_get_parameter_type_info_ids(extern_function.parameters(), ctx); @@ -112,7 +76,5 @@ fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut Col let function_type_info_id = ctx.insert_type_info(function_type_info); - let symbol_id = ctx.nodes_to_symbols[&extern_function.node_id()]; - ctx.symbols_to_type_infos - .insert(symbol_id, function_type_info_id); + ctx.associate_node_and_symbol_to_type(extern_function.node_id(), function_type_info_id); } diff --git a/dmc-lib/src/semantic_analysis/mod.rs b/dmc-lib/src/semantic_analysis/mod.rs index edfcf59..9c9f2c8 100644 --- a/dmc-lib/src/semantic_analysis/mod.rs +++ b/dmc-lib/src/semantic_analysis/mod.rs @@ -1,7 +1,6 @@ -use crate::ast::NodeId; use crate::ast::compilation_unit::CompilationUnit; use crate::ast::statement::Statement; -use crate::diagnostic::{Diagnostic, Diagnostics}; +use crate::diagnostic::Diagnostics; use crate::semantic_analysis::collect_scopes::{ collect_scopes_compilation_unit, collect_scopes_statement, }; @@ -9,19 +8,15 @@ use crate::semantic_analysis::collect_symbols::{ SymbolCollectionResult, collect_symbols_compilation_unit, }; use crate::semantic_analysis::collect_types::collect_types; -use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared; use crate::semantic_analysis::resolve_names::{ NameResolutionResult, resolve_names_in_compilation_unit, resolve_names_in_statement, }; use crate::semantic_analysis::resolve_types::{ ResolveTypesResult, resolve_types_in_compilation_unit, resolve_types_in_statement, }; -use crate::semantic_analysis::scope::{Scope, ScopeId}; -use crate::semantic_analysis::symbol::{Symbol, SymbolId}; -use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; -use std::collections::HashMap; -use std::rc::Rc; +use analysis_context::AnalysisContext; +pub mod analysis_context; mod collect_scopes; mod collect_symbols; mod collect_types; @@ -32,195 +27,6 @@ pub mod scope; pub mod symbol; pub mod type_info; -pub struct AnalysisContext { - fqn_stack: Vec>, - root_scope_id: ScopeId, - scopes: Vec, - current_scope_id: Option, - nodes_to_scopes: HashMap, - symbols: Vec, - nodes_to_symbols: HashMap, - type_infos: Vec, - symbols_to_type_infos: HashMap, - nodes_to_type_infos: HashMap, -} - -impl AnalysisContext { - pub fn new() -> Self { - Self { - fqn_stack: Vec::new(), - root_scope_id: 0, // pushed in vec! below - scopes: vec![Scope::new(None)], - current_scope_id: None, - nodes_to_scopes: HashMap::new(), - symbols: Vec::new(), - nodes_to_symbols: HashMap::new(), - type_infos: Vec::new(), - symbols_to_type_infos: HashMap::new(), - nodes_to_type_infos: HashMap::new(), - } - } - - pub fn commit(&mut self) { - todo!() - } - - pub fn rollback(&mut self) { - todo!() - } - - pub fn root_scope_id(&self) -> ScopeId { - self.root_scope_id - } - - pub fn scopes(&self) -> &[Scope] { - &self.scopes - } - - pub fn scopes_mut(&mut self) -> &mut Vec { - &mut self.scopes - } - - pub fn symbols(&self) -> &[Symbol] { - &self.symbols - } - - pub fn nodes_to_symbols(&self) -> &HashMap { - &self.nodes_to_symbols - } - - pub fn type_infos(&self) -> &[TypeInfo] { - &self.type_infos - } - - pub fn symbols_to_type_infos(&self) -> &HashMap { - &self.symbols_to_type_infos - } - - pub fn nodes_to_type_infos(&self) -> &HashMap { - &self.nodes_to_type_infos - } - - pub fn push_fqn_part(&mut self, part: Rc) { - self.fqn_stack.push(part); - } - - pub fn pop_fqn_part(&mut self) { - self.fqn_stack.pop(); - } - - pub fn resolve_fqn(&self, suffix: &str) -> String { - let mut fqn = self.fqn_stack.clone(); - fqn.push(suffix.into()); - fqn.join("::") - } - - pub fn push_scope(&mut self, _debug_name: &str) { - let scope = Scope::new(self.current_scope_id); - self.scopes.push(scope); - self.current_scope_id = Some(self.scopes.len() - 1); - } - - fn get_scope(&self, scope_id: ScopeId) -> &Scope { - self.scopes.get(scope_id).expect(&format!( - "scope_id {} is not a valid index of self.scopes", - scope_id - )) - } - - fn get_scope_mut(&mut self, scope_id: ScopeId) -> &mut Scope { - self.scopes.get_mut(scope_id).expect(&format!( - "scope_id {} is not a valid index of self.scopes", - scope_id - )) - } - - fn get_current_scope(&self) -> &Scope { - self.get_scope(self.current_scope_id.expect("current_scope_id is None")) - } - - fn get_current_scope_mut(&mut self) -> &mut Scope { - self.get_scope_mut(self.current_scope_id.expect("current_scope_id is None")) - } - - pub fn pop_scope(&mut self) { - self.current_scope_id = self.get_current_scope().parent_id(); - } - - pub fn associate_node_to_current_scope(&mut self, node_id: NodeId) { - self.nodes_to_scopes.insert( - node_id, - self.current_scope_id.expect("current_scope_id is None"), - ); - } - - fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId { - // get declared name - let declared_name = symbol.declared_name_owned(); - - // push the symbol - self.symbols.push(symbol); - let symbol_id = self.symbols.len() - 1; - - // put it in scope - self.get_scope_mut(scope_id) - .insert_symbol(declared_name, symbol_id); - - symbol_id - } - - pub fn associate_node_to_symbol(&mut self, node_id: NodeId, symbol_id: SymbolId) { - self.nodes_to_symbols.insert(node_id, symbol_id); - } - - fn get_symbol_in_scope(&self, scope: &Scope, declared_name: &str) -> Option<&Symbol> { - scope.get_symbol_id(declared_name).map(|symbol_id| { - self.symbols.get(symbol_id).expect(&format!( - "symbol_id {} is not a valid index of self.symbols", - symbol_id - )) - }) - } - - /// Use this function only when there is no node associated with the symbol. Otherwise use - /// `try_insert_symbol_in_node_scope`. - pub fn try_insert_symbol_in_scope( - &mut self, - to_insert: Symbol, - scope_id: ScopeId, - ) -> Result<(), Diagnostic> { - let scope = self.get_scope(scope_id); - if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) { - Err(symbol_already_declared(already_declared, &to_insert)) - } else { - self.insert_symbol_in_scope(scope_id, to_insert); - // no associated node, so no need to update self.nodes_to_symbols - Ok(()) - } - } - - pub fn try_insert_associate_symbol_in_node_scope( - &mut self, - to_insert: Symbol, - owner_node_id: NodeId, - ) -> Result<(), Diagnostic> { - let node_scope_id = self.nodes_to_scopes.get(&owner_node_id).expect(&format!( - "owner_node_id {} is not in self.nodes_to_scopes", - owner_node_id - )); - let node_scope = self.get_scope(*node_scope_id); - if let Some(already_declared) = - self.get_symbol_in_scope(node_scope, to_insert.declared_name()) - { - Err(symbol_already_declared(already_declared, &to_insert)) - } else { - let symbol_id = self.insert_symbol_in_scope(*node_scope_id, to_insert); - self.associate_node_to_symbol(owner_node_id, symbol_id); - Ok(()) - } - } -} - pub fn analyze_compilation_unit( compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext, @@ -233,29 +39,14 @@ pub fn analyze_compilation_unit( collect_symbols_compilation_unit(compilation_unit, ctx); diagnostics.append(&mut collect_symbols_diagnostics); - let NameResolutionResult(mut resolve_names_diagnostics) = resolve_names_in_compilation_unit( - compilation_unit, - &mut ctx.scopes, - &mut ctx.symbols, - &mut ctx.nodes_to_scopes, - &mut ctx.nodes_to_symbols, - ); + let NameResolutionResult(mut resolve_names_diagnostics) = + resolve_names_in_compilation_unit(compilation_unit, ctx); diagnostics.append(&mut resolve_names_diagnostics); - collect_types( - compilation_unit, - &ctx.nodes_to_symbols, - &mut ctx.type_infos, - &mut ctx.symbols_to_type_infos, - ); + collect_types(compilation_unit, ctx); - let ResolveTypesResult(mut resolve_types_diagnostics) = resolve_types_in_compilation_unit( - compilation_unit, - &ctx.nodes_to_symbols, - &mut ctx.type_infos, - &mut ctx.symbols_to_type_infos, - &mut ctx.nodes_to_type_infos, - ); + let ResolveTypesResult(mut resolve_types_diagnostics) = + resolve_types_in_compilation_unit(compilation_unit, ctx); diagnostics.append(&mut resolve_types_diagnostics); diagnostics @@ -268,24 +59,14 @@ pub fn analyze_statement(statement: &Statement, ctx: &mut AnalysisContext) -> Di // collect symbols for a statement is currently a no-op, so not needed here - let NameResolutionResult(mut resolve_names_diagnostics) = resolve_names_in_statement( - statement, - &mut ctx.scopes, - &mut ctx.symbols, - &mut ctx.nodes_to_scopes, - &mut ctx.nodes_to_symbols, - ); + let NameResolutionResult(mut resolve_names_diagnostics) = + resolve_names_in_statement(statement, ctx); diagnostics.append(&mut resolve_names_diagnostics); // collect types for a statement is currently a no-op, so not needed here - let ResolveTypesResult(mut resolve_types_diagnostics) = resolve_types_in_statement( - statement, - &ctx.nodes_to_symbols, - &mut ctx.type_infos, - &mut ctx.symbols_to_type_infos, - &mut ctx.nodes_to_type_infos, - ); + let ResolveTypesResult(mut resolve_types_diagnostics) = + resolve_types_in_statement(statement, ctx); diagnostics.append(&mut resolve_types_diagnostics); diagnostics diff --git a/dmc-lib/src/semantic_analysis/resolve_names.rs b/dmc-lib/src/semantic_analysis/resolve_names.rs index 41e06ce..10f9d5c 100644 --- a/dmc-lib/src/semantic_analysis/resolve_names.rs +++ b/dmc-lib/src/semantic_analysis/resolve_names.rs @@ -1,4 +1,3 @@ -use crate::ast::NodeId; use crate::ast::assign_statement::AssignStatement; use crate::ast::binary_expression::BinaryExpression; use crate::ast::call::Call; @@ -13,70 +12,11 @@ use crate::ast::negative_expression::NegativeExpression; use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; use crate::diagnostic::Diagnostics; -use crate::diagnostic_factories::symbol_not_found; -use crate::semantic_analysis::scope::{Scope, ScopeId}; -use crate::semantic_analysis::symbol::{Symbol, SymbolId, VariableSymbol}; -use std::collections::HashMap; +use crate::semantic_analysis::analysis_context::AnalysisContext; +use crate::semantic_analysis::symbol::{Symbol, VariableSymbol}; pub struct NameResolutionResult(pub Diagnostics); -struct NameResolutionContext<'a> { - scopes: &'a mut Vec, - symbols: &'a mut Vec, - nodes_to_scopes: &'a HashMap, - nodes_to_symbols: &'a mut HashMap, - diagnostics: Diagnostics, -} - -impl<'a> NameResolutionContext<'a> { - pub fn new( - scopes: &'a mut Vec, - symbols: &'a mut Vec, - nodes_to_scopes: &'a HashMap, - nodes_to_symbols: &'a mut HashMap, - ) -> Self { - Self { - scopes, - symbols, - nodes_to_scopes, - nodes_to_symbols, - diagnostics: Diagnostics::new(), - } - } - - pub fn scopes(&self) -> &[Scope] { - &self.scopes - } - - pub fn scopes_mut(&mut self) -> &mut Vec { - &mut self.scopes - } - - pub fn symbols(&self) -> &[Symbol] { - &self.symbols - } - - pub fn symbols_mut(&mut self) -> &mut Vec { - &mut self.symbols - } - - pub fn nodes_to_scopes(&self) -> &HashMap { - &self.nodes_to_scopes - } - - pub fn nodes_to_symbols(&self) -> &HashMap { - &self.nodes_to_symbols - } - - pub fn nodes_to_symbols_mut(&mut self) -> &mut HashMap { - &mut self.nodes_to_symbols - } - - pub fn diagnostics_mut(&mut self) -> &mut Diagnostics { - &mut self.diagnostics - } -} - enum StatementResolutionPhase { Static, } @@ -88,112 +28,98 @@ enum ExpressionResolutionPhase { pub fn resolve_names_in_compilation_unit( compilation_unit: &CompilationUnit, - scopes: &mut Vec, - symbols: &mut Vec, - nodes_to_scopes: &HashMap, - nodes_to_symbols: &mut HashMap, + ctx: &mut AnalysisContext, ) -> NameResolutionResult { - let mut ctx = NameResolutionContext::new(scopes, symbols, nodes_to_scopes, nodes_to_symbols); + let mut diagnostics = Diagnostics::new(); for function in compilation_unit.functions() { - resolve_names_function(function, &mut ctx); + resolve_names_function(function, ctx, &mut diagnostics); } for extern_function in compilation_unit.extern_functions() { - resolve_names_extern_function(extern_function, &mut ctx); + resolve_names_extern_function(extern_function, ctx, &mut diagnostics); } - NameResolutionResult(ctx.diagnostics) + NameResolutionResult(diagnostics) } pub fn resolve_names_in_statement( statement: &Statement, - scopes: &mut Vec, - symbols: &mut Vec, - nodes_to_scopes: &HashMap, - nodes_to_symbols: &mut HashMap, + ctx: &mut AnalysisContext, ) -> NameResolutionResult { - let mut ctx = NameResolutionContext::new(scopes, symbols, nodes_to_scopes, nodes_to_symbols); - resolve_names_statement(statement, &mut ctx, StatementResolutionPhase::Static); - NameResolutionResult(ctx.diagnostics) + let mut diagnostics = Diagnostics::new(); + resolve_names_statement( + statement, + ctx, + &mut diagnostics, + StatementResolutionPhase::Static, + ); + NameResolutionResult(diagnostics) } -fn resolve_names_function(function: &Function, ctx: &mut NameResolutionContext) { +fn resolve_names_function( + function: &Function, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { for parameter in function.parameters() { resolve_names_parameter(parameter, ctx); } for statement in function.statements() { - resolve_names_statement(statement, ctx, StatementResolutionPhase::Static); + resolve_names_statement( + statement, + ctx, + diagnostics, + StatementResolutionPhase::Static, + ); } // point this function's node_id to the symbol_id for the function symbol - let scope_id = ctx.nodes_to_scopes[&function.node_id()]; - let scope = &ctx.scopes[scope_id]; - let symbol_id = scope - .get_symbol_id(function.declared_name()) - .expect(&format!( - "function's symbol_id could not be found for {}", - function.declared_name() - )); - ctx.nodes_to_symbols.insert(function.node_id(), symbol_id); + ctx.associate_node_with_self_symbol(function.node_id(), function.declared_name()); } fn resolve_names_extern_function( extern_function: &ExternFunction, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, ) { for parameter in extern_function.parameters() { resolve_names_parameter(parameter, ctx); } // point this extern function's node_id to the symbol_id for this function symbol - let scope_id = ctx.nodes_to_scopes[&extern_function.node_id()]; - let scope = &ctx.scopes[scope_id]; - let symbol_id = scope - .get_symbol_id(extern_function.declared_name()) - .expect(&format!( - "extern function's symbol_id could not be found for {}", - extern_function.declared_name() - )); - ctx.nodes_to_symbols - .insert(extern_function.node_id(), symbol_id); + ctx.associate_node_with_self_symbol(extern_function.node_id(), extern_function.declared_name()); } -fn resolve_names_parameter(parameter: &Parameter, ctx: &mut NameResolutionContext) { - // point this parameter's node_id to the symbol_id for the parameter symbol - let scope_id = ctx.nodes_to_scopes[¶meter.node_id()]; - let scope = &ctx.scopes[scope_id]; - let symbol_id = scope - .get_symbol_id(parameter.declared_name()) - .expect(&format!( - "parameter's symbol_id could not be found for {}", - parameter.declared_name() - )); - ctx.nodes_to_symbols.insert(parameter.node_id(), symbol_id); +fn resolve_names_parameter(parameter: &Parameter, ctx: &mut AnalysisContext) { + // point this parameter's node_id to the symbol_id for this parameter symbol + ctx.associate_node_with_self_symbol(parameter.node_id(), parameter.declared_name()); } fn resolve_names_statement( statement: &Statement, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, phase: StatementResolutionPhase, ) { match statement { Statement::Let(let_statement) => { - resolve_names_let_statement(let_statement, ctx, phase); + resolve_names_let_statement(let_statement, ctx, diagnostics, phase); } Statement::Expression(expression_statement) => { - resolve_names_expression_statement(expression_statement, ctx); + resolve_names_expression_statement(expression_statement, ctx, diagnostics); } Statement::Assign(assign_statement) => { - resolve_names_assign_statement(assign_statement, ctx); + resolve_names_assign_statement(assign_statement, ctx, diagnostics); } } } fn resolve_names_let_statement( let_statement: &LetStatement, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, phase: StatementResolutionPhase, ) { match phase { @@ -202,72 +128,75 @@ fn resolve_names_let_statement( resolve_names_expression( let_statement.initializer(), ctx, + diagnostics, ExpressionResolutionPhase::Static, ); // now add the declared name to the ctx so later usages can access it - let scope_id = ctx.nodes_to_scopes()[&let_statement.node_id()]; - let symbol = Symbol::Variable(VariableSymbol::new( - let_statement.declared_name_owned(), - Some(let_statement.declared_name_source_range()), - let_statement.is_mut(), - )); - // the following could be a method, but this is probably the only place in this phase - // where we are pushing symbols still - ctx.symbols_mut().push(symbol); - let symbol_id = ctx.symbols().len() - 1; - let scope = &mut ctx.scopes_mut()[scope_id]; - scope.insert_symbol(let_statement.declared_name_owned(), symbol_id); - // associate the let statement with the symbol id - ctx.nodes_to_symbols - .insert(let_statement.node_id(), symbol_id); + let insert_result = ctx.try_insert_associate_symbol_in_node_scope( + Symbol::Variable(VariableSymbol::new( + let_statement.declared_name_owned(), + Some(let_statement.declared_name_source_range()), + let_statement.is_mut(), + )), + let_statement.node_id(), + ); + if let Err(diagnostic) = insert_result { + diagnostics.push(diagnostic); + } } } } fn resolve_names_expression_statement( expression_statement: &ExpressionStatement, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, ) { resolve_names_expression( expression_statement.expression(), ctx, + diagnostics, ExpressionResolutionPhase::Static, ); } fn resolve_names_assign_statement( assign_statement: &AssignStatement, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, ) { resolve_names_expression( assign_statement.value(), ctx, + diagnostics, ExpressionResolutionPhase::Static, ); resolve_names_expression( assign_statement.destination(), ctx, + diagnostics, ExpressionResolutionPhase::Static, ); } fn resolve_names_expression( expression: &Expression, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, phase: ExpressionResolutionPhase, ) { match expression { Expression::Binary(binary_expression) => { - resolve_names_binary_expression(binary_expression, ctx, phase); + resolve_names_binary_expression(binary_expression, ctx, diagnostics, phase); } Expression::Negative(negative_expression) => { - resolve_names_negative_expression(negative_expression, ctx, phase); + resolve_names_negative_expression(negative_expression, ctx, diagnostics, phase); } Expression::Call(call) => { - resolve_names_call(call, ctx, phase); + resolve_names_call(call, ctx, diagnostics, phase); } Expression::Identifier(identifier) => { - resolve_names_identifier(identifier, ctx, phase); + resolve_names_identifier(identifier, ctx, diagnostics, phase); } Expression::Integer(_) => {} Expression::Double(_) => {} @@ -277,71 +206,50 @@ fn resolve_names_expression( fn resolve_names_binary_expression( binary_expression: &BinaryExpression, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, phase: ExpressionResolutionPhase, ) { - resolve_names_expression(binary_expression.lhs(), ctx, phase); - resolve_names_expression(binary_expression.rhs(), ctx, phase); + resolve_names_expression(binary_expression.lhs(), ctx, diagnostics, phase); + resolve_names_expression(binary_expression.rhs(), ctx, diagnostics, phase); } fn resolve_names_negative_expression( negative_expression: &NegativeExpression, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, phase: ExpressionResolutionPhase, ) { - resolve_names_expression(negative_expression.operand(), ctx, phase); + resolve_names_expression(negative_expression.operand(), ctx, diagnostics, phase); } fn resolve_names_call( call: &Call, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, phase: ExpressionResolutionPhase, ) { for argument in call.arguments() { - resolve_names_expression(argument, ctx, phase); + resolve_names_expression(argument, ctx, diagnostics, phase); } - resolve_names_expression(call.callee(), ctx, phase); + resolve_names_expression(call.callee(), ctx, diagnostics, phase); } fn resolve_names_identifier( identifier: &Identifier, - ctx: &mut NameResolutionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, phase: ExpressionResolutionPhase, ) { match phase { ExpressionResolutionPhase::Static => { - let scope_id = ctx.nodes_to_scopes()[&identifier.node_id()]; - let mut maybe_scope = Some(&ctx.scopes()[scope_id]); - let mut found_symbol_id: Option = None; - while let Some(scope) = maybe_scope { - let maybe_symbol_id = scope.get_symbol_id(identifier.name()); // cloned because ctx cannot be borrowed both immutably and mutably - match maybe_symbol_id { - None => { - maybe_scope = match scope.parent_id() { - None => None, - Some(parent_id) => Some(&ctx.scopes()[parent_id]), - } - } - Some(symbol_id) => { - found_symbol_id = Some(symbol_id); - break; - } - } - } - match found_symbol_id { - None => { - ctx.diagnostics_mut().push(symbol_not_found( - identifier.name(), - identifier.source_range(), - )); - } - Some(symbol_id) => { - // In the future, we may want to differentiate between different types of - // symbols, but for now, let's just assume that we always refer to the - // nearest symbol in the ancestor scope chain. - ctx.nodes_to_symbols_mut() - .insert(identifier.node_id(), symbol_id); - } + let lookup_result = ctx.lookup_and_associate_symbol_to_node( + identifier.node_id(), + identifier.name(), + identifier.source_range(), + ); + if let Err(diagnostic) = lookup_result { + diagnostics.push(diagnostic); } } } diff --git a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs index b86e836..57831ee 100644 --- a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs +++ b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs @@ -1,6 +1,5 @@ mod type_analysis; -use crate::ast::NodeId; use crate::ast::assign_statement::AssignStatement; use crate::ast::binary_expression::{BinaryExpression, BinaryOperation}; use crate::ast::call::Call; @@ -14,138 +13,101 @@ use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; use crate::diagnostic::{Diagnostic, Diagnostics}; use crate::error_codes::BINARY_INCOMPATIBLE_TYPES; +use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::semantic_analysis::resolve_types::type_analysis::{ are_binary_op_compatible, binary_op_result, can_assign_right_to_left, can_negate, negate_result, }; -use crate::semantic_analysis::symbol::SymbolId; -use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; -use std::collections::HashMap; +use crate::semantic_analysis::type_info::TypeInfo; pub struct ResolveTypesResult(pub Diagnostics); -struct ResolveTypesContext<'a> { - nodes_to_symbols: &'a HashMap, - type_infos: &'a mut Vec, - symbols_to_type_infos: &'a mut HashMap, - nodes_to_type_infos: &'a mut HashMap, - diagnostics: Diagnostics, -} - -impl<'a> ResolveTypesContext<'a> { - pub fn new( - nodes_to_symbols: &'a HashMap, - type_infos: &'a mut Vec, - symbols_to_type_infos: &'a mut HashMap, - nodes_to_type_infos: &'a mut HashMap, - ) -> Self { - Self { - nodes_to_symbols, - type_infos, - symbols_to_type_infos, - nodes_to_type_infos, - diagnostics: Diagnostics::new(), - } - } -} - pub fn resolve_types_in_compilation_unit( compilation_unit: &CompilationUnit, - nodes_to_symbols: &HashMap, - type_infos: &mut Vec, - symbols_to_type_infos: &mut HashMap, - nodes_to_type_infos: &mut HashMap, + ctx: &mut AnalysisContext, ) -> ResolveTypesResult { - let mut ctx = ResolveTypesContext::new( - nodes_to_symbols, - type_infos, - symbols_to_type_infos, - nodes_to_type_infos, - ); + let mut diagnostics = Diagnostics::new(); for function in compilation_unit.functions() { - resolve_types_function(function, &mut ctx); + resolve_types_function(function, ctx, &mut diagnostics); } - ResolveTypesResult(ctx.diagnostics) + ResolveTypesResult(diagnostics) } pub fn resolve_types_in_statement( statement: &Statement, - nodes_to_symbols: &HashMap, - type_infos: &mut Vec, - symbols_to_type_infos: &mut HashMap, - nodes_to_type_infos: &mut HashMap, + ctx: &mut AnalysisContext, ) -> ResolveTypesResult { - let mut ctx = ResolveTypesContext::new( - nodes_to_symbols, - type_infos, - symbols_to_type_infos, - nodes_to_type_infos, - ); - resolve_types_statement(statement, &mut ctx); - ResolveTypesResult(ctx.diagnostics) + let mut diagnostics = Diagnostics::new(); + resolve_types_statement(statement, ctx, &mut diagnostics); + ResolveTypesResult(diagnostics) } -fn resolve_types_function(function: &Function, ctx: &mut ResolveTypesContext) { +fn resolve_types_function( + function: &Function, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { for parameter in function.parameters() { - resolve_types_parameter(parameter, ctx); + resolve_types_parameter(parameter, ctx, diagnostics); } for statement in function.statements() { - resolve_types_statement(statement, ctx); + resolve_types_statement(statement, ctx, diagnostics); } } -fn resolve_types_parameter(parameter: &Parameter, ctx: &mut ResolveTypesContext) { - // point the parameter's node_id to the associated type_info - let symbol_id = ctx.nodes_to_symbols[¶meter.node_id()]; - let type_info_id = ctx.symbols_to_type_infos[&symbol_id]; - ctx.nodes_to_type_infos - .insert(parameter.node_id(), type_info_id); +fn resolve_types_parameter( + _parameter: &Parameter, + _ctx: &mut AnalysisContext, + _diagnostics: &mut Diagnostics, +) { + // no-op } -fn resolve_types_statement(statement: &Statement, ctx: &mut ResolveTypesContext) { +fn resolve_types_statement( + statement: &Statement, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { match statement { Statement::Let(let_statement) => { - resolve_types_let_statement(let_statement, ctx); + resolve_types_let_statement(let_statement, ctx, diagnostics); } Statement::Expression(expression_statement) => { - resolve_types_expression(expression_statement.expression(), ctx); + resolve_types_expression(expression_statement.expression(), ctx, diagnostics); } Statement::Assign(assign_statement) => { - resolve_types_assign_statement(assign_statement, ctx); + resolve_types_assign_statement(assign_statement, ctx, diagnostics); } } } -fn resolve_types_let_statement(let_statement: &LetStatement, ctx: &mut ResolveTypesContext) { - resolve_types_expression(let_statement.initializer(), ctx); +fn resolve_types_let_statement( + let_statement: &LetStatement, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { + resolve_types_expression(let_statement.initializer(), ctx, diagnostics); - let initializer_type_info_id = ctx.nodes_to_type_infos[&let_statement.initializer().node_id()]; - - // update symbol's type - let symbol_id = ctx.nodes_to_symbols[&let_statement.node_id()]; - ctx.symbols_to_type_infos - .insert(symbol_id, initializer_type_info_id); - // also save it as the let statement's type info - ctx.nodes_to_type_infos - .insert(let_statement.node_id(), initializer_type_info_id); + let initializer_type_info_id = + ctx.get_type_info_id_for_node(let_statement.initializer().node_id()); + // "bubble up" to let statement and symbol + ctx.associate_node_and_symbol_to_type(let_statement.node_id(), initializer_type_info_id); } fn resolve_types_assign_statement( assign_statement: &AssignStatement, - ctx: &mut ResolveTypesContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, ) { - resolve_types_expression(assign_statement.value(), ctx); - resolve_types_expression(assign_statement.destination(), ctx); + resolve_types_expression(assign_statement.value(), ctx, diagnostics); + resolve_types_expression(assign_statement.destination(), ctx, diagnostics); // check assignability - let value_type_info_id = ctx.nodes_to_type_infos[&assign_statement.value().node_id()]; - let value_type_info = &ctx.type_infos[value_type_info_id]; - - let destination_type_info_id = - ctx.nodes_to_type_infos[&assign_statement.destination().node_id()]; - let destination_type_info = &ctx.type_infos[destination_type_info_id]; + let value_type_info = ctx.get_type_info_for_node(assign_statement.value().node_id()); + let destination_type_info = + ctx.get_type_info_for_node(assign_statement.destination().node_id()); if !can_assign_right_to_left(destination_type_info, value_type_info) { let message = format!( @@ -157,66 +119,55 @@ fn resolve_types_assign_statement( assign_statement.destination().source_range().start(), assign_statement.destination().source_range().end(), ); - ctx.diagnostics.push(diagnostic); + diagnostics.push(diagnostic); } } -fn resolve_types_expression(expression: &Expression, ctx: &mut ResolveTypesContext) { +fn resolve_types_expression( + expression: &Expression, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { match expression { Expression::Binary(binary_expression) => { - resolve_types_binary_expression(binary_expression, ctx); + resolve_types_binary_expression(binary_expression, ctx, diagnostics); } Expression::Negative(negative_expression) => { - resolve_types_negative_expression(negative_expression, ctx); + resolve_types_negative_expression(negative_expression, ctx, diagnostics); } Expression::Call(call) => { - resolve_types_call(call, ctx); + resolve_types_call(call, ctx, diagnostics); } Expression::Identifier(identifier) => { resolve_types_identifier(identifier, ctx); } Expression::Integer(integer_literal) => { - // yes, this is slightly wasteful now, but keeping it simple for mental model. - ctx.type_infos.push(TypeInfo::Int); - let int_literal_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(integer_literal.node_id(), int_literal_type_info_id); + ctx.insert_type_info_for_node(TypeInfo::Int, integer_literal.node_id()); } Expression::Double(double_literal) => { - ctx.type_infos.push(TypeInfo::Double); - let double_literal_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(double_literal.node_id(), double_literal_type_info_id); + ctx.insert_type_info_for_node(TypeInfo::Double, double_literal.node_id()); } Expression::String(string_literal) => { - ctx.type_infos.push(TypeInfo::String); - let string_literal_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(string_literal.node_id(), string_literal_type_info_id); + ctx.insert_type_info_for_node(TypeInfo::String, string_literal.node_id()); } } } fn resolve_types_binary_expression( binary_expression: &BinaryExpression, - ctx: &mut ResolveTypesContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, ) { - resolve_types_expression(binary_expression.lhs(), ctx); - resolve_types_expression(binary_expression.rhs(), ctx); + resolve_types_expression(binary_expression.lhs(), ctx, diagnostics); + resolve_types_expression(binary_expression.rhs(), ctx, diagnostics); - let lhs_type_info_id = ctx.nodes_to_type_infos[&binary_expression.lhs().node_id()]; - let lhs_type_info = &ctx.type_infos[lhs_type_info_id]; - - let rhs_type_info_id = ctx.nodes_to_type_infos[&binary_expression.rhs().node_id()]; - let rhs_type_info = &ctx.type_infos[rhs_type_info_id]; + let lhs_type_info = ctx.get_type_info_for_node(binary_expression.lhs().node_id()); + let rhs_type_info = ctx.get_type_info_for_node(binary_expression.rhs().node_id()); if are_binary_op_compatible(binary_expression.op(), lhs_type_info, rhs_type_info) { let result_type_info = binary_op_result(binary_expression.op(), lhs_type_info, rhs_type_info); - ctx.type_infos.push(result_type_info); - let result_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(binary_expression.node_id(), result_type_info_id); + ctx.insert_type_info_for_node(result_type_info, binary_expression.node_id()); } else { let op_name = match binary_expression.op() { BinaryOperation::Multiply => "multiply", @@ -240,31 +191,25 @@ fn resolve_types_binary_expression( binary_expression.source_range().end(), ) .with_error_code(BINARY_INCOMPATIBLE_TYPES); - ctx.diagnostics.push(diagnostic); + diagnostics.push(diagnostic); // push error type so it bubbles up - ctx.type_infos.push(TypeInfo::__Error); - let result_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(binary_expression.node_id(), result_type_info_id); + ctx.insert_type_info_for_node(TypeInfo::__Error, binary_expression.node_id()); } } fn resolve_types_negative_expression( negative_expression: &NegativeExpression, - ctx: &mut ResolveTypesContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, ) { - resolve_types_expression(negative_expression.operand(), ctx); + resolve_types_expression(negative_expression.operand(), ctx, diagnostics); - let operand_type_info_id = ctx.nodes_to_type_infos[&negative_expression.operand().node_id()]; - let operand_type_info = &ctx.type_infos[operand_type_info_id]; + let operand_type_info = ctx.get_type_info_for_node(negative_expression.operand().node_id()); if can_negate(operand_type_info) { let result_type_info = negate_result(operand_type_info); - ctx.type_infos.push(result_type_info); - let result_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(negative_expression.node_id(), result_type_info_id); + ctx.insert_type_info_for_node(result_type_info, negative_expression.node_id()); } else { let message = format!("Incompatible type: cannot negate {}", operand_type_info); let diagnostic = Diagnostic::new( @@ -272,26 +217,22 @@ fn resolve_types_negative_expression( negative_expression.source_range().start(), negative_expression.source_range().end(), ); - ctx.diagnostics.push(diagnostic); + diagnostics.push(diagnostic); // bubble up error type - ctx.type_infos.push(TypeInfo::__Error); - let result_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(negative_expression.node_id(), result_type_info_id); + ctx.insert_type_info_for_node(TypeInfo::__Error, negative_expression.node_id()); } } -fn resolve_types_call(call: &Call, ctx: &mut ResolveTypesContext) { - resolve_types_expression(call.callee(), ctx); +fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) { + resolve_types_expression(call.callee(), ctx, diagnostics); // get types of arguments for argument in call.arguments() { - resolve_types_expression(argument, ctx); + resolve_types_expression(argument, ctx, diagnostics); } - let callee_type_info_id = ctx.nodes_to_type_infos[&call.callee().node_id()]; - let callee_type_info = &ctx.type_infos[callee_type_info_id]; + let callee_type_info = ctx.get_type_info_for_node(call.callee().node_id()); match callee_type_info { TypeInfo::Function(function_type_info) => { @@ -308,7 +249,7 @@ fn resolve_types_call(call: &Call, ctx: &mut ResolveTypesContext) { call.source_range().start(), call.source_range().end(), ); - ctx.diagnostics.push(diagnostic); + diagnostics.push(diagnostic); // do not push an error type because the result of the whole call is the return type // of the function @@ -317,9 +258,8 @@ fn resolve_types_call(call: &Call, ctx: &mut ResolveTypesContext) { // check argument types let parameter_type_ids = function_type_info.parameter_type_ids(); for i in 0..parameter_type_ids.len() { - let argument_type_info_id = ctx.nodes_to_type_infos[&arguments[i].node_id()]; - let argument_type_info = &ctx.type_infos[argument_type_info_id]; - let parameter_type_info = &ctx.type_infos[parameter_type_ids[i]]; + let argument_type_info = ctx.get_type_info_for_node(arguments[i].node_id()); + let parameter_type_info = ctx.get_type_info_by_id(parameter_type_ids[i]); // n.b. different method if !can_assign_right_to_left(parameter_type_info, argument_type_info) { let message = format!( "Incompatible types: cannot assign {} to {}", @@ -330,7 +270,7 @@ fn resolve_types_call(call: &Call, ctx: &mut ResolveTypesContext) { arguments[i].source_range().start(), arguments[i].source_range().end(), ); - ctx.diagnostics.push(diagnostic); + diagnostics.push(diagnostic); // do not push an error type because the result of the whole call is the return type // of the function @@ -339,15 +279,11 @@ fn resolve_types_call(call: &Call, ctx: &mut ResolveTypesContext) { // set return type as type of call expression let return_type_info_id = function_type_info.return_type_id(); - ctx.nodes_to_type_infos - .insert(call.node_id(), return_type_info_id); + ctx.associate_node_to_type(call.node_id(), return_type_info_id); } TypeInfo::__Error => { // bubble it up - ctx.type_infos.push(TypeInfo::__Error); - let error_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(call.node_id(), error_type_info_id); + ctx.insert_type_info_for_node(TypeInfo::__Error, call.node_id()); } _ => { let message = format!("Incompatible type: cannot call type {}", callee_type_info); @@ -356,21 +292,15 @@ fn resolve_types_call(call: &Call, ctx: &mut ResolveTypesContext) { call.callee().source_range().start(), call.callee().source_range().end(), ); - ctx.diagnostics.push(diagnostic); + diagnostics.push(diagnostic); // bubble up error type - ctx.type_infos.push(TypeInfo::__Error); - let error_type_info_id = ctx.type_infos.len() - 1; - ctx.nodes_to_type_infos - .insert(call.node_id(), error_type_info_id); + ctx.insert_type_info_for_node(TypeInfo::__Error, call.node_id()); } } } -fn resolve_types_identifier(identifier: &Identifier, ctx: &mut ResolveTypesContext) { - let symbol_id = ctx.nodes_to_symbols[&identifier.node_id()]; - let symbol_type_info_id = ctx.symbols_to_type_infos[&symbol_id]; - // bubble up - ctx.nodes_to_type_infos - .insert(identifier.node_id(), symbol_type_info_id); +fn resolve_types_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) { + let type_info_id = ctx.lookup_type_for_node_via_symbol(identifier.node_id()); + ctx.associate_node_to_type(identifier.node_id(), type_info_id); } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index e81a669..4b31e76 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -3,7 +3,7 @@ mod e2e_tests { use dmc_lib::compile_compilation_unit; use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::{Diagnostic, Diagnostics}; - use dmc_lib::semantic_analysis::AnalysisContext; + use dmc_lib::semantic_analysis::analysis_context::AnalysisContext; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::value::Value; use dvm_lib::vm::{DvmContext, call};