diff --git a/dm/src/repl.rs b/dm/src/repl.rs index d83c827..9942cbc 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -174,62 +174,63 @@ fn compile_expression( offset_counter: &mut OffsetCounter, constants_table: &mut ConstantsTable, ) -> Result> { - // parse - let (mut expression, parse_diagnostics) = parse_expression(input); - if !parse_diagnostics.is_empty() { - return Err(parse_diagnostics); - } - - // init scopes, if necessary - let container_scope = prepare_scopes(symbol_table, fn_body_scope_id); - - // inner scopes - expression.init_scopes(symbol_table, container_scope); - - // names - let diagnostics = expression.check_static_fn_local_names(&symbol_table); - if !diagnostics.is_empty() { - return Err(diagnostics); - } - - // type check - expression.type_check(&symbol_table, types_table)?; - - // synthesize a function - // init ir_builder - let mut ir_builder = IrBuilder::new(); - - // copy all previous declared variables to here so we preserve their stack offsets - for (key, value) in fn_local_variables { - ir_builder - .local_variables_mut() - .insert(key.clone(), value.clone()); - } - - let entry_block_id = ir_builder.new_block(); - - let maybe_ir_expression = - expression.to_ir_expression(&mut ir_builder, &symbol_table, &types_table); - - // if Some, return the value - ir_builder - .current_block_mut() - .add_statement(IrStatement::Return(IrReturn::new(maybe_ir_expression))); - - ir_builder.finish_block(); - let entry_block = ir_builder.get_block(entry_block_id); - - let mut ir_function = IrFunction::new( - "__repl".into(), - vec![], - expression.type_info(&symbol_table, &types_table), - entry_block.clone(), - ); - - // spilled registers are put on the stack, so we need to add it to our stack size - ir_function.assign_registers(register_count, offset_counter); - - Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) + // // 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( @@ -242,63 +243,64 @@ fn compile_let_statement( types_table: &mut TypesTable, constants_table: &mut ConstantsTable, ) -> Result> { - // parse - let (maybe_let_statement, parse_diagnostics) = parse_let_statement(input); - if !parse_diagnostics.is_empty() { - return Err(parse_diagnostics); - } - let mut let_statement = maybe_let_statement.unwrap(); - - // names - let container_scope_id = prepare_scopes(symbol_table, body_scope_id); - - let_statement.init_scopes(symbol_table, container_scope_id); - - let name_diagnostics = let_statement.analyze_static_fn_local_names(symbol_table); - if !name_diagnostics.is_empty() { - return Err(name_diagnostics); - } - - // types - let_statement.type_check(&symbol_table, types_table)?; - - // init the ir builder - let mut ir_builder = IrBuilder::new(); - - // put previous locals in ir_builder so expressions can find them - for (k, v) in local_variables.iter() { - ir_builder - .local_variables_mut() - .insert(k.clone(), v.clone()); - } - - // ir function - let entry_block_id = ir_builder.new_block(); - - let destination_ir_variable = let_statement.to_repl_ir( - &mut ir_builder, - symbol_table, - types_table, - offset_counter.next() as isize, - ); // put it on top - - // Now that we've translated to ir, we can add the new local variable in the IrBuilder to our - // record of them to be used for next loop iteration. - let variable_symbol = let_statement.get_destination_symbol(symbol_table); - local_variables.insert(variable_symbol, destination_ir_variable); - - ir_builder.finish_block(); - let entry_block = ir_builder.get_block(entry_block_id); - let mut ir_function = IrFunction::new( - "__repl".into(), - vec![], - &TypeInfo::Void, - entry_block.clone(), - ); - - // By here, the variables should all be assigned to their new or existing stack slots. - // Register allocation should only be required for temp variables. - ir_function.assign_registers(register_count, offset_counter); - - Ok(ir_function.assemble(offset_counter.get_count(), constants_table)) + // // 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!() } diff --git a/dm/src/run.rs b/dm/src/run.rs index 06644f4..c22eb8f 100644 --- a/dm/src/run.rs +++ b/dm/src/run.rs @@ -3,18 +3,12 @@ use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use dm_std_lib::add_all_std_core; +use dmc_lib::compile_compilation_unit; use dmc_lib::constants_table::ConstantsTable; -use dmc_lib::diagnostic::Diagnostic; -use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types}; -use dmc_lib::offset_counter::OffsetCounter; -use dmc_lib::parser::get_compilation_unit; -use dmc_lib::symbol_table::SymbolTable; -use dmc_lib::types_table::TypesTable; +use dmc_lib::diagnostic::{Diagnostic, Diagnostics}; use dvm_lib::vm::constant::{Constant, StringConstant}; -use dvm_lib::vm::function::Function; use dvm_lib::vm::{DvmContext, call}; use std::path::PathBuf; -use std::rc::Rc; pub fn compile_and_run_script( script: &PathBuf, @@ -40,65 +34,18 @@ fn run( show_ir: bool, show_asm: bool, register_count: usize, -) -> Result<(), Vec> { - let mut compilation_unit = get_compilation_unit(&input, None)?; - - let mut symbol_table = SymbolTable::new(); - symbol_table.push_module_scope("global scope"); - insert_intrinsic_symbols(&mut symbol_table); - let mut types_table = TypesTable::new(); - insert_intrinsic_types(&symbol_table, &mut types_table); - - compilation_unit.init_scopes(&mut symbol_table); - compilation_unit.gather_symbols_into(&mut symbol_table)?; - compilation_unit.check_names(&mut symbol_table)?; - compilation_unit.gather_types_into(&symbol_table, &mut types_table)?; - compilation_unit.type_check(&symbol_table, &mut types_table)?; - - let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table, &types_table); - - if show_ir { - for ir_function in &ir_functions { - println!("{}", ir_function); - } - } - - let mut functions: Vec = vec![]; +) -> Result<(), Diagnostics> { let mut constants_table = ConstantsTable::new(); - - for ir_function in &mut ir_functions { - 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); - } - - let classes = ir_classes - .iter() - .map(|ir_class| ir_class.to_vm_class()) - .collect::>(); - - if show_asm { - for function in &functions { - println!("{}", function); - } - } + let compile_compilation_unit_result = + compile_compilation_unit(input, register_count, &mut constants_table)?; let mut dvm_context = DvmContext::new(); // add std::core fns add_all_std_core(&mut dvm_context); - for function in functions { - dvm_context - .functions_mut() - .insert(function.name_owned(), function); - } - - for class in classes { - dvm_context - .classes_mut() - .insert(class.fqn().into(), Rc::new(class)); + for (name, function) in compile_compilation_unit_result.functions { + dvm_context.functions_mut().insert(name, function); } for (name, content) in &constants_table.string_constants() { diff --git a/dmc-lib/src/ir/assemble.rs b/dmc-lib/src/ir/assemble.rs index 2ebca6b..e48b94f 100644 --- a/dmc-lib/src/ir/assemble.rs +++ b/dmc-lib/src/ir/assemble.rs @@ -243,6 +243,10 @@ fn to_add_operand(ir_expression: &IrExpression, ctx: &mut FunctionAssemblyContex IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => { AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } + _ => panic!( + "Attempt to add with non-integer type (found {})", + ir_parameter.type_info() + ), } } IrExpression::Variable(ir_variable_id) => { @@ -251,6 +255,10 @@ fn to_add_operand(ir_expression: &IrExpression, ctx: &mut FunctionAssemblyContex IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => AddOperand::Location( to_location(ctx.variable_locations.get_variable_location(ir_variable_id)), ), + _ => panic!( + "Attempt to add with non-integer type (found {})", + ir_variable.type_info() + ), } } IrExpression::Int(i) => AddOperand::Int(*i), diff --git a/dmc-lib/src/ir/ir_type_info.rs b/dmc-lib/src/ir/ir_type_info.rs index 560ba71..01d4089 100644 --- a/dmc-lib/src/ir/ir_type_info.rs +++ b/dmc-lib/src/ir/ir_type_info.rs @@ -7,6 +7,7 @@ pub enum IrTypeInfo { String, Int, Double, + Void, } impl Display for IrTypeInfo { diff --git a/dmc-lib/src/lowering/mod.rs b/dmc-lib/src/lowering/mod.rs index 42bb745..55c153c 100644 --- a/dmc-lib/src/lowering/mod.rs +++ b/dmc-lib/src/lowering/mod.rs @@ -145,9 +145,20 @@ fn lower_to_ir_function(function: &Function, ctx: &mut LowerToIrContext) { lower_to_ir_parameters(function, ctx, &mut fn_ctx); + let function_symbol_id = ctx.nodes_to_symbols[&function.node_id()]; + let function_type_info_id = ctx.symbols_to_type_infos[&function_symbol_id]; + let function_type_info = match &ctx.type_infos[function_type_info_id] { + TypeInfo::Function(function_type_info) => function_type_info, + _ => panic!("Expected FunctionTypeInfo"), + }; + let is_void_function = match ctx.type_infos[function_type_info.return_type_id()] { + TypeInfo::Void => true, + _ => false, + }; + let n_statements = function.statements().len(); for (i, statement) in function.statements().iter().enumerate() { - let can_return_value = i == n_statements - 1; + let can_return_value = i == n_statements - 1 && !is_void_function; lower_to_ir_statement(statement, ctx, &mut fn_ctx, can_return_value); } @@ -265,10 +276,8 @@ fn lower_to_ir_expression_statement( let ir_statement = IrStatement::Return(IrReturn::new(Some(ir_expression))); fn_ctx.current_block_statements.push(ir_statement); } else { - // we could try to filter out useless code, but it's not guaranteed that there aren't - // intended side effects from the lowering, so, for now, let's throw the result in a t_var. - // in the future we may want to somehow enforce with the type system a way that we can't - // create useless, dead ir code + // This block executes when the expression has side effects. For now, we just throw the + // result away in a temporary variable. let ir_operation = lower_expression_to_ir_operation(expression_statement.expression(), ctx, fn_ctx); diff --git a/dmc-lib/src/lowering/util.rs b/dmc-lib/src/lowering/util.rs index 71b661e..00ac153 100644 --- a/dmc-lib/src/lowering/util.rs +++ b/dmc-lib/src/lowering/util.rs @@ -6,8 +6,9 @@ pub fn to_ir_type_info(sa_type_info: &TypeInfo) -> IrTypeInfo { TypeInfo::String => IrTypeInfo::String, TypeInfo::Int => IrTypeInfo::Int, TypeInfo::Double => IrTypeInfo::Double, + TypeInfo::Void => IrTypeInfo::Void, _ => { - panic!() + panic!("BUG! Unknown sa_type_info: {}", sa_type_info); } } } diff --git a/dmc-lib/src/semantic_analysis/resolve_names.rs b/dmc-lib/src/semantic_analysis/resolve_names.rs index c4c6a89..dbf5fad 100644 --- a/dmc-lib/src/semantic_analysis/resolve_names.rs +++ b/dmc-lib/src/semantic_analysis/resolve_names.rs @@ -5,6 +5,7 @@ use crate::ast::call::Call; use crate::ast::compilation_unit::CompilationUnit; use crate::ast::expression::Expression; use crate::ast::expression_statement::ExpressionStatement; +use crate::ast::extern_function::ExternFunction; use crate::ast::function::Function; use crate::ast::identifier::Identifier; use crate::ast::let_statement::LetStatement; @@ -99,6 +100,10 @@ pub fn resolve_names( resolve_names_function(function, &mut ctx); } + for extern_function in compilation_unit.extern_functions() { + resolve_names_extern_function(extern_function, &mut ctx); + } + NameResolutionResult { nodes_to_symbols: ctx.nodes_to_symbols, diagnostics: ctx.diagnostics, @@ -121,6 +126,22 @@ fn resolve_names_function(function: &Function, ctx: &mut NameResolutionContext) ctx.nodes_to_symbols.insert(function.node_id(), symbol_id); } +fn resolve_names_extern_function( + extern_function: &ExternFunction, + ctx: &mut NameResolutionContext, +) { + for parameter in extern_function.parameters() { + resolve_names_parameter(parameter, ctx); + } + + // 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 = &ctx.scopes[scope_id]; + let symbol_id = scope.symbols()[extern_function.declared_name()]; + ctx.nodes_to_symbols + .insert(extern_function.node_id(), symbol_id); +} + fn resolve_names_parameter(parameter: &Parameter, ctx: &mut NameResolutionContext) { // point this parameter's node_id to the symbol_id for the parameter symbol let scope_id = ctx.nodes_to_scopes[¶meter.node_id()];