Work on better AnalysisContext abstractions.
This commit is contained in:
parent
57abc03145
commit
c54db2d70d
@ -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;
|
||||
|
||||
576
dm/src/repl.rs
576
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<usize> = 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<Operand> = 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>) -> 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<usize>,
|
||||
fn_local_variables: &HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
|
||||
offset_counter: &mut OffsetCounter,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) -> Result<Function, Vec<Diagnostic>> {
|
||||
// // 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<usize>,
|
||||
offset_counter: &mut OffsetCounter,
|
||||
local_variables: &mut HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
|
||||
types_table: &mut TypesTable,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) -> Result<Function, Vec<Diagnostic>> {
|
||||
// // 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<usize> = 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<Operand> = 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>) -> 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<usize>,
|
||||
// fn_local_variables: &HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
|
||||
// offset_counter: &mut OffsetCounter,
|
||||
// constants_table: &mut ConstantsTable,
|
||||
// ) -> Result<Function, Vec<Diagnostic>> {
|
||||
// // // 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<usize>,
|
||||
// offset_counter: &mut OffsetCounter,
|
||||
// local_variables: &mut HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
|
||||
// types_table: &mut TypesTable,
|
||||
// constants_table: &mut ConstantsTable,
|
||||
// ) -> Result<Function, Vec<Diagnostic>> {
|
||||
// // // 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()
|
||||
|
||||
@ -85,7 +85,7 @@ pub fn compile_statement_to_synthetic_function(
|
||||
register_count: usize,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) -> Result<Function, Diagnostics> {
|
||||
let diagnostics = analyze_statement(statement, analysis_context, function_scope_id);
|
||||
let diagnostics = analyze_statement(statement, analysis_context);
|
||||
if !diagnostics.is_empty() {
|
||||
return Err(diagnostics);
|
||||
}
|
||||
|
||||
@ -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<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
|
||||
}
|
||||
}
|
||||
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<Scope>,
|
||||
nodes_to_scopes: &mut HashMap<NodeId, ScopeId>,
|
||||
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<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);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
@ -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<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.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<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(
|
||||
pub fn collect_symbols_compilation_unit(
|
||||
compilation_unit: &CompilationUnit,
|
||||
scopes: &mut Vec<Scope>,
|
||||
nodes_to_scopes: &HashMap<NodeId, ScopeId>,
|
||||
symbols: &mut Vec<Symbol>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Rc<str>>,
|
||||
root_scope_id: ScopeId,
|
||||
scopes: Vec<Scope>,
|
||||
current_scope_id: Option<ScopeId>,
|
||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||
symbols: Vec<Symbol>,
|
||||
nodes_to_symbols: HashMap<NodeId, SymbolId>,
|
||||
@ -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<NodeId, TypeInfoId> {
|
||||
&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(
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user