diff --git a/dm/src/repl.rs b/dm/src/repl.rs index 9942cbc..ac12223 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -1,17 +1,18 @@ -use dmc_lib::ast::ir_builder::IrBuilder; +use dmc_lib::ast::expression_statement::ExpressionStatement; +use dmc_lib::ast::statement::Statement; +use dmc_lib::compile_statement_to_synthetic_function; use dmc_lib::constants_table::ConstantsTable; -use dmc_lib::diagnostic::Diagnostic; -use dmc_lib::ir::ir_function::IrFunction; -use dmc_lib::ir::ir_return::IrReturn; -use dmc_lib::ir::ir_statement::IrStatement; +use dmc_lib::diagnostic::{Diagnostic, Diagnostics}; use dmc_lib::ir::ir_variable::IrVariable; +use dmc_lib::ir::variable_locations::VariableLocations; use dmc_lib::lexer::Lexer; use dmc_lib::offset_counter::OffsetCounter; -use dmc_lib::parser::{parse_expression, parse_let_statement}; +use dmc_lib::parser::parse_expression; +use dmc_lib::semantic_analysis::AnalysisContext; +use dmc_lib::semantic_analysis::scope::ScopeId; use dmc_lib::symbol::variable_symbol::VariableSymbol; use dmc_lib::symbol_table::SymbolTable; use dmc_lib::token::TokenKind; -use dmc_lib::type_info::TypeInfo; use dmc_lib::types_table::TypesTable; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; @@ -304,3 +305,69 @@ fn compile_let_statement( // Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) todo!() } + +pub fn repl_2(read: &mut impl BufRead, register_count: usize) { + let mut buffer = String::new(); + + let mut analysis_context = AnalysisContext::new(); + let mut function_scope_id: Option = None; + let fqn: Rc = Rc::from("repl"); + let mut variable_locations = VariableLocations::new(); + let mut constants_table = ConstantsTable::new(); + + let mut dvm_context = DvmContext::new(); + let mut repl_fn_stack_locals: Vec = Vec::new(); + + 'repl: loop { + print!("> "); + io::stdout().flush().unwrap(); + read.read_line(&mut buffer).unwrap(); + let input = buffer.trim(); + if input.is_empty() { + buffer.clear(); + continue; + } + + let mut lexer = Lexer::new(input); + let first_token = match lexer.next() { + None => { + continue; + } + Some(result) => match result { + Ok(first_token) => first_token, + Err(lexer_error) => { + eprintln!("{:?}", lexer_error); + buffer.clear(); + continue; + } + }, + }; + } +} + +fn compile_expression_2( + input: &str, + analysis_context: &mut AnalysisContext, + function_scope_id: ScopeId, + fqn: &Rc, + variable_locations: &mut VariableLocations, + register_count: usize, + constants_table: &mut ConstantsTable, +) -> Result { + let (expression, parse_diagnostics) = parse_expression(input); + if !parse_diagnostics.is_empty() { + return Err(parse_diagnostics); + } + + let statement = Statement::Expression(ExpressionStatement::new(0, expression)); + + compile_statement_to_synthetic_function( + &statement, + analysis_context, + function_scope_id, + fqn.clone(), + variable_locations, + register_count, + constants_table, + ) +} diff --git a/dm/src/run.rs b/dm/src/run.rs index c22eb8f..368aff3 100644 --- a/dm/src/run.rs +++ b/dm/src/run.rs @@ -6,6 +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 dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::{DvmContext, call}; use std::path::PathBuf; @@ -35,9 +36,14 @@ fn run( show_asm: bool, register_count: usize, ) -> Result<(), Diagnostics> { + let mut analysis_context = AnalysisContext::new(); let mut constants_table = ConstantsTable::new(); - let compile_compilation_unit_result = - compile_compilation_unit(input, register_count, &mut constants_table)?; + let compile_compilation_unit_result = compile_compilation_unit( + input, + &mut analysis_context, + register_count, + &mut constants_table, + )?; let mut dvm_context = DvmContext::new(); diff --git a/dmc-lib/src/ir/ir_function.rs b/dmc-lib/src/ir/ir_function.rs index 29e0f7b..b4e8852 100644 --- a/dmc-lib/src/ir/ir_function.rs +++ b/dmc-lib/src/ir/ir_function.rs @@ -57,7 +57,7 @@ impl IrFunction { #[deprecated] pub fn assign_registers(&self, register_count: usize) -> VariableLocations { if self.blocks.is_empty() { - return VariableLocations::new(HashMap::new(), HashMap::new()); + return VariableLocations::new(); } if self.blocks.len() > 1 { unimplemented!("having more than one block in a function is not yet implemented.") diff --git a/dmc-lib/src/ir/mod.rs b/dmc-lib/src/ir/mod.rs index 8e7254c..7775f98 100644 --- a/dmc-lib/src/ir/mod.rs +++ b/dmc-lib/src/ir/mod.rs @@ -1,7 +1,8 @@ use crate::constants_table::ConstantsTable; use crate::ir::assemble::assemble_ir_function; use crate::ir::ir_function::IrFunction; -use crate::ir::register_allocation::assign_registers; +use crate::ir::register_allocation::{AssignRegistersResult, assign_registers}; +use crate::ir::variable_locations::VariableLocations; use dvm_lib::vm::function::Function; mod assemble; @@ -27,14 +28,24 @@ pub mod ir_type_info; pub mod ir_variable; mod register_allocation; mod util; -mod variable_locations; +pub mod variable_locations; pub fn compile_dvm_function( ir_function: &IrFunction, register_count: usize, + variable_locations: &mut VariableLocations, constants_table: &mut ConstantsTable, ) -> Function { - let variable_locations = assign_registers(ir_function, register_count); + let AssignRegistersResult { + register_variables, + spilled_variables, + } = assign_registers(ir_function, register_count); + + variable_locations.push_all_register_variables(®ister_variables); + for spilled_variable in spilled_variables { + variable_locations.push_stack_variable(spilled_variable); + } + let instructions = assemble_ir_function(ir_function, &variable_locations, constants_table); Function::new( ir_function.fqn().into(), diff --git a/dmc-lib/src/ir/register_allocation.rs b/dmc-lib/src/ir/register_allocation.rs index b9d97a6..21a6bbd 100644 --- a/dmc-lib/src/ir/register_allocation.rs +++ b/dmc-lib/src/ir/register_allocation.rs @@ -3,17 +3,24 @@ use crate::ir::ir_block::IrBlock; use crate::ir::ir_function::IrFunction; use crate::ir::ir_variable::IrVariableId; -use crate::ir::variable_locations::VariableLocations; -use dvm_lib::instruction::{Register, StackFrameOffset}; +use dvm_lib::instruction::Register; use std::collections::{HashMap, HashSet}; pub type RegisterAssignment = Register; pub type InterferenceGraph = HashMap>; pub type LivenessSets = Vec>; -pub fn assign_registers(ir_function: &IrFunction, register_count: usize) -> VariableLocations { +pub struct AssignRegistersResult { + pub register_variables: HashMap, + pub spilled_variables: HashSet, +} + +pub fn assign_registers(ir_function: &IrFunction, register_count: usize) -> AssignRegistersResult { if ir_function.blocks().is_empty() { - return VariableLocations::new(HashMap::new(), HashMap::new()); + return AssignRegistersResult { + register_variables: HashMap::new(), + spilled_variables: HashSet::new(), + }; } if ir_function.blocks().len() > 1 { unimplemented!("having more than one block in a function is not yet implemented.") @@ -32,7 +39,7 @@ fn block_live_in_live_out(ir_block: &IrBlock) -> (LivenessSets, LivenessSets) { let mut did_work = false; // Go backwards for efficiency for (statement_index, ir_statement) in ir_block.statements().iter().enumerate().rev() { - // out (union of successors ins) + // out (union of successors' ins) // for now, a statement can only have one successor // this will need to be updated when we add jumps let statement_live_out = &mut live_out[statement_index]; @@ -120,7 +127,7 @@ fn block_interference_graph( graph } -fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> VariableLocations { +fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> AssignRegistersResult { let mut spilled: HashSet = HashSet::new(); loop { let mut interference_graph = block_interference_graph(ir_block, &spilled); @@ -130,14 +137,10 @@ fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> Variable spilled.extend(new_spills); // check if added zero spills to get to fixed point if old_n_spilled == spilled.len() { - let mut spills_to_stack_offsets: HashMap = - HashMap::new(); - let mut offset_counter = 0isize; - for spill in spilled { - spills_to_stack_offsets.insert(spill, offset_counter); - offset_counter += 1; - } - return VariableLocations::new(registers, spills_to_stack_offsets); + return AssignRegistersResult { + register_variables: registers, + spilled_variables: spilled, + }; } } } @@ -182,7 +185,7 @@ fn registers_and_spills( assign_register(&work_item, &mut rebuilt_graph, k, &mut register_assignments); } else { // spill - spills.insert(work_item.vr.clone()); + spills.insert(work_item.vr); } } @@ -320,8 +323,9 @@ fn can_optimistically_color( mod tests { use super::*; use crate::diagnostic::Diagnostics; - use crate::lowering::lower_to_ir; + use crate::lowering::lower_to_ir_compilation_unit; use crate::parser::get_compilation_unit; + use crate::semantic_analysis::AnalysisContext; use crate::semantic_analysis::analyze_compilation_unit; fn line_graph() -> InterferenceGraph { @@ -452,23 +456,20 @@ mod tests { None, )?; - let (analysis_result, diagnostics) = analyze_compilation_unit(&compilation_unit); + let mut analysis_context = AnalysisContext::new(); + let diagnostics = analyze_compilation_unit(&compilation_unit, &mut analysis_context); assert!(diagnostics.is_empty()); - let lower_result = lower_to_ir( - &compilation_unit, - &analysis_result.symbols, - &analysis_result.nodes_to_symbols, - &analysis_result.type_infos, - &analysis_result.symbols_to_type_infos, - &analysis_result.nodes_to_type_infos, - ); // todo: make this API friendlier + let lower_result = lower_to_ir_compilation_unit(&compilation_unit, &analysis_context); assert_eq!(lower_result.functions.len(), 1); let main_fn = &lower_result.functions[0]; - let variable_locations = assign_registers(main_fn, 2); - assert_eq!(variable_locations.register_variables_count(), 4); + let AssignRegistersResult { + register_variables, + spilled_variables: _, + } = assign_registers(main_fn, 2); + assert_eq!(register_variables.len(), 4); Ok(()) } diff --git a/dmc-lib/src/ir/variable_locations.rs b/dmc-lib/src/ir/variable_locations.rs index 386fcd9..c841b24 100644 --- a/dmc-lib/src/ir/variable_locations.rs +++ b/dmc-lib/src/ir/variable_locations.rs @@ -12,19 +12,25 @@ pub enum VariableLocation { pub struct VariableLocations { register_variables: HashMap, stack_variables: HashMap, + next_stack_variable_offset: StackVariableOffset, } impl VariableLocations { - pub fn new( - register_variables: HashMap, - stack_variables: HashMap, - ) -> Self { + pub fn new() -> Self { Self { - register_variables, - stack_variables, + register_variables: HashMap::new(), + stack_variables: HashMap::new(), + next_stack_variable_offset: 0, } } + pub fn push_all_register_variables( + &mut self, + register_variables: &HashMap, + ) { + self.register_variables.extend(register_variables); + } + pub fn get_variable_location(&self, id: &IrVariableId) -> VariableLocation { match self.register_variables.get(id) { Some(register_assignment) => VariableLocation::Register(*register_assignment), @@ -39,4 +45,18 @@ impl VariableLocations { pub fn stack_variables_count(&self) -> usize { self.stack_variables.len() } + + pub fn extend(&mut self, other: &VariableLocations) { + // todo: add logic to transpose the `other`'s stack assignments on top of the current ones. + + self.register_variables + .extend(other.register_variables.clone()); + self.stack_variables.extend(other.stack_variables.clone()); + } + + pub fn push_stack_variable(&mut self, ir_variable_id: IrVariableId) { + self.stack_variables + .insert(ir_variable_id, self.next_stack_variable_offset); + self.next_stack_variable_offset += 1; + } } diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index 57abfa6..df22e2b 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -1,10 +1,14 @@ +use crate::ast::statement::Statement; use crate::constants_table::ConstantsTable; use crate::diagnostic::Diagnostics; use crate::ir::compile_dvm_function; -use crate::lowering::lower_to_ir; +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::analyze_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 std::collections::HashMap; use std::rc::Rc; @@ -39,6 +43,7 @@ pub struct CompileCompilationUnitResult { pub fn compile_compilation_unit( input: &str, + analysis_context: &mut AnalysisContext, register_count: usize, constants_table: &mut ConstantsTable, ) -> Result { @@ -47,24 +52,22 @@ pub fn compile_compilation_unit( return Err(diagnostics); } - let (analysis_result, diagnostics) = analyze_compilation_unit(&compilation_unit); + let diagnostics = analyze_compilation_unit(&compilation_unit, analysis_context); if !diagnostics.is_empty() { return Err(diagnostics); } - let lower_to_ir_result = lower_to_ir( - &compilation_unit, - &analysis_result.symbols, - &analysis_result.nodes_to_symbols, - &analysis_result.type_infos, - &analysis_result.symbols_to_type_infos, - &analysis_result.nodes_to_type_infos, - ); + let lower_to_ir_result = lower_to_ir_compilation_unit(&compilation_unit, analysis_context); let mut dvm_functions = HashMap::new(); for ir_function in &lower_to_ir_result.functions { - let dvm_function = compile_dvm_function(ir_function, register_count, constants_table); + let dvm_function = compile_dvm_function( + ir_function, + register_count, + &mut VariableLocations::new(), + constants_table, + ); dvm_functions.insert(dvm_function.name_owned(), dvm_function); } @@ -72,3 +75,25 @@ pub fn compile_compilation_unit( functions: dvm_functions, }) } + +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, + constants_table: &mut ConstantsTable, +) -> Result { + let diagnostics = analyze_statement(statement, analysis_context, function_scope_id); + if !diagnostics.is_empty() { + return Err(diagnostics); + } + let ir_function = lower_to_ir_synthetic_function(statement, analysis_context, fqn); + Ok(compile_dvm_function( + &ir_function, + register_count, + variable_locations, + constants_table, + )) +} diff --git a/dmc-lib/src/lowering/mod.rs b/dmc-lib/src/lowering/mod.rs index 55c153c..bc5c0d1 100644 --- a/dmc-lib/src/lowering/mod.rs +++ b/dmc-lib/src/lowering/mod.rs @@ -23,10 +23,12 @@ 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::symbol::{Symbol, SymbolId}; use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; use std::collections::HashMap; use std::ops::Neg; +use std::rc::Rc; pub struct LowerToIrResult { pub functions: Vec, @@ -41,20 +43,16 @@ struct LowerToIrContext<'a> { ir_functions: Vec, } -pub fn lower_to_ir( +pub fn lower_to_ir_compilation_unit( compilation_unit: &CompilationUnit, - symbols: &[Symbol], - nodes_to_symbols: &HashMap, - type_infos: &[TypeInfo], - symbols_to_type_infos: &HashMap, - nodes_to_type_infos: &HashMap, + analysis_context: &AnalysisContext, ) -> LowerToIrResult { let mut ctx = LowerToIrContext { - symbols, - nodes_to_symbols, - type_infos, - symbols_to_type_infos, - nodes_to_type_infos, + symbols: analysis_context.symbols(), + nodes_to_symbols: analysis_context.nodes_to_symbols(), + type_infos: analysis_context.type_infos(), + symbols_to_type_infos: analysis_context.symbols_to_type_infos(), + nodes_to_type_infos: analysis_context.nodes_to_type_infos(), ir_functions: Vec::new(), }; @@ -67,6 +65,45 @@ pub fn lower_to_ir( } } +pub fn lower_to_ir_synthetic_function( + statement: &Statement, + analysis_ctx: &AnalysisContext, + fqn: Rc, +) -> IrFunction { + let mut ctx = LowerToIrContext { + symbols: analysis_ctx.symbols(), + nodes_to_symbols: analysis_ctx.nodes_to_symbols(), + type_infos: analysis_ctx.type_infos(), + symbols_to_type_infos: analysis_ctx.symbols_to_type_infos(), + nodes_to_type_infos: analysis_ctx.nodes_to_type_infos(), + ir_functions: Vec::new(), + }; + + let mut fn_ctx = LowerToIrFunctionContext::new(); + + lower_to_ir_statement(statement, &mut ctx, &mut fn_ctx, true); + fn_ctx.finish_block(); + + // infer return type from statement + let maybe_return_ir_type_info = match statement { + Statement::Let(_) | Statement::Assign(_) => None, + Statement::Expression(expression_statement) => { + let type_info_id = + ctx.nodes_to_type_infos[&expression_statement.expression().node_id()]; + let type_info = &ctx.type_infos[type_info_id]; + return_type_info_to_ir_type_info(type_info) + } + }; + + IrFunction::new( + fqn, + fn_ctx.ir_parameters, + fn_ctx.ir_variables, + maybe_return_ir_type_info, + fn_ctx.blocks, + ) +} + struct LowerToIrFunctionContext { blocks: Vec, ir_variables: Vec, @@ -145,37 +182,30 @@ fn lower_to_ir_function(function: &Function, ctx: &mut LowerToIrContext) { lower_to_ir_parameters(function, ctx, &mut fn_ctx); - let function_symbol_id = ctx.nodes_to_symbols[&function.node_id()]; - let function_type_info_id = ctx.symbols_to_type_infos[&function_symbol_id]; - let function_type_info = match &ctx.type_infos[function_type_info_id] { - TypeInfo::Function(function_type_info) => function_type_info, - _ => panic!("Expected FunctionTypeInfo"), - }; - let is_void_function = match ctx.type_infos[function_type_info.return_type_id()] { - TypeInfo::Void => true, - _ => false, - }; - - let n_statements = function.statements().len(); - for (i, statement) in function.statements().iter().enumerate() { - let can_return_value = i == n_statements - 1 && !is_void_function; - lower_to_ir_statement(statement, ctx, &mut fn_ctx, can_return_value); - } - - fn_ctx.finish_block(); - + // get various function info let function_symbol_id = ctx.nodes_to_symbols[&function.node_id()]; let function_symbol = match &ctx.symbols[function_symbol_id] { Symbol::Function(function_symbol) => function_symbol, - _ => panic!("Expected function symbol"), + _ => panic!("Expected FunctionSymbol"), }; - let function_type_info_id = ctx.symbols_to_type_infos[&function_symbol_id]; let function_type_info = match &ctx.type_infos[function_type_info_id] { TypeInfo::Function(function_type_info) => function_type_info, _ => panic!("Expected FunctionTypeInfo"), }; let return_type_info = &ctx.type_infos[function_type_info.return_type_id()]; + let is_void_function = match return_type_info { + TypeInfo::Void => true, + _ => false, + }; + + // lower function body + let n_statements = function.statements().len(); + for (i, statement) in function.statements().iter().enumerate() { + let can_return_value = i == n_statements - 1 && !is_void_function; + lower_to_ir_statement(statement, ctx, &mut fn_ctx, can_return_value); + } + fn_ctx.finish_block(); let ir_function = IrFunction::new( function_symbol.fqn_owned(), diff --git a/dmc-lib/src/semantic_analysis/collect_scopes.rs b/dmc-lib/src/semantic_analysis/collect_scopes.rs index 0a55587..4c6809a 100644 --- a/dmc-lib/src/semantic_analysis/collect_scopes.rs +++ b/dmc-lib/src/semantic_analysis/collect_scopes.rs @@ -14,34 +14,37 @@ use crate::ast::statement::Statement; use crate::semantic_analysis::scope::{Scope, ScopeId}; use std::collections::HashMap; -pub struct ScopeCollectionResult { - pub scopes: Vec, - pub nodes_to_scopes: HashMap, -} - -struct ScopeCollectionContext { - scopes: Vec, +struct ScopeCollectionContext<'a> { + scopes: &'a mut Vec, + nodes_to_scopes: &'a mut HashMap, current_scope_id: Option, - nodes_to_scopes: HashMap, } -impl ScopeCollectionContext { - pub fn new() -> Self { +impl<'a> ScopeCollectionContext<'a> { + pub fn new( + scopes: &'a mut Vec, + nodes_to_scopes: &'a mut HashMap, + current_scope_id: ScopeId, + ) -> Self { Self { - scopes: Vec::new(), - current_scope_id: None, - nodes_to_scopes: HashMap::new(), + scopes, + nodes_to_scopes, + current_scope_id: Some(current_scope_id), } } - pub fn push_scope(&mut self, scope: Scope) -> ScopeId { + pub fn down_scope(&mut self) -> ScopeId { + let scope = Scope::new(self.current_scope_id); // current is parent before push self.scopes.push(scope); self.current_scope_id = Some(self.scopes.len() - 1); - self.current_scope_id.unwrap() // guaranteed because we just set it + self.current_scope_id.unwrap() } - pub fn pop_scope(&mut self) { - self.current_scope_id = self.scopes[self.current_scope_id.unwrap()].parent_id(); + pub fn up_scope(&mut self) { + let current_scope_id = self + .current_scope_id + .expect("Cannot up_scope() when there is no current_scope"); + self.current_scope_id = self.scopes[current_scope_id].parent_id(); } pub fn current_scope_id(&self) -> Option { @@ -53,20 +56,35 @@ impl ScopeCollectionContext { } } -pub fn collect_scopes(compilation_unit: &CompilationUnit) -> ScopeCollectionResult { - let mut ctx = ScopeCollectionContext::new(); - ctx.push_scope(Scope::new(None)); +/// 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`. +pub fn collect_scopes_in_compilation_unit( + compilation_unit: &CompilationUnit, + scopes: &mut Vec, + nodes_to_scopes: &mut HashMap, + parent_scope_id: ScopeId, +) { + let mut ctx = ScopeCollectionContext::new(scopes, nodes_to_scopes, parent_scope_id); + ctx.down_scope(); // compilation unit scope for function in compilation_unit.functions() { collect_scopes_function(function, &mut ctx); } for extern_function in compilation_unit.extern_functions() { collect_scopes_extern_function(extern_function, &mut ctx); } - ctx.pop_scope(); - ScopeCollectionResult { - scopes: ctx.scopes, - nodes_to_scopes: ctx.nodes_to_scopes, - } + ctx.up_scope(); // compilation unit scope +} + +/// N.b. the parent_scope_id refers to the scope of the statement and must be a valid +/// (non-panicking) index of `scopes`. +pub fn collect_scopes_in_statement( + statement: &Statement, + scopes: &mut Vec, + nodes_to_scopes: &mut HashMap, + parent_scope_id: ScopeId, +) { + let mut ctx = ScopeCollectionContext::new(scopes, nodes_to_scopes, parent_scope_id); + collect_scopes_statement(statement, &mut ctx); } fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext) { @@ -78,8 +96,7 @@ fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext .insert(function.node_id(), containing_scope_id); // push function scope - let function_scope = Scope::new(Some(containing_scope_id)); - let function_scope_id = ctx.push_scope(function_scope); + let function_scope_id = ctx.down_scope(); // save return-type and parameters' scope id for parameter in function.parameters() { @@ -97,15 +114,14 @@ fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext } // push block scope for body - let block_scope = Scope::new(Some(function_scope_id)); - ctx.push_scope(block_scope); + ctx.down_scope(); for statement in function.statements() { collect_scopes_statement(statement, ctx); } - ctx.pop_scope(); // block - ctx.pop_scope(); // function + ctx.up_scope(); // block + ctx.up_scope(); // function } fn collect_scopes_extern_function( @@ -117,8 +133,7 @@ fn collect_scopes_extern_function( ctx.nodes_to_scopes_mut() .insert(extern_function.node_id(), containing_scope_id); - let function_scope = Scope::new(Some(containing_scope_id)); - let function_scope_id = ctx.push_scope(function_scope); + let function_scope_id = ctx.down_scope(); for parameter in extern_function.parameters() { ctx.nodes_to_scopes_mut() @@ -127,7 +142,7 @@ fn collect_scopes_extern_function( ctx.nodes_to_scopes_mut() .insert(extern_function.return_type().node_id(), function_scope_id); - ctx.pop_scope(); + ctx.up_scope(); } fn collect_scopes_statement(statement: &Statement, ctx: &mut ScopeCollectionContext) { diff --git a/dmc-lib/src/semantic_analysis/collect_symbols.rs b/dmc-lib/src/semantic_analysis/collect_symbols.rs index 2a56e0a..5b31997 100644 --- a/dmc-lib/src/semantic_analysis/collect_symbols.rs +++ b/dmc-lib/src/semantic_analysis/collect_symbols.rs @@ -3,6 +3,7 @@ use crate::ast::compilation_unit::CompilationUnit; use crate::ast::extern_function::ExternFunction; use crate::ast::function::Function; use crate::ast::parameter::Parameter; +use crate::ast::statement::Statement; use crate::diagnostic::Diagnostics; use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared; use crate::semantic_analysis::scope::{Scope, ScopeId}; @@ -10,26 +11,27 @@ use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol}; use std::collections::HashMap; use std::rc::Rc; -pub struct SymbolCollectionResult { - pub symbols: Vec, - pub diagnostics: Diagnostics, -} +pub struct SymbolCollectionResult(pub Diagnostics); struct SymbolCollectionContext<'a> { scopes: &'a mut Vec, nodes_to_scopes: &'a HashMap, + symbols: &'a mut Vec, fqn_context: Vec>, - symbols: Vec, diagnostics: Diagnostics, } impl<'a> SymbolCollectionContext<'a> { - pub fn new(scopes: &'a mut Vec, nodes_to_scopes: &'a HashMap) -> Self { + pub fn new( + scopes: &'a mut Vec, + nodes_to_scopes: &'a HashMap, + symbols: &'a mut Vec, + ) -> Self { Self { scopes, nodes_to_scopes, + symbols, fqn_context: Vec::new(), - symbols: Vec::new(), diagnostics: Diagnostics::new(), } } @@ -79,12 +81,13 @@ impl<'a> SymbolCollectionContext<'a> { } } -pub fn collect_symbols( +pub fn collect_symbols_in_compilation_unit( compilation_unit: &CompilationUnit, scopes: &mut Vec, nodes_to_scopes: &HashMap, + symbols: &mut Vec, ) -> SymbolCollectionResult { - let mut ctx = SymbolCollectionContext::new(scopes, nodes_to_scopes); + let mut ctx = SymbolCollectionContext::new(scopes, nodes_to_scopes, symbols); for function in compilation_unit.functions() { collect_symbols_function(function, &mut ctx); @@ -94,10 +97,7 @@ pub fn collect_symbols( collect_symbols_extern_function(extern_function, &mut ctx); } - SymbolCollectionResult { - symbols: ctx.symbols, - diagnostics: ctx.diagnostics, - } + SymbolCollectionResult(ctx.diagnostics) } fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionContext) { diff --git a/dmc-lib/src/semantic_analysis/collect_types.rs b/dmc-lib/src/semantic_analysis/collect_types.rs index 5bead2c..8e1cc1d 100644 --- a/dmc-lib/src/semantic_analysis/collect_types.rs +++ b/dmc-lib/src/semantic_analysis/collect_types.rs @@ -7,23 +7,22 @@ use crate::semantic_analysis::symbol::SymbolId; use crate::semantic_analysis::type_info::{FunctionTypeInfo, TypeInfo, TypeInfoId}; use std::collections::HashMap; -pub struct CollectTypesResult { - pub type_infos: Vec, - pub symbols_to_type_infos: HashMap, -} - struct CollectTypesContext<'a> { nodes_to_symbols: &'a HashMap, - type_infos: Vec, - symbols_to_type_infos: 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) -> Self { + 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: Vec::new(), - symbols_to_type_infos: HashMap::new(), + type_infos, + symbols_to_type_infos, } } @@ -36,8 +35,10 @@ impl<'a> CollectTypesContext<'a> { pub fn collect_types( compilation_unit: &CompilationUnit, nodes_to_symbols: &HashMap, -) -> CollectTypesResult { - let mut ctx = CollectTypesContext::new(nodes_to_symbols); + type_infos: &mut Vec, + symbols_to_type_infos: &mut HashMap, +) { + let mut ctx = CollectTypesContext::new(nodes_to_symbols, type_infos, symbols_to_type_infos); for function in compilation_unit.functions() { collect_types_function(function, &mut ctx); } @@ -45,11 +46,6 @@ pub fn collect_types( for extern_function in compilation_unit.extern_functions() { collect_types_extern_function(extern_function, &mut ctx); } - - CollectTypesResult { - type_infos: ctx.type_infos, - symbols_to_type_infos: ctx.symbols_to_type_infos, - } } fn collect_types_function(function: &Function, ctx: &mut CollectTypesContext) { diff --git a/dmc-lib/src/semantic_analysis/mod.rs b/dmc-lib/src/semantic_analysis/mod.rs index 531e629..e6935fa 100644 --- a/dmc-lib/src/semantic_analysis/mod.rs +++ b/dmc-lib/src/semantic_analysis/mod.rs @@ -1,11 +1,21 @@ use crate::ast::NodeId; use crate::ast::compilation_unit::CompilationUnit; +use crate::ast::statement::Statement; use crate::diagnostic::Diagnostics; -use crate::semantic_analysis::collect_scopes::collect_scopes; -use crate::semantic_analysis::collect_symbols::collect_symbols; +use crate::semantic_analysis::collect_scopes::{ + collect_scopes_in_compilation_unit, collect_scopes_in_statement, +}; +use crate::semantic_analysis::collect_symbols::{ + SymbolCollectionResult, collect_symbols_in_compilation_unit, +}; use crate::semantic_analysis::collect_types::collect_types; -use crate::semantic_analysis::resolve_names::resolve_names; -use crate::semantic_analysis::resolve_types::resolve_types; +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; @@ -16,59 +26,141 @@ mod collect_types; mod diagnostic_helpers; mod resolve_names; mod resolve_types; -mod scope; -mod semantic_context; +pub mod scope; pub mod symbol; pub mod type_info; -pub struct AnalysisResult { - pub symbols: Vec, - pub nodes_to_symbols: HashMap, - pub type_infos: Vec, - pub symbols_to_type_infos: HashMap, - pub nodes_to_type_infos: HashMap, +pub struct AnalysisContext { + root_scope_id: ScopeId, + scopes: Vec, + 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 { + scopes: vec![Scope::new(None)], + nodes_to_scopes: HashMap::new(), + root_scope_id: 0, // already pushed in vec! above + 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 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 analyze_compilation_unit( compilation_unit: &CompilationUnit, -) -> (AnalysisResult, Diagnostics) { + ctx: &mut AnalysisContext, +) -> Diagnostics { let mut diagnostics = Diagnostics::new(); - let mut scopes_result = collect_scopes(compilation_unit); - - let mut symbols_result = collect_symbols( + collect_scopes_in_compilation_unit( compilation_unit, - &mut scopes_result.scopes, - &scopes_result.nodes_to_scopes, + &mut ctx.scopes, + &mut ctx.nodes_to_scopes, + ctx.root_scope_id, ); - diagnostics.append(&mut symbols_result.diagnostics); - let mut names_result = resolve_names( + let SymbolCollectionResult(mut collect_symbols_diagnostics) = + collect_symbols_in_compilation_unit( + compilation_unit, + &mut ctx.scopes, + &ctx.nodes_to_scopes, + &mut ctx.symbols, + ); + diagnostics.append(&mut collect_symbols_diagnostics); + + let NameResolutionResult(mut resolve_names_diagnostics) = resolve_names_in_compilation_unit( compilation_unit, - &mut scopes_result.scopes, - &mut symbols_result.symbols, - &scopes_result.nodes_to_scopes, + &mut ctx.scopes, + &mut ctx.symbols, + &mut ctx.nodes_to_scopes, + &mut ctx.nodes_to_symbols, ); - diagnostics.append(&mut names_result.diagnostics); + diagnostics.append(&mut resolve_names_diagnostics); - let mut collect_types_result = collect_types(compilation_unit, &names_result.nodes_to_symbols); - - let mut resolve_types_result = resolve_types( + collect_types( compilation_unit, - &names_result.nodes_to_symbols, - &mut collect_types_result.type_infos, - &mut collect_types_result.symbols_to_type_infos, + &ctx.nodes_to_symbols, + &mut ctx.type_infos, + &mut ctx.symbols_to_type_infos, ); - diagnostics.append(&mut resolve_types_result.diagnostics); - ( - AnalysisResult { - symbols: symbols_result.symbols, - nodes_to_symbols: names_result.nodes_to_symbols, - type_infos: collect_types_result.type_infos, - symbols_to_type_infos: collect_types_result.symbols_to_type_infos, - nodes_to_type_infos: resolve_types_result.nodes_to_type_infos, - }, - diagnostics, - ) + 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, + ); + diagnostics.append(&mut resolve_types_diagnostics); + + diagnostics +} + +pub fn analyze_statement( + statement: &Statement, + ctx: &mut AnalysisContext, + function_scope_id: ScopeId, +) -> Diagnostics { + let mut diagnostics = Diagnostics::new(); + + collect_scopes_in_statement( + statement, + &mut ctx.scopes, + &mut ctx.nodes_to_scopes, + function_scope_id, + ); + + // 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, + ); + 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, + ); + 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 dbf5fad..def724e 100644 --- a/dmc-lib/src/semantic_analysis/resolve_names.rs +++ b/dmc-lib/src/semantic_analysis/resolve_names.rs @@ -18,16 +18,13 @@ use crate::semantic_analysis::scope::{Scope, ScopeId}; use crate::semantic_analysis::symbol::{Symbol, SymbolId, VariableSymbol}; use std::collections::HashMap; -pub struct NameResolutionResult { - pub nodes_to_symbols: HashMap, - pub diagnostics: Diagnostics, -} +pub struct NameResolutionResult(pub Diagnostics); struct NameResolutionContext<'a> { scopes: &'a mut Vec, symbols: &'a mut Vec, nodes_to_scopes: &'a HashMap, - nodes_to_symbols: HashMap, + nodes_to_symbols: &'a mut HashMap, diagnostics: Diagnostics, } @@ -36,12 +33,13 @@ impl<'a> NameResolutionContext<'a> { 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: HashMap::new(), + nodes_to_symbols, diagnostics: Diagnostics::new(), } } @@ -88,13 +86,14 @@ enum ExpressionResolutionPhase { Static, } -pub fn resolve_names( +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, ) -> NameResolutionResult { - let mut ctx = NameResolutionContext::new(scopes, symbols, nodes_to_scopes); + let mut ctx = NameResolutionContext::new(scopes, symbols, nodes_to_scopes, nodes_to_symbols); for function in compilation_unit.functions() { resolve_names_function(function, &mut ctx); @@ -104,10 +103,19 @@ pub fn resolve_names( resolve_names_extern_function(extern_function, &mut ctx); } - NameResolutionResult { - nodes_to_symbols: ctx.nodes_to_symbols, - diagnostics: ctx.diagnostics, - } + NameResolutionResult(ctx.diagnostics) +} + +pub fn resolve_names_in_statement( + statement: &Statement, + scopes: &mut Vec, + symbols: &mut Vec, + nodes_to_scopes: &HashMap, + nodes_to_symbols: &mut HashMap, +) -> 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) } fn resolve_names_function(function: &Function, ctx: &mut NameResolutionContext) { diff --git a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs index be869cb..b86e836 100644 --- a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs +++ b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs @@ -21,16 +21,13 @@ use crate::semantic_analysis::symbol::SymbolId; use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; use std::collections::HashMap; -pub struct ResolveTypesResult { - pub nodes_to_type_infos: HashMap, - pub diagnostics: Diagnostics, -} +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: HashMap, + nodes_to_type_infos: &'a mut HashMap, diagnostics: Diagnostics, } @@ -39,32 +36,54 @@ impl<'a> 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, ) -> Self { Self { nodes_to_symbols, type_infos, symbols_to_type_infos, - nodes_to_type_infos: HashMap::new(), + nodes_to_type_infos, diagnostics: Diagnostics::new(), } } } -pub fn resolve_types( +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, ) -> ResolveTypesResult { - let mut ctx = ResolveTypesContext::new(nodes_to_symbols, type_infos, symbols_to_type_infos); + let mut ctx = ResolveTypesContext::new( + nodes_to_symbols, + type_infos, + symbols_to_type_infos, + nodes_to_type_infos, + ); + for function in compilation_unit.functions() { resolve_types_function(function, &mut ctx); } - ResolveTypesResult { - nodes_to_type_infos: ctx.nodes_to_type_infos, - diagnostics: ctx.diagnostics, - } + ResolveTypesResult(ctx.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, +) -> 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) } fn resolve_types_function(function: &Function, ctx: &mut ResolveTypesContext) { diff --git a/dmc-lib/src/semantic_analysis/semantic_context.rs b/dmc-lib/src/semantic_analysis/semantic_context.rs deleted file mode 100644 index 5122b32..0000000 --- a/dmc-lib/src/semantic_analysis/semantic_context.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::ast::NodeId; -use crate::semantic_analysis::scope::{Scope, ScopeId}; -use std::collections::HashMap; - -pub struct SemanticContext { - scopes: Vec, - node_scopes: HashMap, -} - -impl SemanticContext {} diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index cb79b44..e81a669 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -3,6 +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 dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::value::Value; use dvm_lib::vm::{DvmContext, call}; @@ -23,10 +24,11 @@ mod e2e_tests { } fn prepare_context(input: &str) -> Result { + let mut ctx = AnalysisContext::new(); let mut constants_table = ConstantsTable::new(); let compile_compilation_unit_result = - compile_compilation_unit(input, REGISTER_COUNT, &mut constants_table); + compile_compilation_unit(input, &mut ctx, REGISTER_COUNT, &mut constants_table); let mut dvm_context = DvmContext::new();