diff --git a/dm/src/main.rs b/dm/src/main.rs index be6bc34..ef67dd6 100644 --- a/dm/src/main.rs +++ b/dm/src/main.rs @@ -1,7 +1,7 @@ mod repl; mod run; -use crate::repl::{repl, repl_2}; +use crate::repl::repl_2; use crate::run::compile_and_run_script; use clap::{Parser, Subcommand}; use std::io; diff --git a/dm/src/repl.rs b/dm/src/repl.rs index 41b19e7..4c55226 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -2,314 +2,310 @@ 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, Diagnostics}; -use dmc_lib::ir::ir_variable::IrVariable; +use dmc_lib::diagnostic::Diagnostics; 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::semantic_analysis::AnalysisContext; use dmc_lib::semantic_analysis::scope::{Scope, ScopeId}; -use dmc_lib::symbol::variable_symbol::VariableSymbol; -use dmc_lib::symbol_table::SymbolTable; use dmc_lib::token::TokenKind; -use dmc_lib::types_table::TypesTable; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; use dvm_lib::vm::operand::Operand; use dvm_lib::vm::{CallStack, DvmContext, loop_instructions, prepare_for_instruction_loop}; -use std::cell::RefCell; -use std::collections::HashMap; use std::io; use std::io::{BufRead, Write}; use std::rc::Rc; - -pub fn repl(read: &mut impl BufRead, register_count: usize) { - let mut buffer = String::new(); - - let mut symbol_table = SymbolTable::new(); - let mut types_table = TypesTable::new(); - - let mut repl_fn_body_scope_id: Option = None; - let mut repl_fn_offset_counter = OffsetCounter::new(); - let mut repl_fn_local_variables = HashMap::new(); - - let mut constants_table = ConstantsTable::new(); - let mut context = DvmContext::new(); - - let mut repl_fn_stack_locals: Vec = vec![]; - - '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; - } - }, - }; - - match first_token.kind() { - TokenKind::Fn => { - todo!("Parse functions in repl") - } - TokenKind::Let => { - match compile_let_statement( - input, - register_count, - &mut symbol_table, - &mut repl_fn_body_scope_id, - &mut repl_fn_offset_counter, - &mut repl_fn_local_variables, - &mut types_table, - &mut constants_table, - ) { - Ok(function) => { - context - .functions_mut() - .insert(function.name_owned(), function); - } - Err(diagnostics) => { - for diagnostic in diagnostics { - eprintln!("{}", diagnostic.message()); - } - buffer.clear(); - continue 'repl; - } - } - } - _ => match compile_expression( - input, - register_count, - &mut symbol_table, - &mut types_table, - &mut repl_fn_body_scope_id, - &mut repl_fn_local_variables, - &mut repl_fn_offset_counter, - &mut constants_table, - ) { - Ok(function) => { - context - .functions_mut() - .insert(function.name_owned(), function); - } - Err(diagnostics) => { - for diagnostic in &diagnostics { - eprintln!("{}", diagnostic.message()); - } - buffer.clear(); - continue 'repl; - } - }, - } - - for (name, content) in constants_table.string_constants() { - context.constants_mut().insert( - name.clone(), - Constant::String(StringConstant::new(name, content)), - ); - } - - let mut call_stack = CallStack::new(); - prepare_for_instruction_loop(&context, "__repl", &mut call_stack, &[]); - - // copy all old locals to current call frame's stack - // this has to be done with indexing because the preparation above creates space for them - for (i, operand) in repl_fn_stack_locals.iter().enumerate() { - let target_index = call_stack.top().fp() + i; - call_stack.top_mut().stack_mut()[target_index] = operand.clone(); - } - - let result = loop_instructions( - &context, - &mut vec![Operand::Null; register_count], - &mut call_stack, - ); - - // copy the top frame's stack locals back to OUR stack locals for next iteration - repl_fn_stack_locals = std::mem::take(call_stack.top_mut().stack_mut()); - - if let Some(value) = result { - println!("{}", value); - } - - buffer.clear(); - } -} - -fn prepare_scopes(symbol_table: &mut SymbolTable, fn_body_scope_id: &mut Option) -> usize { - if let Some(scope_id) = fn_body_scope_id { - symbol_table.change_scope(*scope_id); - *scope_id - } else { - symbol_table.push_module_scope("__repl_module"); - symbol_table.push_function_scope("__repl_fn"); - let container_scope_id = symbol_table.push_block_scope("__repl_fn_body"); - fn_body_scope_id.replace(container_scope_id); - container_scope_id - } -} - -fn compile_expression( - input: &str, - register_count: usize, - symbol_table: &mut SymbolTable, - types_table: &mut TypesTable, - fn_body_scope_id: &mut Option, - fn_local_variables: &HashMap, Rc>>, - offset_counter: &mut OffsetCounter, - constants_table: &mut ConstantsTable, -) -> Result> { - // // parse - // let (mut expression, parse_diagnostics) = parse_expression(input); - // if !parse_diagnostics.is_empty() { - // return Err(parse_diagnostics); - // } - // - // // init scopes, if necessary - // let container_scope = prepare_scopes(symbol_table, fn_body_scope_id); - // - // // inner scopes - // expression.init_scopes(symbol_table, container_scope); - // - // // names - // let diagnostics = expression.check_static_fn_local_names(&symbol_table); - // if !diagnostics.is_empty() { - // return Err(diagnostics); - // } - // - // // type check - // expression.type_check(&symbol_table, types_table)?; - // - // // synthesize a function - // // init ir_builder - // let mut ir_builder = IrBuilder::new(); - // - // // copy all previous declared variables to here so we preserve their stack offsets - // for (key, value) in fn_local_variables { - // ir_builder - // .local_variables_mut() - // .insert(key.clone(), value.clone()); - // } - // - // let entry_block_id = ir_builder.new_block(); - // - // let maybe_ir_expression = - // expression.to_ir_expression(&mut ir_builder, &symbol_table, &types_table); - // - // // if Some, return the value - // ir_builder - // .current_block_mut() - // .add_statement(IrStatement::Return(IrReturn::new(maybe_ir_expression))); - // - // ir_builder.finish_block(); - // let entry_block = ir_builder.get_block(entry_block_id); - // - // let mut ir_function = IrFunction::new( - // "__repl".into(), - // vec![], - // expression.type_info(&symbol_table, &types_table), - // entry_block.clone(), - // ); - // - // // spilled registers are put on the stack, so we need to add it to our stack size - // ir_function.assign_registers(register_count, offset_counter); - // - // Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) - todo!() -} - -fn compile_let_statement( - input: &str, - register_count: usize, - symbol_table: &mut SymbolTable, - body_scope_id: &mut Option, - offset_counter: &mut OffsetCounter, - local_variables: &mut HashMap, Rc>>, - types_table: &mut TypesTable, - constants_table: &mut ConstantsTable, -) -> Result> { - // // parse - // let (maybe_let_statement, parse_diagnostics) = parse_let_statement(input); - // if !parse_diagnostics.is_empty() { - // return Err(parse_diagnostics); - // } - // let mut let_statement = maybe_let_statement.unwrap(); - // - // // names - // let container_scope_id = prepare_scopes(symbol_table, body_scope_id); - // - // let_statement.init_scopes(symbol_table, container_scope_id); - // - // let name_diagnostics = let_statement.analyze_static_fn_local_names(symbol_table); - // if !name_diagnostics.is_empty() { - // return Err(name_diagnostics); - // } - // - // // types - // let_statement.type_check(&symbol_table, types_table)?; - // - // // init the ir builder - // let mut ir_builder = IrBuilder::new(); - // - // // put previous locals in ir_builder so expressions can find them - // for (k, v) in local_variables.iter() { - // ir_builder - // .local_variables_mut() - // .insert(k.clone(), v.clone()); - // } - // - // // ir function - // let entry_block_id = ir_builder.new_block(); - // - // let destination_ir_variable = let_statement.to_repl_ir( - // &mut ir_builder, - // symbol_table, - // types_table, - // offset_counter.next() as isize, - // ); // put it on top - // - // // Now that we've translated to ir, we can add the new local variable in the IrBuilder to our - // // record of them to be used for next loop iteration. - // let variable_symbol = let_statement.get_destination_symbol(symbol_table); - // local_variables.insert(variable_symbol, destination_ir_variable); - // - // ir_builder.finish_block(); - // let entry_block = ir_builder.get_block(entry_block_id); - // let mut ir_function = IrFunction::new( - // "__repl".into(), - // vec![], - // &TypeInfo::Void, - // entry_block.clone(), - // ); - // - // // By here, the variables should all be assigned to their new or existing stack slots. - // // Register allocation should only be required for temp variables. - // ir_function.assign_registers(register_count, offset_counter); - // - // Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) - todo!() -} +// +// pub fn repl(read: &mut impl BufRead, register_count: usize) { +// let mut buffer = String::new(); +// +// let mut symbol_table = SymbolTable::new(); +// let mut types_table = TypesTable::new(); +// +// let mut repl_fn_body_scope_id: Option = None; +// let mut repl_fn_offset_counter = OffsetCounter::new(); +// let mut repl_fn_local_variables = HashMap::new(); +// +// let mut constants_table = ConstantsTable::new(); +// let mut context = DvmContext::new(); +// +// let mut repl_fn_stack_locals: Vec = vec![]; +// +// '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; +// } +// }, +// }; +// +// match first_token.kind() { +// TokenKind::Fn => { +// todo!("Parse functions in repl") +// } +// TokenKind::Let => { +// match compile_let_statement( +// input, +// register_count, +// &mut symbol_table, +// &mut repl_fn_body_scope_id, +// &mut repl_fn_offset_counter, +// &mut repl_fn_local_variables, +// &mut types_table, +// &mut constants_table, +// ) { +// Ok(function) => { +// context +// .functions_mut() +// .insert(function.name_owned(), function); +// } +// Err(diagnostics) => { +// for diagnostic in diagnostics { +// eprintln!("{}", diagnostic.message()); +// } +// buffer.clear(); +// continue 'repl; +// } +// } +// } +// _ => match compile_expression( +// input, +// register_count, +// &mut symbol_table, +// &mut types_table, +// &mut repl_fn_body_scope_id, +// &mut repl_fn_local_variables, +// &mut repl_fn_offset_counter, +// &mut constants_table, +// ) { +// Ok(function) => { +// context +// .functions_mut() +// .insert(function.name_owned(), function); +// } +// Err(diagnostics) => { +// for diagnostic in &diagnostics { +// eprintln!("{}", diagnostic.message()); +// } +// buffer.clear(); +// continue 'repl; +// } +// }, +// } +// +// for (name, content) in constants_table.string_constants() { +// context.constants_mut().insert( +// name.clone(), +// Constant::String(StringConstant::new(name, content)), +// ); +// } +// +// let mut call_stack = CallStack::new(); +// prepare_for_instruction_loop(&context, "__repl", &mut call_stack, &[]); +// +// // copy all old locals to current call frame's stack +// // this has to be done with indexing because the preparation above creates space for them +// for (i, operand) in repl_fn_stack_locals.iter().enumerate() { +// let target_index = call_stack.top().fp() + i; +// call_stack.top_mut().stack_mut()[target_index] = operand.clone(); +// } +// +// let result = loop_instructions( +// &context, +// &mut vec![Operand::Null; register_count], +// &mut call_stack, +// ); +// +// // copy the top frame's stack locals back to OUR stack locals for next iteration +// repl_fn_stack_locals = std::mem::take(call_stack.top_mut().stack_mut()); +// +// if let Some(value) = result { +// println!("{}", value); +// } +// +// buffer.clear(); +// } +// } +// +// fn prepare_scopes(symbol_table: &mut SymbolTable, fn_body_scope_id: &mut Option) -> usize { +// if let Some(scope_id) = fn_body_scope_id { +// symbol_table.change_scope(*scope_id); +// *scope_id +// } else { +// symbol_table.push_module_scope("__repl_module"); +// symbol_table.push_function_scope("__repl_fn"); +// let container_scope_id = symbol_table.push_block_scope("__repl_fn_body"); +// fn_body_scope_id.replace(container_scope_id); +// container_scope_id +// } +// } +// +// fn compile_expression( +// input: &str, +// register_count: usize, +// symbol_table: &mut SymbolTable, +// types_table: &mut TypesTable, +// fn_body_scope_id: &mut Option, +// fn_local_variables: &HashMap, Rc>>, +// offset_counter: &mut OffsetCounter, +// constants_table: &mut ConstantsTable, +// ) -> Result> { +// // // parse +// // let (mut expression, parse_diagnostics) = parse_expression(input); +// // if !parse_diagnostics.is_empty() { +// // return Err(parse_diagnostics); +// // } +// // +// // // init scopes, if necessary +// // let container_scope = prepare_scopes(symbol_table, fn_body_scope_id); +// // +// // // inner scopes +// // expression.init_scopes(symbol_table, container_scope); +// // +// // // names +// // let diagnostics = expression.check_static_fn_local_names(&symbol_table); +// // if !diagnostics.is_empty() { +// // return Err(diagnostics); +// // } +// // +// // // type check +// // expression.type_check(&symbol_table, types_table)?; +// // +// // // synthesize a function +// // // init ir_builder +// // let mut ir_builder = IrBuilder::new(); +// // +// // // copy all previous declared variables to here so we preserve their stack offsets +// // for (key, value) in fn_local_variables { +// // ir_builder +// // .local_variables_mut() +// // .insert(key.clone(), value.clone()); +// // } +// // +// // let entry_block_id = ir_builder.new_block(); +// // +// // let maybe_ir_expression = +// // expression.to_ir_expression(&mut ir_builder, &symbol_table, &types_table); +// // +// // // if Some, return the value +// // ir_builder +// // .current_block_mut() +// // .add_statement(IrStatement::Return(IrReturn::new(maybe_ir_expression))); +// // +// // ir_builder.finish_block(); +// // let entry_block = ir_builder.get_block(entry_block_id); +// // +// // let mut ir_function = IrFunction::new( +// // "__repl".into(), +// // vec![], +// // expression.type_info(&symbol_table, &types_table), +// // entry_block.clone(), +// // ); +// // +// // // spilled registers are put on the stack, so we need to add it to our stack size +// // ir_function.assign_registers(register_count, offset_counter); +// // +// // Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) +// todo!() +// } +// +// fn compile_let_statement( +// input: &str, +// register_count: usize, +// symbol_table: &mut SymbolTable, +// body_scope_id: &mut Option, +// offset_counter: &mut OffsetCounter, +// local_variables: &mut HashMap, Rc>>, +// types_table: &mut TypesTable, +// constants_table: &mut ConstantsTable, +// ) -> Result> { +// // // parse +// // let (maybe_let_statement, parse_diagnostics) = parse_let_statement(input); +// // if !parse_diagnostics.is_empty() { +// // return Err(parse_diagnostics); +// // } +// // let mut let_statement = maybe_let_statement.unwrap(); +// // +// // // names +// // let container_scope_id = prepare_scopes(symbol_table, body_scope_id); +// // +// // let_statement.init_scopes(symbol_table, container_scope_id); +// // +// // let name_diagnostics = let_statement.analyze_static_fn_local_names(symbol_table); +// // if !name_diagnostics.is_empty() { +// // return Err(name_diagnostics); +// // } +// // +// // // types +// // let_statement.type_check(&symbol_table, types_table)?; +// // +// // // init the ir builder +// // let mut ir_builder = IrBuilder::new(); +// // +// // // put previous locals in ir_builder so expressions can find them +// // for (k, v) in local_variables.iter() { +// // ir_builder +// // .local_variables_mut() +// // .insert(k.clone(), v.clone()); +// // } +// // +// // // ir function +// // let entry_block_id = ir_builder.new_block(); +// // +// // let destination_ir_variable = let_statement.to_repl_ir( +// // &mut ir_builder, +// // symbol_table, +// // types_table, +// // offset_counter.next() as isize, +// // ); // put it on top +// // +// // // Now that we've translated to ir, we can add the new local variable in the IrBuilder to our +// // // record of them to be used for next loop iteration. +// // let variable_symbol = let_statement.get_destination_symbol(symbol_table); +// // local_variables.insert(variable_symbol, destination_ir_variable); +// // +// // ir_builder.finish_block(); +// // let entry_block = ir_builder.get_block(entry_block_id); +// // let mut ir_function = IrFunction::new( +// // "__repl".into(), +// // vec![], +// // &TypeInfo::Void, +// // entry_block.clone(), +// // ); +// // +// // // By here, the variables should all be assigned to their new or existing stack slots. +// // // Register allocation should only be required for temp variables. +// // ir_function.assign_registers(register_count, offset_counter); +// // +// // 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(); + + analysis_context.push_scope("__repl_root_scope"); + let root_scope_id = analysis_context.root_scope_id(); analysis_context .scopes_mut() diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index df22e2b..8e91e87 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -85,7 +85,7 @@ pub fn compile_statement_to_synthetic_function( register_count: usize, constants_table: &mut ConstantsTable, ) -> Result { - let diagnostics = analyze_statement(statement, analysis_context, function_scope_id); + let diagnostics = analyze_statement(statement, analysis_context); if !diagnostics.is_empty() { return Err(diagnostics); } diff --git a/dmc-lib/src/semantic_analysis/collect_scopes.rs b/dmc-lib/src/semantic_analysis/collect_scopes.rs index 4c6809a..4040562 100644 --- a/dmc-lib/src/semantic_analysis/collect_scopes.rs +++ b/dmc-lib/src/semantic_analysis/collect_scopes.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; @@ -11,141 +10,69 @@ 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::scope::{Scope, ScopeId}; -use std::collections::HashMap; - -struct ScopeCollectionContext<'a> { - scopes: &'a mut Vec, - nodes_to_scopes: &'a mut HashMap, - current_scope_id: Option, -} - -impl<'a> ScopeCollectionContext<'a> { - pub fn new( - scopes: &'a mut Vec, - nodes_to_scopes: &'a mut HashMap, - current_scope_id: ScopeId, - ) -> Self { - Self { - scopes, - nodes_to_scopes, - current_scope_id: Some(current_scope_id), - } - } - - 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() - } - - 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 { - self.current_scope_id - } - - pub fn nodes_to_scopes_mut(&mut self) -> &mut HashMap { - &mut self.nodes_to_scopes - } -} +use crate::semantic_analysis::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`. -pub fn collect_scopes_in_compilation_unit( +pub fn collect_scopes_compilation_unit( compilation_unit: &CompilationUnit, - scopes: &mut Vec, - nodes_to_scopes: &mut HashMap, - parent_scope_id: ScopeId, + ctx: &mut AnalysisContext, ) { - let mut ctx = ScopeCollectionContext::new(scopes, nodes_to_scopes, parent_scope_id); - ctx.down_scope(); // compilation unit scope + ctx.push_scope("compilation_unit"); for function in compilation_unit.functions() { - collect_scopes_function(function, &mut ctx); + collect_scopes_function(function, ctx); } for extern_function in compilation_unit.extern_functions() { - collect_scopes_extern_function(extern_function, &mut ctx); + collect_scopes_extern_function(extern_function, ctx); } - ctx.up_scope(); // compilation unit scope + ctx.pop_scope(); // compilation_unit } -/// 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 AnalysisContext) { + // associate function with current scope + ctx.associate_node_to_current_scope(function.node_id()); -fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext) { - // get containing scope id - let containing_scope_id = ctx.current_scope_id().unwrap(); // guaranteed because functions are in modules - - // save function's containing scope id - ctx.nodes_to_scopes_mut() - .insert(function.node_id(), containing_scope_id); - - // push function scope - let function_scope_id = ctx.down_scope(); + // push function params scope + ctx.push_scope("function_parameters"); // save return-type and parameters' scope id for parameter in function.parameters() { - ctx.nodes_to_scopes_mut() - .insert(parameter.node_id(), function_scope_id); + ctx.associate_node_to_current_scope(parameter.node_id()); } match function.return_type() { None => { // no-op } Some(type_use) => { - ctx.nodes_to_scopes_mut() - .insert(type_use.node_id(), function_scope_id); + ctx.associate_node_to_current_scope(type_use.node_id()); } } // push block scope for body - ctx.down_scope(); + ctx.push_scope("function_body"); for statement in function.statements() { collect_scopes_statement(statement, ctx); } - ctx.up_scope(); // block - ctx.up_scope(); // function + ctx.pop_scope(); // body + ctx.pop_scope(); // parameters } -fn collect_scopes_extern_function( - extern_function: &ExternFunction, - ctx: &mut ScopeCollectionContext, -) { - let containing_scope_id = ctx.current_scope_id().unwrap(); - - ctx.nodes_to_scopes_mut() - .insert(extern_function.node_id(), containing_scope_id); - - let function_scope_id = ctx.down_scope(); +fn collect_scopes_extern_function(extern_function: &ExternFunction, ctx: &mut AnalysisContext) { + // associate extern_function with current scope + ctx.associate_node_to_current_scope(extern_function.node_id()); + // parameters scope + ctx.push_scope("extern_function_parameters"); for parameter in extern_function.parameters() { - ctx.nodes_to_scopes_mut() - .insert(parameter.node_id(), function_scope_id); + ctx.associate_node_to_current_scope(parameter.node_id()); } - ctx.nodes_to_scopes_mut() - .insert(extern_function.return_type().node_id(), function_scope_id); - - ctx.up_scope(); + ctx.associate_node_to_current_scope(extern_function.return_type().node_id()); + ctx.pop_scope(); // parameters } -fn collect_scopes_statement(statement: &Statement, ctx: &mut ScopeCollectionContext) { +pub fn collect_scopes_statement(statement: &Statement, ctx: &mut AnalysisContext) { match statement { Statement::Let(let_statement) => collect_scopes_let_statement(let_statement, ctx), Statement::Expression(expression_statement) => { @@ -157,29 +84,24 @@ fn collect_scopes_statement(statement: &Statement, ctx: &mut ScopeCollectionCont } } -fn collect_scopes_let_statement(let_statement: &LetStatement, ctx: &mut ScopeCollectionContext) { +fn collect_scopes_let_statement(let_statement: &LetStatement, ctx: &mut AnalysisContext) { collect_scopes_expression(let_statement.initializer(), ctx); - let current_scope_id = ctx.current_scope_id().unwrap(); - ctx.nodes_to_scopes_mut() - .insert(let_statement.node_id(), current_scope_id); + ctx.associate_node_to_current_scope(let_statement.node_id()); } fn collect_scopes_expression_statement( expression_statement: &ExpressionStatement, - ctx: &mut ScopeCollectionContext, + ctx: &mut AnalysisContext, ) { collect_scopes_expression(expression_statement.expression(), ctx); } -fn collect_scopes_assign_statement( - assign_statement: &AssignStatement, - ctx: &mut ScopeCollectionContext, -) { +fn collect_scopes_assign_statement(assign_statement: &AssignStatement, ctx: &mut AnalysisContext) { collect_scopes_expression(assign_statement.value(), ctx); collect_scopes_expression(assign_statement.destination(), ctx); } -fn collect_scopes_expression(expression: &Expression, ctx: &mut ScopeCollectionContext) { +fn collect_scopes_expression(expression: &Expression, ctx: &mut AnalysisContext) { match expression { Expression::Binary(binary_expression) => { collect_scopes_binary_expression(binary_expression, ctx); @@ -201,7 +123,7 @@ fn collect_scopes_expression(expression: &Expression, ctx: &mut ScopeCollectionC fn collect_scopes_binary_expression( binary_expression: &BinaryExpression, - ctx: &mut ScopeCollectionContext, + ctx: &mut AnalysisContext, ) { collect_scopes_expression(binary_expression.lhs(), ctx); collect_scopes_expression(binary_expression.rhs(), ctx); @@ -209,20 +131,18 @@ fn collect_scopes_binary_expression( fn collect_scopes_negative_expression( negative_expression: &NegativeExpression, - ctx: &mut ScopeCollectionContext, + ctx: &mut AnalysisContext, ) { collect_scopes_expression(negative_expression.operand(), ctx); } -fn collect_scopes_call(call: &Call, ctx: &mut ScopeCollectionContext) { +fn collect_scopes_call(call: &Call, ctx: &mut AnalysisContext) { for argument in call.arguments() { collect_scopes_expression(argument, ctx); } collect_scopes_expression(call.callee(), ctx); } -fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut ScopeCollectionContext) { - let current_scope_id = ctx.current_scope_id().unwrap(); // if this fails, we tried to do this without any context - ctx.nodes_to_scopes_mut() - .insert(identifier.node_id(), current_scope_id); +fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) { + ctx.associate_node_to_current_scope(identifier.node_id()); } diff --git a/dmc-lib/src/semantic_analysis/collect_symbols.rs b/dmc-lib/src/semantic_analysis/collect_symbols.rs index d731ec1..f30e373 100644 --- a/dmc-lib/src/semantic_analysis/collect_symbols.rs +++ b/dmc-lib/src/semantic_analysis/collect_symbols.rs @@ -1,107 +1,36 @@ -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::ast::statement::Statement; use crate::diagnostic::Diagnostics; -use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared; -use crate::semantic_analysis::scope::{Scope, ScopeId}; +use crate::semantic_analysis::AnalysisContext; use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol}; -use std::collections::HashMap; -use std::rc::Rc; pub struct SymbolCollectionResult(pub Diagnostics); -struct SymbolCollectionContext<'a> { - scopes: &'a mut Vec, - nodes_to_scopes: &'a HashMap, - symbols: &'a mut Vec, - fqn_context: Vec>, - diagnostics: Diagnostics, -} - -impl<'a> SymbolCollectionContext<'a> { - 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(), - diagnostics: Diagnostics::new(), - } - } - - pub fn find_symbol_in_scope_for(&self, name: &str, node_id: NodeId) -> Option<&Symbol> { - let scope_id = self.nodes_to_scopes[&node_id]; - let scope = &self.scopes[scope_id]; - if let Some(symbol_id) = scope.get_symbol_id(name) { - Some(&self.symbols[symbol_id]) - } else { - None - } - } - - pub fn insert_symbol(&mut self, symbol: Symbol, node_id: NodeId) { - let declared_name = symbol.declared_name_owned(); - self.symbols.push(symbol); - let symbol_id = self.symbols.len() - 1; - let scope_id = self.nodes_to_scopes[&node_id]; - let scope = &mut self.scopes[scope_id]; - scope.insert_symbol(declared_name, symbol_id); - } - - pub fn symbols_mut(&mut self) -> &mut Vec { - &mut self.symbols - } - - pub fn diagnostics_mut(&mut self) -> &mut Diagnostics { - &mut self.diagnostics - } - - fn join_fqn_parts(parts: &[Rc]) -> String { - parts.join("::") - } - - pub fn get_fqn_base(&self) -> String { - Self::join_fqn_parts(&self.fqn_context) - } - - pub fn resolve_fqn(&self, suffix: &Rc) -> String { - let base = self.get_fqn_base(); - if base.is_empty() { - suffix.to_string() - } else { - Self::join_fqn_parts(&[base.into(), suffix.clone()]) - } - } -} - -pub fn collect_symbols_in_compilation_unit( +pub fn collect_symbols_compilation_unit( compilation_unit: &CompilationUnit, - scopes: &mut Vec, - nodes_to_scopes: &HashMap, - symbols: &mut Vec, + analysis_context: &mut AnalysisContext, ) -> SymbolCollectionResult { - let mut ctx = SymbolCollectionContext::new(scopes, nodes_to_scopes, symbols); + let mut diagnostics = Diagnostics::new(); for function in compilation_unit.functions() { - collect_symbols_function(function, &mut ctx); + collect_symbols_function(function, analysis_context, &mut diagnostics); } for extern_function in compilation_unit.extern_functions() { - collect_symbols_extern_function(extern_function, &mut ctx); + collect_symbols_extern_function(extern_function, analysis_context, &mut diagnostics); } - SymbolCollectionResult(ctx.diagnostics) + SymbolCollectionResult(diagnostics) } -fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionContext) { - let fqn = ctx.resolve_fqn(&function.declared_name_owned()).into(); +fn collect_symbols_function( + function: &Function, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { + let fqn = ctx.resolve_fqn(&function.declared_name()).into(); // function itself let function_symbol = Symbol::Function(FunctionSymbol::new( @@ -112,18 +41,15 @@ fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionConte )); // insert - if let Some(already_declared) = - ctx.find_symbol_in_scope_for(function.declared_name(), function.node_id()) + if let Err(diagnostic) = + ctx.try_insert_associate_symbol_in_node_scope(function_symbol, function.node_id()) { - let diagnostic = symbol_already_declared(already_declared, &function_symbol); - ctx.diagnostics_mut().push(diagnostic); - } else { - ctx.insert_symbol(function_symbol, function.node_id()); + diagnostics.push(diagnostic); } // parameters for parameter in function.parameters() { - collect_symbols_parameter(parameter, ctx); + collect_symbols_parameter(parameter, ctx, diagnostics); } // n.b. do not do statements yet, because variables are declared and resolved in the resolution pass @@ -131,7 +57,8 @@ fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionConte fn collect_symbols_extern_function( extern_function: &ExternFunction, - ctx: &mut SymbolCollectionContext, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, ) { let fqn = ctx .resolve_fqn(&extern_function.declared_name_owned()) @@ -146,25 +73,30 @@ fn collect_symbols_extern_function( )); // insert function symbol - if let Some(already_declared) = - ctx.find_symbol_in_scope_for(extern_function.declared_name(), extern_function.node_id()) + if let Err(diagnostic) = + ctx.try_insert_associate_symbol_in_node_scope(function_symbol, extern_function.node_id()) { - let diagnostic = symbol_already_declared(already_declared, &function_symbol); - ctx.diagnostics_mut().push(diagnostic); - } else { - ctx.insert_symbol(function_symbol, extern_function.node_id()); + diagnostics.push(diagnostic); } // parameters for parameter in extern_function.parameters() { - collect_symbols_parameter(parameter, ctx); + collect_symbols_parameter(parameter, ctx, diagnostics); } } -fn collect_symbols_parameter(parameter: &Parameter, ctx: &mut SymbolCollectionContext) { - let parameter_symbol = ParameterSymbol::new( +fn collect_symbols_parameter( + parameter: &Parameter, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { + let parameter_symbol = Symbol::Parameter(ParameterSymbol::new( parameter.declared_name_owned(), Some(parameter.declared_name_source_range()), - ); - ctx.insert_symbol(Symbol::Parameter(parameter_symbol), parameter.node_id()); + )); + if let Err(diagnostic) = + ctx.try_insert_associate_symbol_in_node_scope(parameter_symbol, parameter.node_id()) + { + diagnostics.push(diagnostic); + } } diff --git a/dmc-lib/src/semantic_analysis/mod.rs b/dmc-lib/src/semantic_analysis/mod.rs index 773179c..edfcf59 100644 --- a/dmc-lib/src/semantic_analysis/mod.rs +++ b/dmc-lib/src/semantic_analysis/mod.rs @@ -1,14 +1,15 @@ use crate::ast::NodeId; use crate::ast::compilation_unit::CompilationUnit; use crate::ast::statement::Statement; -use crate::diagnostic::Diagnostics; +use crate::diagnostic::{Diagnostic, Diagnostics}; use crate::semantic_analysis::collect_scopes::{ - collect_scopes_in_compilation_unit, collect_scopes_in_statement, + collect_scopes_compilation_unit, collect_scopes_statement, }; use crate::semantic_analysis::collect_symbols::{ - SymbolCollectionResult, collect_symbols_in_compilation_unit, + 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, }; @@ -19,6 +20,7 @@ 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; mod collect_scopes; mod collect_symbols; @@ -31,8 +33,10 @@ 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, @@ -44,8 +48,10 @@ pub struct AnalysisContext { 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(), @@ -55,6 +61,14 @@ impl AnalysisContext { } } + pub fn commit(&mut self) { + todo!() + } + + pub fn rollback(&mut self) { + todo!() + } + pub fn root_scope_id(&self) -> ScopeId { self.root_scope_id } @@ -86,6 +100,125 @@ impl AnalysisContext { 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( @@ -94,20 +227,10 @@ pub fn analyze_compilation_unit( ) -> Diagnostics { let mut diagnostics = Diagnostics::new(); - collect_scopes_in_compilation_unit( - compilation_unit, - &mut ctx.scopes, - &mut ctx.nodes_to_scopes, - ctx.root_scope_id, - ); + collect_scopes_compilation_unit(compilation_unit, ctx); let SymbolCollectionResult(mut collect_symbols_diagnostics) = - collect_symbols_in_compilation_unit( - compilation_unit, - &mut ctx.scopes, - &ctx.nodes_to_scopes, - &mut ctx.symbols, - ); + collect_symbols_compilation_unit(compilation_unit, ctx); diagnostics.append(&mut collect_symbols_diagnostics); let NameResolutionResult(mut resolve_names_diagnostics) = resolve_names_in_compilation_unit( @@ -138,19 +261,10 @@ pub fn analyze_compilation_unit( diagnostics } -pub fn analyze_statement( - statement: &Statement, - ctx: &mut AnalysisContext, - function_scope_id: ScopeId, -) -> Diagnostics { +pub fn analyze_statement(statement: &Statement, ctx: &mut AnalysisContext) -> Diagnostics { let mut diagnostics = Diagnostics::new(); - collect_scopes_in_statement( - statement, - &mut ctx.scopes, - &mut ctx.nodes_to_scopes, - function_scope_id, - ); + collect_scopes_statement(statement, ctx); // collect symbols for a statement is currently a no-op, so not needed here