use crate::asm::asm_instruction::{Add, AsmInstruction, LoadConstant, Move, Operand, Pop}; use crate::ast::assemble_context::AssembleContext; use crate::ast::expression::Expression; use crate::constants_table::ConstantsTable; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; use crate::symbol::ExpressibleSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; pub struct AdditiveExpression { lhs: Box, rhs: Box, source_range: SourceRange, } impl AdditiveExpression { pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self { Self { lhs: lhs.into(), rhs: rhs.into(), source_range, } } pub fn gather_declared_names( &mut self, symbol_table: &mut SymbolTable, ) -> Result<(), Vec> { let mut diagnostics = vec![]; diagnostics.append(&mut self.lhs.gather_declared_names(symbol_table)); diagnostics.append(&mut self.rhs.gather_declared_names(symbol_table)); if diagnostics.is_empty() { Ok(()) } else { Err(diagnostics) } } pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics = vec![]; diagnostics.append(&mut self.lhs.check_name_usages(symbol_table)); diagnostics.append(&mut self.rhs.check_name_usages(symbol_table)); if diagnostics.is_empty() { Ok(()) } else { Err(diagnostics) } } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { self.lhs.type_check(symbol_table); self.rhs.type_check(symbol_table); let lhs_type_info = self.lhs.type_info(); let rhs_type_info = self.rhs.type_info(); if lhs_type_info.can_add(&rhs_type_info) { Ok(()) } else { Err(vec![Diagnostic::new( &format!("Cannot add {} to {}", lhs_type_info, rhs_type_info), self.source_range.start(), self.source_range.end(), )]) } } fn assemble_side( expression: &Expression, context: &mut AssembleContext, symbol_table: &SymbolTable, constants_table: &mut ConstantsTable, ) -> usize { match expression { Expression::Call(call) => { call.assemble(context, symbol_table, constants_table); let register = context.new_local_register(); context.instruction(AsmInstruction::Pop(Pop::new(register))); register } Expression::IntegerLiteral(integer_literal) => { let register = context.new_local_register(); context.instruction(AsmInstruction::Move(Move::new( Operand::IntegerLiteral(integer_literal.value()), register, ))); register } Expression::String(string_literal) => { let register = context.new_local_register(); let constant_name = constants_table.insert_string(string_literal.content()); context.instruction(AsmInstruction::LoadConstant(LoadConstant::new( &constant_name, register, ))); register } Expression::Identifier(identifier) => { let register = context.new_local_register(); match identifier.expressible_symbol() { ExpressibleSymbol::Function(_) => unreachable!(), ExpressibleSymbol::Parameter(parameter_symbol) => { let offset = parameter_symbol.borrow().stack_frame_offset(); context.instruction(AsmInstruction::Move(Move::new( Operand::StackFrameOffset(offset), register, ))); } ExpressibleSymbol::Variable(variable_symbol) => { context.instruction(AsmInstruction::Move(Move::new( Operand::Register(variable_symbol.borrow().register()), register, ))); } } register } Expression::Additive(additive_expression) => { additive_expression.assemble(context, symbol_table, constants_table) } } } pub fn assemble( &self, context: &mut AssembleContext, symbol_table: &SymbolTable, constants_table: &mut ConstantsTable, ) -> usize { let lhs_register = Self::assemble_side(&self.lhs, context, symbol_table, constants_table); let rhs_register = Self::assemble_side(&self.rhs, context, symbol_table, constants_table); let result_register = context.new_local_register(); match self.lhs.type_info() { TypeInfo::Integer => match self.rhs.type_info() { TypeInfo::Integer => { context.instruction(AsmInstruction::Add(Add::IntInt( lhs_register, rhs_register, result_register, ))); } _ => unreachable!(), }, TypeInfo::String => match self.rhs.type_info() { TypeInfo::Integer => { context.instruction(AsmInstruction::Add(Add::StringInt( lhs_register, rhs_register, result_register, ))); } TypeInfo::String => { context.instruction(AsmInstruction::Add(Add::StringString( lhs_register, rhs_register, result_register, ))); } _ => unreachable!(), }, _ => unreachable!(), } result_register } pub fn type_info(&self) -> TypeInfo { self.lhs.type_info().additive_result(&self.rhs.type_info()) } pub fn source_range(&self) -> &SourceRange { &self.source_range } }