compiler working for some mathematical expressions, function calls and
simple conditionals
This commit is contained in:
@@ -7,6 +7,6 @@ int factorial(int n) {
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
int res = factorial(3);
|
int res = factorial(3);
|
||||||
print(res);
|
printnum(res);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
include print: "lib/io/print.dsa"
|
||||||
|
|
||||||
|
int factorial(int n) {
|
||||||
|
if (n <= 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return n * factorial(n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_(int a, int b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int greater(int a, int b) {
|
||||||
|
if (a + a > b + b) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return b + a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printnum(greater(5, add_(5, 5)));
|
||||||
|
|
||||||
|
printnum(factorial(5));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Register {
|
||||||
|
// general purpose registers
|
||||||
|
Rg0,
|
||||||
|
Rg1,
|
||||||
|
Rg2,
|
||||||
|
Rg3,
|
||||||
|
Rg4,
|
||||||
|
Rg5,
|
||||||
|
Rg6,
|
||||||
|
Rg7,
|
||||||
|
Rg8,
|
||||||
|
Rg9,
|
||||||
|
Rga,
|
||||||
|
Rgb,
|
||||||
|
Rgc,
|
||||||
|
Rgd,
|
||||||
|
Rge,
|
||||||
|
Rgf,
|
||||||
|
|
||||||
|
// special purpose registers
|
||||||
|
Acc,
|
||||||
|
Spr,
|
||||||
|
Bpr,
|
||||||
|
Ret,
|
||||||
|
Idr,
|
||||||
|
Mmr,
|
||||||
|
Zero,
|
||||||
|
NoReg,
|
||||||
|
|
||||||
|
// system registers - can't be written to by instructions.
|
||||||
|
Mar,
|
||||||
|
Mdr,
|
||||||
|
Sts,
|
||||||
|
Cir,
|
||||||
|
Pcx,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
/// A list of all current instructions in the DSA Assembly language.
|
||||||
|
pub enum Instruction {
|
||||||
|
// No-op
|
||||||
|
Nop = 0x0,
|
||||||
|
|
||||||
|
// Data transfer instructions
|
||||||
|
Mov(Register, Register) = 0x1,
|
||||||
|
Movs(Register, Register) = 0x2,
|
||||||
|
|
||||||
|
Ldb(Register, Register, Option<u32>) = 0x3,
|
||||||
|
Ldbs(Register, Register, Option<u32>) = 0x4,
|
||||||
|
Ldh(Register, Register, Option<u32>) = 0x5,
|
||||||
|
Ldhs(Register, Register, Option<u32>) = 0x6,
|
||||||
|
Ldw(Register, Register, Option<u32>) = 0x7,
|
||||||
|
|
||||||
|
Stb(Register, Register, Option<u32>) = 0x8,
|
||||||
|
Sth(Register, Register, Option<u32>) = 0x9,
|
||||||
|
Stw(Register, Register, Option<u32>) = 0xA,
|
||||||
|
|
||||||
|
Lli(u16, Register) = 0xB,
|
||||||
|
Lui(u16, Register) = 0xC,
|
||||||
|
|
||||||
|
// Jump Instructions
|
||||||
|
Jump(u16, Register) = 0xD,
|
||||||
|
JumpEq(u16, Register) = 0xE,
|
||||||
|
JumpNeq(u16, Register) = 0xF,
|
||||||
|
JumpGt(u16, Register) = 0x10,
|
||||||
|
JumpGe(u16, Register) = 0x11,
|
||||||
|
JumpLt(u16, Register) = 0x12,
|
||||||
|
JumpLe(u16, Register) = 0x13,
|
||||||
|
|
||||||
|
// Comparison
|
||||||
|
Compare(Register, Register) = 0x14,
|
||||||
|
|
||||||
|
// // Arithmetic
|
||||||
|
// Add(args::RTypeArgs) = 0x19,
|
||||||
|
// Sub(args::RTypeArgs) = 0x1A,
|
||||||
|
// Increment(args::RTypeArgs) = 0x15,
|
||||||
|
// Decrement(args::RTypeArgs) = 0x16,
|
||||||
|
// ShiftLeft(args::RTypeArgs) = 0x17,
|
||||||
|
// ShiftRight(args::RTypeArgs) = 0x18,
|
||||||
|
|
||||||
|
// // Logical
|
||||||
|
// And(args::RTypeArgs) = 0x1B,
|
||||||
|
// Or(args::RTypeArgs) = 0x1C,
|
||||||
|
// Not(args::RTypeArgs) = 0x1D,
|
||||||
|
// Xor(args::RTypeArgs) = 0x1E,
|
||||||
|
// Nand(args::RTypeArgs) = 0x1F,
|
||||||
|
// Nor(args::RTypeArgs) = 0x20,
|
||||||
|
// Xnor(args::RTypeArgs) = 0x21,
|
||||||
|
|
||||||
|
// // Misc
|
||||||
|
// Interrupt(Interrupt) = 0x22,
|
||||||
|
// IntReturn = 0x23,
|
||||||
|
// Halt = 0x24,
|
||||||
|
|
||||||
|
// // Immediate Arithmetic
|
||||||
|
// AddImmediate(args::ITypeArgs) = 0x25,
|
||||||
|
// SubImmediate(args::ITypeArgs) = 0x26,
|
||||||
|
|
||||||
|
// Fake Instructions
|
||||||
|
Data(u32) = 0x3E,
|
||||||
|
Segment(u32) = 0x3F,
|
||||||
|
}
|
||||||
+63
-33
@@ -1,4 +1,5 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::sync::LazyLock;
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
@@ -18,23 +19,32 @@ pub struct CodeGenerator {
|
|||||||
imports: HashMap<String, String>,
|
imports: HashMap<String, String>,
|
||||||
globals: Vec<String>,
|
globals: Vec<String>,
|
||||||
functions: Vec<String>,
|
functions: Vec<String>,
|
||||||
|
symbols: Vec<String>,
|
||||||
allocator: RegisterAllocator,
|
allocator: RegisterAllocator,
|
||||||
call_stack: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GLOBAL_METHODS: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
|
||||||
|
hash_map! {
|
||||||
|
"print" => "print::print",
|
||||||
|
"printnum" => "print::print_num"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
fn import(name: &str, path: &str) -> String {
|
fn import(name: &str, path: &str) -> String {
|
||||||
format!("include {name}: \"{}\"", path)
|
format!("include {name}: \"{}\"", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeGenerator {
|
impl CodeGenerator {
|
||||||
|
const RET: &'static str = "\tjmp _ret";
|
||||||
|
|
||||||
pub fn new(ast: Program) -> Self {
|
pub fn new(ast: Program) -> Self {
|
||||||
CodeGenerator {
|
CodeGenerator {
|
||||||
ast,
|
ast,
|
||||||
imports: HashMap::new(),
|
imports: HashMap::new(),
|
||||||
globals: Vec::new(),
|
globals: Vec::new(),
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
|
symbols: Vec::new(),
|
||||||
allocator: RegisterAllocator::new(),
|
allocator: RegisterAllocator::new(),
|
||||||
call_stack: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,11 +57,15 @@ impl CodeGenerator {
|
|||||||
self.include("print", "./lib/io/print.dsa");
|
self.include("print", "./lib/io/print.dsa");
|
||||||
|
|
||||||
for block in self.ast.clone().declarations {
|
for block in self.ast.clone().declarations {
|
||||||
self.generate_block(block.clone())?;
|
match block {
|
||||||
|
Declaration::Variable { name, .. } => self.symbols.push(name),
|
||||||
|
Declaration::Function { name, .. } => self.symbols.push(name),
|
||||||
|
Declaration::Import { name, .. } => self.symbols.push(name),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for func in &self.functions {
|
for block in self.ast.clone().declarations {
|
||||||
println!("{func}");
|
self.generate_block(block.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.generate_layout()
|
self.generate_layout()
|
||||||
@@ -88,6 +102,7 @@ impl CodeGenerator {
|
|||||||
dsa![mov bpr, spr],
|
dsa![mov bpr, spr],
|
||||||
dsa![push zero],
|
dsa![push zero],
|
||||||
dsa![call main],
|
dsa![call main],
|
||||||
|
dsa![call print::print_newline],
|
||||||
dsa![lwi message, rg0],
|
dsa![lwi message, rg0],
|
||||||
dsa![push rg0],
|
dsa![push rg0],
|
||||||
dsa![call print::print],
|
dsa![call print::print],
|
||||||
@@ -96,6 +111,13 @@ impl CodeGenerator {
|
|||||||
dsa![pop zero],
|
dsa![pop zero],
|
||||||
dsa![hlt]
|
dsa![hlt]
|
||||||
],
|
],
|
||||||
|
"",
|
||||||
|
comment!("Function return boilerplate"),
|
||||||
|
block! [ "_ret"
|
||||||
|
dsa![mov bpr, spr],
|
||||||
|
dsa![pop bpr],
|
||||||
|
dsa![return]
|
||||||
|
],
|
||||||
// block! [ "main"
|
// block! [ "main"
|
||||||
// dsa![push bpr],
|
// dsa![push bpr],
|
||||||
// dsa![mov spr, bpr],
|
// dsa![mov spr, bpr],
|
||||||
@@ -131,6 +153,9 @@ impl CodeGenerator {
|
|||||||
|
|
||||||
self.functions.push(format!("{func}\n"));
|
self.functions.push(format!("{func}\n"));
|
||||||
}
|
}
|
||||||
|
Declaration::Import { name, path } => {
|
||||||
|
self.imports.insert(name, path);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -143,8 +168,6 @@ impl CodeGenerator {
|
|||||||
params: &[Parameter],
|
params: &[Parameter],
|
||||||
body: &[Statement],
|
body: &[Statement],
|
||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
self.call_stack.push(name.to_string());
|
|
||||||
|
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
|
|
||||||
// Reset allocator for new function
|
// Reset allocator for new function
|
||||||
@@ -154,6 +177,7 @@ impl CodeGenerator {
|
|||||||
code.push(format!("{}:", name));
|
code.push(format!("{}:", name));
|
||||||
code.push("\tpush bpr".to_string());
|
code.push("\tpush bpr".to_string());
|
||||||
code.push("\tmov spr, bpr".to_string());
|
code.push("\tmov spr, bpr".to_string());
|
||||||
|
code.push(String::new());
|
||||||
|
|
||||||
// Allocate parameters to registers or stack locations
|
// Allocate parameters to registers or stack locations
|
||||||
for (i, param) in params.iter().enumerate() {
|
for (i, param) in params.iter().enumerate() {
|
||||||
@@ -170,13 +194,14 @@ impl CodeGenerator {
|
|||||||
code.extend(stmt_code);
|
code.extend(stmt_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function epilogue
|
// automatically return at function end
|
||||||
code.push(format!("_ret_{name}:"));
|
if let Some(x) = code.last()
|
||||||
code.push("\tmov bpr, spr".to_string());
|
&& x == Self::RET
|
||||||
code.push("\tpop bpr".to_string());
|
{
|
||||||
code.push("\treturn".to_string());
|
} else {
|
||||||
|
code.push(Self::RET.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
self.call_stack.pop();
|
|
||||||
code
|
code
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +237,7 @@ impl CodeGenerator {
|
|||||||
let (result_reg, expr_code) = self.generate_expression(e)?;
|
let (result_reg, expr_code) = self.generate_expression(e)?;
|
||||||
code.extend(expr_code);
|
code.extend(expr_code);
|
||||||
code.push(format!("\tstw {}, bpr, 8", result_reg));
|
code.push(format!("\tstw {}, bpr, 8", result_reg));
|
||||||
code.push(format!("\tjmp _ret_{}", self.call_stack.last().unwrap()));
|
code.push(format!("\tjmp _ret"));
|
||||||
self.allocator.free_temp(&result_reg);
|
self.allocator.free_temp(&result_reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,7 +444,7 @@ impl CodeGenerator {
|
|||||||
_ => return Err(format!("Unsupported binary operator: {:?}", op)),
|
_ => return Err(format!("Unsupported binary operator: {:?}", op)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free operand registers
|
// Free operand registers (allocator will protect variables)
|
||||||
self.allocator.free_temp(&left_reg);
|
self.allocator.free_temp(&left_reg);
|
||||||
self.allocator.free_temp(&right_reg);
|
self.allocator.free_temp(&right_reg);
|
||||||
|
|
||||||
@@ -427,9 +452,11 @@ impl CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expression::Call { name, args } => {
|
Expression::Call { name, args } => {
|
||||||
// Save caller-saved registers
|
// Save caller-saved registers and track which ones we saved
|
||||||
let save_code = self.allocator.save_caller_saved();
|
let saved_regs = self.allocator.get_caller_saved_registers();
|
||||||
code.extend(save_code);
|
for reg in &saved_regs {
|
||||||
|
code.push(format!("\tpush {}", reg));
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluate and push arguments in reverse order
|
// Evaluate and push arguments in reverse order
|
||||||
let mut arg_regs = Vec::new();
|
let mut arg_regs = Vec::new();
|
||||||
@@ -440,18 +467,31 @@ impl CodeGenerator {
|
|||||||
arg_regs.push(arg_reg);
|
arg_regs.push(arg_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.functions.contains_key(name) {
|
if GLOBAL_METHODS.contains_key(name.as_str()) {
|
||||||
|
code.push(format!("\tcall {}", GLOBAL_METHODS[name.as_str()]));
|
||||||
|
} else if self.symbols.contains(name) {
|
||||||
// Call local function
|
// Call local function
|
||||||
code.push(format!("\tcall {}", name));
|
code.push(format!("\tcall {}", name));
|
||||||
|
} else {
|
||||||
|
return Err(format!("undefined function {name}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.imports
|
// Result is in rg0, allocate a register and move it
|
||||||
|
let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
|
||||||
|
|
||||||
|
code.extend(result_alloc);
|
||||||
|
code.push(format!("\tpop {}", result_reg));
|
||||||
|
|
||||||
// Clean up arguments
|
// Clean up arguments
|
||||||
for _ in 0..args.len() {
|
if args.len() > 1 {
|
||||||
code.push("\tpop zero".to_string());
|
for _ in 0..(args.len() - 1) {
|
||||||
|
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
|
// Free argument registers
|
||||||
@@ -459,16 +499,6 @@ impl CodeGenerator {
|
|||||||
self.allocator.free_temp(®);
|
self.allocator.free_temp(®);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result is in rg0, allocate a register and move it
|
|
||||||
let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
|
|
||||||
code.extend(result_alloc);
|
|
||||||
|
|
||||||
if result_reg != "rg0" {
|
|
||||||
code.push(format!("\tmov rg0, {}", result_reg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore caller-saved registers (simplified - you'd track which ones)
|
|
||||||
|
|
||||||
Ok((result_reg, code))
|
Ok((result_reg, code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ pub enum TokenType {
|
|||||||
Else,
|
Else,
|
||||||
While,
|
While,
|
||||||
Return,
|
Return,
|
||||||
|
Include,
|
||||||
|
|
||||||
// Identifiers and literals
|
// Identifiers and literals
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
Number(i32),
|
Number(i32),
|
||||||
|
String(String),
|
||||||
|
Char(char),
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
Plus,
|
Plus,
|
||||||
@@ -35,10 +38,22 @@ pub enum TokenType {
|
|||||||
RBrace,
|
RBrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Comma,
|
Comma,
|
||||||
|
Colon,
|
||||||
|
Namespace,
|
||||||
|
|
||||||
Eof,
|
Eof,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Type {
|
||||||
|
Int32,
|
||||||
|
Int16,
|
||||||
|
Int8,
|
||||||
|
Uint32,
|
||||||
|
Uint16,
|
||||||
|
Uint8,
|
||||||
|
Char,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
pub token_type: TokenType,
|
pub token_type: TokenType,
|
||||||
@@ -150,6 +165,45 @@ impl Lexer {
|
|||||||
ident
|
ident
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_string(&mut self) -> Result<String, String> {
|
||||||
|
let mut string = String::new();
|
||||||
|
self.advance(); // Consume the opening quote
|
||||||
|
|
||||||
|
while let Some(ch) = self.peek(0) {
|
||||||
|
if ch == '"' {
|
||||||
|
self.advance(); // Consume the closing quote
|
||||||
|
return Ok(string);
|
||||||
|
} else if ch == '\\' {
|
||||||
|
self.advance(); // Consume the backslash
|
||||||
|
if let Some(escaped_char) = self.peek(0) {
|
||||||
|
string.push(escaped_char);
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
string.push(ch);
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(String::from("Unexpected EOF"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_char(&mut self) -> Result<char, String> {
|
||||||
|
self.advance(); // Consume the opening quote
|
||||||
|
|
||||||
|
if let Some(ch) = self.peek(0) {
|
||||||
|
self.advance();
|
||||||
|
if self.peek(0) == Some('\'') {
|
||||||
|
self.advance();
|
||||||
|
return Ok(ch);
|
||||||
|
} else {
|
||||||
|
Err(String::from("expected closing quote"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(String::from("expected character"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tokenize(&mut self) -> Result<Vec<Token>, String> {
|
pub fn tokenize(&mut self) -> Result<Vec<Token>, String> {
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
@@ -168,6 +222,12 @@ impl Lexer {
|
|||||||
let token_type = if ch.is_ascii_digit() {
|
let token_type = if ch.is_ascii_digit() {
|
||||||
let num = self.read_number();
|
let num = self.read_number();
|
||||||
TokenType::Number(num)
|
TokenType::Number(num)
|
||||||
|
} else if ch == '"' {
|
||||||
|
let string = self.read_string()?;
|
||||||
|
TokenType::String(string)
|
||||||
|
} else if ch == '\'' {
|
||||||
|
let char = self.read_char()?;
|
||||||
|
TokenType::Char(char)
|
||||||
} else if ch.is_alphabetic() || ch == '_' {
|
} else if ch.is_alphabetic() || ch == '_' {
|
||||||
let ident = self.read_identifier();
|
let ident = self.read_identifier();
|
||||||
match ident.as_str() {
|
match ident.as_str() {
|
||||||
@@ -176,10 +236,20 @@ impl Lexer {
|
|||||||
"else" => TokenType::Else,
|
"else" => TokenType::Else,
|
||||||
"while" => TokenType::While,
|
"while" => TokenType::While,
|
||||||
"return" => TokenType::Return,
|
"return" => TokenType::Return,
|
||||||
|
"include" => TokenType::Include,
|
||||||
_ => TokenType::Identifier(ident),
|
_ => TokenType::Identifier(ident),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match ch {
|
match ch {
|
||||||
|
':' if self.peek(1) == Some(':') => {
|
||||||
|
self.advance();
|
||||||
|
self.advance();
|
||||||
|
TokenType::Namespace
|
||||||
|
}
|
||||||
|
':' => {
|
||||||
|
self.advance();
|
||||||
|
TokenType::Colon
|
||||||
|
}
|
||||||
'=' if self.peek(1) == Some('=') => {
|
'=' if self.peek(1) == Some('=') => {
|
||||||
self.advance();
|
self.advance();
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
#![feature(hash_map_macro)]
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::{codegen::CodeGenerator, lexer::Lexer, parser::Parser};
|
use crate::{codegen::CodeGenerator, lexer::Lexer, parser::Parser};
|
||||||
|
|
||||||
|
// mod assembly;
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ pub enum Declaration {
|
|||||||
name: String,
|
name: String,
|
||||||
init: Option<ConstExpr>,
|
init: Option<ConstExpr>,
|
||||||
},
|
},
|
||||||
|
Import {
|
||||||
|
name: String,
|
||||||
|
path: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -225,6 +229,32 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_declaration(&mut self) -> Result<Declaration, String> {
|
fn parse_declaration(&mut self) -> Result<Declaration, String> {
|
||||||
|
// check for an import
|
||||||
|
if let TokenType::Include = self.current().token_type {
|
||||||
|
self.advance();
|
||||||
|
|
||||||
|
let name =
|
||||||
|
if let TokenType::Identifier(id) = self.current().clone().token_type {
|
||||||
|
Some(id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.ok_or(String::from("Expected identifier"))?;
|
||||||
|
|
||||||
|
self.advance();
|
||||||
|
self.expect(TokenType::Colon)?;
|
||||||
|
|
||||||
|
let path = if let TokenType::String(id) = self.current().clone().token_type {
|
||||||
|
Some(id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.ok_or(String::from("Expected string literal"))?;
|
||||||
|
|
||||||
|
self.advance();
|
||||||
|
return Ok(Declaration::Import { name, path });
|
||||||
|
}
|
||||||
|
|
||||||
self.expect(TokenType::Int)?;
|
self.expect(TokenType::Int)?;
|
||||||
|
|
||||||
let name = match &self.current().token_type {
|
let name = match &self.current().token_type {
|
||||||
|
|||||||
@@ -81,7 +81,17 @@ impl RegisterAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Free a temporary register after use
|
/// 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) {
|
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);
|
self.in_use.insert(reg.to_string(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +253,16 @@ impl RegisterAllocator {
|
|||||||
self.variable_locations.remove(var_name);
|
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<String> {
|
||||||
|
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
|
/// Save caller-saved registers before a function call
|
||||||
/// Returns assembly code to save them
|
/// Returns assembly code to save them
|
||||||
pub fn save_caller_saved(&mut self) -> Vec<String> {
|
pub fn save_caller_saved(&mut self) -> Vec<String> {
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
+7
-45
@@ -16,55 +16,17 @@ init:
|
|||||||
dw string: "hello world"
|
dw string: "hello world"
|
||||||
start:
|
start:
|
||||||
|
|
||||||
lwi 37, rg0
|
lwi 1, rg0
|
||||||
lwi 12, rg1
|
lwi 2, rg1
|
||||||
push rg0
|
|
||||||
push rg1
|
|
||||||
call maths::divmod
|
|
||||||
pop rg0 // result
|
|
||||||
pop rg1 // remainder
|
|
||||||
|
|
||||||
push rg1
|
|
||||||
push rg0
|
push rg0
|
||||||
call print::print_hex_byte
|
push rg1
|
||||||
call print::print_whitespace
|
call maths::multiply
|
||||||
|
pop rg0
|
||||||
pop zero
|
pop zero
|
||||||
call print::print_hex_byte
|
|
||||||
call print::print_newline
|
|
||||||
|
|
||||||
lwi string, rg0
|
|
||||||
//lwi 10, rg0
|
|
||||||
pusha 4
|
|
||||||
push rg0
|
push rg0
|
||||||
call print::print
|
call print::print_num
|
||||||
//call fib::fib_n
|
pop zero
|
||||||
pop zero
|
|
||||||
call print::print_newline
|
|
||||||
popa 4
|
|
||||||
|
|
||||||
pusha 4
|
|
||||||
push rg0
|
|
||||||
call print::print
|
|
||||||
//call fib::fib_n
|
|
||||||
pop zero
|
|
||||||
call print::print_newline
|
|
||||||
popa 4
|
|
||||||
|
|
||||||
pusha 4
|
|
||||||
push rg0
|
|
||||||
call print::print
|
|
||||||
//call fib::fib_n
|
|
||||||
pop zero
|
|
||||||
call print::print_newline
|
|
||||||
popa 4
|
|
||||||
|
|
||||||
pusha 4
|
|
||||||
push rg0
|
|
||||||
call print::print
|
|
||||||
//call fib::fib_n
|
|
||||||
pop zero
|
|
||||||
call print::print_newline
|
|
||||||
popa 4
|
|
||||||
|
|
||||||
hlt
|
hlt
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user