Compare commits

..

No commits in common. "c54db2d70d5caff37b6d53b42297d66be44c025a" and "ed6f15a8a9295f0ae237a324f7359f1fea4afae5" have entirely different histories.

27 changed files with 556 additions and 685 deletions

View File

@ -1,7 +1,7 @@
mod repl; mod repl;
mod run; mod run;
use crate::repl::repl_2; use crate::repl::repl;
use crate::run::compile_and_run_script; use crate::run::compile_and_run_script;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::io; use std::io;
@ -44,7 +44,7 @@ fn main() {
compile_and_run_script(script, *show_asm, *show_ir, register_count); compile_and_run_script(script, *show_asm, *show_ir, register_count);
} }
SubCommand::Repl => { SubCommand::Repl => {
repl_2(&mut io::stdin().lock(), register_count); repl(&mut io::stdin().lock(), register_count);
} }
} }
} }

View File

@ -2,321 +2,316 @@ use dmc_lib::ast::expression_statement::ExpressionStatement;
use dmc_lib::ast::statement::Statement; use dmc_lib::ast::statement::Statement;
use dmc_lib::compile_statement_to_synthetic_function; use dmc_lib::compile_statement_to_synthetic_function;
use dmc_lib::constants_table::ConstantsTable; use dmc_lib::constants_table::ConstantsTable;
use dmc_lib::diagnostic::Diagnostics; use dmc_lib::diagnostic::{Diagnostic, Diagnostics};
use dmc_lib::ir::ir_variable::IrVariable;
use dmc_lib::ir::variable_locations::VariableLocations; use dmc_lib::ir::variable_locations::VariableLocations;
use dmc_lib::lexer::Lexer; use dmc_lib::lexer::Lexer;
use dmc_lib::parser::{parse_expression, parse_let_statement}; use dmc_lib::offset_counter::OffsetCounter;
use dmc_lib::parser::parse_expression;
use dmc_lib::semantic_analysis::AnalysisContext; use dmc_lib::semantic_analysis::AnalysisContext;
use dmc_lib::semantic_analysis::scope::{Scope, ScopeId}; 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::token::TokenKind;
use dmc_lib::types_table::TypesTable;
use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::function::Function; use dvm_lib::vm::function::Function;
use dvm_lib::vm::operand::Operand; use dvm_lib::vm::operand::Operand;
use dvm_lib::vm::{CallStack, DvmContext, loop_instructions, prepare_for_instruction_loop}; 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;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use std::rc::Rc; use std::rc::Rc;
//
// pub fn repl(read: &mut impl BufRead, register_count: usize) { pub fn repl(read: &mut impl BufRead, register_count: usize) {
// let mut buffer = String::new(); let mut buffer = String::new();
//
// let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
// let mut types_table = TypesTable::new(); let mut types_table = TypesTable::new();
//
// let mut repl_fn_body_scope_id: Option<usize> = None; let mut repl_fn_body_scope_id: Option<usize> = None;
// let mut repl_fn_offset_counter = OffsetCounter::new(); let mut repl_fn_offset_counter = OffsetCounter::new();
// let mut repl_fn_local_variables = HashMap::new(); let mut repl_fn_local_variables = HashMap::new();
//
// let mut constants_table = ConstantsTable::new(); let mut constants_table = ConstantsTable::new();
// let mut context = DvmContext::new(); let mut context = DvmContext::new();
//
// let mut repl_fn_stack_locals: Vec<Operand> = vec![]; let mut repl_fn_stack_locals: Vec<Operand> = vec![];
//
// 'repl: loop { 'repl: loop {
// print!("> "); print!("> ");
// io::stdout().flush().unwrap(); io::stdout().flush().unwrap();
// read.read_line(&mut buffer).unwrap(); read.read_line(&mut buffer).unwrap();
// let input = buffer.trim(); let input = buffer.trim();
// if input.is_empty() { if input.is_empty() {
// buffer.clear(); buffer.clear();
// continue; continue;
// } }
//
// let mut lexer = Lexer::new(input); let mut lexer = Lexer::new(input);
// let first_token = match lexer.next() { let first_token = match lexer.next() {
// None => { None => {
// continue; continue;
// } }
// Some(result) => match result { Some(result) => match result {
// Ok(first_token) => first_token, Ok(first_token) => first_token,
// Err(lexer_error) => { Err(lexer_error) => {
// eprintln!("{:?}", lexer_error); eprintln!("{:?}", lexer_error);
// buffer.clear(); buffer.clear();
// continue; continue;
// } }
// }, },
// }; };
//
// match first_token.kind() { match first_token.kind() {
// TokenKind::Fn => { TokenKind::Fn => {
// todo!("Parse functions in repl") todo!("Parse functions in repl")
// } }
// TokenKind::Let => { TokenKind::Let => {
// match compile_let_statement( match compile_let_statement(
// input, input,
// register_count, register_count,
// &mut symbol_table, &mut symbol_table,
// &mut repl_fn_body_scope_id, &mut repl_fn_body_scope_id,
// &mut repl_fn_offset_counter, &mut repl_fn_offset_counter,
// &mut repl_fn_local_variables, &mut repl_fn_local_variables,
// &mut types_table, &mut types_table,
// &mut constants_table, &mut constants_table,
// ) { ) {
// Ok(function) => { Ok(function) => {
// context context
// .functions_mut() .functions_mut()
// .insert(function.name_owned(), function); .insert(function.name_owned(), function);
// } }
// Err(diagnostics) => { Err(diagnostics) => {
// for diagnostic in diagnostics { for diagnostic in diagnostics {
// eprintln!("{}", diagnostic.message()); eprintln!("{}", diagnostic.message());
// } }
// buffer.clear(); buffer.clear();
// continue 'repl; continue 'repl;
// } }
// } }
// } }
// _ => match compile_expression( _ => match compile_expression(
// input, input,
// register_count, register_count,
// &mut symbol_table, &mut symbol_table,
// &mut types_table, &mut types_table,
// &mut repl_fn_body_scope_id, &mut repl_fn_body_scope_id,
// &mut repl_fn_local_variables, &mut repl_fn_local_variables,
// &mut repl_fn_offset_counter, &mut repl_fn_offset_counter,
// &mut constants_table, &mut constants_table,
// ) { ) {
// Ok(function) => { Ok(function) => {
// context context
// .functions_mut() .functions_mut()
// .insert(function.name_owned(), function); .insert(function.name_owned(), function);
// } }
// Err(diagnostics) => { Err(diagnostics) => {
// for diagnostic in &diagnostics { for diagnostic in &diagnostics {
// eprintln!("{}", diagnostic.message()); eprintln!("{}", diagnostic.message());
// } }
// buffer.clear(); buffer.clear();
// continue 'repl; continue 'repl;
// } }
// }, },
// } }
//
// for (name, content) in constants_table.string_constants() { for (name, content) in constants_table.string_constants() {
// context.constants_mut().insert( context.constants_mut().insert(
// name.clone(), name.clone(),
// Constant::String(StringConstant::new(name, content)), Constant::String(StringConstant::new(name, content)),
// ); );
// } }
//
// let mut call_stack = CallStack::new(); let mut call_stack = CallStack::new();
// prepare_for_instruction_loop(&context, "__repl", &mut call_stack, &[]); prepare_for_instruction_loop(&context, "__repl", &mut call_stack, &[]);
//
// // copy all old locals to current call frame's 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 // 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() { for (i, operand) in repl_fn_stack_locals.iter().enumerate() {
// let target_index = call_stack.top().fp() + i; let target_index = call_stack.top().fp() + i;
// call_stack.top_mut().stack_mut()[target_index] = operand.clone(); call_stack.top_mut().stack_mut()[target_index] = operand.clone();
// } }
//
// let result = loop_instructions( let result = loop_instructions(
// &context, &context,
// &mut vec![Operand::Null; register_count], &mut vec![Operand::Null; register_count],
// &mut call_stack, &mut call_stack,
// ); );
//
// // copy the top frame's stack locals back to OUR stack locals for next iteration // 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()); repl_fn_stack_locals = std::mem::take(call_stack.top_mut().stack_mut());
//
// if let Some(value) = result { if let Some(value) = result {
// println!("{}", value); println!("{}", value);
// } }
//
// buffer.clear(); buffer.clear();
// } }
// } }
//
// fn prepare_scopes(symbol_table: &mut SymbolTable, fn_body_scope_id: &mut Option<usize>) -> usize { fn prepare_scopes(symbol_table: &mut SymbolTable, fn_body_scope_id: &mut Option<usize>) -> usize {
// if let Some(scope_id) = fn_body_scope_id { if let Some(scope_id) = fn_body_scope_id {
// symbol_table.change_scope(*scope_id); symbol_table.change_scope(*scope_id);
// *scope_id *scope_id
// } else { } else {
// symbol_table.push_module_scope("__repl_module"); symbol_table.push_module_scope("__repl_module");
// symbol_table.push_function_scope("__repl_fn"); symbol_table.push_function_scope("__repl_fn");
// let container_scope_id = symbol_table.push_block_scope("__repl_fn_body"); let container_scope_id = symbol_table.push_block_scope("__repl_fn_body");
// fn_body_scope_id.replace(container_scope_id); fn_body_scope_id.replace(container_scope_id);
// container_scope_id container_scope_id
// } }
// } }
//
// fn compile_expression( fn compile_expression(
// input: &str, input: &str,
// register_count: usize, register_count: usize,
// symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
// types_table: &mut TypesTable, types_table: &mut TypesTable,
// fn_body_scope_id: &mut Option<usize>, fn_body_scope_id: &mut Option<usize>,
// fn_local_variables: &HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>, fn_local_variables: &HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
// offset_counter: &mut OffsetCounter, offset_counter: &mut OffsetCounter,
// constants_table: &mut ConstantsTable, constants_table: &mut ConstantsTable,
// ) -> Result<Function, Vec<Diagnostic>> { ) -> Result<Function, Vec<Diagnostic>> {
// // // parse // // parse
// // let (mut expression, parse_diagnostics) = parse_expression(input); // let (mut expression, parse_diagnostics) = parse_expression(input);
// // if !parse_diagnostics.is_empty() { // if !parse_diagnostics.is_empty() {
// // return Err(parse_diagnostics); // return Err(parse_diagnostics);
// // } // }
// // //
// // // init scopes, if necessary // // init scopes, if necessary
// // let container_scope = prepare_scopes(symbol_table, fn_body_scope_id); // let container_scope = prepare_scopes(symbol_table, fn_body_scope_id);
// // //
// // // inner scopes // // inner scopes
// // expression.init_scopes(symbol_table, container_scope); // expression.init_scopes(symbol_table, container_scope);
// // //
// // // names // // names
// // let diagnostics = expression.check_static_fn_local_names(&symbol_table); // let diagnostics = expression.check_static_fn_local_names(&symbol_table);
// // if !diagnostics.is_empty() { // if !diagnostics.is_empty() {
// // return Err(diagnostics); // return Err(diagnostics);
// // } // }
// // //
// // // type check // // type check
// // expression.type_check(&symbol_table, types_table)?; // expression.type_check(&symbol_table, types_table)?;
// // //
// // // synthesize a function // // synthesize a function
// // // init ir_builder // // init ir_builder
// // let mut ir_builder = IrBuilder::new(); // let mut ir_builder = IrBuilder::new();
// // //
// // // copy all previous declared variables to here so we preserve their stack offsets // // copy all previous declared variables to here so we preserve their stack offsets
// // for (key, value) in fn_local_variables { // for (key, value) in fn_local_variables {
// // ir_builder // ir_builder
// // .local_variables_mut() // .local_variables_mut()
// // .insert(key.clone(), value.clone()); // .insert(key.clone(), value.clone());
// // } // }
// // //
// // let entry_block_id = ir_builder.new_block(); // let entry_block_id = ir_builder.new_block();
// // //
// // let maybe_ir_expression = // let maybe_ir_expression =
// // expression.to_ir_expression(&mut ir_builder, &symbol_table, &types_table); // expression.to_ir_expression(&mut ir_builder, &symbol_table, &types_table);
// // //
// // // if Some, return the value // // if Some, return the value
// // ir_builder // ir_builder
// // .current_block_mut() // .current_block_mut()
// // .add_statement(IrStatement::Return(IrReturn::new(maybe_ir_expression))); // .add_statement(IrStatement::Return(IrReturn::new(maybe_ir_expression)));
// // //
// // ir_builder.finish_block(); // ir_builder.finish_block();
// // let entry_block = ir_builder.get_block(entry_block_id); // let entry_block = ir_builder.get_block(entry_block_id);
// // //
// // let mut ir_function = IrFunction::new( // let mut ir_function = IrFunction::new(
// // "__repl".into(), // "__repl".into(),
// // vec![], // vec![],
// // expression.type_info(&symbol_table, &types_table), // expression.type_info(&symbol_table, &types_table),
// // entry_block.clone(), // entry_block.clone(),
// // ); // );
// // //
// // // spilled registers are put on the stack, so we need to add it to our stack size // // 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); // ir_function.assign_registers(register_count, offset_counter);
// // //
// // Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) // Ok(ir_function.assemble(offset_counter.get_count(), constants_table))
// todo!() todo!()
// } }
//
// fn compile_let_statement( fn compile_let_statement(
// input: &str, input: &str,
// register_count: usize, register_count: usize,
// symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
// body_scope_id: &mut Option<usize>, body_scope_id: &mut Option<usize>,
// offset_counter: &mut OffsetCounter, offset_counter: &mut OffsetCounter,
// local_variables: &mut HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>, local_variables: &mut HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
// types_table: &mut TypesTable, types_table: &mut TypesTable,
// constants_table: &mut ConstantsTable, constants_table: &mut ConstantsTable,
// ) -> Result<Function, Vec<Diagnostic>> { ) -> Result<Function, Vec<Diagnostic>> {
// // // parse // // parse
// // let (maybe_let_statement, parse_diagnostics) = parse_let_statement(input); // let (maybe_let_statement, parse_diagnostics) = parse_let_statement(input);
// // if !parse_diagnostics.is_empty() { // if !parse_diagnostics.is_empty() {
// // return Err(parse_diagnostics); // return Err(parse_diagnostics);
// // } // }
// // let mut let_statement = maybe_let_statement.unwrap(); // let mut let_statement = maybe_let_statement.unwrap();
// // //
// // // names // // names
// // let container_scope_id = prepare_scopes(symbol_table, body_scope_id); // let container_scope_id = prepare_scopes(symbol_table, body_scope_id);
// // //
// // let_statement.init_scopes(symbol_table, container_scope_id); // let_statement.init_scopes(symbol_table, container_scope_id);
// // //
// // let name_diagnostics = let_statement.analyze_static_fn_local_names(symbol_table); // let name_diagnostics = let_statement.analyze_static_fn_local_names(symbol_table);
// // if !name_diagnostics.is_empty() { // if !name_diagnostics.is_empty() {
// // return Err(name_diagnostics); // return Err(name_diagnostics);
// // } // }
// // //
// // // types // // types
// // let_statement.type_check(&symbol_table, types_table)?; // let_statement.type_check(&symbol_table, types_table)?;
// // //
// // // init the ir builder // // init the ir builder
// // let mut ir_builder = IrBuilder::new(); // let mut ir_builder = IrBuilder::new();
// // //
// // // put previous locals in ir_builder so expressions can find them // // put previous locals in ir_builder so expressions can find them
// // for (k, v) in local_variables.iter() { // for (k, v) in local_variables.iter() {
// // ir_builder // ir_builder
// // .local_variables_mut() // .local_variables_mut()
// // .insert(k.clone(), v.clone()); // .insert(k.clone(), v.clone());
// // } // }
// // //
// // // ir function // // ir function
// // let entry_block_id = ir_builder.new_block(); // let entry_block_id = ir_builder.new_block();
// // //
// // let destination_ir_variable = let_statement.to_repl_ir( // let destination_ir_variable = let_statement.to_repl_ir(
// // &mut ir_builder, // &mut ir_builder,
// // symbol_table, // symbol_table,
// // types_table, // types_table,
// // offset_counter.next() as isize, // offset_counter.next() as isize,
// // ); // put it on top // ); // put it on top
// // //
// // // Now that we've translated to ir, we can add the new local variable in the IrBuilder to our // // 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. // // record of them to be used for next loop iteration.
// // let variable_symbol = let_statement.get_destination_symbol(symbol_table); // let variable_symbol = let_statement.get_destination_symbol(symbol_table);
// // local_variables.insert(variable_symbol, destination_ir_variable); // local_variables.insert(variable_symbol, destination_ir_variable);
// // //
// // ir_builder.finish_block(); // ir_builder.finish_block();
// // let entry_block = ir_builder.get_block(entry_block_id); // let entry_block = ir_builder.get_block(entry_block_id);
// // let mut ir_function = IrFunction::new( // let mut ir_function = IrFunction::new(
// // "__repl".into(), // "__repl".into(),
// // vec![], // vec![],
// // &TypeInfo::Void, // &TypeInfo::Void,
// // entry_block.clone(), // entry_block.clone(),
// // ); // );
// // //
// // // By here, the variables should all be assigned to their new or existing stack slots. // // By here, the variables should all be assigned to their new or existing stack slots.
// // // Register allocation should only be required for temp variables. // // Register allocation should only be required for temp variables.
// // ir_function.assign_registers(register_count, offset_counter); // ir_function.assign_registers(register_count, offset_counter);
// // //
// // Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) // Ok(ir_function.assemble(offset_counter.get_count(), constants_table))
// todo!() todo!()
// } }
pub fn repl_2(read: &mut impl BufRead, register_count: usize) { pub fn repl_2(read: &mut impl BufRead, register_count: usize) {
let mut buffer = String::new(); let mut buffer = String::new();
let mut analysis_context = AnalysisContext::new(); let mut analysis_context = AnalysisContext::new();
let mut function_scope_id: Option<ScopeId> = None;
analysis_context.push_scope("__repl_root_scope"); let fqn: Rc<str> = Rc::from("repl");
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;
let fqn: Rc<str> = Rc::from("__repl");
let mut variable_locations = VariableLocations::new(); let mut variable_locations = VariableLocations::new();
let mut constants_table = ConstantsTable::new(); let mut constants_table = ConstantsTable::new();
@ -347,91 +342,6 @@ pub fn repl_2(read: &mut impl BufRead, register_count: usize) {
} }
}, },
}; };
match first_token.kind() {
TokenKind::Let => {
let compile_result = compile_let_statement_2(
input,
&mut analysis_context,
body_scope_id,
&fqn,
&mut variable_locations,
register_count,
&mut constants_table,
);
match compile_result {
Ok(function) => {
dvm_context
.functions_mut()
.insert(function.name_owned(), function);
}
Err(diagnostics) => {
for diagnostic in diagnostics {
eprintln!("{}", diagnostic.message());
}
buffer.clear();
continue 'repl;
}
}
}
_ => {
let compile_result = compile_expression_2(
input,
&mut analysis_context,
body_scope_id,
&fqn,
&mut variable_locations,
register_count,
&mut constants_table,
);
match compile_result {
Ok(function) => {
dvm_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() {
dvm_context.constants_mut().insert(
name.clone(),
Constant::String(StringConstant::new(name, content)),
);
}
let mut call_stack = CallStack::new();
prepare_for_instruction_loop(&dvm_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(
&dvm_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();
} }
} }
@ -461,27 +371,3 @@ fn compile_expression_2(
constants_table, constants_table,
) )
} }
fn compile_let_statement_2(
input: &str,
analysis_context: &mut AnalysisContext,
function_scope_id: ScopeId,
fqn: &Rc<str>,
variable_locations: &mut VariableLocations,
register_count: usize,
constants_table: &mut ConstantsTable,
) -> Result<Function, Diagnostics> {
let (maybe_let_statement, parse_diagnostics) = parse_let_statement(input);
if !parse_diagnostics.is_empty() {
return Err(parse_diagnostics);
}
compile_statement_to_synthetic_function(
&Statement::Let(maybe_let_statement.unwrap()),
analysis_context,
function_scope_id,
fqn.clone(),
variable_locations,
register_count,
constants_table,
)
}

