deimos-lang/dmc-lib/src/ast/additive_expression.rs
2026-03-03 21:06:12 -06:00

174 lines
6.3 KiB
Rust

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<Expression>,
rhs: Box<Expression>,
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<Diagnostic>> {
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<Diagnostic>> {
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<Diagnostic>> {
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
}
}