174 lines
6.3 KiB
Rust
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
|
|
}
|
|
}
|