diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 15e1473..c228e0b 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -5,3 +5,4 @@ edition.workspace = true authors.workspace = true [dependencies] +chrono = "0.4.43" diff --git a/c_compiler/bacon.toml b/compiler/bacon.toml similarity index 98% rename from c_compiler/bacon.toml rename to compiler/bacon.toml index 87af4fa..f016c40 100644 --- a/c_compiler/bacon.toml +++ b/compiler/bacon.toml @@ -84,8 +84,8 @@ command = [ "cargo", "run", "--color", "always", "--", - "example.dsc", - "../resources/dsa/code.dsa" + "../resources/dsc/example.dsc", + "../resources/dsa/output.dsa" # put launch parameters for your program behind a `--` separator ] need_stdout = true diff --git a/compiler/src/codegen.rs b/compiler/src/codegen.rs new file mode 100644 index 0000000..878f167 --- /dev/null +++ b/compiler/src/codegen.rs @@ -0,0 +1,712 @@ +use std::collections::HashMap; +use std::hash::Hash; +use std::sync::LazyLock; +use std::sync::atomic::AtomicU32; +use std::time::SystemTime; + +use chrono::{DateTime, Local}; + +use crate::registers::{Location, RegisterAllocator}; +use crate::{block, cmd, comment, dsa}; + +use crate::parser::{ + BinaryOperator, CompilerError, ConstExpr, Declaration, Dependency, Expression, + Program, Statement, UnaryOperator, Variable, +}; + +pub struct CodeGenerator { + ast: Program, + imports: HashMap, + globals: Vec, + functions: Vec, + symbols: Vec, + allocator: RegisterAllocator, +} + +static GLOBAL_METHODS: LazyLock> = LazyLock::new(|| { + HashMap::from([ + ("print", "print::print"), + ("println", "print::println"), + ("printnum", "print::print_num"), + ("print_space", "print::print_whitespace"), + ("print_char", "print::print_byte"), + ("print_word", "print::print_word"), + ]) +}); + +fn import(name: &str, path: &str) -> String { + format!("include {name}: \"{}\"", path) +} + +impl CodeGenerator { + const RET: &'static str = "\tjmp _ret"; + + pub fn new(ast: Program) -> Self { + CodeGenerator { + ast, + imports: HashMap::new(), + globals: Vec::new(), + functions: Vec::new(), + symbols: Vec::new(), + allocator: RegisterAllocator::new(), + } + } + + pub fn include(&mut self, name: &str, path: &str) { + self.imports.insert(name.to_string(), path.to_string()); + } + + fn is_global(&self, name: &str) -> bool { + // Check if this variable is in the globals list + self.globals + .iter() + .any(|g| g.contains(&format!("dw {}:", name))) + } + + pub fn generate(&mut self) -> Result { + // always include the print library for debugging! + self.include("print", "./lib/io/print.dsa"); + + for block in self.ast.clone().declarations { + match block { + Declaration::Variable { + var: Variable { name, .. }, + .. + } => self.symbols.push(name), + Declaration::Function { name, .. } => self.symbols.push(name), + Declaration::Dependency(Dependency { name, .. }) => { + self.symbols.push(name) + } + } + } + + for block in self.ast.clone().declarations { + self.generate_block(block.clone())?; + } + + self.generate_layout() + } + + fn generate_layout(&mut self) -> Result { + let datetime: DateTime = SystemTime::now().into(); + Ok(dsa![ + "", + comment!("GENERATED BY DSC COMPILER"), + comment!(format!( + "Generated at {}", + datetime.format("%Y-%m-%d %H:%M:%S") + )), + "", + // imports + comment!("Imports"), + self.imports + .iter() + .map(|(k, v)| import(k, v)) + .collect::>() + .join("\n"), + "", + // reserved memory + comment!("Globals & Reserved Memory"), + self.globals.join("\n"), + "", + // entry point + comment!("Entry Point"), + "dw stack: 0x10000", + "db message: \"Process Exited with code:\"", + block! [ "_init" + dsa![ldw stack, bpr], + dsa![mov bpr, spr], + dsa![push zero], + dsa![call main], + dsa![call print::print_newline], + dsa![lwi message, rg0], + dsa![push rg0], + dsa![call print::print], + dsa![pop zero], + dsa![call print::print_hex_word], + dsa![pop zero], + dsa![hlt] + ], + "", + comment!("Return"), + block! [ "_ret" + dsa![mov bpr, spr], + dsa![pop bpr], + dsa![return] + ], + comment!("Compiled Code Starts..."), + // block! [ "main" + // dsa![push bpr], + // dsa![mov spr, bpr], + // dsa![lwi 67, rg1], + // dsa![stw rg1, spr, 8], + // dsa![mov bpr, spr], + // dsa![pop bpr], + // dsa![return] + // ], + self.functions.join("\n"), + ]) + } + + fn generate_global(&mut self, name: &str, init: Option) { + self.globals.push(format!( + "dw {}: {}", + name, + init.unwrap_or(ConstExpr::Number(0)) + )) + } + + fn generate_block(&mut self, block: Declaration) -> Result<(), CompilerError> { + match block { + Declaration::Variable { var, init, .. } => { + self.generate_global(&var.name, init) + } + Declaration::Function { + name, + return_type, + params, + body, + } => { + let func = self.generate_function(&name, ¶ms, &body).join("\n"); + + self.functions.push(format!("{func}\n")); + } + Declaration::Dependency(Dependency { name, path }) => { + self.imports.insert(name, path); + } + }; + + Ok(()) + } + + // Example: Generate code for a function + fn generate_function( + &mut self, + name: &str, + params: &[Variable], + body: &[Statement], + ) -> Vec { + let mut code = Vec::new(); + + // Reset allocator for new function + self.allocator.reset(); + + // Function prologue + code.push(format!("{}:", name)); + code.push("\tpush bpr".to_string()); + code.push("\tmov spr, bpr".to_string()); + code.push(String::new()); + + // Allocate parameters to registers or stack locations + for (i, param) in params.iter().enumerate() { + let offset = 8 + (i as i32 * 4); // Parameters start at bpr+8 + // Track that this parameter is at a stack location + let (reg, load_code) = self.allocator.alloc_var(¶m.name).unwrap(); + code.extend(load_code); + code.push(format!("\tldw bpr, {}, {}", reg, offset)); + } + + // Generate code for function body + for stmt in body { + let stmt_code = self.generate_statement(stmt).unwrap(); + code.extend(stmt_code); + } + + // automatically return at function end + if let Some(x) = code.last() + && x == Self::RET + { + } else { + code.push(Self::RET.to_string()); + } + + code + } + + // Example: Generate code for a statement + fn generate_statement( + &mut self, + stmt: &Statement, + ) -> Result, CompilerError> { + let mut code = Vec::new(); + + match stmt { + Statement::Declaration { var, value } => { + if let Some(expr) = value { + // Evaluate expression + let (result_reg, expr_code) = self.generate_expression(expr, true)?; + code.extend(expr_code); + + // Store result in variable + let store_code = self.allocator.store_var(&var.name, &result_reg); + code.extend(store_code); + + // Free temporary register + self.allocator.free_temp(&result_reg); + } else { + // Just declaring variable without initialization + self.allocator.alloc_var(&var.name)?; + } + } + + Statement::Break => unimplemented!(), + Statement::Continue => unimplemented!(), + + Statement::Assign { varname, value } => { + // Evaluate expression + let (result_reg, expr_code) = self.generate_expression(value, true)?; + code.extend(expr_code); + + // 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); + } + + Statement::Return(expr) => { + if let Some(e) = expr { + let (result_reg, expr_code) = self.generate_expression(e, true)?; + code.extend(expr_code); + code.push(format!("\tstw {}, bpr, 8", result_reg)); + code.push(format!("\tjmp _ret")); + self.allocator.free_temp(&result_reg); + } + } + + Statement::If { + condition, + then_stmt, + else_stmt, + } => { + // Generate condition + let (cond_reg, cond_code) = self.generate_expression(condition, true)?; + code.extend(cond_code); + + // Compare with zero + code.push(format!("\tcmp {}, zero", cond_reg)); + self.allocator.free_temp(&cond_reg); + + // Generate unique labels + let then_label = format!("_then_{}", self.get_unique_label()); + let else_label = format!("_else_{}", self.get_unique_label()); + let end_label = format!("_end_{}", self.get_unique_label()); + + // Jump to else if condition is false (equal to zero) + code.push(format!("\tjeq {}", else_label)); + + // Then block + code.push(format!("{}:", then_label)); + for s in then_stmt { + code.extend(self.generate_statement(s)?); + } + + if then_stmt.len() == 0 { + code.push("\tnop".to_string()); + } + + code.push(format!("\tjmp {}", end_label)); + + // Else block + code.push(format!("{}:", else_label)); + for s in else_stmt { + code.extend(self.generate_statement(s)?); + } + + if else_stmt.len() == 0 { + code.push("\tnop".to_string()); + } + + code.push(format!("{}:", end_label)); + } + + Statement::While { condition, body } => { + let loop_start = format!("_while_start_{}", self.get_unique_label()); + let loop_end = format!("_while_end_{}", self.get_unique_label()); + + code.push(format!("{}:", loop_start)); + + // Generate condition + let (cond_reg, cond_code) = self.generate_expression(condition, true)?; + code.extend(cond_code); + + code.push(format!("\tcmp {}, zero", cond_reg)); + self.allocator.free_temp(&cond_reg); + + code.push(format!("\tjeq {}", loop_end)); + + // Loop body + for s in body { + code.extend(self.generate_statement(s)?); + } + + code.push(format!("\tjmp {}", loop_start)); + code.push(format!("{}:", loop_end)); + } + + Statement::Loop(body) => { + let loop_start = format!("_loop_start_{}", self.get_unique_label()); + + code.push(format!("{}:", loop_start)); + + for s in body { + code.extend(self.generate_statement(s)?); + } + + code.push(format!("\tjmp {}", loop_start)); + } + + Statement::Expression { expr } => { + let (result_reg, expr_code) = self.generate_expression(expr, false)?; + code.extend(expr_code); + self.allocator.free_temp(&result_reg); + } + + Statement::Block(statements) => { + for s in statements { + code.extend(self.generate_statement(s)?); + } + } + } + + Ok(code) + } + + // Example: Generate code for an expression + // Returns (register containing result, assembly code) + fn generate_expression( + &mut self, + expr: &Expression, + use_result: bool, + ) -> Result<(String, Vec), CompilerError> { + let mut code = Vec::new(); + + match expr { + Expression::StringLiteral(value) => { + let (reg, alloc_code) = self.allocator.alloc_temp()?; + code.extend(alloc_code); + + // write string into memory + let uuid = self.get_unique_label(); + code.push(format!("\tdb str_{uuid}: \"{value}\"")); + + // Load pointer to string + code.push(format!("\tlwi str_{uuid}, {reg}")); + + Ok((reg, code)) + } + + Expression::CharLiteral(value) => { + let (reg, alloc_code) = self.allocator.alloc_temp()?; + code.extend(alloc_code); + + // Load immediate value + code.push(format!("\tlli {}, {} // '{value}'", *value as u8, reg)); + + Ok((reg, code)) + } + + Expression::Number(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)); + } + + Ok((reg, code)) + } + + Expression::Variable { name, .. } => { + if self.is_global(&name.name) { + // Allocate a temporary register for the global + let (reg, alloc_code) = self.allocator.alloc_temp()?; + code.extend(alloc_code); + + // Load from global label + code.push(format!("\tldw {}, {}", name.name, reg)); + + Ok((reg, code)) + } else { + // Local variable - use existing allocator logic + let (reg, load_code) = self.allocator.load_var(&name.name)?; + code.extend(load_code); + Ok((reg, code)) + } + } + + Expression::Binary { op, left, right } => { + // Evaluate left operand + let (left_reg, left_code) = self.generate_expression(left, true)?; + code.extend(left_code); + + // Evaluate right operand + let (right_reg, right_code) = self.generate_expression(right, true)?; + code.extend(right_code); + + // Allocate result register + let (result_reg, result_alloc) = self.allocator.alloc_temp()?; + code.extend(result_alloc); + + // Generate operation + match op { + BinaryOperator::Add => { + code.push(format!( + "\tadd {}, {}, {}", + left_reg, right_reg, result_reg + )); + } + BinaryOperator::Sub => { + code.push(format!( + "\tsub {}, {}, {}", + left_reg, right_reg, result_reg + )); + } + BinaryOperator::Mul => { + self.include("maths", "./lib/maths/core.dsa"); + // Call multiply function + code.push(format!("\tpush {}", right_reg)); + code.push(format!("\tpush {}", left_reg)); + code.push("\tcall maths::multiply".to_string()); + code.push(format!("\tpop {}", result_reg)); + code.push("\tpop zero".to_string()); + } + // Comparison operators - return 1 (true) or 0 (false) + BinaryOperator::Eq => { + 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!("{}:", end_label)); + } + BinaryOperator::Ne => { + 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!("{}:", end_label)); + } + BinaryOperator::Lt => { + 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!("{}:", end_label)); + } + BinaryOperator::Le => { + 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!("{}:", end_label)); + } + BinaryOperator::Gt => { + 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!("{}:", end_label)); + } + BinaryOperator::Ge => { + 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!("{}:", end_label)); + } + _ => unimplemented!(), + } + + // Free operand registers (allocator will protect variables) + self.allocator.free_temp(&left_reg); + self.allocator.free_temp(&right_reg); + + Ok((result_reg, code)) + } + + Expression::Call { name, args } => { + // Save caller-saved registers and track which ones we saved + let saved_regs = self.allocator.get_caller_saved_registers(); + for reg in &saved_regs { + code.push(format!("\tpush {}", reg)); + } + + // Evaluate and push arguments in reverse order + let mut arg_regs = Vec::new(); + for arg in args.iter().rev() { + let (arg_reg, arg_code) = self.generate_expression(arg, true)?; + code.extend(arg_code); + code.push(format!("\tpush {}", arg_reg)); + arg_regs.push(arg_reg); + } + + if GLOBAL_METHODS.contains_key(name.name.as_str()) { + code.push(format!("\tcall {}", GLOBAL_METHODS[name.name.as_str()])); + } else if self.symbols.contains(&name.name) { + // Call local function + code.push(format!("\tcall {}", name.name)); + } else { + return Err(CompilerError::Undefined(name.clone())); + } + + let result_reg = String::new(); + + if use_result { + let (result_reg, result_alloc) = self.allocator.alloc_temp()?; + + code.extend(result_alloc); + code.push(format!("\tpop {}", result_reg)); + + // Clean up arguments + if args.len() > 1 { + for _ in 0..(args.len() - 1) { + code.push("\tpop zero".to_string()); + } + } + } else { + // Clean up arguments + if args.len() > 0 { + for _ in 0..(args.len()) { + code.push("\tpop zero".to_string()); + } + } + } + + // Restore caller-saved registers in reverse order (LIFO) + for reg in saved_regs.iter().rev() { + code.push(format!("\tpop {}", reg)); + } + + // Free argument registers + for reg in arg_regs { + self.allocator.free_temp(®); + } + + Ok((result_reg, code)) + } + + Expression::Unary { op, operand } => { + let (operand_reg, operand_code) = + self.generate_expression(operand, true)?; + 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::Reference => { + code.extend(self.allocator.spill_register(&operand_reg)?); + code.push(format!( + "\tsubi bpr {} {}", + -(4 + self.allocator.get_stack_offset()), + result_reg + )) + } + } + + self.allocator.free_temp(&operand_reg); + Ok((result_reg, code)) + } + + Expression::Empty => Ok(("zero".to_string(), code)), + } + } + + // Helper for generating unique labels + fn get_unique_label(&mut self) -> String { + // You'd implement a counter here + static COUNTER: AtomicU32 = AtomicU32::new(0); + + let val = COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + (val + 1).to_string() + } +} + +/// Build a single string from any number of arguments. +/// Each argument must implement `Display` or be convertible to a string. +#[macro_export] +macro_rules! dsa { + ($($arg:expr),* $(,)?) => {{ + // Start with an empty String – we’ll grow it as we go. + use std::fmt::Write; + let mut s = ::std::string::String::new(); + $( + // `write!` is cheaper than `format!` for each element + // because it re‑uses the same buffer. + + write!(s, "{}\n", $arg).expect("write to String failed"); + )* + s + }}; +} + +// ──────────────────────── dsa! ──────────────────────── +// A tiny helper that just turns its token‑stream into a string. +// The trailing comma is kept – it’s part of the syntax you want. +#[macro_export] +macro_rules! cmd { + ($($tokens:tt)*) => {{ + // We’ll just stringify the tokens and return a String. + format!("{}", concat!(stringify!($tokens), "\n")) + }}; +} + +// ──────────────────────── block! ──────────────────────── +// Usage: +// +// let asm = block![ "name" +// dsa![mov rg0, rg1], +// dsa![add rg1, rg1] +// ]; +// +// `asm` is a `&'static str` containing: +// +// name: +// mov rg0, rg1 +// add rg1, rg1 +// +#[macro_export] +macro_rules! block { + // The first token must be a string literal – that’s the label. + ($label:literal $(dsa![$($ins:tt)*]),* ) => {{ + // Build a single string at compile time. + const CODE: &str = concat!( + $label, ":\n", + // Each `dsa!` call yields a string like `"mov rg0, rg1"`. + // We add a newline after each one to get the desired layout. + $(concat!("\t", stringify!($($ins)*), "\n")),* + ); + CODE + }}; +} + +#[macro_export] +macro_rules! comment { + ($text:expr) => {{ format!("// {}", $text) }}; +} diff --git a/compiler/src/lexer.rs b/compiler/src/lexer.rs index e76e131..142343f 100644 --- a/compiler/src/lexer.rs +++ b/compiler/src/lexer.rs @@ -9,15 +9,19 @@ pub enum Token { If, Else, Loop, + While, Break, Return, Continue, Include, + Static, + Const, // Identifiers and literals Identifier(String), String(String), - Number(i64), + Integer(u32), + Char(char), // Symbols LeftParen, // ( @@ -30,9 +34,10 @@ pub enum Token { // Pipe, // | // Operators - Plus, // + - Minus, // - - Star, // * + Plus, // + + Minus, // - + Star, // * + Amphersand, Slash, // / Assign, // = EqualEqual, // == @@ -51,18 +56,22 @@ pub enum Token { impl Token { pub fn tt(&self) -> &str { match self { + Token::Const => "Const", + Token::Static => "Static", Token::Include => "Include", Token::Fn => "Fn", Token::If => "If", Token::Let => "Let", Token::Else => "Else", Token::Loop => "Loop", + Token::While => "While", Token::Break => "Break", Token::Return => "Return", Token::Continue => "Continue", Token::Identifier(_) => "Identifier", Token::String(_) => "String", - Token::Number(_) => "Number", + Token::Integer(_) => "UnsignedInt", + Token::Char(_) => "Char", Token::LeftParen => "LeftParen", Token::RightParen => "RightParen", Token::LeftBrace => "LeftBrace", @@ -75,6 +84,7 @@ impl Token { Token::Plus => "Plus", Token::Minus => "Minus", Token::Star => "Star", + Token::Amphersand => "Amphersand", Token::Slash => "Slash", Token::Assign => "Assign", Token::EqualEqual => "EqualEqual", @@ -143,7 +153,7 @@ impl<'a> Lexer<'a> { } fn read_number(&mut self) -> i64 { - let mut num_str = String::new(); + let mut num_str = String::from(self.current.unwrap()); while let Some(&c) = self.peek() { if c.is_ascii_digit() { num_str.push(c); @@ -152,7 +162,7 @@ impl<'a> Lexer<'a> { break; } } - num_str.parse().unwrap_or(0) + num_str.parse().unwrap() } fn match_next(&mut self, expected: char) -> bool { @@ -176,6 +186,7 @@ impl<'a> Lexer<'a> { Some(';') => Token::Semicolon, Some(':') => Token::Colon, Some(',') => Token::Comma, + Some('&') => Token::Amphersand, // Some('|') => Token::Pipe, Some('+') => Token::Plus, Some('*') => Token::Star, @@ -235,15 +246,19 @@ impl<'a> Lexer<'a> { "fn" => Token::Fn, "if" => Token::If, "else" => Token::Else, + "while" => Token::While, "loop" => Token::Loop, "break" => Token::Break, "return" => Token::Return, "continue" => Token::Continue, "include" => Token::Include, + "let" => Token::Let, + "const" => Token::Const, + "static" => Token::Static, _ => Token::Identifier(ident), } } else if c.is_ascii_digit() { - Token::Number(self.read_number()) + Token::Integer(self.read_number() as u32) } else { // Skip unknown characters for now self.advance(); @@ -298,8 +313,8 @@ mod tests { assert_eq!(lexer.next_token(), Token::Identifier("x".to_string())); assert_eq!(lexer.next_token(), Token::Identifier("y42".to_string())); assert_eq!(lexer.next_token(), Token::Identifier("_test".to_string())); - assert_eq!(lexer.next_token(), Token::Number(123)); - assert_eq!(lexer.next_token(), Token::Number(45)); + assert_eq!(lexer.next_token(), Token::Integer(123)); + assert_eq!(lexer.next_token(), Token::Integer(45)); assert_eq!(lexer.next_token(), Token::Eof); } diff --git a/compiler/src/main.rs b/compiler/src/main.rs index b1a127c..854cd9d 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -3,18 +3,33 @@ use std::{fs, path::Path}; pub mod lexer; -pub mod parserprototype; -use parserprototype::Parser; +pub mod parser; +use parser::Parser; +pub mod codegen; +mod registers; +mod semantic_analyser; -use crate::parserprototype::ParseResult; +use crate::{codegen::CodeGenerator, parser::ParseResult, semantic_analyser::Analyser}; fn main() { - println!("Hello, world!"); + // read from input file: syntax "c_compiler [output.dsa]" + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + eprintln!("Usage: c_compiler [output.dsa]"); + return; + } - let path = Path::new("../resources/dsc/example.dsc"); - let contents = fs::read_to_string(path).expect("Failed to read file"); + let input_file = &args[1]; + let output_file = if args.len() > 2 { + &args[2] + } else { + "output.dsa" + }; - let lexer = lexer::Lexer::new(&contents); + // read input + let input = std::fs::read_to_string(input_file).expect("Failed to read input file"); + + let lexer = lexer::Lexer::new(&input); let tokens = lexer.collect::>(); println!("{tokens:?}"); @@ -29,5 +44,22 @@ fn main() { panic!("Parser denied parsing") } }; - println!("{ast:?}"); + println!("{ast:#?}"); + + let analyser = Analyser::new(); + analyser.analyse(ast.clone()).unwrap(); + + // Code Gen + let mut generator = CodeGenerator::new(ast); + let result = match generator.generate() { + Ok(code) => code, + Err(e) => { + eprintln!("Parsing error: {:?}", e); + return; + } + }; + + println!("{result}"); + std::fs::write(output_file, &result).expect("Failed to write output"); + println!("Result written to {}", output_file); } diff --git a/compiler/src/parser.rs b/compiler/src/parser.rs index 7784e09..755df29 100644 --- a/compiler/src/parser.rs +++ b/compiler/src/parser.rs @@ -1,270 +1,728 @@ -use crate::expect_tt; use crate::lexer::Token; +use crate::{expect_tt, expect_value}; +use core::fmt; +use std::ops::{ControlFlow, FromResidual, Try}; + +#[derive(Debug, Clone)] +pub enum ParseResult { + Accept(T), + Deny, + Reject(E), +} + +#[derive(Debug, Clone)] +pub enum CompilerError { + UnexpectedToken(Token), + UnexpectedEndOfInput, + UnexpectedCharacter(char), + Undefined(Name), + InvalidSyntax(String), + Generic(String), +} pub struct Parser { - ast: Node, - idx: usize, tokens: Vec, + idx: usize, } impl Parser { pub fn new(tokens: Vec) -> Self { - Self { - ast: Node::Block { - children: Vec::new(), - }, - idx: 0, - tokens, - } + Self { tokens, idx: 0 } } - pub fn parse(&mut self) -> Result { - self.parse_block() - } + pub fn parse(&mut self) -> ParseResult { + let mut declarations = Vec::new(); - fn parse_block(&mut self) -> Result { - let mut statements = Vec::new(); - - while self.peek_next().is_ok() { - statements.push(self.parse_statement()?); + while let ParseResult::Accept(_) = self.peek_next() { + declarations.push(self.parse_declaration()?); } - Ok(Node::Block { - children: statements, - }) + ParseResult::Accept(Program { declarations }) } - fn parse_statement(&mut self) -> Result { - // first token in a statement is always an identifier - let left = if let Ok(typed_var) = self.parse_typed_var() { - Box::new(typed_var) - } else { - let tok = expect_tt!(self.next()?, Identifier)?; - Box::new(Node::Terminal { value: tok }) - }; - - let _ = expect_tt!(self.next()?, Assign)?; - - let right = Box::new(self.parse_expression()?); - - Ok(Node::Statement { left, right }) - } - - fn parse_typed_var(&mut self) -> Result { - let name = expect_tt!(self.next()?, Identifier)?; - let _ = expect_tt!(self.next()?, Colon)?; - let type_ = expect_tt!(self.next()?, Identifier)?; - - Ok(Node::TypedVar { name, type_ }) - } - - fn parse_expression(&mut self) -> Result { - if expect_tt!(self.peek_next()?, Pipe).is_ok() { + fn parse_declaration(&mut self) -> ParseResult { + if expect_tt!(self.peek_next()?, Fn).accepted() { return self.parse_func(); } - if expect_tt!(self.peek_next()?, If).is_ok() { - return self.parse_if(); + if expect_tt!(self.peek_next()?, Include).accepted() { + // expect include keyword + let _ = self.next(); + + // expect namespace identifier + let name = expect_value!(self.next()?, Identifier)?; + + // expect colon + let _ = expect_tt!(self.next()?, Colon)?; + + // expect string literal (module path) + let path = expect_value!(self.next()?, String)?; + + // expect semicolon + let _ = expect_tt!(self.next()?, Semicolon)?; + + return ParseResult::Accept(Declaration::Dependency(Dependency { + name, + path, + })); } - if expect_tt!(self.peek_next()?, Loop).is_ok() { - return self.parse_loop(); - } - - if expect_tt!(self.peek_next()?, Identifier, String, Number).is_ok() { - let left = Node::Terminal { - value: self.next()?, + if expect_tt!(self.peek_next()?, Const, Static).accepted() { + let is_const = match self.next()? { + Token::Const => true, + Token::Static => false, + _ => { + return ParseResult::Reject(CompilerError::Generic(String::from( + "This can't happen!", + ))); + } }; - if expect_tt!( - self.next()?, - Plus, - Minus, - Star, - Slash, - EqualEqual, - BangEqual, - Less, - LessEqual, - Greater, - GreaterEqual - ) - .is_err() - { - return Ok(left); - } + let var = self.parse_var_decl()?; - let operator = self.next()?; - let right = Box::new(self.parse_expression()?); + let _ = expect_tt!(self.next()?, Assign)?; - return Ok(Node::BinaryOp { - left: Box::new(left), - op: operator, - right, + let value = self.next()?; + let init = match value { + Token::String(x) => Some(ConstExpr::String(x)), + Token::Integer(x) => Some(ConstExpr::Number(x as i32)), + _ => return ParseResult::Reject(CompilerError::UnexpectedToken(value)), + }; + + let _ = expect_tt!(self.next()?, Semicolon)?; + + return ParseResult::Accept(Declaration::Variable { + var, + init, + is_const, }); } - Err(CompileError::Generic) + ParseResult::Reject(CompilerError::UnexpectedEndOfInput) } - fn parse_func(&mut self) -> Result { - // left arg delimiter - let _ = expect_tt!(self.next()?, Pipe)?; + fn parse_func(&mut self) -> ParseResult { + // expect function keyword + let _ = expect_tt!(self.next()?, Fn); + // expect function name + let name = expect_value!(self.next()?, Identifier)?; - // parse args - let mut args = Vec::new(); + // expect left paren + let _ = expect_tt!(self.next()?, LeftParen)?; - while expect_tt!(self.peek_next()?, Identifier).is_ok() { - // add a typed var - let arg = self.parse_typed_var()?; - args.push(arg); + let mut params = Vec::new(); + while expect_tt!(self.peek_next()?, Identifier).accepted() { + let arg = self.parse_var_decl()?; + params.push(arg); + + if expect_tt!(self.peek_next()?, Comma).accepted() { + self.next()?; + } else { + break; + } } - // right arg delimiter - let _ = expect_tt!(self.next()?, Pipe)?; + // expect right paren + let _ = expect_tt!(self.next()?, RightParen)?; - // ensure we have an open brace - let _ = expect_tt!(self.next()?, LeftBrace)?; + // see if we can parse the return type! + let mut return_type = TypeId::Void; + if expect_tt!(self.peek_next()?, RightArrow).accepted() { + let _ = self.next(); + return_type = self.parse_type()?; + } - // parse the body - let body = Box::new(self.parse_block()?); - - // ensure we have a close brace - let _ = expect_tt!(self.next()?, RightBrace)?; - - Ok(Node::FunctionDef { params: args, body }) + // expect vald block + if expect_tt!(self.peek_next()?, LeftBrace).accepted() { + ParseResult::Accept(Declaration::Function { + name, + params, + return_type, + body: self.parse_block()?, + }) + } else { + ParseResult::Reject(CompilerError::UnexpectedToken(self.peek_next()?)) + } } - fn parse_loop(&mut self) -> Result { - let _ = expect_tt!(self.next()?, Loop)?; - - // ensure we have an open brace + fn parse_block(&mut self) -> ParseResult { + // expect left brace let _ = expect_tt!(self.next()?, LeftBrace)?; - // parse the body - let body = Box::new(self.parse_block()?); + let mut block = Vec::new(); + while !expect_tt!(self.peek_next()?, RightBrace).accepted() { + block.push(self.parse_statement()?); + } - // ensure we have a close brace + // expect right brace let _ = expect_tt!(self.next()?, RightBrace)?; - Ok(Node::Loop { body }) + ParseResult::Accept(block) } - fn parse_if(&mut self) -> Result { - let _ = expect_tt!(self.next()?, If)?; + fn parse_statement(&mut self) -> ParseResult { + // handle if statements + if expect_tt!(self.peek_next()?, If).accepted() { + self.next()?; - // parse condition (expr) - let condition = Box::new(self.parse_expression()?); + let condition = self.parse_expression()?; - // ensure we have an open brace - let _ = expect_tt!(self.next()?, LeftBrace)?; + let then_stmt = self.parse_block()?; - // parse the "then" branch (expr/statement) - let then_branch = Box::new(self.parse_expression()?); + if !expect_tt!(self.peek_next()?, Else).accepted() { + return ParseResult::Accept(Statement::If { + condition, + then_stmt, + else_stmt: vec![], + }); + } - // ensure we have a close brace - let _ = expect_tt!(self.next()?, RightBrace)?; + let _ = expect_tt!(self.next()?, Else)?; - // if there is an else branch, we include it in the statement - let else_branch = self.parse_else()?.map(Box::new); + let else_stmt = self.parse_block()?; - Ok(Node::If { - condition, - then_branch, - else_branch, + return ParseResult::Accept(Statement::If { + condition, + then_stmt, + else_stmt, + }); + } + + // handle while loops + if expect_tt!(self.peek_next()?, While).accepted() { + self.next()?; + + // expect valid expression + let expr = self.parse_expression()?; + + // expect valid block after + let block = self.parse_block()?; + + // return result + return ParseResult::Accept(Statement::While { + condition: expr, + body: block, + }); + } + + // handle indefinite loops + if expect_tt!(self.peek_next()?, Loop).accepted() { + self.next()?; + + // parse the inner block + return ParseResult::Accept(Statement::Loop(self.parse_block()?)); + } + + if expect_tt!(self.peek_next()?, Return).accepted() { + self.next()?; + + // handle case where nothing is returned + if expect_tt!(self.peek_next()?, Semicolon).accepted() { + return ParseResult::Accept(Statement::Return(None)); + } + + let expr = self.parse_expression()?; + expect_tt!(self.next()?, Semicolon)?; + + return ParseResult::Accept(Statement::Return(Some(expr))); + } + + if expect_tt!(self.peek_next()?, Break).accepted() { + self.next()?; + + // expect semicolon + expect_tt!(self.next()?, Semicolon)?; + + // return result + return ParseResult::Accept(Statement::Break); + } + + if expect_tt!(self.peek_next()?, Continue).accepted() { + self.next()?; + + // expect semicolon + expect_tt!(self.next()?, Semicolon)?; + + // return result + return ParseResult::Accept(Statement::Continue); + } + + // handle let statements (declarations) + if expect_tt!(self.peek_next()?, Let).accepted() { + self.next(); + + // expect variable name and type. + let name = self.parse_var_decl()?; + + // handle uninitialised variable case + if expect_tt!(self.peek_next()?, Semicolon).accepted() { + self.next(); + return ParseResult::Accept(Statement::Declaration { + var: name, + value: None, + }); + } + + // handle initialised case + // expect equals + let _ = expect_tt!(self.next()?, Assign)?; + + // expect a valid expression + let expr = self.parse_expression()?; + + let _ = expect_tt!(self.next()?, Semicolon); + + // return statement + return ParseResult::Accept(Statement::Declaration { + var: name, + value: Some(expr), + }); + } + + // handle assignment without "let" + let name = expect_value!(self.peek_next()?, Identifier); + if name.accepted() { + let varname = name?; + + println!("expr acc"); + + if expect_tt!(self.peek(1)?, LeftParen).accepted() { + println!("func call acc"); + let expr = self.parse_expression()?; // a function call expr + let _ = expect_tt!(self.next()?, Semicolon)?; + return ParseResult::Accept(Statement::Expression { expr }); + } + + self.next()?; + let _ = expect_tt!(self.next()?, Assign)?; + + let value = self.parse_expression()?; + + let _ = expect_tt!(self.next()?, Semicolon); + + return ParseResult::Accept(Statement::Assign { varname, value }); + } + + ParseResult::Reject(CompilerError::UnexpectedToken(self.peek_next()?)) + } + + fn parse_expression(&mut self) -> ParseResult { + self.parse_comparison() + } + + fn parse_comparison(&mut self) -> ParseResult { + let mut expr = self.parse_additive()?; + + while let Some(op) = match self.peek_next()? { + Token::EqualEqual => Some(BinaryOperator::Ne), + Token::BangEqual => Some(BinaryOperator::Ne), + Token::Less => Some(BinaryOperator::Lt), + Token::Greater => Some(BinaryOperator::Gt), + Token::LessEqual => Some(BinaryOperator::Le), + Token::GreaterEqual => Some(BinaryOperator::Ge), + _ => None, + } { + self.next()?; + let right = Box::new(self.parse_additive()?); + expr = Expression::Binary { + op, + left: Box::new(expr), + right, + } + } + + ParseResult::Accept(expr) + } + + fn parse_additive(&mut self) -> ParseResult { + let left = self.parse_multiplicative()?; + + let op = match self.peek_next()? { + Token::Plus => BinaryOperator::Add, + Token::Minus => BinaryOperator::Sub, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_additive()?), }) } - fn parse_else(&mut self) -> Result, CompileError> { - // if there is no else branch, return None. - if expect_tt!(self.peek_next()?, Else).is_err() { - return Ok(None); - } - let _ = self.next()?; + fn parse_multiplicative(&mut self) -> ParseResult { + let left = self.parse_unary()?; - if expect_tt!(self.peek_next()?, If).is_ok() { - Ok(Some(self.parse_if()?)) + let op = match self.peek_next()? { + Token::Star => BinaryOperator::Mul, + Token::Slash => BinaryOperator::Div, + _ => return ParseResult::Accept(left), + }; + + self.next()?; + ParseResult::Accept(Expression::Binary { + op, + left: Box::new(left), + right: Box::new(self.parse_multiplicative()?), + }) + } + + fn parse_unary(&mut self) -> ParseResult { + let op = match self.peek_next()? { + Token::Plus => UnaryOperator::Plus, + Token::Minus => UnaryOperator::Minus, + Token::Star => UnaryOperator::Dereference, + Token::Amphersand => UnaryOperator::Reference, + _ => return ParseResult::Accept(self.parse_primary()?), + }; + + self.next()?; + let operand = Box::new(self.parse_unary()?); + ParseResult::Accept(Expression::Unary { op, operand }) + } + + fn parse_primary(&mut self) -> ParseResult { + match self.peek_next()? { + Token::Integer(value) => { + self.next()?; + ParseResult::Accept(Expression::Number(value as isize)) + } + Token::String(value) => { + self.next()?; + ParseResult::Accept(Expression::StringLiteral(value)) + } + Token::Identifier(_) => { + let name = self.parse_identifier()?; + + if matches!(self.peek_next()?, Token::LeftParen) { + // Function call + self.next()?; + let mut args = Vec::new(); + + if !matches!(self.peek_next()?, Token::RightParen) { + args.push(self.parse_expression()?); + + while matches!(self.peek_next()?, Token::Comma) { + self.next()?; + args.push(self.parse_expression()?); + } + } + + let _ = expect_tt!(self.next()?, RightParen)?; + + ParseResult::Accept(Expression::Call { name, args }) + } else { + ParseResult::Accept(Expression::Variable { + name, + expr_type: None, + }) + } + } + Token::LeftParen => { + self.next()?; + let expr = self.parse_expression()?; + let _ = expect_tt!(self.next()?, RightParen)?; + ParseResult::Accept(expr) + } + _ => ParseResult::Reject(CompilerError::UnexpectedToken(self.peek_next()?)), + } + } + + fn parse_var_decl(&mut self) -> ParseResult { + let name = expect_value!(self.next()?, Identifier)?; + + let _ = expect_tt!(self.next()?, Colon)?; + + let type_id = self.parse_type()?; + + ParseResult::Accept(Variable { name, type_id }) + } + + fn parse_type(&mut self) -> ParseResult { + // get the type name incl namespace + let typename = self.parse_identifier()?; + + match typename.name.as_str() { + "u32" => ParseResult::Accept(TypeId::U32), + "u16" => ParseResult::Accept(TypeId::U16), + "u8" => ParseResult::Accept(TypeId::U8), + "i32" => ParseResult::Accept(TypeId::I32), + "i16" => ParseResult::Accept(TypeId::I16), + "i8" => ParseResult::Accept(TypeId::I8), + "void" => ParseResult::Accept(TypeId::Void), + "char" => ParseResult::Accept(TypeId::Char), + "str" => ParseResult::Accept(TypeId::Ptr(Box::new(TypeId::Char))), + _ => todo!("Implement parsing for other types!!"), + } + } + + fn parse_identifier(&mut self) -> ParseResult { + let primary = expect_value!(self.next()?, Identifier)?; + + if expect_tt!(self.peek_next()?, Colon).accepted() { + let _ = expect_tt!(self.next()?, Colon)?; + let _ = expect_tt!(self.next()?, Colon)?; + + let secondary = expect_value!(self.next()?, Identifier)?; + + ParseResult::Accept(Name { + namespace: Some(primary), + name: secondary, + }) } else { - Ok(Some(self.parse_expression()?)) + ParseResult::Accept(Name { + namespace: None, + name: primary, + }) } } - fn next(&mut self) -> Result { + fn next(&mut self) -> ParseResult { if self.idx >= self.tokens.len() { - return Err(CompileError::UnexpectedEOF); + ParseResult::Reject(CompilerError::UnexpectedEndOfInput) + } else { + let token = self.tokens[self.idx].clone(); + self.idx += 1; + ParseResult::Accept(token) } - - let token = self.tokens[self.idx].clone(); - self.idx += 1; - - Ok(token) } - fn peek_next(&mut self) -> Result { + fn peek_next(&self) -> ParseResult { if self.idx >= self.tokens.len() { - return Err(CompileError::UnexpectedEOF); + ParseResult::Reject(CompilerError::UnexpectedEndOfInput) + } else { + ParseResult::Accept(self.tokens[self.idx].clone()) } + } - Ok(self.tokens[self.idx].clone()) + fn peek(&self, offset: usize) -> ParseResult { + if self.idx + offset >= self.tokens.len() { + ParseResult::Reject(CompilerError::UnexpectedEndOfInput) + } else { + ParseResult::Accept(self.tokens[self.idx + offset].clone()) + } } } -#[derive(Debug, Clone, PartialEq)] -pub enum Node { - /// A scope, which is a list of child nodes that are evaluated in order. - Block { children: Vec }, - /// A leaf node with a value. - Terminal { value: Token }, +#[derive(Debug, Clone)] +pub struct Program { + pub declarations: Vec, +} - /// A unary operator with a right operand. - UnaryOp { op: Token, right: Box }, - - /// A binary operator with a left and right operand. - BinaryOp { - left: Box, - op: Token, - right: Box, +#[derive(Debug, Clone)] +pub enum Declaration { + Function { + name: String, + return_type: TypeId, + params: Vec, + body: Block, }, + Variable { + var: Variable, + init: Option, + is_const: bool, + }, + Dependency(Dependency), +} - /// A statement, consisting of a value to assign to, and an expression. - Statement { left: Box, right: Box }, +#[derive(Debug, Clone)] +pub struct Dependency { + pub name: String, + pub path: String, +} - /// An if expression, which evaluates to either the then branch or the else branch. +#[derive(Debug, Clone)] +pub struct Name { + pub name: String, + pub namespace: Option, +} + +#[derive(Debug, Clone)] +pub enum TypeId { + U8, + U16, + U32, + I8, + I16, + I32, + Char, + Void, + Ptr(Box), + Ref(Box), + Array(Box, usize), + Struct { name: Name, fields: Vec }, +} + +pub type Block = Vec; + +#[derive(Debug, Clone)] +pub struct Variable { + pub name: String, + pub type_id: TypeId, +} + +#[derive(Debug, Clone)] +pub enum Statement { + Block(Block), + Declaration { + var: Variable, + value: Option, + }, + Assign { + varname: String, + value: Expression, + }, + Expression { + expr: Expression, + }, If { - condition: Box, - then_branch: Box, - else_branch: Option>, + condition: Expression, + then_stmt: Block, + else_stmt: Block, }, - - /// A loop expression, which evaluates to the last value of the loop. - /// a loop can be exited with the break keyword. - Loop { body: Box }, - - /// A function definition. ``` | param: type .. | -> ret_type { body }``` - FunctionDef { params: Vec, body: Box }, - - /// A typed variable definition: ```val: Type``` - TypedVar { name: Token, type_: Token }, - - /// A type definition, which is a list of fields. ```type MyType { field: Type }``` - TypeDef { name: Token, fields: Vec }, + While { + condition: Expression, + body: Vec, + }, + Loop(Block), + Break, + Continue, + Return(Option), } -#[derive(Debug)] -pub enum CompileError { - Generic, - ExpectedToken { expected: String, found: Token }, - UnexpectedEOF, +#[derive(Debug, Clone)] +pub enum ConstExpr { + Number(i32), + String(String), +} + +impl fmt::Display for ConstExpr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ConstExpr::Number(n) => write!(f, "{}", n), + ConstExpr::String(s) => write!(f, "\"{}\"", s), + } + } +} + +#[derive(Debug, Clone)] +pub enum Expression { + Empty, + Binary { + op: BinaryOperator, + left: Box, + right: Box, + }, + Unary { + op: UnaryOperator, + operand: Box, + }, + Variable { + name: Name, + expr_type: Option, + }, + Call { + name: Name, + args: Vec, + }, + Number(isize), + StringLiteral(String), + CharLiteral(char), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum BinaryOperator { + Add, + Sub, + Mul, + Div, + Eq, + Ne, + Lt, + Gt, + Le, + Ge, +} + +impl fmt::Display for BinaryOperator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + BinaryOperator::Add => write!(f, "+"), + BinaryOperator::Sub => write!(f, "-"), + BinaryOperator::Mul => write!(f, "*"), + BinaryOperator::Div => write!(f, "/"), + BinaryOperator::Eq => write!(f, "=="), + BinaryOperator::Ne => write!(f, "!="), + BinaryOperator::Lt => write!(f, "<"), + BinaryOperator::Gt => write!(f, ">"), + BinaryOperator::Le => write!(f, "<="), + BinaryOperator::Ge => write!(f, ">="), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum UnaryOperator { + Plus, + Minus, + Reference, + Dereference, +} + +impl fmt::Display for UnaryOperator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + UnaryOperator::Plus => write!(f, "+"), + UnaryOperator::Minus => write!(f, "-"), + UnaryOperator::Dereference => write!(f, "*"), + UnaryOperator::Reference => write!(f, "&"), + } + } +} + +impl ParseResult { + pub fn accepted(&self) -> bool { + matches!(self, ParseResult::Accept(_)) + } +} + +pub enum ParseResultResidual { + Deny, + Reject(T), +} + +impl Try for ParseResult { + type Output = T; + type Residual = ParseResultResidual; + + fn from_output(output: T) -> Self { + ParseResult::Accept(output) + } + + fn branch(self) -> ControlFlow { + match self { + ParseResult::Accept(v) => ControlFlow::Continue(v), + ParseResult::Deny => ControlFlow::Break(ParseResultResidual::Deny), + ParseResult::Reject(e) => ControlFlow::Break(ParseResultResidual::Reject(e)), + } + } +} + +impl FromResidual for ParseResult { + fn from_residual(residual: ParseResultResidual) -> Self { + match residual { + ParseResultResidual::Deny => ParseResult::Deny, + ParseResultResidual::Reject(e) => ParseResult::Reject(e), + } + } } #[macro_export] macro_rules! expect_tt { ($token:expr, $($variant:ident),+) => {{ - let tt = $token.tt().to_string(); - - println!("CASE"); - println!("TOK {:?}", $token); - println!("TT {}", tt); + let token = $token.clone(); + let tt = token.tt().to_string(); let mut vs = String::new(); $( @@ -275,15 +733,11 @@ macro_rules! expect_tt { match tt.as_str() { $( - stringify!($variant) => Ok($token.clone()), + stringify!($variant) => ParseResult::Accept(token), )+ _ => { - println!("EXPECTED!! {} [{}]", tt, vs); - let expected = format!("[{}]", vec![$(stringify!($variant)),+].join(" | ")); - Err(CompileError::ExpectedToken { - expected, - found: $token.clone(), - }) + // let expected = format!("[{}]", vec![$(stringify!($variant)),+].join(" | ")); + ParseResult::Reject(CompilerError::UnexpectedToken(token)) } } }}; @@ -291,16 +745,11 @@ macro_rules! expect_tt { #[macro_export] macro_rules! expect_value { - ($token:expr, $variant:expr) => {{ - match $token { - $variant(x) => Ok(x), - _ => { - let expected = format!("[{}]") - Err(CompileError::ExpectedToken { - expected, - found: $token.clone(), - }) - } + ($expr:expr, $variant:ident) => {{ + let tok = $expr; + match tok.clone() { + Token::$variant(value) => ParseResult::Accept(value), + _ => ParseResult::Reject(CompilerError::UnexpectedToken(tok)), } }}; } diff --git a/compiler/src/parserprototype.rs b/compiler/src/parserprototype.rs deleted file mode 100644 index 72c3441..0000000 --- a/compiler/src/parserprototype.rs +++ /dev/null @@ -1,435 +0,0 @@ -use crate::lexer::Token; -use crate::{expect_tt, expect_value}; -use core::fmt; -use std::ops::{ControlFlow, FromResidual, Try}; - -#[derive(Debug, Clone)] -pub enum ParseResult { - Accept(T), - Deny, - Reject(E), -} - -#[derive(Debug, Clone)] -pub enum CompilerError { - UnexpectedToken(Token), - UnexpectedEndOfInput, - UnexpectedCharacter(char), - InvalidSyntax(String), - Generic(String), -} - -pub struct Parser { - tokens: Vec, - idx: usize, -} - -impl Parser { - pub fn new(tokens: Vec) -> Self { - Self { tokens, idx: 0 } - } - - pub fn parse(&mut self) -> ParseResult { - let mut declarations = Vec::new(); - - while let ParseResult::Accept(_) = self.peek_next() { - declarations.push(self.parse_declaration()?); - } - - ParseResult::Accept(Program { - imports: vec![], - declarations, - }) - } - - fn parse_declaration(&mut self) -> ParseResult { - if expect_tt!(self.peek_next()?, Fn).accepted() { - let x = self.parse_func(); - println!("function {:?}", x); - return x; - } - - println!("{:?}", self.peek_next()?); - - ParseResult::Reject(CompilerError::UnexpectedEndOfInput) - } - - fn parse_func(&mut self) -> ParseResult { - // expect function keyword - // - println!("pre name! {:?}", self.peek_next()?); - - let _ = expect_tt!(self.next()?, Fn); - - println!("this is the name! {:?}", self.peek_next()?); - - // expect function name - let name = match self.next()? { - Token::Identifier(name) => name, - id => return ParseResult::Reject(CompilerError::UnexpectedToken(id)), - }; - - // expect left paren - let _ = expect_tt!(self.next()?, LParen); - - let mut params = Vec::new(); - while expect_tt!(self.peek_next()?, Identifier).accepted() { - let arg = self.parse_var_decl()?; - params.push(arg); - } - - // expect right paren - let _ = expect_tt!(self.next()?, RParen); - - // see if we can parse the return type! - let mut return_type = TypeId::Void; - - if expect_tt!(self.peek_next()?, RightArrow).accepted() { - let _ = self.next(); - return_type = self.parse_type()?; - } - - // expect left brace - let _ = expect_tt!(self.next()?, LBrace); - - let mut body = Vec::new(); - - // expect right brace - let _ = expect_tt!(self.next()?, RBrace); - - ParseResult::Accept(Declaration::Function { - name, - params, - return_type, - body, - }) - } - - fn parse_var_decl(&mut self) -> ParseResult { - let name = match self.next()? { - Token::Identifier(name) => name, - id => return ParseResult::Reject(CompilerError::UnexpectedToken(id)), - }; - - let _ = expect_tt!(self.next()?, Colon); - - let type_ = self.parse_type()?; - - ParseResult::Accept(Variable { - name, - param_type: Some(type_), - }) - } - - fn parse_type(&mut self) -> ParseResult { - // get the type name incl namespace - let typename = self.parse_identifier()?; - - match typename.name.as_str() { - "u32" => ParseResult::Accept(TypeId::U32), - "u16" => ParseResult::Accept(TypeId::U16), - "u8" => ParseResult::Accept(TypeId::U8), - "i32" => ParseResult::Accept(TypeId::I32), - "i16" => ParseResult::Accept(TypeId::I16), - "i8" => ParseResult::Accept(TypeId::I8), - "void" => ParseResult::Accept(TypeId::Void), - "char" => ParseResult::Accept(TypeId::Char), - _ => todo!("Implement parsing for other types!!"), - } - } - - fn parse_identifier(&mut self) -> ParseResult { - let primary = match self.next()? { - Token::Identifier(namespace) => namespace, - id => return ParseResult::Reject(CompilerError::UnexpectedToken(id)), - }; - - if expect_tt!(self.peek_next()?, Colon).accepted() { - let _ = expect_tt!(self.next()?, Colon); - let _ = expect_tt!(self.next()?, Colon); - - let secondary = match self.next()? { - Token::Identifier(name) => name, - id => return ParseResult::Reject(CompilerError::UnexpectedToken(id)), - }; - - ParseResult::Accept(Name { - namespace: Some(primary), - name: secondary, - }) - } else { - ParseResult::Accept(Name { - namespace: None, - name: primary, - }) - } - } - - fn next(&mut self) -> ParseResult { - if self.idx >= self.tokens.len() { - ParseResult::Reject(CompilerError::UnexpectedEndOfInput) - } else { - let token = self.tokens[self.idx].clone(); - println!("NEXT {:?}", token); - self.idx += 1; - ParseResult::Accept(token) - } - } - - fn peek_next(&self) -> ParseResult { - if self.idx >= self.tokens.len() { - ParseResult::Reject(CompilerError::UnexpectedEndOfInput) - } else { - ParseResult::Accept(self.tokens[self.idx].clone()) - } - } -} - -#[derive(Debug, Clone)] -pub struct Program { - pub imports: Vec, - pub declarations: Vec, -} - -#[derive(Debug, Clone)] -pub enum Declaration { - Function { - name: String, - return_type: TypeId, - params: Vec, - body: Block, - }, - Variable { - name: String, - init: Option, - }, -} - -#[derive(Debug, Clone)] -pub struct Dependency { - pub name: String, - pub path: String, -} - -#[derive(Debug, Clone)] -pub struct Variable { - pub name: String, - pub param_type: Option, -} - -#[derive(Debug, Clone)] -pub struct Name { - pub name: String, - pub namespace: Option, -} - -#[derive(Debug, Clone)] -pub enum TypeId { - U8, - U16, - U32, - I8, - I16, - I32, - Char, - Void, - Ptr(Box), - Ref(Box), - Array(Box, usize), - Struct { - name: Name, - fields: Vec<(String, TypeId)>, - }, -} - -pub type Block = Vec; - -#[derive(Debug, Clone)] -pub enum Statement { - Block(Block), - Assign { - var: Variable, - value: Option>, - }, - Expression { - expr: Expression, - }, - If { - condition: Expression, - then_stmt: Block, - else_stmt: Block, - }, - While { - condition: Expression, - body: Vec, - }, - Loop(Block), - Break, - Continue, - Return(Option), -} - -#[derive(Debug, Clone)] -pub enum ConstExpr { - Number(i32), - String(String), -} - -impl fmt::Display for ConstExpr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ConstExpr::Number(n) => write!(f, "{}", n), - ConstExpr::String(s) => write!(f, "\"{}\"", s), - } - } -} - -#[derive(Debug, Clone)] -pub enum Expression { - Empty, - Binary { - op: BinaryOperator, - left: Box, - right: Box, - }, - Unary { - op: UnaryOperator, - operand: Box, - }, - Variable { - name: Name, - expr_type: Option, - }, - Number { - value: i32, - }, - Call { - name: Name, - args: Vec, - }, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum BinaryOperator { - Add, - Sub, - Mul, - Div, - Eq, - Ne, - Lt, - Gt, - Le, - Ge, -} - -impl fmt::Display for BinaryOperator { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - BinaryOperator::Add => write!(f, "+"), - BinaryOperator::Sub => write!(f, "-"), - BinaryOperator::Mul => write!(f, "*"), - BinaryOperator::Div => write!(f, "/"), - BinaryOperator::Eq => write!(f, "=="), - BinaryOperator::Ne => write!(f, "!="), - BinaryOperator::Lt => write!(f, "<"), - BinaryOperator::Gt => write!(f, ">"), - BinaryOperator::Le => write!(f, "<="), - BinaryOperator::Ge => write!(f, ">="), - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum UnaryOperator { - Plus, - Minus, -} - -impl fmt::Display for UnaryOperator { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - UnaryOperator::Plus => write!(f, "+"), - UnaryOperator::Minus => write!(f, "-"), - } - } -} - -impl ParseResult { - pub fn accepted(&self) -> bool { - matches!(self, ParseResult::Accept(_)) - } -} - -pub enum ParseResultResidual { - Deny, - Reject(T), -} - -impl Try for ParseResult { - type Output = T; - type Residual = ParseResultResidual; - - fn from_output(output: T) -> Self { - ParseResult::Accept(output) - } - - fn branch(self) -> ControlFlow { - match self { - ParseResult::Accept(v) => ControlFlow::Continue(v), - ParseResult::Deny => ControlFlow::Break(ParseResultResidual::Deny), - ParseResult::Reject(e) => ControlFlow::Break(ParseResultResidual::Reject(e)), - } - } -} - -impl FromResidual for ParseResult { - fn from_residual(residual: ParseResultResidual) -> Self { - match residual { - ParseResultResidual::Deny => ParseResult::Deny, - ParseResultResidual::Reject(e) => ParseResult::Reject(e), - } - } -} - -#[macro_export] -macro_rules! expect_tt { - ($token:expr, $($variant:ident),+) => {{ - let tt = $token.tt().to_string(); - - // for some reason the code trips tf out without this line - - println!("token {:?}", $token); - - let mut vs = String::new(); - $( - let s = stringify!($variant); - vs.push_str(s); - vs.push_str("|"); - )+ - - match tt.as_str() { - $( - stringify!($variant) => ParseResult::Accept($token.clone()), - )+ - _ => { - println!("EXPECTED!! {} [{}]", tt, vs); - // let expected = format!("[{}]", vec![$(stringify!($variant)),+].join(" | ")); - ParseResult::Reject(CompilerError::UnexpectedToken($token.clone())) - } - } - }}; -} - -#[macro_export] -macro_rules! expect_value { - ($token:expr, $variant:expr) => {{ - match $token { - $variant(x) => ParseResult::Accept(x), - _ => { - let expected = format!("[{}]") - ParseResult::Reject(CompilerError::UnexpectedToken($token.clone())) - } - } - }}; -} diff --git a/compiler/src/registers.rs b/compiler/src/registers.rs new file mode 100644 index 0000000..910f065 --- /dev/null +++ b/compiler/src/registers.rs @@ -0,0 +1,375 @@ +use std::collections::HashMap; + +use crate::parser::CompilerError; + +/// Register allocator for DSA assembly generation +/// Manages general-purpose registers (rg0-rgf) and handles stack spilling +pub struct RegisterAllocator { + /// Available general-purpose registers + available_registers: Vec, + + /// Maps variable names to their current location (register or stack offset) + variable_locations: HashMap, + + /// Maps registers to the variables they currently hold + register_contents: HashMap, + + /// Current stack offset for local variables (relative to bpr) + /// Starts at -4 (going downward from base pointer) + stack_offset: i32, + + /// Track which registers are currently in use + in_use: HashMap, +} + +#[derive(Debug, Clone)] +pub enum Location { + Register(String), + Stack(i32), // offset from bpr +} + +impl RegisterAllocator { + pub fn new() -> Self { + // Initialize with available GP registers (rg0-rgf = 16 registers) + let registers = vec![ + "rg0", "rg1", "rg2", "rg3", "rg4", "rg5", "rg6", "rg7", "rg8", "rg9", "rga", + "rgb", "rgc", "rgd", "rge", "rgf", + ] + .into_iter() + .map(String::from) + .collect(); + + RegisterAllocator { + available_registers: registers, + variable_locations: HashMap::new(), + register_contents: HashMap::new(), + stack_offset: -4, // Start at -4 (first local below saved bpr) + in_use: HashMap::new(), + } + } + + /// Allocate a temporary register for expression evaluation + /// Returns the register name and optionally assembly code to save it + pub fn alloc_temp(&mut self) -> Result<(String, Vec), CompilerError> { + let mut code = Vec::new(); + + // Try to find an unused register + for reg in &self.available_registers { + if !self.in_use.get(reg).unwrap_or(&false) { + self.in_use.insert(reg.clone(), true); + return Ok((reg.clone(), code)); + } + } + + // All registers in use - need to spill one + // Choose the first register with a variable we can 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 { + // Spill this variable to stack + let spill_code = self.spill_register(®)?; + code.extend(spill_code); + + self.in_use.insert(reg.clone(), true); + return Ok((reg, code)); + } + + Err(CompilerError::Generic( + "All registers are used up yet there are no variables to spill to the stack" + .to_string(), + )) + } + + /// Free a temporary register after use + /// NOTE: This will NOT free registers that contain variables! + /// Variables persist throughout their scope and must not be freed + pub fn free_temp(&mut self, reg: &str) { + // Check if this register contains a variable + if self.register_contents.contains_key(reg) { + // This register holds a variable - don't free it! + // Variables are only freed when they go out of scope via free_var() + return; + } + + // This is a true temporary - safe to free + self.in_use.insert(reg.to_string(), false); + } + + /// Allocate a register for a named variable + /// Returns the register and any necessary assembly code + pub fn alloc_var( + &mut self, + var_name: &str, + ) -> Result<(String, Vec), CompilerError> { + if let Some(location) = self.variable_locations.get(var_name).cloned() { + match location { + Location::Register(reg) => { + return Ok((reg.clone(), 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) + code.push(format!("\tsubi bpr {} {}", -(offset + 4), reg)); + code.push(format!("\tldw {}, {}", reg, reg)); + + // Update location to register + self.variable_locations + .insert(var_name.to_string(), Location::Register(reg.clone())); + 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 + let (reg, code) = self.alloc_temp()?; + self.variable_locations + .insert(var_name.to_string(), Location::Register(reg.clone())); + self.register_contents + .insert(reg.clone(), var_name.to_string()); + + Ok((reg, code)) + } + + /// Get the current location of a variable + pub fn get_var_location(&self, var_name: &str) -> Option<&Location> { + self.variable_locations.get(var_name) + } + + /// Load a variable into a register (allocating if necessary) + /// Returns the register and assembly code to load it + pub fn load_var( + &mut self, + var_name: &str, + ) -> Result<(String, Vec), CompilerError> { + self.alloc_var(var_name) + } + + /// Store a value from a register into a variable + /// Updates tracking and returns any necessary assembly code + pub fn store_var(&mut self, var_name: &str, source_reg: &str) -> Vec { + let mut code = Vec::new(); + + // Check if variable already has a location + if let Some(location) = self.variable_locations.get(var_name) { + match location { + Location::Register(dest_reg) => { + if dest_reg != source_reg { + code.push(format!("\tmov {}, {}", source_reg, dest_reg)); + } + } + Location::Stack(offset) => { + code.push(format!("\tstw {}, bpr, {}", source_reg, offset)); + } + } + } else { + // Variable doesn't exist yet, we can just use the same reg. + + self.variable_locations.insert( + var_name.to_string(), + Location::Register(source_reg.to_string()), + ); + self.register_contents + .insert(source_reg.to_string(), var_name.to_string()); + self.in_use.insert(source_reg.to_string(), true); + + // this is not needed for now as if we're storing a var we already have a temp + // register allocated. + // if let Some(free_reg) = self.find_free_register() { + // if &free_reg != source_reg { + // 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 + // } + } + + code + } + + /// Spill a register to the stack + /// Returns assembly code to perform the spill + pub fn spill_register(&mut self, reg: &str) -> Result, CompilerError> { + let mut code = Vec::new(); + + if let Some(var_name) = self.register_contents.get(reg).cloned() { + // PUSH register to stack (spr decrements automatically) + code.push(format!("\tpush {}", reg)); + + // 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 + self.variable_locations + .insert(var_name.clone(), Location::Stack(self.stack_offset)); + + // Remove from register tracking + self.register_contents.remove(reg); + } + + Ok(code) + } + + /// Find a free register (not currently in use) + fn find_free_register(&self) -> Option { + for reg in &self.available_registers { + if !self.in_use.get(reg).unwrap_or(&false) { + return Some(reg.clone()); + } + } + None + } + + /// Spill all registers to stack (useful before function calls) + pub fn spill_all(&mut self) -> Vec { + let mut code = Vec::new(); + + let regs_to_spill: Vec = self.register_contents.keys().cloned().collect(); + + for reg in regs_to_spill { + if let Ok(spill_code) = self.spill_register(®) { + code.extend(spill_code); + } + } + + code + } + + /// Get the total stack offset + pub fn get_stack_offset(&self) -> i32 { + self.stack_offset + } + + /// Get the total stack space needed for local variables + pub fn get_stack_size(&self) -> i32 { + -self.stack_offset // Convert negative offset to positive size + } + + /// Reset allocator for a new function + pub fn reset(&mut self) { + self.variable_locations.clear(); + self.register_contents.clear(); + self.stack_offset = -4; + self.in_use.clear(); + } + + /// Mark a variable as dead (no longer needed) + /// Frees its register if it's in one + pub fn free_var(&mut self, var_name: &str) { + if let Some(Location::Register(reg)) = self.variable_locations.get(var_name) { + let reg = reg.clone(); + self.register_contents.remove(®); + self.in_use.insert(reg, false); + } + self.variable_locations.remove(var_name); + } + + /// Get list of registers that contain variables and are in use + /// These need to be saved before function calls + pub fn get_caller_saved_registers(&self) -> Vec { + self.register_contents + .iter() + .filter(|(reg, _)| *self.in_use.get(*reg).unwrap_or(&false)) + .map(|(reg, _)| reg.clone()) + .collect() + } + + /// Save caller-saved registers before a function call + /// Returns assembly code to save them + pub fn save_caller_saved(&mut self) -> Vec { + let mut code = Vec::new(); + + // For simplicity, save all currently used registers + // In a more sophisticated compiler, you'd only save registers that are live + for (reg, var_name) in self.register_contents.clone() { + if *self.in_use.get(®).unwrap_or(&false) { + code.push(format!("\tpush {}", reg)); + } + } + + code + } + + /// Restore caller-saved registers after a function call + /// Returns assembly code to restore them + pub fn restore_caller_saved(&mut self, saved_regs: &[String]) -> Vec { + let mut code = Vec::new(); + + // Restore in reverse order (LIFO) + for reg in saved_regs.iter().rev() { + code.push(format!("\tpop {}", reg)); + } + + code + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic_allocation() { + let mut allocator = RegisterAllocator::new(); + + let (reg1, code1) = allocator.alloc_temp().unwrap(); + assert_eq!(code1.len(), 0); // No spill needed + assert_eq!(reg1, "rg0"); + + let (reg2, code2) = allocator.alloc_temp().unwrap(); + assert_eq!(code2.len(), 0); + assert_eq!(reg2, "rg1"); + + allocator.free_temp(®1); + + let (reg3, code3) = allocator.alloc_temp().unwrap(); + assert_eq!(code3.len(), 0); + assert_eq!(reg3, "rg0"); // Reuses freed register + } + + #[test] + fn test_variable_allocation() { + let mut allocator = RegisterAllocator::new(); + + let (reg, _) = allocator.alloc_var("x").unwrap(); + assert_eq!(reg, "rg0"); + + // Requesting same variable again should return same register + let (reg2, _) = allocator.alloc_var("x").unwrap(); + assert_eq!(reg2, "rg0"); + } + + #[test] + fn test_stack_allocation() { + let mut allocator = RegisterAllocator::new(); + + // Allocate all 16 registers + for i in 0..16 { + allocator.alloc_var(&format!("var{}", i)).unwrap(); + } + + // Next allocation should spill to stack + let (reg, code) = allocator.alloc_var("var16").unwrap(); + assert!(code.len() > 0); // Should have spill code + } +} diff --git a/compiler/src/semantic_analyser.rs b/compiler/src/semantic_analyser.rs new file mode 100644 index 0000000..d8f3ce5 --- /dev/null +++ b/compiler/src/semantic_analyser.rs @@ -0,0 +1,13 @@ +use crate::parser::{CompilerError, Program}; + +pub struct Analyser; + +impl Analyser { + pub fn new() -> Self { + Self + } + + pub fn analyse(&self, ast: Program) -> Result<(), CompilerError> { + Ok(()) + } +} diff --git a/resources/dsa/code.dsa b/resources/dsa/code.dsa deleted file mode 100644 index 6a78ddc..0000000 --- a/resources/dsa/code.dsa +++ /dev/null @@ -1,139 +0,0 @@ - -// GENERATED BY DSA-C COMPILER -// Generated at 2026-01-31 01:39:55 - -// Imports -include maths: "./lib/maths/core.dsa" -include print: "lib/io/print.dsa" - -// Globals & Reserved Memory - - -// Entry Point -dw stack: 0x10000 -db message: "Process Exited with code:" -_init: - ldw stack, bpr - mov bpr, spr - push zero - call main - call print::print_newline - lwi message, rg0 - push rg0 - call print::print - pop zero - call print::print_hex_word - pop zero - hlt - - -// Function return boilerplate -_ret: - mov bpr, spr - pop bpr - return - - -factorial: - push bpr - mov spr, bpr - - ldw bpr, rg0, 8 - lli 1, rg1 - cmp rg0, rg1 - lli 0, rg2 - jgt _cmp_end_1 - lli 1, rg2 -_cmp_end_1: - cmp rg2, zero - jeq _else_3 -_then_2: - lli 1, rg1 - stw rg1, bpr, 8 - jmp _ret - jmp _end_4 -_else_3: - nop -_end_4: - push rg0 - lli 1, rg1 - sub rg0, rg1, rg2 - push rg2 - call factorial - pop rg1 - pop rg0 - push rg1 - push rg0 - call maths::multiply - pop rg2 - pop zero - stw rg2, bpr, 8 - jmp _ret - -add_: - push bpr - mov spr, bpr - - ldw bpr, rg0, 8 - ldw bpr, rg1, 12 - add rg0, rg1, rg2 - stw rg2, bpr, 8 - jmp _ret - -greater: - push bpr - mov spr, bpr - - ldw bpr, rg0, 8 - ldw bpr, rg1, 12 - add rg0, rg0, rg2 - add rg1, rg1, rg3 - cmp rg2, rg3 - lli 0, rg4 - jle _cmp_end_5 - lli 1, rg4 -_cmp_end_5: - cmp rg4, zero - jeq _else_7 -_then_6: - stw rg0, bpr, 8 - jmp _ret - jmp _end_8 -_else_7: - add rg1, rg0, rg2 - stw rg2, bpr, 8 - jmp _ret -_end_8: - jmp _ret - -main: - push bpr - mov spr, bpr - - lli 5, rg0 - push rg0 - lli 5, rg1 - push rg1 - call add_ - pop rg2 - pop zero - push rg2 - lli 5, rg0 - push rg0 - call greater - pop rg1 - pop zero - push rg1 - call print::print_num - pop rg0 - lli 5, rg0 - push rg0 - call factorial - pop rg1 - push rg1 - call print::print_num - pop rg0 - lli 0, rg0 - stw rg0, bpr, 8 - jmp _ret - diff --git a/resources/dsa/lib/io/print.dsa b/resources/dsa/lib/io/print.dsa index 43b2e83..8a5626c 100644 --- a/resources/dsa/lib/io/print.dsa +++ b/resources/dsa/lib/io/print.dsa @@ -40,7 +40,7 @@ dw display: 0x20000 dw current: 0x20000 // ------------------------------------------ -// prints the string at addr(arg[0]) to the screen. +// prints the string at addr(arg[0]) to the screen. (no trailing whitespace unless explicitly provided) print: push bpr mov spr, bpr @@ -50,13 +50,36 @@ print: _print_loop: ldb rg0, acc + cmp acc, zero + jeq _end stb acc, rg1 addi rg0, 1 addi rg1, 1 - cmp acc, zero - jne _print_loop + jmp _print_loop + +// ------------------------------------------ +println: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + +_println_loop: + ldb rg0, acc + cmp acc, zero + jeq _println_end + stb acc, rg1 + + addi rg0, 1 + addi rg1, 1 + + jmp _println_loop + +_println_end: + call print_newline jmp _end // ------------------------------------------ diff --git a/resources/dsa/lib/maths/core.dsa b/resources/dsa/lib/maths/core.dsa index 25a2574..9fc72e8 100644 --- a/resources/dsa/lib/maths/core.dsa +++ b/resources/dsa/lib/maths/core.dsa @@ -59,3 +59,46 @@ _divmod_end: mov bpr, spr pop bpr return + +// multiply.dsa - improved version +// Multiplies two 32-bit numbers using shift-and-add +// +// Usage: +// push operand2 (multiplier) +// push operand1 (multiplicand) +// call multiply::multiply +// pop result +// pop zero (discard second argument) + +new_multiply: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // rg0 = multiplicand + ldw bpr, rg1, 12 // rg1 = multiplier + + lli 0, rg2 // rg2 = result (accumulator) + lli 32, rg3 // rg3 = bit counter + +mult_loop: + // Check if lowest bit of multiplier is 1 + lli 1, acc + and rg1, acc, acc // acc = rg1 & 1 + cmp acc, zero + jeq skip_add // if (rg1 & 1) == 0, skip addition + + // Add multiplicand to result + add rg2, rg0, rg2 + +skip_add: + shl rg0, 1 // shift multiplicand left + shr rg1, 1 // shift multiplier right + + dec rg3 + cmp rg3, zero + jgt mult_loop + + stw rg2, bpr, 8 // store result + mov bpr, spr + pop bpr + return diff --git a/resources/dsa/lib/memory/alloc.dsa b/resources/dsa/lib/memory/alloc.dsa new file mode 100644 index 0000000..29a0243 --- /dev/null +++ b/resources/dsa/lib/memory/alloc.dsa @@ -0,0 +1,37 @@ +dw global_arena_start: 0x30000 +dw global_arena_current: 0x30000 +dw global_arena_end: 0x40000 + +arena_alloc: + // Just like bump allocator + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // size argument + ldw global_arena_current, rg1 + + add rg1, rg0, rg2 // new_current = current + size + ldw global_arena_end, rg3 + + cmp rg2, rg3 + jgt out_of_memory + + stw rg2, global_arena_current + mov rg1, acc // return old current + stw acc, bpr, 8 + + mov bpr, spr + pop bpr + return + +arena_reset: + // Reset to start + push bpr + mov spr, bpr + + ldw global_arena_start, rg0 + stw rg0, global_arena_current + + mov bpr, spr + pop bpr + return diff --git a/resources/dsa/main.dsa b/resources/dsa/main.dsa index 3020247..0f8daa2 100644 --- a/resources/dsa/main.dsa +++ b/resources/dsa/main.dsa @@ -15,13 +15,16 @@ init: dw string: "hello world" start: + lwi 100, rg0 + lwi 10, rg1 - lwi 1, rg0 - lwi 2, rg1 - - push rg0 push rg1 - call maths::multiply + push rg0 + call maths::new_divide + pop rg0 + pop rg1 + hlt + pop rg0 pop zero push rg0 diff --git a/resources/dsa/output.dsa b/resources/dsa/output.dsa new file mode 100644 index 0000000..23b3407 --- /dev/null +++ b/resources/dsa/output.dsa @@ -0,0 +1,67 @@ + +// GENERATED BY DSC COMPILER +// Generated at 2026-02-03 02:08:02 + +// Imports +include print: "./lib/io/print.dsa" + +// Globals & Reserved Memory + + +// Entry Point +dw stack: 0x10000 +db message: "Process Exited with code:" +_init: + ldw stack, bpr + mov bpr, spr + push zero + call main + call print::print_newline + lwi message, rg0 + push rg0 + call print::print + pop zero + call print::print_hex_word + pop zero + hlt + + +// Return +_ret: + mov bpr, spr + pop bpr + return + +// Compiled Code Starts... +main: + push bpr + mov spr, bpr + + lli 5, rg0 + db str_1: "Hello world" + lwi str_1, rg1 + db str_2: "test" + lwi str_2, rg2 + push rg0 + push rg1 + push rg2 + db str_3: "hello world 2 electric boogaloo" + lwi str_3, rg3 + push rg3 + call print::println + pop zero + pop rg2 + pop rg1 + pop rg0 + push rg0 + push rg1 + push rg2 + lli 213, rg3 + push rg3 + call print::print_num + pop zero + pop rg2 + pop rg1 + pop rg0 + jmp _ret + diff --git a/resources/dsc/example.dsc b/resources/dsc/example.dsc index 0f09703..cb07da2 100644 --- a/resources/dsc/example.dsc +++ b/resources/dsc/example.dsc @@ -1,2 +1,8 @@ -fn factorial(n: u32) -> u32 {} -fn main(x: u32, y: u32) -> u32 {} +fn main() -> u32 { + let x: u32 = 5; + let stringgg: str = "Hello world"; + let test: str = "test"; + + println("hello world 2 electric boogaloo"); + printnum(213); +} diff --git a/resources/ideas/design.rnote b/resources/ideas/design.rnote new file mode 100644 index 0000000..1e96927 Binary files /dev/null and b/resources/ideas/design.rnote differ