diff --git a/c_compiler/Cargo.toml b/c_compiler/Cargo.toml index 787ae6a..ef733ea 100644 --- a/c_compiler/Cargo.toml +++ b/c_compiler/Cargo.toml @@ -5,3 +5,4 @@ edition.workspace = true authors.workspace = true [dependencies] +chrono = "0.4.42" diff --git a/c_compiler/code.c b/c_compiler/code.c index cc43221..5840a39 100644 --- a/c_compiler/code.c +++ b/c_compiler/code.c @@ -1,3 +1,7 @@ +int x = 5; + +int add(int a, int b) { return a + b; } + int factorial(int n) { if (n <= 1) { return 1; @@ -5,8 +9,6 @@ int factorial(int n) { return n * factorial(n - 1); } -int add(int a, int b) { return a + b; } - int main() { int x; x = 5; diff --git a/c_compiler/output.dsa b/c_compiler/output.dsa new file mode 100644 index 0000000..54e84c0 --- /dev/null +++ b/c_compiler/output.dsa @@ -0,0 +1,5 @@ +// Imports +include maths: "./lib/maths/core.dsa" + +// Reserved Memory + diff --git a/c_compiler/src/codegen.rs b/c_compiler/src/codegen.rs index 06e030d..201ad29 100644 --- a/c_compiler/src/codegen.rs +++ b/c_compiler/src/codegen.rs @@ -1,13 +1,193 @@ -use crate::parser::Program; +use std::time::SystemTime; +use std::{collections::HashMap, path::PathBuf}; -pub struct CodeGenerator; +use chrono::{DateTime, Local}; + +use crate::{block, cmd, comment, dsa}; + +use crate::parser::{ConstExpr, Declaration, Program}; + +pub struct CodeGenerator { + ast: Program, + imports: HashMap, + globals: Vec, + functions: Vec, +} + +fn import(name: &str, path: &str) -> String { + format!("include {name}: \"{}\"", path) +} impl CodeGenerator { pub fn new(ast: Program) -> Self { - CodeGenerator {} + CodeGenerator { + ast, + imports: HashMap::new(), + globals: Vec::new(), + functions: Vec::new(), + } } - pub fn run(&mut self) -> Result { - Ok(String::new()) + pub fn include(&mut self, name: &str, path: &str) { + self.imports.insert(name.to_string(), path.to_string()); + } + + 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 { + self.generate_block(block.clone()); + } + + self.generate_layout() + } + + fn generate_block(&mut self, block: Declaration) { + match block { + Declaration::Variable { name, init } => self.globals.push(format!( + "dw {}: {}", + name, + init.unwrap_or(ConstExpr::Number(0)) + )), + Declaration::Function { + name, + return_type, + params, + body, + } => { + let function_start = format!( + "{name}: \n\t\ + push bpr \n\t\ + mov spr, bpr" + ); + + let function_end = format!( + "\n\t\ + mov bpr, spr \n\t\ + pop bpr \n\t\ + return\n" + ); + + self.functions + .push(format!("{function_start}\n{function_end}")); + } + } + } + + fn generate_layout(&mut self) -> Result { + let datetime: DateTime = SystemTime::now().into(); + Ok(dsa![ + "", + comment!("GENERATED BY DSA-C 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![lwi message, rg0], + dsa![push rg0], + dsa![call print::print], + dsa![pop zero], + dsa![call print::print_hex_word], + dsa![pop zero], + dsa![hlt] + ], + 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"), + ]) } } + +/// 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/c_compiler/src/main.rs b/c_compiler/src/main.rs index cd52c21..812d1ba 100644 --- a/c_compiler/src/main.rs +++ b/c_compiler/src/main.rs @@ -59,7 +59,7 @@ fn main() { // Code Gen let mut generator = CodeGenerator::new(ast); - let result = match generator.run() { + let result = match generator.generate() { Ok(code) => code, Err(e) => { eprintln!("Parsing error: {}", e); @@ -67,6 +67,6 @@ fn main() { } }; - println!("CODE:"); - println!("{:#?}", result); + std::fs::write(output_file, &result).expect("Failed to write output"); + println!("Result written to {}", output_file); } diff --git a/c_compiler/src/parser.rs b/c_compiler/src/parser.rs index 1ae587a..b89aaa1 100644 --- a/c_compiler/src/parser.rs +++ b/c_compiler/src/parser.rs @@ -21,7 +21,7 @@ pub enum Declaration { }, Variable { name: String, - init: Option, + init: Option, }, } @@ -74,6 +74,21 @@ pub enum Statement { }, } +#[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, @@ -269,7 +284,14 @@ impl Parser { // Variable declaration let init = if matches!(self.current().token_type, TokenType::Assign) { self.advance(); - Some(self.parse_expression()?) + + if let TokenType::Number(n) = self.current().token_type { + self.advance(); + Some(ConstExpr::Number(n)) + } else { + return Err(self + .error("Expected constant in global variable declaration")); + } } else { None };