diff --git a/dm/src/main.rs b/dm/src/main.rs index 700f8ab..c99038c 100644 --- a/dm/src/main.rs +++ b/dm/src/main.rs @@ -4,6 +4,7 @@ mod run; use crate::repl::repl; use crate::run::compile_and_run_script; use clap::{Parser, Subcommand}; +use std::io; use std::path::PathBuf; #[derive(Debug, Parser)] @@ -23,29 +24,27 @@ enum SubCommand { #[arg(long)] show_ir: bool, - - #[arg(long, default_value = "8")] - register_count: usize, - }, - Repl { - #[arg(long, default_value = "8")] - register_count: usize, }, + Repl, } fn main() { + let register_count = std::env::var("DVM_REGISTER_COUNT") + .map(|v| v.parse::()) + .unwrap_or(Ok(8)) + .unwrap(); + let args = Cli::parse(); match &args.sub_command { SubCommand::Run { script, show_asm, show_ir, - register_count, } => { - compile_and_run_script(script, *show_asm, *show_ir, *register_count); + compile_and_run_script(script, *show_asm, *show_ir, register_count); } - SubCommand::Repl { register_count } => { - repl(*register_count); + SubCommand::Repl => { + repl(&mut io::stdin().lock(), register_count); } } } diff --git a/dm/src/repl.rs b/dm/src/repl.rs index c09acb9..27f109d 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -4,28 +4,44 @@ use dmc_lib::diagnostic::Diagnostic; use dmc_lib::ir::ir_function::IrFunction; use dmc_lib::ir::ir_return::IrReturn; use dmc_lib::ir::ir_statement::IrStatement; +use dmc_lib::ir::ir_variable::IrVariable; use dmc_lib::lexer::Lexer; -use dmc_lib::parser::parse_expression; +use dmc_lib::offset_counter::OffsetCounter; +use dmc_lib::parser::{parse_expression, parse_let_statement}; +use dmc_lib::symbol::variable_symbol::VariableSymbol; use dmc_lib::symbol_table::SymbolTable; use dmc_lib::token::TokenKind; +use dmc_lib::type_info::TypeInfo; use dmc_lib::types_table::TypesTable; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; use dvm_lib::vm::operand::Operand; -use dvm_lib::vm::{CallStack, DvmContext, call}; +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::Write; +use std::io::{BufRead, Write}; +use std::rc::Rc; -pub fn repl(register_count: usize) { +pub fn repl(read: &mut impl BufRead, register_count: usize) { let mut buffer = String::new(); + let mut symbol_table = SymbolTable::new(); + let mut types_table = TypesTable::new(); + + let mut repl_fn_body_scope_id: Option = None; + let mut repl_fn_offset_counter = OffsetCounter::new(); + let mut repl_fn_local_variables = HashMap::new(); + let mut constants_table = ConstantsTable::new(); let mut context = DvmContext::new(); + let mut repl_fn_stack_locals: Vec = vec![]; + 'repl: loop { print!("> "); io::stdout().flush().unwrap(); - io::stdin().read_line(&mut buffer).unwrap(); + read.read_line(&mut buffer).unwrap(); let input = buffer.trim(); if input.is_empty() { buffer.clear(); @@ -52,9 +68,40 @@ pub fn repl(register_count: usize) { todo!("Parse functions in repl") } TokenKind::Let => { - todo!("Parse let statements in repl") + 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 constants_table) { + _ => 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() @@ -63,9 +110,9 @@ pub fn repl(register_count: usize) { Err(diagnostics) => { for diagnostic in &diagnostics { eprintln!("{}", diagnostic.message()); - buffer.clear(); - continue 'repl; } + buffer.clear(); + continue 'repl; } }, } @@ -77,10 +124,25 @@ pub fn repl(register_count: usize) { ); } - let mut registers = vec![Operand::Null; register_count]; 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()); - let result = call(&context, &mut registers, &mut call_stack, "__repl", &[]); if let Some(value) = result { println!("{}", value); } @@ -89,35 +151,58 @@ pub fn repl(register_count: usize) { } } +fn prepare_scopes(symbol_table: &mut SymbolTable, fn_body_scope_id: &mut Option) -> usize { + if let Some(scope_id) = fn_body_scope_id { + symbol_table.change_scope(*scope_id); + *scope_id + } else { + symbol_table.push_module_scope("__repl_module"); + symbol_table.push_function_scope("__repl_fn"); + let container_scope_id = symbol_table.push_block_scope("__repl_fn_body"); + fn_body_scope_id.replace(container_scope_id); + container_scope_id + } +} + fn compile_expression( input: &str, register_count: usize, + symbol_table: &mut SymbolTable, + types_table: &mut TypesTable, + fn_body_scope_id: &mut Option, + fn_local_variables: &HashMap, Rc>>, + offset_counter: &mut OffsetCounter, constants_table: &mut ConstantsTable, ) -> Result> { - let parse_result = parse_expression(input); - let mut expression = parse_result?; + // parse + let mut expression = parse_expression(input)?; - let mut symbol_table = SymbolTable::new(); - let mut types_table = TypesTable::new(); + // init scopes, if necessary + let container_scope = prepare_scopes(symbol_table, fn_body_scope_id); - // "fake" scopes - symbol_table.push_module_scope("__repl_module"); - let function_scope_id = symbol_table.push_function_scope("__repl_function"); - - expression.init_scopes(&mut symbol_table, function_scope_id); - - symbol_table.pop_scope(); // function - symbol_table.pop_scope(); // module + // 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); } - expression.type_check(&symbol_table, &mut types_table)?; + // 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 = @@ -138,6 +223,75 @@ fn compile_expression( entry_block.clone(), ); - let (_, stack_size) = ir_function.assign_registers(register_count); - Ok(ir_function.assemble(stack_size, constants_table)) + // 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)) +} + +fn compile_let_statement( + input: &str, + register_count: usize, + symbol_table: &mut SymbolTable, + body_scope_id: &mut Option, + offset_counter: &mut OffsetCounter, + local_variables: &mut HashMap, Rc>>, + types_table: &mut TypesTable, + constants_table: &mut ConstantsTable, +) -> Result> { + // parse + let mut let_statement = parse_let_statement(input)?; + + // 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)) } diff --git a/dm/src/run.rs b/dm/src/run.rs index 4079786..5a3fcc7 100644 --- a/dm/src/run.rs +++ b/dm/src/run.rs @@ -6,13 +6,13 @@ use dm_std_lib::add_all_std_core; use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::Diagnostic; use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types}; +use dmc_lib::offset_counter::OffsetCounter; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; 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, call}; +use dvm_lib::vm::{DvmContext, call}; use std::path::PathBuf; use std::rc::Rc; @@ -67,8 +67,9 @@ fn run( let mut constants_table = ConstantsTable::new(); for ir_function in &mut ir_functions { - let (_, stack_size) = ir_function.assign_registers(register_count); - let function = ir_function.assemble(stack_size, &mut constants_table); + let mut offset_counter = OffsetCounter::new(); + ir_function.assign_registers(register_count, &mut offset_counter); + let function = ir_function.assemble(offset_counter.get_count(), &mut constants_table); functions.push(function); } @@ -107,16 +108,8 @@ fn run( ); } - let mut registers: Vec = vec![Operand::Null; register_count]; - let mut call_stack = CallStack::new(); + let result = call(&dvm_context, "main", &vec![], register_count); - let result = call( - &dvm_context, - &mut registers, - &mut call_stack, - "main", - &vec![], - ); if let Some(value) = result { println!("{}", value); } diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index 88dbdcc..feea21a 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -10,6 +10,7 @@ use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol_table::SymbolTable; +use crate::type_info::TypeInfo; use crate::types_table::TypesTable; use std::cell::RefCell; use std::rc::Rc; @@ -55,6 +56,10 @@ impl LetStatement { self.initializer.init_scopes(symbol_table, container_scope); } + pub fn scope_id(&self) -> usize { + self.scope_id.unwrap() + } + fn make_and_insert_variable_symbol( &self, symbol_table: &mut SymbolTable, @@ -131,6 +136,7 @@ impl LetStatement { types_table: &mut TypesTable, ) -> Result<(), Vec> { self.initializer.type_check(symbol_table, types_table)?; + // TODO: this is wrong. We need to check assignability let initializer_type_info = self .initializer .type_info(symbol_table, types_table) @@ -144,6 +150,34 @@ impl LetStatement { Ok(()) } + fn make_vr_variable(&self, builder: &mut IrBuilder, destination_type: &TypeInfo) -> IrVariable { + IrVariable::new_vr( + self.declared_name().into(), + builder.current_block().id(), + destination_type, + ) + } + + fn make_stack_variable( + &self, + builder: &mut IrBuilder, + destination_type: &TypeInfo, + offset: isize, + ) -> IrVariable { + IrVariable::new_stack_with_offset( + self.declared_name().into(), + builder.current_block().id(), + destination_type, + offset, + ) + } + + pub fn get_destination_symbol(&self, symbol_table: &SymbolTable) -> Rc { + symbol_table + .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) + .unwrap() + } + pub fn to_ir( &self, builder: &mut IrBuilder, @@ -154,20 +188,14 @@ impl LetStatement { .initializer .to_ir_operation(builder, symbol_table, types_table); - let destination_symbol = symbol_table - .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) - .unwrap(); + let destination_symbol = self.get_destination_symbol(symbol_table); let destination_type = types_table .variable_types() .get(&destination_symbol) .unwrap(); - let destination_vr_variable = IrVariable::new_vr( - self.declared_name().into(), - builder.current_block().id(), - destination_type, - ); + let destination_vr_variable = self.make_vr_variable(builder, destination_type); let as_rc = Rc::new(RefCell::new(destination_vr_variable)); let ir_assign = IrAssign::new(as_rc.clone(), init_operation); @@ -180,4 +208,30 @@ impl LetStatement { .current_block_mut() .add_statement(IrStatement::Assign(ir_assign)); } + + pub fn to_repl_ir( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + types_table: &TypesTable, + destination_stack_offset: isize, + ) -> Rc> { + let init_operation = self + .initializer + .to_ir_operation(builder, symbol_table, types_table); + let destination_symbol = self.get_destination_symbol(symbol_table); + let destination_type = types_table + .variable_types() + .get(&destination_symbol) + .unwrap(); + let destination_stack_variable = + self.make_stack_variable(builder, destination_type, destination_stack_offset); + let as_rc = Rc::new(RefCell::new(destination_stack_variable)); + let ir_assign = IrAssign::new(as_rc.clone(), init_operation); + // do not need to save variable to builder as a new one is created for each repl function + builder + .current_block_mut() + .add_statement(IrStatement::Assign(ir_assign)); + as_rc + } } diff --git a/dmc-lib/src/ir/ir_assign.rs b/dmc-lib/src/ir/ir_assign.rs index 32709e1..65400b9 100644 --- a/dmc-lib/src/ir/ir_assign.rs +++ b/dmc-lib/src/ir/ir_assign.rs @@ -2,8 +2,9 @@ use crate::constants_table::ConstantsTable; use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::ir_operation::IrOperation; use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor}; -use crate::ir::register_allocation::{OffsetCounter, VrUser}; +use crate::ir::register_allocation::VrUser; use crate::ir::util::propagate_spills; +use crate::offset_counter::OffsetCounter; use dvm_lib::instruction::Instruction; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; @@ -57,7 +58,7 @@ impl VrUser for IrAssign { fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter) { let mut borrowed_destination = self.destination.borrow_mut(); if let IrVariableDescriptor::Stack(stack_variable) = borrowed_destination.descriptor_mut() { - stack_variable.set_offset(counter.next()); + stack_variable.use_next_offset(counter); } } } diff --git a/dmc-lib/src/ir/ir_block.rs b/dmc-lib/src/ir/ir_block.rs index 626c7d9..9d3f25c 100644 --- a/dmc-lib/src/ir/ir_block.rs +++ b/dmc-lib/src/ir/ir_block.rs @@ -2,7 +2,8 @@ use crate::constants_table::ConstantsTable; use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVrVariableDescriptor; -use crate::ir::register_allocation::{HasVrUsers, OffsetCounter, VrUser}; +use crate::ir::register_allocation::{HasVrUsers, VrUser}; +use crate::offset_counter::OffsetCounter; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; @@ -122,6 +123,7 @@ impl Display for IrBlock { #[cfg(test)] mod tests { use crate::diagnostic::Diagnostic; + use crate::offset_counter::OffsetCounter; use crate::parser::parse_compilation_unit; use crate::symbol_table::SymbolTable; use crate::types_table::TypesTable; @@ -155,7 +157,7 @@ mod tests { .unwrap(); let mut main_ir = main.to_ir(&symbol_table, &types_table, None); - let (register_assignments, _) = main_ir.assign_registers(2); + let register_assignments = main_ir.assign_registers(2, &mut OffsetCounter::new()); assert_eq!(register_assignments.len(), 4); Ok(()) diff --git a/dmc-lib/src/ir/ir_function.rs b/dmc-lib/src/ir/ir_function.rs index 3482809..b5d890a 100644 --- a/dmc-lib/src/ir/ir_function.rs +++ b/dmc-lib/src/ir/ir_function.rs @@ -4,6 +4,7 @@ use crate::ir::ir_block::IrBlock; use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::register_allocation::HasVrUsers; +use crate::offset_counter::OffsetCounter; use crate::type_info::TypeInfo; use dvm_lib::vm::function::Function; use std::cell::RefCell; @@ -36,11 +37,14 @@ impl IrFunction { pub fn assign_registers( &mut self, register_count: usize, - ) -> (HashMap, isize) { - self.entry.borrow_mut().assign_registers(register_count) + offset_counter: &mut OffsetCounter, + ) -> HashMap { + self.entry + .borrow_mut() + .assign_registers(register_count, offset_counter) } - pub fn assemble(&self, stack_size: isize, constants_table: &mut ConstantsTable) -> Function { + pub fn assemble(&self, stack_size: usize, constants_table: &mut ConstantsTable) -> Function { let mut builder = InstructionsBuilder::new(); self.entry.borrow().assemble(&mut builder, constants_table); let instructions = builder.take_instructions(); diff --git a/dmc-lib/src/ir/ir_statement.rs b/dmc-lib/src/ir/ir_statement.rs index 8c222ef..8360258 100644 --- a/dmc-lib/src/ir/ir_statement.rs +++ b/dmc-lib/src/ir/ir_statement.rs @@ -5,7 +5,8 @@ use crate::ir::ir_call::IrCall; use crate::ir::ir_return::IrReturn; use crate::ir::ir_set_field::IrSetField; use crate::ir::ir_variable::IrVrVariableDescriptor; -use crate::ir::register_allocation::{OffsetCounter, VrUser}; +use crate::ir::register_allocation::VrUser; +use crate::offset_counter::OffsetCounter; use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; diff --git a/dmc-lib/src/ir/ir_variable.rs b/dmc-lib/src/ir/ir_variable.rs index b0106d1..005b3ad 100644 --- a/dmc-lib/src/ir/ir_variable.rs +++ b/dmc-lib/src/ir/ir_variable.rs @@ -1,3 +1,4 @@ +use crate::offset_counter::OffsetCounter; use crate::type_info::TypeInfo; use dvm_lib::instruction::Location; use std::collections::HashSet; @@ -20,9 +21,16 @@ impl IrVariable { } } - pub fn new_stack(name: Rc, block_id: usize, type_info: TypeInfo) -> Self { + pub fn new_stack_with_offset( + name: Rc, + block_id: usize, + type_info: &TypeInfo, + offset: isize, + ) -> Self { + let mut descriptor = IrStackVariableDescriptor::new(name, block_id); + descriptor.set_offset(offset); Self { - descriptor: IrVariableDescriptor::Stack(IrStackVariableDescriptor::new(name, block_id)), + descriptor: IrVariableDescriptor::Stack(descriptor), type_info: type_info.clone(), } } @@ -177,6 +185,12 @@ impl IrStackVariableDescriptor { self.name.clone() } + pub fn use_next_offset(&mut self, offset_counter: &mut OffsetCounter) { + if self.offset.is_none() { + self.offset = Some(offset_counter.next() as isize); + } + } + pub fn set_offset(&mut self, offset: isize) { self.offset = Some(offset); } diff --git a/dmc-lib/src/ir/register_allocation.rs b/dmc-lib/src/ir/register_allocation.rs index 9be61ef..d8b4c22 100644 --- a/dmc-lib/src/ir/register_allocation.rs +++ b/dmc-lib/src/ir/register_allocation.rs @@ -1,4 +1,5 @@ use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::offset_counter::OffsetCounter; use std::collections::{HashMap, HashSet}; pub type InterferenceGraph = HashMap>; @@ -104,7 +105,8 @@ pub trait HasVrUsers { fn assign_registers( &mut self, register_count: usize, - ) -> (HashMap, isize) { + offset_counter: &mut OffsetCounter, + ) -> HashMap { let mut spills: HashSet = HashSet::new(); loop { let mut interference_graph = self.interference_graph(); @@ -123,12 +125,11 @@ pub trait HasVrUsers { vr_user.propagate_register_assignments(®isters); } // also set offsets - let mut offset_counter = OffsetCounter::new(); for vr_user in self.vr_users_mut() { - vr_user.propagate_stack_offsets(&mut offset_counter); + vr_user.propagate_stack_offsets(offset_counter); } - return (registers, offset_counter.get_count()); + return registers; } } } @@ -157,26 +158,6 @@ pub trait VrUser { } } -pub struct OffsetCounter { - counter: isize, -} - -impl OffsetCounter { - pub fn new() -> Self { - Self { counter: 0 } - } - - pub fn next(&mut self) -> isize { - let offset = self.counter; - self.counter += 1; - offset - } - - pub fn get_count(&self) -> isize { - self.counter - } -} - #[derive(Debug)] struct WorkItem { vr: IrVrVariableDescriptor, diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index 610ba90..af00528 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -5,6 +5,7 @@ pub mod error_codes; pub mod intrinsics; pub mod ir; pub mod lexer; +pub mod offset_counter; pub mod parser; pub mod scope; pub mod source_range; diff --git a/dmc-lib/src/offset_counter.rs b/dmc-lib/src/offset_counter.rs new file mode 100644 index 0000000..d2f457b --- /dev/null +++ b/dmc-lib/src/offset_counter.rs @@ -0,0 +1,23 @@ +pub struct OffsetCounter { + counter: usize, +} + +impl OffsetCounter { + pub fn new() -> Self { + Self { counter: 0 } + } + + pub fn with_base(base: usize) -> Self { + Self { counter: base } + } + + pub fn next(&mut self) -> usize { + let offset = self.counter; + self.counter += 1; + offset + } + + pub fn get_count(&self) -> usize { + self.counter + } +} diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index 790cfba..ab0e46b 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -41,6 +41,12 @@ pub fn parse_expression(input: &str) -> ParseResult { parser.expression() } +pub fn parse_let_statement(input: &str) -> ParseResult { + let mut parser = Parser::new(input); + parser.advance()?; + parser.let_statement() +} + macro_rules! matches_expression_first { ( $token_kind : expr ) => { matches!( diff --git a/dmc-lib/src/symbol_table/mod.rs b/dmc-lib/src/symbol_table/mod.rs index 36b75c2..e2fdfaf 100644 --- a/dmc-lib/src/symbol_table/mod.rs +++ b/dmc-lib/src/symbol_table/mod.rs @@ -89,6 +89,10 @@ impl SymbolTable { self.current_scope_id.unwrap() } + pub fn change_scope(&mut self, scope_id: usize) { + self.current_scope_id = Some(scope_id); + } + fn scope(&self, scope_id: usize) -> &Scope { &self.scopes[scope_id] } diff --git a/dvm-lib/src/vm/function.rs b/dvm-lib/src/vm/function.rs index 081f9c1..fbe69c8 100644 --- a/dvm-lib/src/vm/function.rs +++ b/dvm-lib/src/vm/function.rs @@ -5,7 +5,7 @@ use std::rc::Rc; pub struct Function { name: Rc, parameter_count: usize, - stack_size: isize, + stack_size: usize, instructions: Vec, } @@ -13,7 +13,7 @@ impl Function { pub fn new( name: Rc, parameter_count: usize, - stack_size: isize, + stack_size: usize, instructions: Vec, ) -> Self { Self { @@ -36,7 +36,7 @@ impl Function { self.parameter_count } - pub fn stack_size(&self) -> isize { + pub fn stack_size(&self) -> usize { self.stack_size } diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index 7350159..033c18d 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -115,15 +115,15 @@ impl<'a> CallFrame<'a> { self.ip += 1; } - fn stack(&self) -> &[Operand] { + pub fn stack(&self) -> &[Operand] { &self.stack } - fn stack_mut(&mut self) -> &mut Vec { + pub fn stack_mut(&mut self) -> &mut Vec { &mut self.stack } - fn fp(&self) -> usize { + pub fn fp(&self) -> usize { self.fp } @@ -163,11 +163,11 @@ impl<'a> CallStack<'a> { self.call_frames.pop().unwrap() } - fn top(&self) -> &CallFrame<'a> { + pub fn top(&self) -> &CallFrame<'a> { &self.call_frames[self.call_frames.len() - 1] } - fn top_mut(&mut self) -> &mut CallFrame<'a> { + pub fn top_mut(&mut self) -> &mut CallFrame<'a> { let call_frames_len = self.call_frames.len(); &mut self.call_frames[call_frames_len - 1] // dang borrow checker } @@ -190,18 +190,24 @@ impl<'a> CallStack<'a> { } } -pub fn call<'a>( - context: &'a DvmContext, - registers: &mut Vec, - call_stack: &mut CallStack<'a>, +pub fn call( + context: &DvmContext, function_name: &str, arguments: &[Value], + register_count: usize, ) -> Option { - // check if DVM_DEBUG is enabled - let debug = std::env::var("DVM_DEBUG") - .map(|s| s.eq("1")) - .unwrap_or(false); + let mut call_stack = CallStack::new(); + let mut registers = vec![Operand::Null; register_count]; + prepare_for_instruction_loop(context, function_name, &mut call_stack, arguments); + loop_instructions(context, &mut registers, &mut call_stack) +} +pub fn prepare_for_instruction_loop<'a>( + context: &'a DvmContext, + function_name: &str, + call_stack: &mut CallStack<'a>, + arguments: &[Value], +) { let function = context .functions .get(function_name) @@ -222,12 +228,22 @@ pub fn call<'a>( call_stack.top_mut().set_fp(arguments.len()); // ensure enough stack space - call_stack.top_mut().stack_mut().resize( - arguments.len() + (function.stack_size() as usize), - Operand::Null, - ); + call_stack + .top_mut() + .stack_mut() + .resize(arguments.len() + function.stack_size(), Operand::Null); +} + +pub fn loop_instructions<'a>( + context: &'a DvmContext, + registers: &mut Vec, + call_stack: &mut CallStack<'a>, +) -> Option { + // check if DVM_DEBUG is enabled + let debug = std::env::var("DVM_DEBUG") + .map(|s| s.eq("1")) + .unwrap_or(false); - // container for final return value let mut return_value: Option = None; while call_stack.maybe_top().is_some() @@ -296,7 +312,7 @@ pub fn call<'a>( call_stack .top_mut() .stack_mut() - .resize(arg_count + (function.stack_size() as usize), Operand::Null); + .resize(arg_count + function.stack_size(), Operand::Null); // set fp for callee frame let callee_frame = call_stack.top_mut(); @@ -690,6 +706,9 @@ pub fn call<'a>( // callee is the bottommost frame ("main" or whatever was called) // set the return value return_value = callee.take_return_value(); + // push the frame back on the call stack for further inspection by caller + call_stack.push(callee); + return return_value; } // do not increment ip of the caller; this was done above BEFORE the call } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index a517f10..34b8c2b 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -3,15 +3,15 @@ mod e2e_tests { use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::Diagnostic; use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types}; + use dmc_lib::offset_counter::OffsetCounter; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; use dmc_lib::types_table::TypesTable; use dvm_lib::vm::class::Class; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; - use dvm_lib::vm::operand::Operand; use dvm_lib::vm::value::Value; - use dvm_lib::vm::{CallStack, DvmContext, call}; + use dvm_lib::vm::{DvmContext, call}; use std::rc::Rc; const REGISTER_COUNT: usize = 8; @@ -56,7 +56,9 @@ mod e2e_tests { let mut constants_table = ConstantsTable::new(); for ir_function in &mut ir_functions { - let (_, stack_size) = ir_function.assign_registers(REGISTER_COUNT); + let mut offset_counter = OffsetCounter::new(); + ir_function.assign_registers(REGISTER_COUNT, &mut offset_counter); + let stack_size = offset_counter.get_count(); let function = ir_function.assemble(stack_size, &mut constants_table); functions.push(function); } @@ -95,16 +97,7 @@ mod e2e_tests { function_name: &str, arguments: &[Value], ) -> Option { - let mut registers: Vec = vec![Operand::Null; REGISTER_COUNT]; - let mut call_stack = CallStack::new(); - - call( - &dvm_context, - &mut registers, - &mut call_stack, - function_name, - &arguments, - ) + call(&dvm_context, function_name, &arguments, REGISTER_COUNT) } fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) {