- C frontend broken for now

- If statements work properly now (hopefully)
- still issues with while loops pushing vars to the stack. need scoping
  implemented to fix this!

- refactored registers.rs and fixed faulty logic.
- made register allocation optimisations
This commit is contained in:
2026-02-08 00:14:18 +00:00
parent e9329eca95
commit 6699333b2c
9 changed files with 745 additions and 232 deletions
+88 -57
View File
@@ -5,11 +5,12 @@ use std::time::SystemTime;
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use super::registers::RegisterAllocator; use super::registers::RegisterAllocator;
use crate::backend::dsa::registers::Register;
use crate::{block, comment, dsa}; use crate::{block, comment, dsa};
use crate::model::{ use crate::model::{
BinaryOperator, CompilerError, ConstExpr, Declaration, Dependency, Expression, BinaryOperator, Call, CompilerError, ConstExpr, Declaration, Dependency, Expression,
Program, Statement, UnaryOperator, Variable, Program, Statement, TypeId, UnaryOperator, Variable,
}; };
pub struct CodeGenerator { pub struct CodeGenerator {
@@ -149,9 +150,14 @@ impl CodeGenerator {
self.generate_global(&var.name, init) self.generate_global(&var.name, init)
} }
Declaration::Function { Declaration::Function {
name, params, body, .. name,
params,
body,
return_type,
} => { } => {
let func = self.generate_function(&name, &params, &body).join("\n"); let func = self
.generate_function(&name, &params, &body, return_type)
.join("\n");
self.functions.push(format!("{func}\n")); self.functions.push(format!("{func}\n"));
} }
@@ -169,12 +175,23 @@ impl CodeGenerator {
name: &str, name: &str,
params: &[Variable], params: &[Variable],
body: &[Statement], body: &[Statement],
return_type: TypeId,
) -> Vec<String> { ) -> Vec<String> {
let mut code = Vec::new(); let mut code = Vec::new();
// Reset allocator for new function // Reset allocator for new function
self.allocator.reset(); self.allocator.reset();
code.push(format!(
"// fn {name}({}) -> {}",
params
.iter()
.map(|p| format!("{}: {}", p.name, p.type_id))
.collect::<Vec<String>>()
.join(", "),
return_type
));
// Function prologue // Function prologue
code.push(format!("{}:", name)); code.push(format!("{}:", name));
code.push("\tpush bpr".to_string()); code.push("\tpush bpr".to_string());
@@ -192,7 +209,7 @@ impl CodeGenerator {
// Generate code for function body // Generate code for function body
for stmt in body { for stmt in body {
let stmt_code = self.generate_statement(stmt).unwrap(); let stmt_code = self.generate_statement(stmt, &mut code).unwrap();
code.extend(stmt_code); code.extend(stmt_code);
} }
@@ -211,6 +228,7 @@ impl CodeGenerator {
fn generate_statement( fn generate_statement(
&mut self, &mut self,
stmt: &Statement, stmt: &Statement,
func_body: &mut Vec<String>,
) -> Result<Vec<String>, CompilerError> { ) -> Result<Vec<String>, CompilerError> {
let mut code = Vec::new(); let mut code = Vec::new();
@@ -218,7 +236,8 @@ impl CodeGenerator {
Statement::Declaration { var, value } => { Statement::Declaration { var, value } => {
if let Some(expr) = value { if let Some(expr) = value {
// Evaluate expression // Evaluate expression
let (result_reg, expr_code) = self.generate_expression(expr, true)?; let (result_reg, expr_code) =
self.generate_expression(expr, true, func_body)?;
code.extend(expr_code); code.extend(expr_code);
// Store result in variable // Store result in variable
@@ -233,14 +252,17 @@ impl CodeGenerator {
} }
} }
Statement::Break => unimplemented!(), Statement::Break => unimplemented!("need scope tracking first!"),
Statement::Continue => unimplemented!(), Statement::Continue => unimplemented!("need scope tracking first!"),
Statement::Defer(_func) => unimplemented!("we need scope tracking first!"),
Statement::PtrWrite { ptr, value } => { Statement::PtrWrite { ptr, value } => {
let (result_reg, expr_code) = self.generate_expression(value, true)?; let (result_reg, expr_code) =
self.generate_expression(value, true, func_body)?;
code.extend(expr_code); code.extend(expr_code);
let (ptr_reg, ptr_code) = self.generate_expression(ptr, true)?; let (ptr_reg, ptr_code) =
self.generate_expression(ptr, true, func_body)?;
code.extend(ptr_code); code.extend(ptr_code);
code.push(format!("\tstw {}, {}", result_reg, ptr_reg)); code.push(format!("\tstw {}, {}", result_reg, ptr_reg));
@@ -251,7 +273,8 @@ impl CodeGenerator {
Statement::Assign { varname, value } => { Statement::Assign { varname, value } => {
// Evaluate expression // Evaluate expression
let (result_reg, expr_code) = self.generate_expression(value, true)?; let (result_reg, expr_code) =
self.generate_expression(value, true, func_body)?;
code.extend(expr_code); code.extend(expr_code);
// Check if this is a global variable // Check if this is a global variable
@@ -270,7 +293,8 @@ impl CodeGenerator {
Statement::Return(expr) => { Statement::Return(expr) => {
if let Some(e) = expr { if let Some(e) = expr {
let (result_reg, expr_code) = self.generate_expression(e, true)?; let (result_reg, expr_code) =
self.generate_expression(e, true, func_body)?;
code.extend(expr_code); code.extend(expr_code);
code.push(format!("\tstw {}, bpr, 8", result_reg)); code.push(format!("\tstw {}, bpr, 8", result_reg));
code.push(format!("\tjmp _ret")); code.push(format!("\tjmp _ret"));
@@ -284,7 +308,8 @@ impl CodeGenerator {
else_stmt, else_stmt,
} => { } => {
// Generate condition // Generate condition
let (cond_reg, cond_code) = self.generate_expression(condition, true)?; let (cond_reg, cond_code) =
self.generate_expression(condition, true, func_body)?;
code.extend(cond_code); code.extend(cond_code);
// Compare with zero // Compare with zero
@@ -302,7 +327,7 @@ impl CodeGenerator {
// Then block // Then block
code.push(format!("{}:", then_label)); code.push(format!("{}:", then_label));
for s in then_stmt { for s in then_stmt {
code.extend(self.generate_statement(s)?); code.extend(self.generate_statement(s, func_body)?);
} }
if then_stmt.len() == 0 { if then_stmt.len() == 0 {
@@ -314,7 +339,7 @@ impl CodeGenerator {
// Else block // Else block
code.push(format!("{}:", else_label)); code.push(format!("{}:", else_label));
for s in else_stmt { for s in else_stmt {
code.extend(self.generate_statement(s)?); code.extend(self.generate_statement(s, func_body)?);
} }
if else_stmt.len() == 0 { if else_stmt.len() == 0 {
@@ -331,7 +356,8 @@ impl CodeGenerator {
code.push(format!("{}:", loop_start)); code.push(format!("{}:", loop_start));
// Generate condition // Generate condition
let (cond_reg, cond_code) = self.generate_expression(condition, true)?; let (cond_reg, cond_code) =
self.generate_expression(condition, true, func_body)?;
code.extend(cond_code); code.extend(cond_code);
code.push(format!("\tcmp {}, zero", cond_reg)); code.push(format!("\tcmp {}, zero", cond_reg));
@@ -341,7 +367,7 @@ impl CodeGenerator {
// Loop body // Loop body
for s in body { for s in body {
code.extend(self.generate_statement(s)?); code.extend(self.generate_statement(s, func_body)?);
} }
code.push(format!("\tjmp {}", loop_start)); code.push(format!("\tjmp {}", loop_start));
@@ -354,21 +380,22 @@ impl CodeGenerator {
code.push(format!("{}:", loop_start)); code.push(format!("{}:", loop_start));
for s in body { for s in body {
code.extend(self.generate_statement(s)?); code.extend(self.generate_statement(s, func_body)?);
} }
code.push(format!("\tjmp {}", loop_start)); code.push(format!("\tjmp {}", loop_start));
} }
Statement::Expression { expr } => { Statement::Expression { expr } => {
let (result_reg, expr_code) = self.generate_expression(expr, false)?; let (result_reg, expr_code) =
self.generate_expression(expr, false, func_body)?;
code.extend(expr_code); code.extend(expr_code);
self.allocator.free_temp(&result_reg); self.allocator.free_temp(&result_reg);
} }
Statement::Block(statements) => { Statement::Block(statements) => {
for s in statements { for s in statements {
code.extend(self.generate_statement(s)?); code.extend(self.generate_statement(s, func_body)?);
} }
} }
} }
@@ -382,14 +409,10 @@ impl CodeGenerator {
&mut self, &mut self,
expr: &Expression, expr: &Expression,
use_result: bool, use_result: bool,
) -> Result<(String, Vec<String>), CompilerError> { func_body: &mut Vec<String>,
) -> Result<(Register, Vec<String>), CompilerError> {
let mut code = Vec::new(); let mut code = Vec::new();
// optimisation to prevent generating dead code!
if expr.is_pure() && !use_result {
return Ok((String::new(), code));
}
match expr { match expr {
Expression::StringLiteral(value) => { Expression::StringLiteral(value) => {
let (reg, alloc_code) = self.allocator.alloc_temp()?; let (reg, alloc_code) = self.allocator.alloc_temp()?;
@@ -397,7 +420,7 @@ impl CodeGenerator {
// write string into memory // write string into memory
let uuid = self.get_unique_label(); let uuid = self.get_unique_label();
code.push(format!("\tdb str_{uuid}: \"{value}\"")); func_body.insert(0, format!("db str_{uuid}: \"{value}\""));
// Load pointer to string // Load pointer to string
code.push(format!("\tlwi str_{uuid}, {reg}")); code.push(format!("\tlwi str_{uuid}, {reg}"));
@@ -415,7 +438,7 @@ impl CodeGenerator {
Ok((reg, code)) Ok((reg, code))
} }
Expression::Number(value) => { Expression::Number { value, .. } => {
let (reg, alloc_code) = self.allocator.alloc_temp()?; let (reg, alloc_code) = self.allocator.alloc_temp()?;
code.extend(alloc_code); code.extend(alloc_code);
@@ -446,13 +469,17 @@ impl CodeGenerator {
} }
} }
Expression::Binary { op, left, right } => { Expression::Binary {
op, left, right, ..
} => {
// Evaluate left operand // Evaluate left operand
let (left_reg, left_code) = self.generate_expression(left, true)?; let (left_reg, left_code) =
self.generate_expression(left, true, func_body)?;
code.extend(left_code); code.extend(left_code);
// Evaluate right operand // Evaluate right operand
let (right_reg, right_code) = self.generate_expression(right, true)?; let (right_reg, right_code) =
self.generate_expression(right, true, func_body)?;
code.extend(right_code); code.extend(right_code);
// Allocate result register // Allocate result register
@@ -485,50 +512,50 @@ impl CodeGenerator {
// Comparison operators - return 1 (true) or 0 (false) // Comparison operators - return 1 (true) or 0 (false)
BinaryOperator::Eq => { BinaryOperator::Eq => {
code.push(format!("\tcmp {}, {}", left_reg, right_reg)); code.push(format!("\tcmp {}, {}", left_reg, right_reg));
code.push(format!("\tlli 0, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjne {}", end_label)); // If not equal, skip setting to 1
code.push(format!("\tlli 1, {}", result_reg)); code.push(format!("\tlli 1, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjeq {}", end_label));
code.push(format!("\tlli 0, {}", result_reg));
code.push(format!("{}:", end_label)); code.push(format!("{}:", end_label));
} }
BinaryOperator::Ne => { BinaryOperator::Ne => {
code.push(format!("\tcmp {}, {}", left_reg, right_reg)); code.push(format!("\tcmp {}, {}", left_reg, right_reg));
code.push(format!("\tlli 0, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjeq {}", end_label)); // If equal, skip setting to 1
code.push(format!("\tlli 1, {}", result_reg)); code.push(format!("\tlli 1, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjne {}", end_label));
code.push(format!("\tlli 0, {}", result_reg));
code.push(format!("{}:", end_label)); code.push(format!("{}:", end_label));
} }
BinaryOperator::Lt => { BinaryOperator::Lt => {
code.push(format!("\tcmp {}, {}", left_reg, right_reg)); code.push(format!("\tcmp {}, {}", left_reg, right_reg));
code.push(format!("\tlli 0, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjge {}", end_label)); // If greater or equal, skip setting to 1
code.push(format!("\tlli 1, {}", result_reg)); code.push(format!("\tlli 1, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjlt {}", end_label));
code.push(format!("\tlli 0, {}", result_reg));
code.push(format!("{}:", end_label)); code.push(format!("{}:", end_label));
} }
BinaryOperator::Le => { BinaryOperator::Le => {
code.push(format!("\tcmp {}, {}", left_reg, right_reg)); code.push(format!("\tcmp {}, {}", left_reg, right_reg));
code.push(format!("\tlli 0, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjgt {}", end_label)); // If greater than, skip setting to 1
code.push(format!("\tlli 1, {}", result_reg)); code.push(format!("\tlli 1, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjle {}", end_label));
code.push(format!("\tlli 0, {}", result_reg));
code.push(format!("{}:", end_label)); code.push(format!("{}:", end_label));
} }
BinaryOperator::Gt => { BinaryOperator::Gt => {
code.push(format!("\tcmp {}, {}", left_reg, right_reg)); code.push(format!("\tcmp {}, {}", left_reg, right_reg));
code.push(format!("\tlli 0, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjle {}", end_label)); // If less or equal, skip setting to 1
code.push(format!("\tlli 1, {}", result_reg)); code.push(format!("\tlli 1, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjgt {}", end_label));
code.push(format!("\tlli 0, {}", result_reg));
code.push(format!("{}:", end_label)); code.push(format!("{}:", end_label));
} }
BinaryOperator::Ge => { BinaryOperator::Ge => {
code.push(format!("\tcmp {}, {}", left_reg, right_reg)); code.push(format!("\tcmp {}, {}", left_reg, right_reg));
code.push(format!("\tlli 0, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjlt {}", end_label)); // If less than, skip setting to 1
code.push(format!("\tlli 1, {}", result_reg)); code.push(format!("\tlli 1, {}", result_reg));
let end_label = format!("_cmp_end_{}", self.get_unique_label());
code.push(format!("\tjge {}", end_label));
code.push(format!("\tlli 8, {}", result_reg));
code.push(format!("{}:", end_label)); code.push(format!("{}:", end_label));
} }
_ => unimplemented!(), _ => unimplemented!(),
@@ -541,11 +568,15 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::Call { name, args } => { Expression::Call {
func: Call { name, args },
..
} => {
// first evaluate all the args we're going to need // first evaluate all the args we're going to need
let mut arg_regs = Vec::new(); let mut arg_regs = Vec::new();
for arg in args.iter().rev() { for arg in args.iter().rev() {
let (arg_reg, arg_code) = self.generate_expression(arg, true)?; let (arg_reg, arg_code) =
self.generate_expression(arg, true, func_body)?;
code.extend(arg_code); code.extend(arg_code);
arg_regs.push(arg_reg); arg_regs.push(arg_reg);
} }
@@ -561,7 +592,7 @@ impl CodeGenerator {
let saved_regs = self.allocator.get_caller_saved_registers(); let saved_regs = self.allocator.get_caller_saved_registers();
for reg in &saved_regs { for reg in &saved_regs {
// spill variables to stack // spill variables to stack
code.extend(self.allocator.spill_register(reg).unwrap()); code.extend(self.allocator.free_register(reg).unwrap());
} }
// Evaluate and push arguments in reverse order // Evaluate and push arguments in reverse order
@@ -587,7 +618,7 @@ impl CodeGenerator {
return Err(CompilerError::Undefined(name.clone())); return Err(CompilerError::Undefined(name.clone()));
} }
let result_reg: String; let result_reg: Register;
if use_result { if use_result {
let (temp_result_reg, result_alloc) = self.allocator.alloc_temp()?; let (temp_result_reg, result_alloc) = self.allocator.alloc_temp()?;
@@ -603,7 +634,7 @@ impl CodeGenerator {
} }
} }
} else { } else {
result_reg = "zero".to_string(); result_reg = Register::Zero;
// Clean up arguments // Clean up arguments
if args.len() > 0 { if args.len() > 0 {
@@ -626,9 +657,9 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::Unary { op, operand } => { Expression::Unary { op, operand, .. } => {
let (operand_reg, operand_code) = let (operand_reg, operand_code) =
self.generate_expression(operand, true)?; self.generate_expression(operand, true, func_body)?;
code.extend(operand_code); code.extend(operand_code);
let (result_reg, result_alloc) = self.allocator.alloc_temp()?; let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
@@ -660,7 +691,7 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::Empty => Ok(("zero".to_string(), code)), Expression::Empty => Ok((Register::Null, code)),
} }
} }
+315 -148
View File
@@ -1,4 +1,4 @@
use std::collections::HashMap; use std::{collections::HashMap, fmt};
use crate::model::CompilerError; use crate::model::CompilerError;
@@ -6,78 +6,109 @@ use crate::model::CompilerError;
/// Manages general-purpose registers (rg0-rgf) and handles stack spilling /// Manages general-purpose registers (rg0-rgf) and handles stack spilling
pub struct RegisterAllocator { pub struct RegisterAllocator {
/// Available general-purpose registers /// Available general-purpose registers
available_registers: Vec<String>,
/// Maps variable names to their current location (register or stack offset) /// Maps variable names to their current location (register or stack offset)
variable_locations: HashMap<String, Location>, variable_locations: HashMap<String, Location>,
/// Maps registers to the variables they currently hold /// Maps registers to the variables they currently hold
register_contents: HashMap<String, String>, register_contents: HashMap<Register, String>,
/// Current stack offset for local variables (relative to bpr) /// Current stack offset for local variables (relative to bpr)
/// Starts at -4 (going downward from base pointer) /// Starts at -4 (going downward from base pointer)
stack_offset: i32, stack_offset: i32,
/// Track which registers are currently in use /// Track which registers are currently in use
in_use: HashMap<String, bool>, in_use: HashMap<Register, bool>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Location { pub struct Location {
Register(String), register: Option<Register>,
Stack(i32), // offset from bpr stack: Option<i32>,
}
impl Location {
pub fn stack(offset: i32) -> Self {
Location {
register: None,
stack: Some(offset),
}
}
pub fn register(register: Register) -> Self {
Location {
register: Some(register),
stack: None,
}
}
} }
impl RegisterAllocator { impl RegisterAllocator {
pub fn new() -> Self { pub fn new() -> Self {
// Initialize with available GP registers (rg0-rgf = 16 registers) // Initialize with available GP registers (rg0-rgf = 16 registers)
let registers = vec![ let in_use = vec![
"rg0", "rg1", "rg2", "rg3", "rg4", "rg5", "rg6", "rg7", "rg8", "rg9", "rga", Register::Rg0,
"rgb", "rgc", "rgd", "rge", "rgf", Register::Rg1,
Register::Rg2,
Register::Rg3,
Register::Rg4,
Register::Rg5,
Register::Rg6,
Register::Rg7,
Register::Rg8,
Register::Rg9,
Register::Rga,
Register::Rgb,
Register::Rgc,
Register::Rgd,
Register::Rge,
Register::Rgf,
] ]
.into_iter() .iter()
.map(String::from) .map(|&reg| (reg, false))
.collect(); .collect();
RegisterAllocator { RegisterAllocator {
available_registers: registers, // available_registers: registers,
variable_locations: HashMap::new(), variable_locations: HashMap::new(),
register_contents: HashMap::new(), register_contents: HashMap::new(),
stack_offset: -4, // Start at -4 (first local below saved bpr) stack_offset: -4, // Start at -4 (first local below saved bpr)
in_use: HashMap::new(), in_use,
} }
} }
/// Allocate a temporary register for expression evaluation /// Allocate a temporary register for expression evaluation
/// Returns the register name and optionally assembly code to save it /// Returns the register name and optionally assembly code to save it
pub fn alloc_temp(&mut self) -> Result<(String, Vec<String>), CompilerError> { pub fn alloc_temp(&mut self) -> Result<(Register, Vec<String>), CompilerError> {
let mut code = Vec::new();
// Try to find an unused register // Try to find an unused register
for reg in &self.available_registers {
if !self.in_use.get(reg).unwrap_or(&false) { println!("finding! {:#?}", self.in_use);
self.in_use.insert(reg.clone(), true);
return Ok((reg.clone(), code)); if let Some(reg) = self.find_free_register() {
} self.in_use.insert(reg, true);
return Ok((reg, Vec::new()));
} }
// All registers in use - need to spill one // All registers in use - need to spill one
// Choose the first register with a variable we can spill // Choose the first register with a variable we can spill
// Find a register to spill // Find a register to spill
let reg_to_spill = self
.available_registers
.iter()
.find(|reg| self.register_contents.contains_key(*reg))
.cloned();
if let Some(reg) = reg_to_spill { // let reg_to_spill = self
// Spill this variable to stack // .available_registers
let spill_code = self.spill_register(&reg)?; // .iter()
code.extend(spill_code); // .find(|reg| self.register_contents.contains_key(*reg))
// .cloned();
self.in_use.insert(reg.clone(), true); // if let Some(reg) = reg_to_spill {
return Ok((reg, code)); // // Spill this variable to stack
} // let spill_code = self.spill_register(&reg)?;
// code.extend(spill_code);
// self.in_use.insert(reg.clone(), true);
// return Ok((reg, code));
// }
todo!("an efficient stack spilling algorithm. needs scope awareness.");
Err(CompilerError::Generic( Err(CompilerError::Generic(
"All registers are used up yet there are no variables to spill to the stack" "All registers are used up yet there are no variables to spill to the stack"
@@ -88,7 +119,7 @@ impl RegisterAllocator {
/// Free a temporary register after use /// Free a temporary register after use
/// NOTE: This will NOT free registers that contain variables! /// NOTE: This will NOT free registers that contain variables!
/// Variables persist throughout their scope and must not be freed /// Variables persist throughout their scope and must not be freed
pub fn free_temp(&mut self, reg: &str) { pub fn free_temp(&mut self, reg: &Register) {
// Check if this register contains a variable // Check if this register contains a variable
if self.register_contents.contains_key(reg) { if self.register_contents.contains_key(reg) {
// This register holds a variable - don't free it! // This register holds a variable - don't free it!
@@ -97,7 +128,19 @@ impl RegisterAllocator {
} }
// This is a true temporary - safe to free // This is a true temporary - safe to free
self.in_use.insert(reg.to_string(), false); self.in_use.insert(*reg, false);
}
pub fn free_var(&mut self, var: &str) {
// Check if this variable is in a register
if let Some(location) = self.variable_locations.get(var).cloned() {
if let Some(reg) = location.register {
self.register_contents.remove(&reg);
self.in_use.insert(reg, false);
}
self.variable_locations.remove(var);
}
} }
/// Allocate a register for a named variable /// Allocate a register for a named variable
@@ -105,41 +148,46 @@ impl RegisterAllocator {
pub fn alloc_var( pub fn alloc_var(
&mut self, &mut self,
var_name: &str, var_name: &str,
) -> Result<(String, Vec<String>), CompilerError> { ) -> Result<(Register, Vec<String>), CompilerError> {
if let Some(location) = self.variable_locations.get(var_name).cloned() { if let Some(mut location) = self.variable_locations.get(var_name).cloned() {
match location { // if the var is in a register we can use it already.
Location::Register(reg) => { if let Some(reg) = location.register {
return Ok((reg.clone(), Vec::new())); return Ok((reg, Vec::new()));
} }
Location::Stack(offset) => {
// Variable was pushed, need to calculate actual position
let (reg, mut code) = self.alloc_temp()?;
// Load from bpr + offset (offset is negative) // if the variable is on the stack only, we need to get it in a register.
code.push(format!("\tsubi bpr {} {}", -(offset + 4), reg)); if let Some(offset) = location.stack {
code.push(format!( // Variable was pushed, need to calculate actual position and update its
"\tldw {}, {} // bpr{}: {}", // location.
reg, let (reg, mut code) = self.alloc_temp()?;
reg,
offset - 4,
var_name
));
// Update location to register // acknowledge var is now in a reg as well.
self.variable_locations location.register = Some(reg);
.insert(var_name.to_string(), Location::Register(reg.clone()));
self.register_contents
.insert(reg.clone(), var_name.to_string());
return Ok((reg, code)); // Load from bpr + offset (offset is negative)
} code.push(format!("\tsubi bpr {} {}", -(offset + 4), reg));
code.push(format!(
"\tldw {}, {} // bpr{}: {}",
reg,
reg,
offset - 4,
var_name
));
// Update location to register
self.variable_locations
.insert(var_name.to_string(), location);
self.register_contents
.insert(reg.clone(), var_name.to_string());
return Ok((reg, code));
} }
} }
// Variable doesn't have a location yet, allocate a new register // Variable doesn't have a location yet, allocate a new register
let (reg, code) = self.alloc_temp()?; let (reg, code) = self.alloc_temp()?;
self.variable_locations self.variable_locations
.insert(var_name.to_string(), Location::Register(reg.clone())); .insert(var_name.to_string(), Location::register(reg));
self.register_contents self.register_contents
.insert(reg.clone(), var_name.to_string()); .insert(reg.clone(), var_name.to_string());
@@ -156,83 +204,89 @@ impl RegisterAllocator {
pub fn load_var( pub fn load_var(
&mut self, &mut self,
var_name: &str, var_name: &str,
) -> Result<(String, Vec<String>), CompilerError> { ) -> Result<(Register, Vec<String>), CompilerError> {
self.alloc_var(var_name) self.alloc_var(var_name)
} }
/// Store a value from a register into a variable /// Store a value from a register into a variable
/// Updates tracking and returns any necessary assembly code /// Updates tracking and returns any necessary assembly code
pub fn store_var(&mut self, var_name: &str, source_reg: &str) -> Vec<String> { pub fn store_var(&mut self, var_name: &str, source_reg: &Register) -> Vec<String> {
let mut code = Vec::new();
// Check if variable already has a location // Check if variable already has a location
if let Some(location) = self.variable_locations.get(var_name) { if let Some(location) = self.variable_locations.get(var_name) {
match location { // if the variable exists in a register we write to that.
Location::Register(dest_reg) => { if let Some(reg) = location.register {
if dest_reg != source_reg { if reg == *source_reg {
code.push(format!( return vec![format!(
"\tmov {}, {} // var {}", "\tmov {}, {} // var {}",
source_reg, dest_reg, var_name source_reg, reg, var_name
)); )];
}
}
Location::Stack(offset) => {
code.push(format!(
"\tstw {}, bpr, {} // var {}",
source_reg, offset, var_name
));
} }
} }
} else {
// Variable doesn't exist yet, we can just use the same reg.
// self.variable_locations.insert( // if the variable exists on the stack but not a register we write here.
// var_name.to_string(), if let Some(offset) = location.stack {
// Location::Register(source_reg.to_string()), return vec![format!(
// ); "\tstw {}, bpr, {} // var {}",
// self.register_contents source_reg, offset, var_name
// .insert(source_reg.to_string(), var_name.to_string()); )];
// self.in_use.insert(source_reg.to_string(), true);
let source_reg = source_reg.to_string();
// if we can avoid a move, absolutely do that.
if self.available_registers.contains(&source_reg) {
self.variable_locations
.insert(var_name.to_string(), Location::Register(source_reg.clone()));
self.register_contents
.insert(source_reg.clone(), var_name.to_string());
self.in_use.insert(source_reg, true);
} else if let Some(free_reg) = self.find_free_register() {
code.push(format!("\tmov {}, {}", source_reg, free_reg));
self.variable_locations
.insert(var_name.to_string(), Location::Register(free_reg.clone()));
self.register_contents
.insert(free_reg.clone(), var_name.to_string());
self.in_use.insert(free_reg, true);
} else {
// No free registers - allocate on stack
// code.push(format!("\tstw {}, bpr, {}", source_reg, self.stack_offset));
// self.variable_locations
// .insert(var_name.to_string(), Location::Stack(self.stack_offset));
// self.stack_offset -= 4; // Move to next stack slot
//
todo!(
"we should spill other registers and keep this variable on the stack as it's more recent!"
);
} }
} }
code // Variable doesn't exist yet, we can just use the same reg.
// if we can avoid a move, absolutely do that.
// if this is true then there's no permanent variable here so it's safe to use.
if !self.register_contents.contains_key(source_reg) {
self.variable_locations
.insert(var_name.to_string(), Location::register(*source_reg));
self.register_contents
.insert(*source_reg, var_name.to_string());
self.in_use.insert(*source_reg, true);
return Vec::new();
}
// if current register isn't free, (eg is another variable) we assign somewhere
// else.
if let Some(free_reg) = self.find_free_register() {
self.variable_locations
.insert(var_name.to_string(), Location::register(free_reg));
self.register_contents
.insert(free_reg.clone(), var_name.to_string());
self.in_use.insert(free_reg, true);
return vec![format!("\tmov {}, {}", source_reg, free_reg)];
}
// No free registers - allocate on stack
// code.push(format!("\tstw {}, bpr, {}", source_reg, self.stack_offset));
// self.variable_locations
// .insert(var_name.to_string(), Location::Stack(self.stack_offset));
// self.stack_offset -= 4; // Move to next stack slot
//
todo!("an efficient stack spilling algorithm. needs scope awareness.");
} }
/// Spill a register to the stack /// spill a register to the stack (WITHOUT FREEING)
/// Returns assembly code to perform the spill pub fn spill_register(
pub fn spill_register(&mut self, reg: &str) -> Result<Vec<String>, CompilerError> { &mut self,
reg: &Register,
) -> Result<Vec<String>, CompilerError> {
let mut code = Vec::new(); let mut code = Vec::new();
if let Some(var_name) = self.register_contents.get(reg).cloned() { // check if the variable is declared.
// PUSH register to stack (spr decrements automatically) if let Some(var_name) = self.register_contents.get(reg).cloned()
&& let Some(location) = self.variable_locations.get_mut(&var_name)
{
// check if var is on the stack
if let Some(offset) = location.stack {
// ensure stack value is up to date with register value.
code.push(format!("\tstw {}, {}", reg, offset));
return Ok(code);
}
// if the variable is not on the stack:
// push register to stack (spr decrements automatically)
code.push(format!( code.push(format!(
"\tpush {} // bpr{}: {}", "\tpush {} // bpr{}: {}",
reg, self.stack_offset, var_name reg, self.stack_offset, var_name
@@ -240,37 +294,83 @@ impl RegisterAllocator {
// Track that we pushed one word // Track that we pushed one word
self.stack_offset -= 4; self.stack_offset -= 4;
// Update variable location - it's now at current spr // Update variable location - it's now at current spr
// Note: We track offset from bpr for consistency // Note: We track offset from bpr for consistency
self.variable_locations location.stack = Some(self.stack_offset);
.insert(var_name.clone(), Location::Stack(self.stack_offset)); Ok(code)
} else {
// Remove from register tracking Err(CompilerError::Generic(format!(
self.register_contents.remove(reg); "Register {} does not contain a variable to spill!",
reg
)))
} }
}
Ok(code) /// free a register by spilling it to the stack.
/// Returns assembly code to perform the spill
pub fn free_register(
&mut self,
reg: &Register,
) -> Result<Vec<String>, CompilerError> {
let mut code = Vec::new();
// check if the variable is declared.
if let Some(var_name) = self.register_contents.get(reg).cloned()
&& let Some(location) = self.variable_locations.get_mut(&var_name)
{
// check if var name is on the stack
if let Some(offset) = location.stack {
// store current register value in stack location
code.push(format!("\tstw {}, {}", reg, offset));
// free the register.
location.register = None;
self.register_contents.remove(reg);
return Ok(code);
}
// if the variable is not on the stack:
// push register to stack (spr decrements automatically)
code.push(format!(
"\tpush {} // bpr{}: {}",
reg, self.stack_offset, var_name
));
// Track that we pushed one word
self.stack_offset -= 4;
// Update variable location - it's now at current spr
// Note: We track offset from bpr for consistency
location.stack = Some(self.stack_offset);
location.register = None;
self.register_contents.remove(reg);
Ok(code)
} else {
Err(CompilerError::Generic(format!(
"Register {} does not contain a variable to spill!",
reg
)))
}
} }
/// Find a free register (not currently in use) /// Find a free register (not currently in use)
fn find_free_register(&self) -> Option<String> { fn find_free_register(&self) -> Option<Register> {
for reg in &self.available_registers { self.in_use
if !self.in_use.get(reg).unwrap_or(&false) { .iter()
return Some(reg.clone()); .filter(|(_, in_use)| !**in_use)
} .map(|(reg, _)| *reg)
} .next()
None
} }
/// Spill all registers to stack (useful before function calls) /// Spill all registers to stack (useful before function calls)
pub fn _spill_all(&mut self) -> Vec<String> { pub fn _spill_all(&mut self) -> Vec<String> {
let mut code = Vec::new(); let mut code = Vec::new();
let regs_to_spill: Vec<String> = self.register_contents.keys().cloned().collect(); let regs_to_spill: Vec<Register> =
self.register_contents.keys().cloned().collect();
for reg in regs_to_spill { for reg in regs_to_spill {
if let Ok(spill_code) = self.spill_register(&reg) { if let Ok(spill_code) = self.free_register(&reg) {
code.extend(spill_code); code.extend(spill_code);
} }
} }
@@ -293,23 +393,43 @@ impl RegisterAllocator {
self.variable_locations.clear(); self.variable_locations.clear();
self.register_contents.clear(); self.register_contents.clear();
self.stack_offset = -4; self.stack_offset = -4;
self.in_use.clear(); self.in_use = vec![
Register::Rg0,
Register::Rg1,
Register::Rg2,
Register::Rg3,
Register::Rg4,
Register::Rg5,
Register::Rg6,
Register::Rg7,
Register::Rg8,
Register::Rg9,
Register::Rga,
Register::Rgb,
Register::Rgc,
Register::Rgd,
Register::Rge,
Register::Rgf,
]
.iter()
.map(|&reg| (reg, false))
.collect();
} }
/// Mark a variable as dead (no longer needed) /// Mark a variable as dead (no longer needed)
/// Frees its register if it's in one /// Frees its register if it's in one
pub fn _free_var(&mut self, var_name: &str) { // pub fn _free_var(&mut self, var_name: &str) {
if let Some(Location::Register(reg)) = self.variable_locations.get(var_name) { // if let Some(Location::Register(reg)) = self.variable_locations.get(var_name) {
let reg = reg.clone(); // let reg = reg.clone();
self.register_contents.remove(&reg); // self.register_contents.remove(&reg);
self.in_use.insert(reg, false); // self.in_use.insert(reg, false);
} // }
self.variable_locations.remove(var_name); // self.variable_locations.remove(var_name);
} // }
/// Get list of registers that contain variables and are in use /// Get list of registers that contain variables and are in use
/// These need to be saved before function calls /// These need to be saved before function calls
pub fn get_caller_saved_registers(&self) -> Vec<String> { pub fn get_caller_saved_registers(&self) -> Vec<Register> {
self.register_contents self.register_contents
.iter() .iter()
.filter(|(reg, _)| *self.in_use.get(*reg).unwrap_or(&false)) .filter(|(reg, _)| *self.in_use.get(*reg).unwrap_or(&false))
@@ -346,3 +466,50 @@ impl RegisterAllocator {
code code
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Register {
Rg0,
Rg1,
Rg2,
Rg3,
Rg4,
Rg5,
Rg6,
Rg7,
Rg8,
Rg9,
Rga,
Rgb,
Rgc,
Rgd,
Rge,
Rgf,
Zero,
Null,
}
impl fmt::Display for Register {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rg0 => write!(f, "rg0"),
Self::Rg1 => write!(f, "rg1"),
Self::Rg2 => write!(f, "rg2"),
Self::Rg3 => write!(f, "rg3"),
Self::Rg4 => write!(f, "rg4"),
Self::Rg5 => write!(f, "rg5"),
Self::Rg6 => write!(f, "rg6"),
Self::Rg7 => write!(f, "rg7"),
Self::Rg8 => write!(f, "rg8"),
Self::Rg9 => write!(f, "rg9"),
Self::Rga => write!(f, "rga"),
Self::Rgb => write!(f, "rgb"),
Self::Rgc => write!(f, "rgc"),
Self::Rgd => write!(f, "rgd"),
Self::Rge => write!(f, "rge"),
Self::Rgf => write!(f, "rgf"),
Self::Zero => write!(f, "zero"),
Self::Null => write!(f, "null"),
}
}
}
+13 -2
View File
@@ -351,6 +351,7 @@ impl Parser {
op, op,
left: Box::new(expr), left: Box::new(expr),
right, right,
type_id: None,
}; };
} }
@@ -371,6 +372,7 @@ impl Parser {
op, op,
left: Box::new(expr), left: Box::new(expr),
right, right,
type_id: None,
}; };
} }
@@ -391,6 +393,7 @@ impl Parser {
op, op,
left: Box::new(expr), left: Box::new(expr),
right, right,
type_id: None,
}; };
} }
@@ -407,7 +410,11 @@ impl Parser {
if let Some(op) = op { if let Some(op) = op {
self.advance(); self.advance();
let operand = Box::new(self.parse_unary()?); let operand = Box::new(self.parse_unary()?);
return Ok(Expression::Unary { op, operand }); return Ok(Expression::Unary {
op,
operand,
type_id: None,
});
} }
self.parse_primary() self.parse_primary()
@@ -418,7 +425,10 @@ impl Parser {
TokenType::Number(n) => { TokenType::Number(n) => {
let value = *n; let value = *n;
self.advance(); self.advance();
Ok(Expression::Number(value as isize)) Ok(Expression::Number {
value: value as isize,
type_id: None,
})
} }
TokenType::Identifier(name) => { TokenType::Identifier(name) => {
let name = name.clone(); let name = name.clone();
@@ -445,6 +455,7 @@ impl Parser {
namespace: None, namespace: None,
}, },
args, args,
type_id: None,
}) })
} else { } else {
Ok(Expression::Variable { Ok(Expression::Variable {
+4 -4
View File
@@ -2,11 +2,11 @@ use common::logging::log;
use crate::model::{CompilerError, Program}; use crate::model::{CompilerError, Program};
use parser::{ParseResult, Parser}; use parser::{ParseResult, Parser};
use semantic_analyser::Analyser; // use semantic_analyser::Analyser;
pub mod lexer; pub mod lexer;
pub mod parser; pub mod parser;
pub mod semantic_analyser; // pub mod semantic_analyser;
pub fn generate_ast(input: &str) -> Result<Program, CompilerError> { pub fn generate_ast(input: &str) -> Result<Program, CompilerError> {
log("Tokenising Input..."); log("Tokenising Input...");
@@ -30,8 +30,8 @@ pub fn generate_ast(input: &str) -> Result<Program, CompilerError> {
log("Analyzing AST..."); log("Analyzing AST...");
log("Checking Type Information..."); log("Checking Type Information...");
let analyser = Analyser::new(); // let mut analyser = Analyser::new();
analyser.analyse(ast.clone()).unwrap(); // analyser.analyse(ast.clone()).unwrap();
log("Type Checking Complete..."); log("Type Checking Complete...");
Ok(ast) Ok(ast)
+24 -7
View File
@@ -1,7 +1,7 @@
use super::lexer::Token; use super::lexer::Token;
use crate::model::{ use crate::model::{
BinaryOperator, Block, CompilerError, ConstExpr, Declaration, Dependency, Expression, BinaryOperator, Block, Call, CompilerError, ConstExpr, Declaration, Dependency,
Program, Statement, TypeId, UnaryOperator, Variable, Expression, Program, Statement, TypeId, UnaryOperator, Variable,
}; };
use crate::{expect_tt, expect_value}; use crate::{expect_tt, expect_value};
use std::ops::{ControlFlow, FromResidual, Try}; use std::ops::{ControlFlow, FromResidual, Try};
@@ -353,7 +353,7 @@ impl Parser {
let mut expr = self.parse_additive()?; let mut expr = self.parse_additive()?;
while let Some(op) = match self.peek_next()? { while let Some(op) = match self.peek_next()? {
Token::EqualEqual => Some(BinaryOperator::Ne), Token::EqualEqual => Some(BinaryOperator::Eq),
Token::BangEqual => Some(BinaryOperator::Ne), Token::BangEqual => Some(BinaryOperator::Ne),
Token::Less => Some(BinaryOperator::Lt), Token::Less => Some(BinaryOperator::Lt),
Token::Greater => Some(BinaryOperator::Gt), Token::Greater => Some(BinaryOperator::Gt),
@@ -367,7 +367,8 @@ impl Parser {
op, op,
left: Box::new(expr), left: Box::new(expr),
right, right,
} type_id: Some(TypeId::Bool),
};
} }
ParseResult::Accept(expr) ParseResult::Accept(expr)
@@ -387,6 +388,7 @@ impl Parser {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_additive()?), right: Box::new(self.parse_additive()?),
type_id: Some(TypeId::U32),
}) })
} }
@@ -404,6 +406,7 @@ impl Parser {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_multiplicative()?), right: Box::new(self.parse_multiplicative()?),
type_id: None,
}) })
} }
@@ -418,14 +421,21 @@ impl Parser {
self.next()?; self.next()?;
let operand = Box::new(self.parse_unary()?); let operand = Box::new(self.parse_unary()?);
ParseResult::Accept(Expression::Unary { op, operand }) ParseResult::Accept(Expression::Unary {
op,
operand,
type_id: None,
})
} }
fn parse_primary(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_primary(&mut self) -> ParseResult<Expression, CompilerError> {
match self.peek_next()? { match self.peek_next()? {
Token::Integer(value) => { Token::Integer(value) => {
self.next()?; self.next()?;
ParseResult::Accept(Expression::Number(value as isize)) ParseResult::Accept(Expression::Number {
value: value as isize,
type_id: None,
})
} }
Token::String(value) => { Token::String(value) => {
self.next()?; self.next()?;
@@ -450,7 +460,14 @@ impl Parser {
let _ = expect_tt!(self.next()?, RightParen)?; let _ = expect_tt!(self.next()?, RightParen)?;
ParseResult::Accept(Expression::Call { name, args }) ParseResult::Accept(Expression::Call {
func: Call {
name: name.clone(),
args,
},
type_id: None,
})
} else { } else {
ParseResult::Accept(Expression::Variable { ParseResult::Accept(Expression::Variable {
name, name,
+217 -4
View File
@@ -1,13 +1,226 @@
use crate::model::{CompilerError, Program}; use std::collections::HashMap;
pub struct Analyser; use crate::model::{
BinaryOperator, // You'll need to add this to your imports
CompilerError,
Declaration,
Dependency,
Expression,
Program,
TypeId,
UnaryOperator,
};
pub struct Analyser {
symbol_table: HashMap<String, Declaration>,
}
const NUMERIC_TYPES: &[TypeId] = &[
TypeId::U32,
TypeId::I32,
TypeId::I16,
TypeId::U16,
TypeId::I8,
TypeId::U8,
];
impl Analyser { impl Analyser {
pub fn new() -> Self { pub fn new() -> Self {
Self Self {
symbol_table: HashMap::new(),
}
} }
pub fn analyse(&self, _ast: Program) -> Result<(), CompilerError> { pub fn analyse(&mut self, ast: Program) -> Result<(), CompilerError> {
// build table of global symbols.
for dec in ast.declarations {
let name = match dec.clone() {
Declaration::Function { name, .. } => name,
Declaration::Variable { var, .. } => var.name,
Declaration::Dependency(Dependency { name, .. }) => name,
};
self.symbol_table.insert(name, dec);
}
Ok(()) Ok(())
} }
fn match_type(
actual: TypeId,
expected: Option<TypeId>,
) -> Result<TypeId, CompilerError> {
match expected {
Some(id) => {
if id != actual {
Err(CompilerError::TypeMismatch(id, actual))
} else {
Ok(actual)
}
}
None => Ok(actual),
}
}
fn get_type(
&mut self, // Changed from &self to &mut self since we modify expr
expr: &mut Expression,
expected_type: Option<TypeId>,
) -> Result<TypeId, CompilerError> {
match expr {
// Correct IFF we're expecting a void type
Expression::Empty => Self::match_type(TypeId::Void, expected_type),
// Correct IFF we're expecting a char type
Expression::CharLiteral(_) => Self::match_type(TypeId::Char, expected_type),
// Correct IFF we're expecting a string slice type
Expression::StringLiteral(_) => {
Self::match_type(TypeId::Ptr(Box::new(TypeId::Char)), expected_type)
}
Expression::Variable { name, expr_type } => {
let actual = expr_type.clone().ok_or(CompilerError::UnknownType)?;
Self::match_type(actual, expected_type)
}
Expression::Number { value, type_id } => {
// If we already know the TypeId
if let Some(id) = type_id {
return Self::match_type(id.clone(), expected_type);
}
// If we're expecting a type id, check it's numeric.
// TODO: add checks to make sure it's valid for its size eg u8 cant be
// more than 255
if let Some(expected) = expected_type {
if NUMERIC_TYPES.contains(&expected) {
*type_id = Some(expected.clone());
return Ok(expected);
} else {
return Err(CompilerError::TypeMismatch(expected, TypeId::U32));
}
}
// Default to i32 if no type information is available
*type_id = Some(TypeId::I32);
Ok(TypeId::I32)
}
Expression::Binary {
op,
left,
right,
type_id,
} => {
// For binary operations, both operands should have compatible types
// and the result type depends on the operation
let left_type = self.get_type(left, None)?;
let right_type = self.get_type(right, Some(left_type.clone()))?;
// For numeric operations, result has the same type as operands
if NUMERIC_TYPES.contains(&left_type)
&& NUMERIC_TYPES.contains(&right_type)
{
*type_id = Some(left_type);
Self::match_type(left_type, expected_type)
} else {
Err(CompilerError::TypeMismatch(left_type, right_type))
}
}
Expression::Unary {
op,
operand,
type_id,
} => {
match op {
UnaryOperator::Plus | UnaryOperator::Minus => {
// Unary +/- require numeric operands
let inner_type = self.get_type(operand, None)?;
if NUMERIC_TYPES.contains(&inner_type) {
*type_id = Some(inner_type.clone());
Self::match_type(inner_type, expected_type)
} else {
Err(CompilerError::TypeMismatch(inner_type, TypeId::I32))
}
}
UnaryOperator::Dereference => {
// For dereference (*ptr), the operand must be a pointer
// and the result type is what the pointer points to
let inner_type = self.get_type(operand, None)?;
match inner_type {
TypeId::Ptr(inner) => {
let deref_type = *inner;
*type_id = Some(deref_type.clone());
Self::match_type(deref_type, expected_type)
}
_ => Err(CompilerError::Generic(format!(
"Cannot dereference non-pointer type: {:?}",
inner_type
))),
}
}
UnaryOperator::Reference => {
// For reference (&var), we need to determine what we're taking
// a reference to, then wrap it in a Ptr
// If expected_type is Ptr(T), then operand should have type T
let expected_inner = match expected_type.clone() {
Some(TypeId::Ptr(inner)) => Some(*inner),
_ => None,
};
let inner_type = self.get_type(operand, expected_inner)?;
let ref_type = TypeId::Ptr(Box::new(inner_type));
*type_id = Some(ref_type.clone());
Self::match_type(ref_type, expected_type)
}
}
}
Expression::Call {
name,
args,
type_id,
} => match self.symbol_table.get(&name.name) {
Some(Declaration::Function {
params,
return_type,
..
}) => {
// check that we've given the right number of arguments.
if args.len() != params.len() {
return Err(CompilerError::Generic(format!(
"Function {} expected {} arguments but received {}",
name.name,
params.len(),
args.len()
)));
}
for (arg, param) in args.iter_mut().zip(params.iter()) {
// check that the argument type matches the parameter type.
let provided_type = self.get_type(arg, Some(param.type_id))?;
if provided_type != param.type_id {
return Err(CompilerError::TypeMismatch(
param.type_id,
provided_type,
));
}
}
*type_id = Some(return_type.clone());
Self::match_type(return_type.clone(), expected_type)
}
_ => Err(CompilerError::Generic(format!(
"Function {} not found in symbol table",
name.name
))),
},
}
}
} }
+2 -2
View File
@@ -1,12 +1,12 @@
use crate::model::{CompilerError, Program}; use crate::model::{CompilerError, Program};
mod c; // mod c;
mod dsc; mod dsc;
pub fn compiler_frontend(ext: &str, data: &str) -> Result<Program, CompilerError> { pub fn compiler_frontend(ext: &str, data: &str) -> Result<Program, CompilerError> {
match ext { match ext {
"dsc" => Ok(dsc::generate_ast(&data)?), "dsc" => Ok(dsc::generate_ast(&data)?),
"c" => Ok(c::generate_ast(&data)?), // "c" => Ok(c::generate_ast(&data)?),
_ => Err(CompilerError::Generic(format!( _ => Err(CompilerError::Generic(format!(
"File type {} not supported", "File type {} not supported",
ext ext
+2
View File
@@ -46,6 +46,8 @@ pub fn compile_file(
Err(err) => return Err(format!("Compilation failed: {err:?}").into()), Err(err) => return Err(format!("Compilation failed: {err:?}").into()),
}; };
println!("Parsed AST: {:#?}", ast);
let output_ext = output_path let output_ext = output_path
.extension() .extension()
.and_then(|s| s.to_str()) .and_then(|s| s.to_str())
+80 -8
View File
@@ -9,9 +9,11 @@ pub enum CompilerError {
Undefined(Name), Undefined(Name),
InvalidSyntax(String), InvalidSyntax(String),
Generic(String), Generic(String),
UnknownType,
TypeMismatch(TypeId, TypeId),
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Name { pub struct Name {
pub name: String, pub name: String,
pub namespace: Option<String>, pub namespace: Option<String>,
@@ -46,7 +48,7 @@ pub struct Dependency {
} }
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum TypeId { pub enum TypeId {
U8, U8,
U16, U16,
@@ -54,18 +56,45 @@ pub enum TypeId {
I8, I8,
I16, I16,
I32, I32,
Bool,
Char, Char,
Void, Void,
Ptr(Box<TypeId>), Ptr(Box<TypeId>),
Ref(Box<TypeId>), Ref(Box<TypeId>),
Array(Box<TypeId>, usize), Array(Box<TypeId>, usize),
Struct { name: Name, fields: Vec<Variable> }, Struct { name: Name, fields: Vec<TypeId> },
}
impl fmt::Display for TypeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::U8 => write!(f, "u8"),
Self::U16 => write!(f, "u16"),
Self::U32 => write!(f, "u32"),
Self::I8 => write!(f, "i8"),
Self::I16 => write!(f, "i16"),
Self::I32 => write!(f, "i32"),
Self::Bool => write!(f, "bool"),
Self::Char => write!(f, "char"),
Self::Void => write!(f, "void"),
Self::Ptr(t) => write!(f, "*{}", t),
Self::Ref(t) => write!(f, "&{}", t),
Self::Array(t, len) => write!(f, "[{}; {}]", t, len),
Self::Struct { name, fields } => {
write!(f, "struct {} {{", name)?;
for (i, field) in fields.iter().enumerate() {
write!(f, "{}: {}", i, field)?;
}
write!(f, "}}")
}
}
}
} }
pub type Block = Vec<Statement>; pub type Block = Vec<Statement>;
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Variable { pub struct Variable {
pub name: String, pub name: String,
pub type_id: TypeId, pub type_id: TypeId,
@@ -100,6 +129,7 @@ pub enum Statement {
body: Vec<Statement>, body: Vec<Statement>,
}, },
Loop(Block), Loop(Block),
Defer(Call),
Break, Break,
Continue, Continue,
Return(Option<Expression>), Return(Option<Expression>),
@@ -128,28 +158,47 @@ pub enum Expression {
op: BinaryOperator, op: BinaryOperator,
left: Box<Expression>, left: Box<Expression>,
right: Box<Expression>, right: Box<Expression>,
// Post-Semantic Analysis
type_id: Option<TypeId>,
}, },
Unary { Unary {
op: UnaryOperator, op: UnaryOperator,
operand: Box<Expression>, operand: Box<Expression>,
// Post-Semantic Analysis
type_id: Option<TypeId>,
}, },
Variable { Variable {
name: Name, name: Name,
expr_type: Option<TypeId>, expr_type: Option<TypeId>,
}, },
Call { Call {
name: Name, func: Call,
args: Vec<Expression>,
// Post-Semantic Analysis
type_id: Option<TypeId>,
},
Number {
value: isize,
// Post-Semantic Analysis
type_id: Option<TypeId>,
}, },
Number(isize),
StringLiteral(String), StringLiteral(String),
CharLiteral(char), CharLiteral(char),
} }
#[derive(Debug, Clone)]
pub struct Call {
pub name: Name,
pub args: Vec<Expression>,
}
impl Expression { impl Expression {
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
Expression::Number(_) => true, Expression::Number { .. } => true,
Expression::StringLiteral(_) => true, Expression::StringLiteral(_) => true,
Expression::CharLiteral(_) => true, Expression::CharLiteral(_) => true,
Expression::Call { .. } => false, Expression::Call { .. } => false,
@@ -159,6 +208,29 @@ impl Expression {
Expression::Variable { .. } => true, Expression::Variable { .. } => true,
} }
} }
pub fn type_id(&self) -> Result<TypeId, CompilerError> {
match self {
Expression::Number { type_id, .. } => {
type_id.clone().ok_or(CompilerError::UnknownType)
}
Expression::StringLiteral(_) => Ok(TypeId::Ptr(Box::new(TypeId::Char))),
Expression::CharLiteral(_) => Ok(TypeId::Char),
Expression::Call { type_id, .. } => {
type_id.clone().ok_or(CompilerError::UnknownType)
}
Expression::Binary { type_id, .. } => {
type_id.clone().ok_or(CompilerError::UnknownType)
}
Expression::Unary { type_id, .. } => {
type_id.clone().ok_or(CompilerError::UnknownType)
}
Expression::Empty => Ok(TypeId::Void),
Expression::Variable { expr_type, .. } => {
expr_type.clone().ok_or(CompilerError::UnknownType)
}
}
}
} }
#[allow(unused)] #[allow(unused)]