use crate::ast::expression::Expression; use crate::ast::ir_builder::IrBuilder; use crate::diagnostic::Diagnostic; use crate::ir::ir_assign::IrAssign; use crate::ir::ir_expression::IrExpression; use crate::ir::ir_operation::IrOperation; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_subtract::IrSubtract; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; use std::cell::RefCell; use std::rc::Rc; pub struct SubtractExpression { lhs: Box, rhs: Box, source_range: SourceRange, type_info: Option, } impl SubtractExpression { pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self { Self { lhs: lhs.into(), rhs: rhs.into(), source_range, type_info: None, } } pub fn lhs(&self) -> &Expression { &self.lhs } pub fn rhs(&self) -> &Expression { &self.rhs } pub fn gather_declared_names( &mut self, symbol_table: &mut SymbolTable, ) -> Result<(), Vec> { let diagnostics = [&mut self.lhs, &mut self.rhs] .iter_mut() .map(|expression| expression.gather_declared_names(symbol_table)) .filter_map(|result| result.err()) .flatten() .collect::>(); if diagnostics.is_empty() { Ok(()) } else { Err(diagnostics) } } pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let diagnostics: Vec = [&mut self.lhs, &mut self.rhs] .iter_mut() .map(|expression| expression.check_name_usages(symbol_table)) .filter_map(Result::err) .flatten() .collect(); 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_subtract(rhs_type_info) { self.type_info = Some(lhs_type_info.add_result(rhs_type_info)); Ok(()) } else { Err(vec![Diagnostic::new( &format!( "Incompatible types: cannot subtract {} from {}", rhs_type_info, lhs_type_info ), // n.b. order self.lhs.source_range().start(), self.lhs.source_range().end(), )]) } } pub fn type_info(&self) -> &TypeInfo { self.type_info.as_ref().unwrap() } pub fn source_range(&self) -> &SourceRange { &self.source_range } pub fn to_ir_subtract( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, ) -> IrSubtract { let lhs = self .lhs .to_ir(builder, symbol_table) .expect("Attempt to subtract non-expression"); let rhs = self .rhs .to_ir(builder, symbol_table) .expect("Attempt to subtract non-expression"); IrSubtract::new(lhs, rhs) } pub fn to_ir_expression( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, ) -> IrExpression { let ir_subtract = self.to_ir_subtract(builder, symbol_table); let t_var = IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), self.type_info(), ); let as_rc = Rc::new(RefCell::new(t_var)); let assign = IrAssign::new(as_rc.clone(), IrOperation::Subtract(ir_subtract)); builder .current_block_mut() .add_statement(IrStatement::Assign(assign)); IrExpression::Variable(as_rc) } }