View File

@ -1,7 +1,6 @@
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug)]
pub struct IrAllocate { pub struct IrAllocate {
class_fqn: Rc<str>, class_fqn: Rc<str>,
} }

View File

@ -3,7 +3,6 @@ use crate::ir::ir_variable::IrVariableId;
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
#[derive(Debug)]
pub struct IrAssign { pub struct IrAssign {
destination: IrVariableId, destination: IrVariableId,
initializer: Box<IrOperation>, initializer: Box<IrOperation>,

View File

@ -4,7 +4,6 @@ use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum IrBinaryOperator { pub enum IrBinaryOperator {
Multiply, Multiply,
Divide, Divide,
@ -18,7 +17,6 @@ pub enum IrBinaryOperator {
BitwiseOr, BitwiseOr,
} }
#[derive(Debug)]
pub struct IrBinaryOperation { pub struct IrBinaryOperation {
left: Box<IrExpression>, left: Box<IrExpression>,
right: Box<IrExpression>, right: Box<IrExpression>,

View File

@ -5,7 +5,6 @@ use std::collections::HashSet;
pub type IrBlockId = usize; pub type IrBlockId = usize;
#[derive(Debug)]
pub struct IrBlock { pub struct IrBlock {
id: IrBlockId, id: IrBlockId,
debug_label: String, debug_label: String,

View File

@ -5,7 +5,6 @@ use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug)]
pub struct IrCall { pub struct IrCall {
function_name: Rc<str>, function_name: Rc<str>,
arguments: Vec<IrExpression>, arguments: Vec<IrExpression>,

View File

@ -5,7 +5,6 @@ use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug)]
pub enum IrExpression { pub enum IrExpression {
Parameter(IrParameterId), Parameter(IrParameterId),
Variable(IrVariableId), Variable(IrVariableId),

View File

@ -9,7 +9,6 @@ use dvm_lib::vm::function::Function;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug)]
pub struct IrFunction { pub struct IrFunction {
fqn: Rc<str>, fqn: Rc<str>,
parameters: Vec<IrParameter>, parameters: Vec<IrParameter>,

View File

@ -4,7 +4,6 @@ use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub struct IrGetFieldRef { pub struct IrGetFieldRef {
self_variable_or_parameter: IrParameterOrVariable, self_variable_or_parameter: IrParameterOrVariable,
field_index: usize, field_index: usize,

View File

@ -4,7 +4,6 @@ use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub struct IrGetFieldRefMut { pub struct IrGetFieldRefMut {
self_variable_or_parameter: IrParameterOrVariable, self_variable_or_parameter: IrParameterOrVariable,
field_index: usize, field_index: usize,

View File

@ -10,7 +10,6 @@ use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum IrOperation { pub enum IrOperation {
GetFieldRef(IrGetFieldRef), GetFieldRef(IrGetFieldRef),
GetFieldRefMut(IrGetFieldRefMut), GetFieldRefMut(IrGetFieldRefMut),

View File

@ -5,7 +5,6 @@ use std::rc::Rc;
pub type IrParameterId = usize; pub type IrParameterId = usize;
#[derive(Debug)]
pub struct IrParameter { pub struct IrParameter {
name: Rc<str>, name: Rc<str>,
type_info: IrTypeInfo, type_info: IrTypeInfo,

View File

@ -3,7 +3,6 @@ use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub struct IrReadField { pub struct IrReadField {
field_ref_variable: IrVariableId, field_ref_variable: IrVariableId,
} }

View File

@ -4,7 +4,6 @@ use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub struct IrReturn { pub struct IrReturn {
value: Option<IrExpression>, value: Option<IrExpression>,
} }

View File

@ -4,7 +4,6 @@ use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub struct IrSetField { pub struct IrSetField {
field_ref_variable: IrVariableId, field_ref_variable: IrVariableId,
initializer: Box<IrExpression>, initializer: Box<IrExpression>,

View File

@ -6,7 +6,6 @@ use crate::ir::ir_variable::IrVariableId;
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::VrUser;
use std::collections::HashSet; use std::collections::HashSet;
#[derive(Debug)]
pub enum IrStatement { pub enum IrStatement {
Assign(IrAssign), Assign(IrAssign),
Call(IrCall), Call(IrCall),

View File

@ -4,7 +4,6 @@ use std::rc::Rc;
pub type IrVariableId = usize; pub type IrVariableId = usize;
#[derive(Debug)]
pub struct IrVariable { pub struct IrVariable {
name: Rc<str>, name: Rc<str>,
type_info: IrTypeInfo, type_info: IrTypeInfo,

View File

@ -85,7 +85,7 @@ pub fn compile_statement_to_synthetic_function(
register_count: usize, register_count: usize,
constants_table: &mut ConstantsTable, constants_table: &mut ConstantsTable,
) -> Result<Function, Diagnostics> { ) -> Result<Function, Diagnostics> {
let diagnostics = analyze_statement(statement, analysis_context); let diagnostics = analyze_statement(statement, analysis_context, function_scope_id);
if !diagnostics.is_empty() { if !diagnostics.is_empty() {
return Err(diagnostics); return Err(diagnostics);
} }

View File

@ -34,7 +34,6 @@ pub struct LowerToIrResult {
pub functions: Vec<IrFunction>, pub functions: Vec<IrFunction>,
} }
#[derive(Debug)]
struct LowerToIrContext<'a> { struct LowerToIrContext<'a> {
symbols: &'a [Symbol], symbols: &'a [Symbol],
nodes_to_symbols: &'a HashMap<NodeId, SymbolId>, nodes_to_symbols: &'a HashMap<NodeId, SymbolId>,
@ -105,7 +104,6 @@ pub fn lower_to_ir_synthetic_function(
) )
} }
#[derive(Debug)]
struct LowerToIrFunctionContext { struct LowerToIrFunctionContext {
blocks: Vec<IrBlock>, blocks: Vec<IrBlock>,
ir_variables: Vec<IrVariable>, ir_variables: Vec<IrVariable>,
@ -465,7 +463,6 @@ fn lower_expression_to_ir_expression(
{ {
IrExpression::Parameter(*rhs_ir_parameter_id) IrExpression::Parameter(*rhs_ir_parameter_id)
} else { } else {
println!("Dump:\n{:#?}\n{:#?}", ctx, fn_ctx);
panic!( panic!(
"Could not find parameter or variable for symbol_id {}", "Could not find parameter or variable for symbol_id {}",
rhs_symbol_id rhs_symbol_id

View File

@ -1,3 +1,4 @@
use crate::ast::NodeId;
use crate::ast::assign_statement::AssignStatement; use crate::ast::assign_statement::AssignStatement;
use crate::ast::binary_expression::BinaryExpression; use crate::ast::binary_expression::BinaryExpression;
use crate::ast::call::Call; use crate::ast::call::Call;
@ -10,69 +11,141 @@ use crate::ast::identifier::Identifier;
use crate::ast::let_statement::LetStatement; use crate::ast::let_statement::LetStatement;
use crate::ast::negative_expression::NegativeExpression; use crate::ast::negative_expression::NegativeExpression;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::semantic_analysis::AnalysisContext; use crate::semantic_analysis::scope::{Scope, ScopeId};
use std::collections::HashMap;
struct ScopeCollectionContext<'a> {
scopes: &'a mut Vec<Scope>,
nodes_to_scopes: &'a mut HashMap<NodeId, ScopeId>,
current_scope_id: Option<ScopeId>,
}
impl<'a> ScopeCollectionContext<'a> {
pub fn new(
scopes: &'a mut Vec<Scope>,
nodes_to_scopes: &'a mut HashMap<NodeId, ScopeId>,
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<ScopeId> {
self.current_scope_id
}
pub fn nodes_to_scopes_mut(&mut self) -> &mut HashMap<NodeId, ScopeId> {
&mut self.nodes_to_scopes
}
}
/// N.b.: the parent_scope_id refers to the **parent** of the compilation unit scope, and must /// 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`. /// be a valid (non-panicking) index of `scopes`.
pub fn collect_scopes_compilation_unit( pub fn collect_scopes_in_compilation_unit(
compilation_unit: &CompilationUnit, compilation_unit: &CompilationUnit,
ctx: &mut AnalysisContext, scopes: &mut Vec<Scope>,
nodes_to_scopes: &mut HashMap<NodeId, ScopeId>,
parent_scope_id: ScopeId,
) { ) {
ctx.push_scope("compilation_unit"); let mut ctx = ScopeCollectionContext::new(scopes, nodes_to_scopes, parent_scope_id);
ctx.down_scope(); // compilation unit scope
for function in compilation_unit.functions() { for function in compilation_unit.functions() {
collect_scopes_function(function, ctx); collect_scopes_function(function, &mut ctx);
} }
for extern_function in compilation_unit.extern_functions() { for extern_function in compilation_unit.extern_functions() {
collect_scopes_extern_function(extern_function, ctx); collect_scopes_extern_function(extern_function, &mut ctx);
} }
ctx.pop_scope(); // compilation_unit ctx.up_scope(); // compilation unit scope
} }
fn collect_scopes_function(function: &Function, ctx: &mut AnalysisContext) { /// N.b. the parent_scope_id refers to the scope of the statement and must be a valid
// associate function with current scope /// (non-panicking) index of `scopes`.
ctx.associate_node_to_current_scope(function.node_id()); pub fn collect_scopes_in_statement(
statement: &Statement,
scopes: &mut Vec<Scope>,
nodes_to_scopes: &mut HashMap<NodeId, ScopeId>,
parent_scope_id: ScopeId,
) {
let mut ctx = ScopeCollectionContext::new(scopes, nodes_to_scopes, parent_scope_id);
collect_scopes_statement(statement, &mut ctx);
}
// push function params scope fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext) {
ctx.push_scope("function_parameters"); // 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();
// save return-type and parameters' scope id // save return-type and parameters' scope id
for parameter in function.parameters() { for parameter in function.parameters() {
ctx.associate_node_to_current_scope(parameter.node_id()); ctx.nodes_to_scopes_mut()
.insert(parameter.node_id(), function_scope_id);
} }
match function.return_type() { match function.return_type() {
None => { None => {
// no-op // no-op
} }
Some(type_use) => { Some(type_use) => {
ctx.associate_node_to_current_scope(type_use.node_id()); ctx.nodes_to_scopes_mut()
.insert(type_use.node_id(), function_scope_id);
} }
} }
// push block scope for body // push block scope for body
ctx.push_scope("function_body"); ctx.down_scope();
for statement in function.statements() { for statement in function.statements() {
collect_scopes_statement(statement, ctx); collect_scopes_statement(statement, ctx);
} }
ctx.pop_scope(); // body ctx.up_scope(); // block
ctx.pop_scope(); // parameters ctx.up_scope(); // function
} }
fn collect_scopes_extern_function(extern_function: &ExternFunction, ctx: &mut AnalysisContext) { fn collect_scopes_extern_function(
// associate extern_function with current scope extern_function: &ExternFunction,
ctx.associate_node_to_current_scope(extern_function.node_id()); 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();
// parameters scope
ctx.push_scope("extern_function_parameters");
for parameter in extern_function.parameters() { for parameter in extern_function.parameters() {
ctx.associate_node_to_current_scope(parameter.node_id()); ctx.nodes_to_scopes_mut()
.insert(parameter.node_id(), function_scope_id);
} }
ctx.associate_node_to_current_scope(extern_function.return_type().node_id()); ctx.nodes_to_scopes_mut()
ctx.pop_scope(); // parameters .insert(extern_function.return_type().node_id(), function_scope_id);
ctx.up_scope();
} }
pub fn collect_scopes_statement(statement: &Statement, ctx: &mut AnalysisContext) { fn collect_scopes_statement(statement: &Statement, ctx: &mut ScopeCollectionContext) {
match statement { match statement {
Statement::Let(let_statement) => collect_scopes_let_statement(let_statement, ctx), Statement::Let(let_statement) => collect_scopes_let_statement(let_statement, ctx),
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {
@ -84,24 +157,29 @@ pub fn collect_scopes_statement(statement: &Statement, ctx: &mut AnalysisContext
} }
} }
fn collect_scopes_let_statement(let_statement: &LetStatement, ctx: &mut AnalysisContext) { fn collect_scopes_let_statement(let_statement: &LetStatement, ctx: &mut ScopeCollectionContext) {
collect_scopes_expression(let_statement.initializer(), ctx); collect_scopes_expression(let_statement.initializer(), ctx);
ctx.associate_node_to_current_scope(let_statement.node_id()); let current_scope_id = ctx.current_scope_id().unwrap();
ctx.nodes_to_scopes_mut()
.insert(let_statement.node_id(), current_scope_id);
} }
fn collect_scopes_expression_statement( fn collect_scopes_expression_statement(
expression_statement: &ExpressionStatement, expression_statement: &ExpressionStatement,
ctx: &mut AnalysisContext, ctx: &mut ScopeCollectionContext,
) { ) {
collect_scopes_expression(expression_statement.expression(), ctx); collect_scopes_expression(expression_statement.expression(), ctx);
} }
fn collect_scopes_assign_statement(assign_statement: &AssignStatement, ctx: &mut AnalysisContext) { fn collect_scopes_assign_statement(
assign_statement: &AssignStatement,
ctx: &mut ScopeCollectionContext,
) {
collect_scopes_expression(assign_statement.value(), ctx); collect_scopes_expression(assign_statement.value(), ctx);
collect_scopes_expression(assign_statement.destination(), ctx); collect_scopes_expression(assign_statement.destination(), ctx);
} }
fn collect_scopes_expression(expression: &Expression, ctx: &mut AnalysisContext) { fn collect_scopes_expression(expression: &Expression, ctx: &mut ScopeCollectionContext) {
match expression { match expression {
Expression::Binary(binary_expression) => { Expression::Binary(binary_expression) => {
collect_scopes_binary_expression(binary_expression, ctx); collect_scopes_binary_expression(binary_expression, ctx);
@ -123,7 +201,7 @@ fn collect_scopes_expression(expression: &Expression, ctx: &mut AnalysisContext)
fn collect_scopes_binary_expression( fn collect_scopes_binary_expression(
binary_expression: &BinaryExpression, binary_expression: &BinaryExpression,
ctx: &mut AnalysisContext, ctx: &mut ScopeCollectionContext,
) { ) {
collect_scopes_expression(binary_expression.lhs(), ctx); collect_scopes_expression(binary_expression.lhs(), ctx);
collect_scopes_expression(binary_expression.rhs(), ctx); collect_scopes_expression(binary_expression.rhs(), ctx);
@ -131,18 +209,20 @@ fn collect_scopes_binary_expression(
fn collect_scopes_negative_expression( fn collect_scopes_negative_expression(
negative_expression: &NegativeExpression, negative_expression: &NegativeExpression,
ctx: &mut AnalysisContext, ctx: &mut ScopeCollectionContext,
) { ) {
collect_scopes_expression(negative_expression.operand(), ctx); collect_scopes_expression(negative_expression.operand(), ctx);
} }
fn collect_scopes_call(call: &Call, ctx: &mut AnalysisContext) { fn collect_scopes_call(call: &Call, ctx: &mut ScopeCollectionContext) {
for argument in call.arguments() { for argument in call.arguments() {
collect_scopes_expression(argument, ctx); collect_scopes_expression(argument, ctx);
} }
collect_scopes_expression(call.callee(), ctx); collect_scopes_expression(call.callee(), ctx);
} }
fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) { fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut ScopeCollectionContext) {
ctx.associate_node_to_current_scope(identifier.node_id()); 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);
} }

View File

@ -1,36 +1,107 @@
use crate::ast::NodeId;
use crate::ast::compilation_unit::CompilationUnit; use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::extern_function::ExternFunction; use crate::ast::extern_function::ExternFunction;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::ast::parameter::Parameter; use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement;
use crate::diagnostic::Diagnostics; use crate::diagnostic::Diagnostics;
use crate::semantic_analysis::AnalysisContext; use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared;
use crate::semantic_analysis::scope::{Scope, ScopeId};
use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol}; use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol};
use std::collections::HashMap;
use std::rc::Rc;
pub struct SymbolCollectionResult(pub Diagnostics); pub struct SymbolCollectionResult(pub Diagnostics);
pub fn collect_symbols_compilation_unit( struct SymbolCollectionContext<'a> {
scopes: &'a mut Vec<Scope>,
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
symbols: &'a mut Vec<Symbol>,
fqn_context: Vec<Rc<str>>,
diagnostics: Diagnostics,
}
impl<'a> SymbolCollectionContext<'a> {
pub fn new(
scopes: &'a mut Vec<Scope>,
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
symbols: &'a mut Vec<Symbol>,
) -> 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.symbols().get(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.symbols_mut().insert(declared_name, symbol_id);
}
pub fn symbols_mut(&mut self) -> &mut Vec<Symbol> {
&mut self.symbols
}
pub fn diagnostics_mut(&mut self) -> &mut Diagnostics {
&mut self.diagnostics
}
fn join_fqn_parts(parts: &[Rc<str>]) -> String {
parts.join("::")
}
pub fn get_fqn_base(&self) -> String {
Self::join_fqn_parts(&self.fqn_context)
}
pub fn resolve_fqn(&self, suffix: &Rc<str>) -> 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(
compilation_unit: &CompilationUnit, compilation_unit: &CompilationUnit,
analysis_context: &mut AnalysisContext, scopes: &mut Vec<Scope>,
nodes_to_scopes: &HashMap<NodeId, ScopeId>,
symbols: &mut Vec<Symbol>,
) -> SymbolCollectionResult { ) -> SymbolCollectionResult {
let mut diagnostics = Diagnostics::new(); let mut ctx = SymbolCollectionContext::new(scopes, nodes_to_scopes, symbols);
for function in compilation_unit.functions() { for function in compilation_unit.functions() {
collect_symbols_function(function, analysis_context, &mut diagnostics); collect_symbols_function(function, &mut ctx);
} }
for extern_function in compilation_unit.extern_functions() { for extern_function in compilation_unit.extern_functions() {
collect_symbols_extern_function(extern_function, analysis_context, &mut diagnostics); collect_symbols_extern_function(extern_function, &mut ctx);
} }
SymbolCollectionResult(diagnostics) SymbolCollectionResult(ctx.diagnostics)
} }
fn collect_symbols_function( fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionContext) {
function: &Function, let fqn = ctx.resolve_fqn(&function.declared_name_owned()).into();
ctx: &mut AnalysisContext,
diagnostics: &mut Diagnostics,
) {
let fqn = ctx.resolve_fqn(&function.declared_name()).into();
// function itself // function itself
let function_symbol = Symbol::Function(FunctionSymbol::new( let function_symbol = Symbol::Function(FunctionSymbol::new(
@ -41,15 +112,18 @@ fn collect_symbols_function(
)); ));
// insert // insert
if let Err(diagnostic) = if let Some(already_declared) =
ctx.try_insert_associate_symbol_in_node_scope(function_symbol, function.node_id()) ctx.find_symbol_in_scope_for(function.declared_name(), function.node_id())
{ {
diagnostics.push(diagnostic); let diagnostic = symbol_already_declared(already_declared, &function_symbol);
ctx.diagnostics_mut().push(diagnostic);
} else {
ctx.insert_symbol(function_symbol, function.node_id());
} }
// parameters // parameters
for parameter in function.parameters() { for parameter in function.parameters() {
collect_symbols_parameter(parameter, ctx, diagnostics); collect_symbols_parameter(parameter, ctx);
} }
// n.b. do not do statements yet, because variables are declared and resolved in the resolution pass // n.b. do not do statements yet, because variables are declared and resolved in the resolution pass
@ -57,8 +131,7 @@ fn collect_symbols_function(
fn collect_symbols_extern_function( fn collect_symbols_extern_function(
extern_function: &ExternFunction, extern_function: &ExternFunction,
ctx: &mut AnalysisContext, ctx: &mut SymbolCollectionContext,
diagnostics: &mut Diagnostics,
) { ) {
let fqn = ctx let fqn = ctx
.resolve_fqn(&extern_function.declared_name_owned()) .resolve_fqn(&extern_function.declared_name_owned())
@ -73,30 +146,25 @@ fn collect_symbols_extern_function(
)); ));
// insert function symbol // insert function symbol
if let Err(diagnostic) = if let Some(already_declared) =
ctx.try_insert_associate_symbol_in_node_scope(function_symbol, extern_function.node_id()) ctx.find_symbol_in_scope_for(extern_function.declared_name(), extern_function.node_id())
{ {
diagnostics.push(diagnostic); let diagnostic = symbol_already_declared(already_declared, &function_symbol);
ctx.diagnostics_mut().push(diagnostic);
} else {
ctx.insert_symbol(function_symbol, extern_function.node_id());
} }
// parameters // parameters
for parameter in extern_function.parameters() { for parameter in extern_function.parameters() {
collect_symbols_parameter(parameter, ctx, diagnostics); collect_symbols_parameter(parameter, ctx);
} }
} }
fn collect_symbols_parameter( fn collect_symbols_parameter(parameter: &Parameter, ctx: &mut SymbolCollectionContext) {
parameter: &Parameter, let parameter_symbol = ParameterSymbol::new(
ctx: &mut AnalysisContext,
diagnostics: &mut Diagnostics,
) {
let parameter_symbol = Symbol::Parameter(ParameterSymbol::new(
parameter.declared_name_owned(), parameter.declared_name_owned(),
Some(parameter.declared_name_source_range()), Some(parameter.declared_name_source_range()),
)); );
if let Err(diagnostic) = ctx.insert_symbol(Symbol::Parameter(parameter_symbol), parameter.node_id());
ctx.try_insert_associate_symbol_in_node_scope(parameter_symbol, parameter.node_id())
{
diagnostics.push(diagnostic);
}
} }

View File

@ -1,15 +1,14 @@
use crate::ast::NodeId; use crate::ast::NodeId;
use crate::ast::compilation_unit::CompilationUnit; use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::diagnostic::{Diagnostic, Diagnostics}; use crate::diagnostic::Diagnostics;
use crate::semantic_analysis::collect_scopes::{ use crate::semantic_analysis::collect_scopes::{
collect_scopes_compilation_unit, collect_scopes_statement, collect_scopes_in_compilation_unit, collect_scopes_in_statement,
}; };
use crate::semantic_analysis::collect_symbols::{ use crate::semantic_analysis::collect_symbols::{
SymbolCollectionResult, collect_symbols_compilation_unit, SymbolCollectionResult, collect_symbols_in_compilation_unit,
}; };
use crate::semantic_analysis::collect_types::collect_types; use crate::semantic_analysis::collect_types::collect_types;
use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared;
use crate::semantic_analysis::resolve_names::{ use crate::semantic_analysis::resolve_names::{
NameResolutionResult, resolve_names_in_compilation_unit, resolve_names_in_statement, NameResolutionResult, resolve_names_in_compilation_unit, resolve_names_in_statement,
}; };
@ -20,7 +19,6 @@ use crate::semantic_analysis::scope::{Scope, ScopeId};
use crate::semantic_analysis::symbol::{Symbol, SymbolId}; use crate::semantic_analysis::symbol::{Symbol, SymbolId};
use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
mod collect_scopes; mod collect_scopes;
mod collect_symbols; mod collect_symbols;
@ -33,10 +31,8 @@ pub mod symbol;
pub mod type_info; pub mod type_info;
pub struct AnalysisContext { pub struct AnalysisContext {
fqn_stack: Vec<Rc<str>>,
root_scope_id: ScopeId, root_scope_id: ScopeId,
scopes: Vec<Scope>, scopes: Vec<Scope>,
current_scope_id: Option<ScopeId>,
nodes_to_scopes: HashMap<NodeId, ScopeId>, nodes_to_scopes: HashMap<NodeId, ScopeId>,
symbols: Vec<Symbol>, symbols: Vec<Symbol>,
nodes_to_symbols: HashMap<NodeId, SymbolId>, nodes_to_symbols: HashMap<NodeId, SymbolId>,
@ -48,11 +44,9 @@ pub struct AnalysisContext {
impl AnalysisContext { impl AnalysisContext {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
fqn_stack: Vec::new(),
root_scope_id: 0, // pushed in vec! below
scopes: vec![Scope::new(None)], scopes: vec![Scope::new(None)],
current_scope_id: None,
nodes_to_scopes: HashMap::new(), nodes_to_scopes: HashMap::new(),
root_scope_id: 0, // already pushed in vec! above
symbols: Vec::new(), symbols: Vec::new(),
nodes_to_symbols: HashMap::new(), nodes_to_symbols: HashMap::new(),
type_infos: Vec::new(), type_infos: Vec::new(),
@ -61,26 +55,6 @@ 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
}
pub fn scopes(&self) -> &[Scope] {
&self.scopes
}
pub fn scopes_mut(&mut self) -> &mut Vec<Scope> {
&mut self.scopes
}
pub fn symbols(&self) -> &[Symbol] { pub fn symbols(&self) -> &[Symbol] {
&self.symbols &self.symbols
} }
@ -100,125 +74,6 @@ impl AnalysisContext {
pub fn nodes_to_type_infos(&self) -> &HashMap<NodeId, TypeInfoId> { pub fn nodes_to_type_infos(&self) -> &HashMap<NodeId, TypeInfoId> {
&self.nodes_to_type_infos &self.nodes_to_type_infos
} }
pub fn push_fqn_part(&mut self, part: Rc<str>) {
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( pub fn analyze_compilation_unit(
@ -227,10 +82,20 @@ pub fn analyze_compilation_unit(
) -> Diagnostics { ) -> Diagnostics {
let mut diagnostics = Diagnostics::new(); let mut diagnostics = Diagnostics::new();
collect_scopes_compilation_unit(compilation_unit, ctx); collect_scopes_in_compilation_unit(
compilation_unit,
&mut ctx.scopes,
&mut ctx.nodes_to_scopes,
ctx.root_scope_id,
);
let SymbolCollectionResult(mut collect_symbols_diagnostics) = let SymbolCollectionResult(mut collect_symbols_diagnostics) =
collect_symbols_compilation_unit(compilation_unit, ctx); collect_symbols_in_compilation_unit(
compilation_unit,
&mut ctx.scopes,
&ctx.nodes_to_scopes,
&mut ctx.symbols,
);
diagnostics.append(&mut collect_symbols_diagnostics); diagnostics.append(&mut collect_symbols_diagnostics);
let NameResolutionResult(mut resolve_names_diagnostics) = resolve_names_in_compilation_unit( let NameResolutionResult(mut resolve_names_diagnostics) = resolve_names_in_compilation_unit(
@ -261,10 +126,19 @@ pub fn analyze_compilation_unit(
diagnostics diagnostics
} }
pub fn analyze_statement(statement: &Statement, ctx: &mut AnalysisContext) -> Diagnostics { pub fn analyze_statement(
statement: &Statement,
ctx: &mut AnalysisContext,
function_scope_id: ScopeId,
) -> Diagnostics {
let mut diagnostics = Diagnostics::new(); let mut diagnostics = Diagnostics::new();
collect_scopes_statement(statement, ctx); 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 // collect symbols for a statement is currently a no-op, so not needed here

View File

@ -130,12 +130,7 @@ fn resolve_names_function(function: &Function, ctx: &mut NameResolutionContext)
// point this function's node_id to the symbol_id for the function symbol // 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_id = ctx.nodes_to_scopes[&function.node_id()];
let scope = &ctx.scopes[scope_id]; let scope = &ctx.scopes[scope_id];
let symbol_id = scope let symbol_id = scope.symbols()[function.declared_name()];
.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.nodes_to_symbols.insert(function.node_id(), symbol_id);
} }
@ -150,12 +145,7 @@ fn resolve_names_extern_function(
// point this extern function's node_id to the symbol_id for this function symbol // 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_id = ctx.nodes_to_scopes[&extern_function.node_id()];
let scope = &ctx.scopes[scope_id]; let scope = &ctx.scopes[scope_id];
let symbol_id = scope let symbol_id = scope.symbols()[extern_function.declared_name()];
.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 ctx.nodes_to_symbols
.insert(extern_function.node_id(), symbol_id); .insert(extern_function.node_id(), symbol_id);
} }
@ -164,12 +154,7 @@ fn resolve_names_parameter(parameter: &Parameter, ctx: &mut NameResolutionContex
// point this parameter's node_id to the symbol_id for the parameter symbol // point this parameter's node_id to the symbol_id for the parameter symbol
let scope_id = ctx.nodes_to_scopes[&parameter.node_id()]; let scope_id = ctx.nodes_to_scopes[&parameter.node_id()];
let scope = &ctx.scopes[scope_id]; let scope = &ctx.scopes[scope_id];
let symbol_id = scope let symbol_id = scope.symbols()[parameter.declared_name()];
.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); ctx.nodes_to_symbols.insert(parameter.node_id(), symbol_id);
} }
@ -216,7 +201,9 @@ fn resolve_names_let_statement(
ctx.symbols_mut().push(symbol); ctx.symbols_mut().push(symbol);
let symbol_id = ctx.symbols().len() - 1; let symbol_id = ctx.symbols().len() - 1;
let scope = &mut ctx.scopes_mut()[scope_id]; let scope = &mut ctx.scopes_mut()[scope_id];
scope.insert_symbol(let_statement.declared_name_owned(), symbol_id); scope
.symbols_mut()
.insert(let_statement.declared_name_owned(), symbol_id);
// associate the let statement with the symbol id // associate the let statement with the symbol id
ctx.nodes_to_symbols ctx.nodes_to_symbols
.insert(let_statement.node_id(), symbol_id); .insert(let_statement.node_id(), symbol_id);
@ -314,7 +301,7 @@ fn resolve_names_identifier(
let mut maybe_scope = Some(&ctx.scopes()[scope_id]); let mut maybe_scope = Some(&ctx.scopes()[scope_id]);
let mut found_symbol_id: Option<SymbolId> = None; let mut found_symbol_id: Option<SymbolId> = None;
while let Some(scope) = maybe_scope { 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 let maybe_symbol_id = scope.symbols().get(identifier.name()).cloned(); // cloned because ctx cannot be borrowed both immutably and mutably
match maybe_symbol_id { match maybe_symbol_id {
None => { None => {
maybe_scope = match scope.parent_id() { maybe_scope = match scope.parent_id() {

View File

@ -21,11 +21,11 @@ impl Scope {
self.parent_id self.parent_id
} }
pub fn insert_symbol(&mut self, declared_name: Rc<str>, symbol_id: SymbolId) { pub fn symbols(&self) -> &HashMap<Rc<str>, SymbolId> {
self.symbols.insert(declared_name, symbol_id); &self.symbols
} }
pub fn get_symbol_id(&self, declared_name: &str) -> Option<SymbolId> { pub fn symbols_mut(&mut self) -> &mut HashMap<Rc<str>, SymbolId> {
self.symbols.get(declared_name).cloned() &mut self.symbols
} }
} }

View File

@ -3,7 +3,6 @@ use std::rc::Rc;
pub type SymbolId = usize; pub type SymbolId = usize;
#[derive(Debug)]
pub enum Symbol { pub enum Symbol {
Function(FunctionSymbol), Function(FunctionSymbol),
Parameter(ParameterSymbol), Parameter(ParameterSymbol),
@ -36,7 +35,6 @@ impl Symbol {
} }
} }
#[derive(Debug)]
pub struct FunctionSymbol { pub struct FunctionSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: Option<SourceRange>, declared_name_source_range: Option<SourceRange>,
@ -84,7 +82,6 @@ impl FunctionSymbol {
} }
} }
#[derive(Debug)]
pub struct ParameterSymbol { pub struct ParameterSymbol {
name: Rc<str>, name: Rc<str>,
source_range: Option<SourceRange>, source_range: Option<SourceRange>,
@ -108,7 +105,6 @@ impl ParameterSymbol {
} }
} }
#[derive(Debug)]
pub struct VariableSymbol { pub struct VariableSymbol {
name: Rc<str>, name: Rc<str>,
source_range: Option<SourceRange>, source_range: Option<SourceRange>,

View File

@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
pub type TypeInfoId = usize; pub type TypeInfoId = usize;
#[derive(Clone, Debug)] #[derive(Clone)]
pub enum TypeInfo { pub enum TypeInfo {
Any, Any,
Function(FunctionTypeInfo), Function(FunctionTypeInfo),
@ -31,7 +31,7 @@ impl Display for TypeInfo {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct FunctionTypeInfo { pub struct FunctionTypeInfo {
parameter_type_info_ids: Vec<TypeInfoId>, parameter_type_info_ids: Vec<TypeInfoId>,
return_type_info_id: TypeInfoId, return_type_info_id: TypeInfoId,