Merge compiler and emulator progress from last few months into main. #11
@@ -5,3 +5,4 @@ edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[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) {
|
||||
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;
|
||||
|
||||
@@ -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 {
|
||||
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> {
|
||||
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<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
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ pub enum Declaration {
|
||||
},
|
||||
Variable {
|
||||
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)]
|
||||
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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user