From 22241a5633713e8b874f188e3f069e6c481dde93 Mon Sep 17 00:00:00 2001 From: zxq5 Date: Mon, 9 Feb 2026 00:10:37 +0000 Subject: [PATCH] - implementation of ` = ` type statements such as `x += 5` - implementation of logical and shift operations in parser and codegen. - implementation of sizeof keyword as unary operator in progress (non functional) - implementation of prefix and postfix inc/dec operators - array access by index (implemented, untested as arrays aren't implemented yet). essentially just a pointer write with offset. - struct/member access (parsing implemented, untested.) --- compiler/src/backend/dsa/codegen.rs | 378 ++++++++++++++++++++++------ compiler/src/frontend/dsc/parser.rs | 169 +++++++++++-- compiler/src/model.rs | 19 +- 3 files changed, 457 insertions(+), 109 deletions(-) diff --git a/compiler/src/backend/dsa/codegen.rs b/compiler/src/backend/dsa/codegen.rs index 64fa0a2..7ba886c 100644 --- a/compiler/src/backend/dsa/codegen.rs +++ b/compiler/src/backend/dsa/codegen.rs @@ -9,8 +9,8 @@ use crate::backend::dsa::registers::Register; use crate::{block, comment, dsa}; use crate::model::{ - BinaryOperator, Call, CompilerError, ConstExpr, Declaration, Dependency, Expression, - Program, Statement, TypeId, UnaryOperator, Variable, + AssignmentOperator, BinaryOperator, Call, CompilerError, ConstExpr, Declaration, + Dependency, Expression, Program, Statement, TypeId, UnaryOperator, Variable, }; pub struct CodeGenerator { @@ -271,24 +271,106 @@ impl CodeGenerator { self.allocator.free_temp(ptr_reg); } - Statement::Assign { varname, value } => { + Statement::Assign { + varname, + value, + operator, + } => { // Evaluate expression let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?; code.extend(expr_code); + if *operator == AssignmentOperator::Assign { + // Check if this is a global variable + if self.is_global(varname) { + // Store to global label + code.push(format!("\tstw {}, {}", result_reg, varname)); + } else { + // Store result in local variable + let store_code = self.allocator.store_var(varname, &result_reg); + code.extend(store_code); + } + + // Free temporary register + self.allocator.free_temp(result_reg); + + return Ok(code); + } + + // for more complex assignment cases we need an intermediate register. + let (temp_reg, temp_code) = self.allocator.alloc_temp()?; + code.extend(temp_code); + + // fetch the value of the variable + let (var_reg, var_code) = if self.is_global(varname) { + (temp_reg, vec![format!("\tldw {}, {}", varname, temp_reg)]) + } else { + self.allocator.load_var(varname)? + }; + code.extend(var_code); + + let assign_code = match operator { + AssignmentOperator::Assign => { + unreachable!("assignment was already checked earlier.") + } + AssignmentOperator::AddAssign => { + format!("\tadd {var_reg}, {result_reg}, {temp_reg}") + } + AssignmentOperator::SubAssign => { + format!("\tsub {var_reg}, {result_reg}, {temp_reg}") + } + AssignmentOperator::MulAssign => { + return Err(CompilerError::Unimplemented(format!( + "TODO: implement multiplication for assignment" + ))); + } + AssignmentOperator::DivAssign => { + return Err(CompilerError::Unimplemented(format!( + "TODO: write proper div function for DSA" + ))); + } + AssignmentOperator::ModAssign => { + format!("\tmod {var_reg}, {result_reg}, {temp_reg}") + } + AssignmentOperator::AndAssign => { + format!("\tand {var_reg}, {result_reg}, {temp_reg}") + } + AssignmentOperator::OrAssign => { + format!("\tor {var_reg}, {result_reg}, {temp_reg}") + } + AssignmentOperator::XorAssign => { + format!("\txor {var_reg}, {result_reg}, {temp_reg}") + } + AssignmentOperator::LeftShiftAssign => { + // this is only useful if we optimise out the register allocation + // inside value. if let Expression::Number + // { value, .. } = *value { format!("\ + // tshl {var_reg}, {value}, {temp_reg}") } + format!("\tshl {var_reg}, {result_reg}, 0, {temp_reg}") + } + AssignmentOperator::RightShiftAssign => { + // this is only useful if we optimise out the register allocation + // if let Expression::Number { value, .. } = *value { + // format!("\tshr {var_reg}, {value}, {temp_reg}") + // } + format!("\tshr {var_reg}, {result_reg}, 0, {temp_reg}") + } + }; + code.push(assign_code); + // Check if this is a global variable if self.is_global(varname) { // Store to global label - code.push(format!("\tstw {}, {}", result_reg, varname)); + code.push(format!("\tstw {}, {}", temp_reg, varname)); } else { // Store result in local variable - let store_code = self.allocator.store_var(varname, &result_reg); + let store_code = self.allocator.store_var(varname, &temp_reg); code.extend(store_code); } - // Free temporary register self.allocator.free_temp(result_reg); + self.allocator.free_temp(temp_reg); } Statement::Return(expr) => { @@ -414,16 +496,17 @@ impl CodeGenerator { let mut code = Vec::new(); match expr { - Expression::StringLiteral(value) => { + Expression::Empty => Ok((Register::Null, code)), + + Expression::Number { value, .. } => { let (reg, alloc_code) = self.allocator.alloc_temp()?; code.extend(alloc_code); - // write string into memory - let uuid = self.get_unique_label(); - func_body.insert(0, format!("db str_{uuid}: \"{value}\"")); - - // Load pointer to string - code.push(format!("\tlwi str_{uuid}, {reg}")); + // Load immediate value + code.push(format!("\tlli {}, {}", value & 0xFFFF, reg)); + if *value > 0xFFFF || *value < 0 { + code.push(format!("\tlui {}, {}", (value >> 16) & 0xFFFF, reg)); + } Ok((reg, code)) } @@ -438,19 +521,22 @@ impl CodeGenerator { Ok((reg, code)) } - Expression::Number { value, .. } => { + Expression::StringLiteral(value) => { let (reg, alloc_code) = self.allocator.alloc_temp()?; code.extend(alloc_code); - // Load immediate value - code.push(format!("\tlli {}, {}", value & 0xFFFF, reg)); - if *value > 0xFFFF || *value < 0 { - code.push(format!("\tlui {}, {}", (value >> 16) & 0xFFFF, reg)); - } + // write string into memory + let uuid = self.get_unique_label(); + func_body.insert(0, format!("db str_{uuid}: \"{value}\"")); + + // Load pointer to string + code.push(format!("\tlwi str_{uuid}, {reg}")); Ok((reg, code)) } + Expression::ArrayLiteral { elements, type_id } => todo!(), + Expression::Variable { name, .. } => { if self.is_global(&name.name) { // Allocate a temporary register for the global @@ -489,16 +575,14 @@ impl CodeGenerator { // Generate operation match op { BinaryOperator::Add => { - code.push(format!( - "\tadd {}, {}, {}", - left_reg, right_reg, result_reg - )); + code.push( + format!("\tadd {left_reg}, {right_reg}, {result_reg}",), + ); } BinaryOperator::Sub => { - code.push(format!( - "\tsub {}, {}, {}", - left_reg, right_reg, result_reg - )); + code.push( + format!("\tsub {left_reg}, {right_reg}, {result_reg}",), + ); } BinaryOperator::Mul => { self.include("maths", "./lib/maths/core.dsa"); @@ -509,6 +593,53 @@ impl CodeGenerator { code.push(format!("\tpop {}", result_reg)); code.push("\tpop zero".to_string()); } + BinaryOperator::Div => { + return Err(CompilerError::Unimplemented(format!( + "TODO: write proper div function for DSA" + ))); + // self.include("maths", "./lib/maths/core.dsa"); + // // Call divide function + // code.push(format!("\tpush {}", right_reg)); + // code.push(format!("\tpush {}", left_reg)); + // code.push("\tcall maths::divide".to_string()); + // code.push(format!("\tpop {}", result_reg)); + // code.push("\tpop zero".to_string()); + } + BinaryOperator::Mod => { + return Err(CompilerError::Unimplemented(format!( + "TODO: write proper mod function for DSA" + ))); + // self.include("maths", "./lib/maths/core.dsa"); + // // Call modulo function + // code.push(format!("\tpush {}", right_reg)); + // code.push(format!("\tpush {}", left_reg)); + // code.push("\tcall maths::modulo".to_string()); + // code.push(format!("\tpop {}", result_reg)); + // code.push("\tpop zero".to_string()); + } + BinaryOperator::BitwiseAnd => { + code.push(format!("\tand {left_reg}, {right_reg}, {result_reg}")); + } + BinaryOperator::BitwiseOr => { + code.push(format!("\tor {left_reg}, {right_reg}, {result_reg}")); + } + BinaryOperator::BitwiseXor => { + code.push(format!("\txor {left_reg}, {right_reg}, {result_reg}")); + } + BinaryOperator::LogicalAnd => { + return Err(CompilerError::Unimplemented(format!( + "assembler/ISA does not yet support logical and!" + ))); + } + BinaryOperator::LogicalOr => { + return Err(CompilerError::Unimplemented(format!( + "assembler/ISA does not yet support logical or!" + ))); + } + BinaryOperator::LeftShift => code + .push(format!("\tshl {left_reg}, {right_reg}, 0, {result_reg}")), + BinaryOperator::RightShift => code + .push(format!("\tshr {left_reg}, {right_reg}, 0, {result_reg}")), // Comparison operators - return 1 (true) or 0 (false) BinaryOperator::Equal => { code.push(format!("\tcmp {}, {}", left_reg, right_reg)); @@ -557,8 +688,7 @@ impl CodeGenerator { code.push(format!("\tjge {}", end_label)); code.push(format!("\tlli 8, {}", result_reg)); code.push(format!("{}:", end_label)); - } - _ => unimplemented!(), + } // _ => unimplemented!(), } // Free operand registers (allocator will protect variables) @@ -568,6 +698,105 @@ impl CodeGenerator { Ok((result_reg, code)) } + Expression::UnaryPostfix { op, operand, .. } => { + let (operand_reg, operand_code) = + self.generate_expression(operand, true, func_body)?; + code.extend(operand_code); + + let (result_reg, result_alloc) = self.allocator.alloc_temp()?; + code.extend(result_alloc); + + match op { + UnaryOperator::Increment => { + // prefix increment + // code.push(format!("\taddi {}, 1, {}", operand_reg, + // operand_reg)); + code.push(format!("\tmov {}, {}", operand_reg, result_reg)); + } + UnaryOperator::Decrement => { + // prefix decrement + // code.push(format!("\tsubi {}, 1, {}", operand_reg, + // operand_reg)); + code.push(format!("\tmov {}, {}", operand_reg, result_reg)); + } + _ => { + return Err(CompilerError::Generic(format!( + "{op} is prefix only!" + ))); + } + } + + self.allocator.free_temp(operand_reg); + Ok((result_reg, code)) + } + + Expression::Unary { op, operand, .. } => { + let (operand_reg, operand_code) = + self.generate_expression(operand, true, func_body)?; + code.extend(operand_code); + + let (result_reg, result_alloc) = self.allocator.alloc_temp()?; + code.extend(result_alloc); + + match op { + UnaryOperator::Minus => { + // Negate: result = 0 - operand + code.push(format!("\tsub zero, {}, {}", operand_reg, result_reg)); + } + UnaryOperator::Plus => { + // Just move + code.push(format!("\tmov {}, {}", operand_reg, result_reg)); + } + UnaryOperator::Dereference => { + code.push(format!("\tldw {}, {}", operand_reg, result_reg)); + } + UnaryOperator::AddressOf => { + // ensure the referenced variable is on the stack and return its + // address. + let (offset, alloc_code) = + self.allocator.free_register(&operand_reg)?; + code.extend(alloc_code); + code.push(format!( + "\taddi spr, {}, {}", + offset - self.allocator.get_stack_offset(), + result_reg + )); + } + UnaryOperator::SizeOf => { + if let Ok(id) = operand.type_id() { + let size = id.size(); + code.push(format!("\tmov {}, {}", size, result_reg)); + } + } + UnaryOperator::Increment => { + // prefix increment + // code.push(format!("\tmov {}, {}", operand_reg, result_reg)); + code.push(format!("\taddi {}, 1, {}", operand_reg, result_reg)); + } + UnaryOperator::Decrement => { + // prefix decrement + // code.push(format!("\tmov {}, {}", operand_reg, result_reg)); + code.push(format!("\tsubi {}, 1, {}", operand_reg, result_reg)); + } + UnaryOperator::BitwiseNot => { + code.push(format!("\tnot {}, {}", operand_reg, result_reg)); + } + UnaryOperator::LogicalNot => { + return Err(CompilerError::Unimplemented(format!( + "Assembler/ISA does not yet support logical not" + ))); + } + _ => { + return Err(CompilerError::Generic(format!( + "{op} is postfix only!" + ))); + } + } + + self.allocator.free_temp(operand_reg); + Ok((result_reg, code)) + } + Expression::Call { func: Call { name, args }, .. @@ -642,69 +871,52 @@ impl CodeGenerator { Ok((result_reg, code)) } - Expression::Unary { op, operand, .. } => { - let (operand_reg, operand_code) = - self.generate_expression(operand, true, func_body)?; - code.extend(operand_code); + Expression::IndexAccess { + expr, + index, + type_id, + } => { + let (expr_reg, expr_alloc) = + self.generate_expression(expr, true, func_body)?; + code.extend(expr_alloc); + + let (index_reg, index_alloc) = + self.generate_expression(index, true, func_body)?; + code.extend(index_alloc); let (result_reg, result_alloc) = self.allocator.alloc_temp()?; code.extend(result_alloc); - match op { - UnaryOperator::Minus => { - // Negate: result = 0 - operand - code.push(format!("\tsub zero, {}, {}", operand_reg, result_reg)); - } - UnaryOperator::Plus => { - // Just move - code.push(format!("\tmov {}, {}", operand_reg, result_reg)); - } - UnaryOperator::Dereference => { - code.push(format!("\tldw {}, {}", operand_reg, result_reg)); - } - UnaryOperator::AddressOf => { - // ensure the referenced variable is on the stack and return its - // address. - let (offset, alloc_code) = - self.allocator.free_register(&operand_reg)?; - code.extend(alloc_code); - code.push(format!( - "\taddi spr, {}, {}", - offset - self.allocator.get_stack_offset(), - result_reg - )); - } - UnaryOperator::SizeOf => { - if let Ok(id) = operand.type_id() { - let size = id.size(); - code.push(format!("\tmov {}, {}", size, result_reg)); - } - } - UnaryOperator::CastAs => {} /* this should be removed once the */ - // semantic analyser can handle it! - UnaryOperator::Increment => { - // prefix increment - code.push(format!("\tmov {}, {}", operand_reg, result_reg)); - code.push(format!("\taddi {}, {}, 1", operand_reg, operand_reg)); - } - UnaryOperator::Decrement => { - // prefix decrement - code.push(format!("\tmov {}, {}", operand_reg, result_reg)); - code.push(format!("\tsubi {}, {}, 1", operand_reg, operand_reg)); - } - UnaryOperator::BitwiseNot => { - code.push(format!("\tnot {}, {}", operand_reg, result_reg)); - } - UnaryOperator::LogicalNot => unimplemented!(), - } + // add the expr pointer to the index to get the final address. + code.push(format!("\tadd {expr_reg} {index_reg} {result_reg}")); + // load the value at the address. + code.push(format!("\tldw {result_reg} {result_reg}")); + + self.allocator.free_temp(expr_reg); + self.allocator.free_temp(index_reg); - self.allocator.free_temp(operand_reg); Ok((result_reg, code)) } + Expression::MemberAccess { + expr, + field_name, + type_id, + } => Err(CompilerError::Unimplemented(format!( + "Structs are not yet implemented!" + ))), - Expression::Empty => Ok((Register::Null, code)), + Expression::TypeCast { + expr, + target_type, + type_id, + } => { + let (expr_reg, expr_code) = + self.generate_expression(expr, true, func_body)?; - _ => unimplemented!(), + // not sure if we actually need to do anything here. + // for now we just return the previous expression. + Ok((expr_reg, expr_code)) + } } } diff --git a/compiler/src/frontend/dsc/parser.rs b/compiler/src/frontend/dsc/parser.rs index a922a8e..78cf2a9 100644 --- a/compiler/src/frontend/dsc/parser.rs +++ b/compiler/src/frontend/dsc/parser.rs @@ -1,7 +1,8 @@ use super::lexer::Token; use crate::model::{ - BinaryOperator, Block, Call, CompilerError, ConstExpr, Declaration, Dependency, - Expression, Program, Statement, TypeId, UnaryOperator, Variable, + AssignmentOperator, BinaryOperator, Block, Call, CompilerError, ConstExpr, + Declaration, Dependency, Expression, Program, Statement, TypeId, UnaryOperator, + Variable, }; use crate::{expect_tt, expect_value}; use std::ops::{ControlFlow, FromResidual, Try}; @@ -328,7 +329,26 @@ impl Parser { } self.next()?; - let _ = expect_tt!(self.next()?, Assign)?; + let operator = match self.peek_next()? { + Token::Assign => AssignmentOperator::Assign, + Token::PlusEqual => AssignmentOperator::AddAssign, + Token::MinusEqual => AssignmentOperator::SubAssign, + Token::StarEqual => AssignmentOperator::MulAssign, + Token::SlashEqual => AssignmentOperator::DivAssign, + Token::PercentEqual => AssignmentOperator::ModAssign, + Token::AndEqual => AssignmentOperator::AndAssign, + Token::OrEqual => AssignmentOperator::OrAssign, + Token::XorEqual => AssignmentOperator::XorAssign, + Token::ShlEqual => AssignmentOperator::LeftShiftAssign, + Token::ShrEqual => AssignmentOperator::RightShiftAssign, + _ => { + return ParseResult::Reject(CompilerError::UnexpectedToken( + self.peek_next()?.tt().to_string(), + )); + } + }; + + self.next()?; let value = self.parse_expression()?; @@ -336,6 +356,7 @@ impl Parser { return ParseResult::Accept(Statement::Assign { varname: varname.name, + operator, value, }); } @@ -346,32 +367,132 @@ impl Parser { } fn parse_expression(&mut self) -> ParseResult { - self.parse_comparison() + self.parse_logical_or() + } + + fn parse_logical_or(&mut self) -> ParseResult { + let left = self.parse_logical_and()?; + + let op = match self.peek_next()? { + Token::Ampersand => BinaryOperator::LogicalOr, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_logical_or()?), + type_id: Some(TypeId::U32), + }) + } + + fn parse_logical_and(&mut self) -> ParseResult { + let left = self.parse_bitwise_or()?; + + let op = match self.peek_next()? { + Token::Ampersand => BinaryOperator::LogicalAnd, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_logical_and()?), + type_id: Some(TypeId::U32), + }) + } + + fn parse_bitwise_or(&mut self) -> ParseResult { + let left = self.parse_bitwise_xor()?; + + let op = match self.peek_next()? { + Token::Ampersand => BinaryOperator::BitwiseOr, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_bitwise_or()?), + type_id: Some(TypeId::U32), + }) + } + + fn parse_bitwise_xor(&mut self) -> ParseResult { + let left = self.parse_bitwise_and()?; + + let op = match self.peek_next()? { + Token::Ampersand => BinaryOperator::BitwiseXor, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_bitwise_xor()?), + type_id: Some(TypeId::U32), + }) + } + + fn parse_bitwise_and(&mut self) -> ParseResult { + let left = self.parse_comparison()?; + + let op = match self.peek_next()? { + Token::Ampersand => BinaryOperator::BitwiseAnd, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_bitwise_and()?), + type_id: Some(TypeId::U32), + }) } fn parse_comparison(&mut self) -> ParseResult { - let mut expr = self.parse_additive()?; + let left = self.parse_shift()?; - while let Some(op) = match self.peek_next()? { - Token::EqualEqual => Some(BinaryOperator::Equal), - Token::BangEqual => Some(BinaryOperator::NotEqual), - Token::Less => Some(BinaryOperator::LessThan), - Token::Greater => Some(BinaryOperator::GreaterThan), - Token::LessEqual => Some(BinaryOperator::LessOrEqual), - Token::GreaterEqual => Some(BinaryOperator::GreaterOrEqual), - _ => None, - } { - self.next()?; - let right = Box::new(self.parse_additive()?); - expr = Expression::Binary { - op, - left: Box::new(expr), - right, - type_id: Some(TypeId::Bool), - }; - } + let op = match self.peek_next()? { + Token::EqualEqual => BinaryOperator::Equal, + Token::BangEqual => BinaryOperator::NotEqual, + Token::Less => BinaryOperator::LessThan, + Token::Greater => BinaryOperator::GreaterThan, + Token::LessEqual => BinaryOperator::LessOrEqual, + Token::GreaterEqual => BinaryOperator::GreaterOrEqual, + _ => return ParseResult::Accept(left), + }; - ParseResult::Accept(expr) + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_comparison()?), + type_id: Some(TypeId::Bool), + }) + } + + fn parse_shift(&mut self) -> ParseResult { + let left = self.parse_additive()?; + + let op = match self.peek_next()? { + Token::LeftShift => BinaryOperator::LeftShift, + Token::RightShift => BinaryOperator::RightShift, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_shift()?), + type_id: Some(TypeId::U32), + }) } fn parse_additive(&mut self) -> ParseResult { diff --git a/compiler/src/model.rs b/compiler/src/model.rs index a1abd87..1ad5cc4 100644 --- a/compiler/src/model.rs +++ b/compiler/src/model.rs @@ -11,6 +11,7 @@ pub enum CompilerError { Generic(String), UnknownType, TypeMismatch(TypeId, TypeId), + Unimplemented(String), } #[derive(Debug, PartialEq, Eq, Clone)] @@ -130,6 +131,7 @@ pub enum Statement { }, Assign { varname: String, + operator: AssignmentOperator, value: Expression, }, PtrWrite { @@ -308,6 +310,21 @@ impl Expression { } } +#[derive(Debug, Clone, PartialEq)] +pub enum AssignmentOperator { + Assign, + AddAssign, + SubAssign, + MulAssign, + DivAssign, + ModAssign, + AndAssign, + OrAssign, + XorAssign, + LeftShiftAssign, + RightShiftAssign, +} + #[allow(unused)] #[derive(Debug, Clone, PartialEq)] pub enum BinaryOperator { @@ -371,7 +388,6 @@ pub enum UnaryOperator { Minus, AddressOf, Dereference, - CastAs, BitwiseNot, LogicalNot, Increment, @@ -388,7 +404,6 @@ impl fmt::Display for UnaryOperator { Self::Minus => write!(f, "-"), Self::Dereference => write!(f, "*"), Self::AddressOf => write!(f, "&"), - Self::CastAs => write!(f, "as"), Self::BitwiseNot => write!(f, "~"), Self::LogicalNot => write!(f, "!"), Self::SizeOf => write!(f, "sizeof"),