started on codegen, scaffolding basically done
This commit is contained in:
@@ -5,3 +5,4 @@ edition.workspace = true
|
|||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono = "0.4.42"
|
||||||
|
|||||||
+4
-2
@@ -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;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Imports
|
||||||
|
include maths: "./lib/maths/core.dsa"
|
||||||
|
|
||||||
|
// Reserved Memory
|
||||||
|
|
||||||
+185
-5
@@ -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 – 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) }};
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user