started on codegen, scaffolding basically done

This commit is contained in:
2025-11-15 02:58:36 +00:00
parent 091dabfbf3
commit b9f98bff7b
6 changed files with 222 additions and 12 deletions
+1
View File
@@ -5,3 +5,4 @@ edition.workspace = true
authors.workspace = true authors.workspace = true
[dependencies] [dependencies]
chrono = "0.4.42"
+4 -2
View File
@@ -1,3 +1,7 @@
int x = 5;
int add(int a, int b) { return a + b; }
int factorial(int n) { int factorial(int n) {
if (n <= 1) { if (n <= 1) {
return 1; return 1;
@@ -5,8 +9,6 @@ int factorial(int n) {
return n * factorial(n - 1); return n * factorial(n - 1);
} }
int add(int a, int b) { return a + b; }
int main() { int main() {
int x; int x;
x = 5; x = 5;
+5
View File
@@ -0,0 +1,5 @@
// Imports
include maths: "./lib/maths/core.dsa"
// Reserved Memory
+185 -5
View File
@@ -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<String, String>,
globals: Vec<String>,
functions: Vec<String>,
}
fn import(name: &str, path: &str) -> String {
format!("include {name}: \"{}\"", path)
}
impl CodeGenerator { impl CodeGenerator {
pub fn new(ast: Program) -> Self { pub fn new(ast: Program) -> Self {
CodeGenerator {} CodeGenerator {
ast,
imports: HashMap::new(),
globals: Vec::new(),
functions: Vec::new(),
}
} }
pub fn run(&mut self) -> Result<String, String> { pub fn include(&mut self, name: &str, path: &str) {
Ok(String::new()) self.imports.insert(name.to_string(), path.to_string());
}
pub fn generate(&mut self) -> Result<String, String> {
// 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<String, String> {
let datetime: DateTime<Local> = 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::<Vec<String>>()
.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 well 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 reuses the same buffer.
write!(s, "{}\n", $arg).expect("write to String failed");
)*
s
}};
}
// ──────────────────────── dsa! ────────────────────────
// A tiny helper that just turns its tokenstream into a string.
// The trailing comma is kept its part of the syntax you want.
#[macro_export]
macro_rules! cmd {
($($tokens:tt)*) => {{
// Well 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 thats 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) }};
}
+3 -3
View File
@@ -59,7 +59,7 @@ fn main() {
// Code Gen // Code Gen
let mut generator = CodeGenerator::new(ast); let mut generator = CodeGenerator::new(ast);
let result = match generator.run() { let result = match generator.generate() {
Ok(code) => code, Ok(code) => code,
Err(e) => { Err(e) => {
eprintln!("Parsing error: {}", e); eprintln!("Parsing error: {}", e);
@@ -67,6 +67,6 @@ fn main() {
} }
}; };
println!("CODE:"); std::fs::write(output_file, &result).expect("Failed to write output");
println!("{:#?}", result); println!("Result written to {}", output_file);
} }
+24 -2
View File
@@ -21,7 +21,7 @@ pub enum Declaration {
}, },
Variable { Variable {
name: String, name: String,
init: Option<Expression>, init: Option<ConstExpr>,
}, },
} }
@@ -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)] #[derive(Debug, Clone)]
pub enum Expression { pub enum Expression {
Empty, Empty,
@@ -269,7 +284,14 @@ impl Parser {
// Variable declaration // Variable declaration
let init = if matches!(self.current().token_type, TokenType::Assign) { let init = if matches!(self.current().token_type, TokenType::Assign) {
self.advance(); 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 { } else {
None None
}; };