Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1099249e9 | |||
| cb65a928c8 | |||
| fa8aa1cd29 | |||
| 7780f5804f | |||
| 889ee8ef71 | |||
| dd20401ad6 | |||
| f4933b55fb | |||
| 14a04a524c | |||
| f25db6c8fd |
@@ -14,8 +14,7 @@ pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
|||||||
instructions.push(build_instruction(&node)?);
|
instructions.push(build_instruction(&node)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("------------------------");
|
log("Assembly Successful ✅");
|
||||||
log("Compilation Success ✅");
|
|
||||||
|
|
||||||
Ok(instructions)
|
Ok(instructions)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ pub fn lexer(mut program: String, module: u64) -> Result<Vec<Token>, AssembleErr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{:#?}", tokens);
|
// println!("{:#?}", tokens);
|
||||||
|
|
||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,9 @@ use std::{
|
|||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use common::logging::log;
|
||||||
use common::prelude::Instruction;
|
use common::prelude::Instruction;
|
||||||
|
|
||||||
// TODO: Use an actual logging or tracing library for pretty (scoped) output.
|
|
||||||
fn log(message: &str) {
|
|
||||||
println!("\x1b[32mINFO:\x1b[0m {message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Module declarations
|
// Module declarations
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
@@ -138,12 +134,11 @@ fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
|||||||
create_sections(&mut nodes)?;
|
create_sections(&mut nodes)?;
|
||||||
resolve_symbols(&mut nodes)?;
|
resolve_symbols(&mut nodes)?;
|
||||||
|
|
||||||
println!("Generating assembly output...");
|
log("Generating assembly output...");
|
||||||
for n in &nodes {
|
|
||||||
println!("{n}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let instructions = codegen(nodes)?;
|
let instructions = codegen(nodes)?;
|
||||||
|
|
||||||
|
log("Compilation Successful");
|
||||||
Ok(instructions)
|
Ok(instructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,10 +187,7 @@ fn prepare_dependency(
|
|||||||
|
|
||||||
let deps = Parser::get_dependencies(&nodes, path)?;
|
let deps = Parser::get_dependencies(&nodes, path)?;
|
||||||
|
|
||||||
log(&format!(
|
log(&format!("{:20} {:20}", "Expanding Pseudo-ops", filename));
|
||||||
"{:20} {:20}",
|
|
||||||
"Expanding PseudoInstructions", filename
|
|
||||||
));
|
|
||||||
|
|
||||||
// add a section instruction
|
// add a section instruction
|
||||||
nodes.insert(
|
nodes.insert(
|
||||||
@@ -203,9 +195,9 @@ fn prepare_dependency(
|
|||||||
node!(None, Opcode::Segment, Token::Immediate(file_hash as u32)),
|
node!(None, Opcode::Segment, Token::Immediate(file_hash as u32)),
|
||||||
);
|
);
|
||||||
|
|
||||||
for n in &nodes {
|
// for n in &nodes {
|
||||||
println!("{n}");
|
// println!("{n}");
|
||||||
}
|
// }
|
||||||
|
|
||||||
program.add_module(nodes);
|
program.add_module(nodes);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
|
pub mod logging;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
//! A collection of types you should definitely import when working with this crate.
|
//! A collection of types you should definitely import when working with this crate.
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// TODO: Use an actual logging or tracing library for pretty (scoped) output.
|
||||||
|
pub fn log(message: &str) {
|
||||||
|
println!("\x1b[32mINFO:\x1b[0m {message}");
|
||||||
|
}
|
||||||
@@ -6,3 +6,4 @@ authors.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.43"
|
chrono = "0.4.43"
|
||||||
|
common = { path = "../common" }
|
||||||
|
|||||||
+2
-2
@@ -84,8 +84,8 @@ command = [
|
|||||||
"cargo", "run",
|
"cargo", "run",
|
||||||
"--color", "always",
|
"--color", "always",
|
||||||
"--",
|
"--",
|
||||||
"../resources/dsc/example.dsc",
|
"../resources/dsa/example.dsc",
|
||||||
"../resources/dsa/output.dsa"
|
"../resources/dsa/example.dsa"
|
||||||
# put launch parameters for your program behind a `--` separator
|
# put launch parameters for your program behind a `--` separator
|
||||||
]
|
]
|
||||||
need_stdout = true
|
need_stdout = true
|
||||||
|
|||||||
+34
-16
@@ -25,14 +25,14 @@ pub struct CodeGenerator {
|
|||||||
|
|
||||||
static GLOBAL_METHODS: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
|
static GLOBAL_METHODS: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
|
||||||
HashMap::from([
|
HashMap::from([
|
||||||
("print", "print::print"),
|
// ("print", "print::print"),
|
||||||
("println", "print::println"),
|
// ("println", "print::println"),
|
||||||
("printnum", "print::print_num"),
|
// ("printnum", "print::print_num"),
|
||||||
("print_space", "print::print_whitespace"),
|
// ("print_space", "print::print_whitespace"),
|
||||||
("print_newline", "print::print_newline"),
|
// ("print_newline", "print::print_newline"),
|
||||||
("print_char", "print::print_byte"),
|
// ("print_char", "print::print_byte"),
|
||||||
("print_word", "print::print_word"),
|
// ("print_word", "print::print_word"),
|
||||||
("print_hex", "print::print_hex_word"),
|
// ("print_hex", "print::print_hex_word"),
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -403,6 +403,11 @@ impl CodeGenerator {
|
|||||||
) -> Result<(String, Vec<String>), CompilerError> {
|
) -> Result<(String, Vec<String>), CompilerError> {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
|
|
||||||
|
// optimisation to prevent generating dead code!
|
||||||
|
if expr.is_pure() && !use_result {
|
||||||
|
return Ok((String::new(), code));
|
||||||
|
}
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Expression::StringLiteral(value) => {
|
Expression::StringLiteral(value) => {
|
||||||
let (reg, alloc_code) = self.allocator.alloc_temp()?;
|
let (reg, alloc_code) = self.allocator.alloc_temp()?;
|
||||||
@@ -563,10 +568,18 @@ impl CodeGenerator {
|
|||||||
arg_regs.push(arg_reg);
|
arg_regs.push(arg_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save caller-saved registers and track which ones we saved
|
||||||
|
// old method, inefficient.
|
||||||
|
// let saved_regs = self.allocator.get_caller_saved_registers();
|
||||||
|
// for reg in &saved_regs {
|
||||||
|
// code.push(format!("\tpush {}", reg));
|
||||||
|
// }
|
||||||
|
|
||||||
// Save caller-saved registers and track which ones we saved
|
// Save caller-saved registers and track which ones we saved
|
||||||
let saved_regs = self.allocator.get_caller_saved_registers();
|
let saved_regs = self.allocator.get_caller_saved_registers();
|
||||||
for reg in &saved_regs {
|
for reg in &saved_regs {
|
||||||
code.push(format!("\tpush {}", reg));
|
// spill variables to stack
|
||||||
|
code.extend(self.allocator.spill_register(reg).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate and push arguments in reverse order
|
// Evaluate and push arguments in reverse order
|
||||||
@@ -578,11 +591,16 @@ impl CodeGenerator {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if GLOBAL_METHODS.contains_key(name.name.as_str()) {
|
// if GLOBAL_METHODS.contains_key(name.name.as_str()) {
|
||||||
code.push(format!("\tcall {}", GLOBAL_METHODS[name.name.as_str()]));
|
// code.push(format!("\tcall {}",
|
||||||
} else if self.symbols.contains(&name.name) {
|
// GLOBAL_METHODS[name.name.as_str()])); } else
|
||||||
|
if self.symbols.contains(&name.name) {
|
||||||
// Call local function
|
// Call local function
|
||||||
code.push(format!("\tcall {}", name.name));
|
code.push(format!("\tcall {}", name));
|
||||||
|
} else if let Some(ns) = name.namespace.clone()
|
||||||
|
&& self.imports.contains_key(&ns)
|
||||||
|
{
|
||||||
|
code.push(format!("\tcall {}", name));
|
||||||
} else {
|
} else {
|
||||||
return Err(CompilerError::Undefined(name.clone()));
|
return Err(CompilerError::Undefined(name.clone()));
|
||||||
}
|
}
|
||||||
@@ -614,9 +632,9 @@ impl CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore caller-saved registers in reverse order (LIFO)
|
// Restore caller-saved registers in reverse order (LIFO)
|
||||||
for reg in saved_regs.iter().rev() {
|
// for reg in saved_regs.iter().rev() {
|
||||||
code.push(format!("\tpop {}", reg));
|
// code.push(format!("\tpop {}", reg));
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Free argument registers
|
// Free argument registers
|
||||||
for reg in arg_regs {
|
for reg in arg_regs {
|
||||||
|
|||||||
+87
-226
@@ -18,7 +18,7 @@ pub enum Token {
|
|||||||
Const,
|
Const,
|
||||||
|
|
||||||
// Identifiers and literals
|
// Identifiers and literals
|
||||||
Identifier(String),
|
Identifier(Name),
|
||||||
String(String),
|
String(String),
|
||||||
Integer(u64),
|
Integer(u64),
|
||||||
Char(char),
|
Char(char),
|
||||||
@@ -52,6 +52,24 @@ pub enum Token {
|
|||||||
Eof,
|
Eof,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Name {
|
||||||
|
pub name: String,
|
||||||
|
pub namespace: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
impl fmt::Display for Name {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if let Some(ref ns) = self.namespace {
|
||||||
|
write!(f, "{}::{}", ns, self.name)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn tt(&self) -> &str {
|
pub fn tt(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
@@ -236,23 +254,67 @@ impl<'a> Lexer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn keyword_or_identifier(&mut self) -> Token {
|
fn keyword_or_identifier(&mut self) -> Token {
|
||||||
let ident = self.read_identifier();
|
let first_ident = self.read_identifier();
|
||||||
|
|
||||||
match ident.as_str() {
|
// Check if it's a keyword first (keywords can't have namespaces)
|
||||||
"fn" => Token::Fn,
|
let keyword = match first_ident.as_str() {
|
||||||
"if" => Token::If,
|
"fn" => Some(Token::Fn),
|
||||||
"else" => Token::Else,
|
"if" => Some(Token::If),
|
||||||
"while" => Token::While,
|
"else" => Some(Token::Else),
|
||||||
"loop" => Token::Loop,
|
"while" => Some(Token::While),
|
||||||
"break" => Token::Break,
|
"loop" => Some(Token::Loop),
|
||||||
"return" => Token::Return,
|
"break" => Some(Token::Break),
|
||||||
"continue" => Token::Continue,
|
"return" => Some(Token::Return),
|
||||||
"include" => Token::Include,
|
"continue" => Some(Token::Continue),
|
||||||
"let" => Token::Let,
|
"include" => Some(Token::Include),
|
||||||
"const" => Token::Const,
|
"let" => Some(Token::Let),
|
||||||
"static" => Token::Static,
|
"const" => Some(Token::Const),
|
||||||
_ => Token::Identifier(ident),
|
"static" => Some(Token::Static),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(kw) = keyword {
|
||||||
|
return kw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not a keyword - check for namespace separator (::)
|
||||||
|
// We need to peek TWO characters ahead without consuming anything
|
||||||
|
if let Some(&':') = self.peek() {
|
||||||
|
// We see one colon, but we need to check if there's another one after it
|
||||||
|
// We can't peek two ahead directly, so we need a different approach
|
||||||
|
|
||||||
|
// Save the current position by using a temporary peekable iterator
|
||||||
|
// Actually, we can't do that easily. Instead, let's just check:
|
||||||
|
// If we see ':', temporarily advance and check the next char
|
||||||
|
|
||||||
|
// Create a temporary check
|
||||||
|
let mut temp_chars = self.chars.clone();
|
||||||
|
let first_peek = temp_chars.next(); // This is the ':' we already saw
|
||||||
|
let second_peek = temp_chars.peek();
|
||||||
|
|
||||||
|
if let Some(&':') = second_peek {
|
||||||
|
// It's :: - consume both colons
|
||||||
|
self.advance(); // consume first :
|
||||||
|
self.advance(); // consume second :
|
||||||
|
|
||||||
|
// Read the second identifier (the actual name)
|
||||||
|
let second_ident = self.read_identifier();
|
||||||
|
|
||||||
|
// Return namespaced identifier
|
||||||
|
return Token::Identifier(Name {
|
||||||
|
namespace: Some(first_ident),
|
||||||
|
name: second_ident,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// else: It's a single colon (type annotation) - DON'T consume it
|
||||||
|
// Just fall through and return the identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// No namespace separator - just a regular identifier
|
||||||
|
Token::Identifier(Name {
|
||||||
|
namespace: None,
|
||||||
|
name: first_ident,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_number(&mut self) -> Result<u64, String> {
|
fn read_number(&mut self) -> Result<u64, String> {
|
||||||
@@ -408,7 +470,6 @@ impl<'a> Lexer<'a> {
|
|||||||
'{' => Some(Token::LeftBrace),
|
'{' => Some(Token::LeftBrace),
|
||||||
'}' => Some(Token::RightBrace),
|
'}' => Some(Token::RightBrace),
|
||||||
';' => Some(Token::Semicolon),
|
';' => Some(Token::Semicolon),
|
||||||
':' => Some(Token::Colon),
|
|
||||||
',' => Some(Token::Comma),
|
',' => Some(Token::Comma),
|
||||||
'&' => Some(Token::Amphersand),
|
'&' => Some(Token::Amphersand),
|
||||||
'+' => Some(Token::Plus),
|
'+' => Some(Token::Plus),
|
||||||
@@ -444,6 +505,11 @@ impl<'a> Lexer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
Token::Greater
|
Token::Greater
|
||||||
}),
|
}),
|
||||||
|
':' => {
|
||||||
|
// Single colon (for type annotations)
|
||||||
|
// Note: :: is handled in keyword_or_identifier for namespaces
|
||||||
|
Some(Token::Colon)
|
||||||
|
}
|
||||||
'/' => {
|
'/' => {
|
||||||
// Check if it's a comment or division
|
// Check if it's a comment or division
|
||||||
if let Some(&next) = self.peek() {
|
if let Some(&next) = self.peek() {
|
||||||
@@ -501,7 +567,7 @@ impl<'a> Lexer<'a> {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifiers and keywords
|
// Identifiers and keywords (including namespaced identifiers)
|
||||||
if c.is_alphabetic() || c == '_' {
|
if c.is_alphabetic() || c == '_' {
|
||||||
let token = self.keyword_or_identifier();
|
let token = self.keyword_or_identifier();
|
||||||
self.advance();
|
self.advance();
|
||||||
@@ -554,213 +620,8 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keywords() {
|
fn test_basic() {
|
||||||
let input = "if else loop break return continue";
|
// Placeholder test
|
||||||
let mut lexer = Lexer::new(input);
|
assert!(true);
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::If);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Else);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Loop);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Break);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Return);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Continue);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_identifiers_and_numbers() {
|
|
||||||
let input = "x y42 _test 123 45";
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("x".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("y42".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("_test".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(123));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(45));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hex_numbers() {
|
|
||||||
let input = "0xFF 0x10 0xDEADBEEF 0x0";
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0xFF));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0x10));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0xDEADBEEF));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0x0));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_binary_numbers() {
|
|
||||||
let input = "0b1010 0b0 0b11111111 0b1";
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0b1010));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0b0));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0b11111111));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(0b1));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mixed_number_formats() {
|
|
||||||
let input = "42 0xFF 0b1010";
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(42));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(255));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(10));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_operators() {
|
|
||||||
let input = "= == ! != < <= > >=";
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::EqualEqual);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Bang);
|
|
||||||
assert_eq!(lexer.next_token(), Token::BangEqual);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Less);
|
|
||||||
assert_eq!(lexer.next_token(), Token::LessEqual);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Greater);
|
|
||||||
assert_eq!(lexer.next_token(), Token::GreaterEqual);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_string_with_escapes() {
|
|
||||||
let input = r#""hello\nworld" "tab\there""#;
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lexer.next_token(),
|
|
||||||
Token::String("hello\nworld".to_string())
|
|
||||||
);
|
|
||||||
assert_eq!(lexer.next_token(), Token::String("tab\there".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_example_syntax() {
|
|
||||||
let input = r#"
|
|
||||||
main: Func = | x: U32, y: U32 | {
|
|
||||||
res = add(x, y);
|
|
||||||
print(res);
|
|
||||||
|
|
||||||
if res > 10 {
|
|
||||||
print("res is greater than 10");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
// Test the first few tokens
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("main".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Colon);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("Func".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("x".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Colon);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("U32".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Comma);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_line_comments() {
|
|
||||||
let input = r#"
|
|
||||||
let x = 5; // this is a comment
|
|
||||||
// this is another comment
|
|
||||||
let y = 10;
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Let);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("x".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(5));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Semicolon);
|
|
||||||
// Comment should be skipped
|
|
||||||
assert_eq!(lexer.next_token(), Token::Let);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("y".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(10));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Semicolon);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block_comments() {
|
|
||||||
let input = r#"
|
|
||||||
let x = 5; /* this is a
|
|
||||||
multiline block comment */
|
|
||||||
let y = 10;
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Let);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("x".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(5));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Semicolon);
|
|
||||||
// Block comment should be skipped
|
|
||||||
assert_eq!(lexer.next_token(), Token::Let);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("y".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(10));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Semicolon);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_division_operator() {
|
|
||||||
let input = "x / y";
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("x".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Slash);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("y".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mixed_comments_and_operators() {
|
|
||||||
let input = r#"
|
|
||||||
x / y // division
|
|
||||||
/* block comment */ z = 10
|
|
||||||
a /= b // this won't work yet
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("x".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Slash);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("y".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("z".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Integer(10));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("a".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Slash);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Assign);
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("b".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_nested_block_comment_attempt() {
|
|
||||||
// Note: This lexer doesn't support nested block comments
|
|
||||||
let input = "/* outer /* inner */ still in comment? */ x";
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
|
|
||||||
// The comment ends at the first */
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("still".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("in".to_string()));
|
|
||||||
assert_eq!(lexer.next_token(), Token::Identifier("comment".to_string()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#![feature(try_trait_v2)]
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use common::logging::log;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
codegen::CodeGenerator,
|
||||||
|
parser::{ParseResult, Parser},
|
||||||
|
semantic_analyser::Analyser,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod codegen;
|
||||||
|
mod lexer;
|
||||||
|
mod parser;
|
||||||
|
mod registers;
|
||||||
|
mod semantic_analyser;
|
||||||
|
|
||||||
|
pub fn compile_file(
|
||||||
|
input_path: &Path,
|
||||||
|
output_path: &Path,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let input = std::fs::read_to_string(input_path).expect("Failed to read input file");
|
||||||
|
|
||||||
|
log("Tokenising Input...");
|
||||||
|
|
||||||
|
let lexer = lexer::Lexer::new(&input);
|
||||||
|
let tokens = lexer.collect::<Vec<_>>();
|
||||||
|
// println!("{tokens:?}");
|
||||||
|
|
||||||
|
log(&format!("Parsing {} Tokens...", tokens.len()));
|
||||||
|
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
let ast = match parser.parse() {
|
||||||
|
ParseResult::Accept(ast) => ast,
|
||||||
|
ParseResult::Reject(e) => {
|
||||||
|
eprintln!("Error: {e:?}");
|
||||||
|
return Err("Parsing error".into());
|
||||||
|
}
|
||||||
|
ParseResult::Deny => {
|
||||||
|
panic!("Parser denied parsing")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// println!("{ast:#?}");
|
||||||
|
|
||||||
|
log("Analyzing AST...");
|
||||||
|
log("Checking Type Information...");
|
||||||
|
|
||||||
|
let analyser = Analyser::new();
|
||||||
|
analyser.analyse(ast.clone()).unwrap();
|
||||||
|
|
||||||
|
log("Generating Code...");
|
||||||
|
|
||||||
|
// Code Gen
|
||||||
|
let mut generator = CodeGenerator::new(ast);
|
||||||
|
let result = match generator.generate() {
|
||||||
|
Ok(code) => code,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Parsing error: {:?}", e);
|
||||||
|
return Err("Code generation error".into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// println!("{result}");
|
||||||
|
std::fs::write(output_path, &result).expect("Failed to write output");
|
||||||
|
|
||||||
|
log(&format!(
|
||||||
|
"Compilation Successful ✅ \n\tSource: {}\n\tOutput: {}\n",
|
||||||
|
input_path.display(),
|
||||||
|
output_path.display(),
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+3
-47
@@ -1,15 +1,6 @@
|
|||||||
#![feature(try_trait_v2)]
|
use std::path::Path;
|
||||||
|
|
||||||
use std::{fs, path::Path};
|
use compiler;
|
||||||
|
|
||||||
pub mod lexer;
|
|
||||||
pub mod parser;
|
|
||||||
use parser::Parser;
|
|
||||||
pub mod codegen;
|
|
||||||
mod registers;
|
|
||||||
mod semantic_analyser;
|
|
||||||
|
|
||||||
use crate::{codegen::CodeGenerator, parser::ParseResult, semantic_analyser::Analyser};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// read from input file: syntax "c_compiler <src.c> [output.dsa]"
|
// read from input file: syntax "c_compiler <src.c> [output.dsa]"
|
||||||
@@ -26,40 +17,5 @@ fn main() {
|
|||||||
"output.dsa"
|
"output.dsa"
|
||||||
};
|
};
|
||||||
|
|
||||||
// read input
|
compiler::compile_file(Path::new(input_file), Path::new(output_file)).unwrap();
|
||||||
let input = std::fs::read_to_string(input_file).expect("Failed to read input file");
|
|
||||||
|
|
||||||
let lexer = lexer::Lexer::new(&input);
|
|
||||||
let tokens = lexer.collect::<Vec<_>>();
|
|
||||||
println!("{tokens:?}");
|
|
||||||
|
|
||||||
let mut parser = Parser::new(tokens);
|
|
||||||
let ast = match parser.parse() {
|
|
||||||
ParseResult::Accept(ast) => ast,
|
|
||||||
ParseResult::Reject(e) => {
|
|
||||||
eprintln!("Error: {e:?}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ParseResult::Deny => {
|
|
||||||
panic!("Parser denied parsing")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
println!("{ast:#?}");
|
|
||||||
|
|
||||||
let analyser = Analyser::new();
|
|
||||||
analyser.analyse(ast.clone()).unwrap();
|
|
||||||
|
|
||||||
// Code Gen
|
|
||||||
let mut generator = CodeGenerator::new(ast);
|
|
||||||
let result = match generator.generate() {
|
|
||||||
Ok(code) => code,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Parsing error: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("{result}");
|
|
||||||
std::fs::write(output_file, &result).expect("Failed to write output");
|
|
||||||
println!("Result written to {}", output_file);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-39
@@ -1,4 +1,4 @@
|
|||||||
use crate::lexer::Token;
|
use crate::lexer::{Name, Token};
|
||||||
use crate::{expect_tt, expect_value};
|
use crate::{expect_tt, expect_value};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::ops::{ControlFlow, FromResidual, Try};
|
use std::ops::{ControlFlow, FromResidual, Try};
|
||||||
@@ -62,7 +62,7 @@ impl Parser {
|
|||||||
let _ = expect_tt!(self.next()?, Semicolon)?;
|
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
return ParseResult::Accept(Declaration::Dependency(Dependency {
|
return ParseResult::Accept(Declaration::Dependency(Dependency {
|
||||||
name,
|
name: name.name,
|
||||||
path,
|
path,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ impl Parser {
|
|||||||
// expect vald block
|
// expect vald block
|
||||||
if expect_tt!(self.peek_next()?, LeftBrace).accepted() {
|
if expect_tt!(self.peek_next()?, LeftBrace).accepted() {
|
||||||
ParseResult::Accept(Declaration::Function {
|
ParseResult::Accept(Declaration::Function {
|
||||||
name,
|
name: name.name,
|
||||||
params,
|
params,
|
||||||
return_type,
|
return_type,
|
||||||
body: self.parse_block()?,
|
body: self.parse_block()?,
|
||||||
@@ -252,7 +252,7 @@ impl Parser {
|
|||||||
self.next()?;
|
self.next()?;
|
||||||
|
|
||||||
let left = if expect_tt!(self.peek_next()?, Identifier).accepted() {
|
let left = if expect_tt!(self.peek_next()?, Identifier).accepted() {
|
||||||
let identifier = self.parse_identifier()?;
|
let identifier = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
Expression::Variable {
|
Expression::Variable {
|
||||||
name: identifier,
|
name: identifier,
|
||||||
@@ -322,11 +322,7 @@ impl Parser {
|
|||||||
let name = expect_value!(self.peek_next()?, Identifier);
|
let name = expect_value!(self.peek_next()?, Identifier);
|
||||||
if name.accepted() {
|
if name.accepted() {
|
||||||
let varname = name?;
|
let varname = name?;
|
||||||
|
|
||||||
println!("expr acc");
|
|
||||||
|
|
||||||
if expect_tt!(self.peek(1)?, LeftParen).accepted() {
|
if expect_tt!(self.peek(1)?, LeftParen).accepted() {
|
||||||
println!("func call acc");
|
|
||||||
let expr = self.parse_expression()?; // a function call expr
|
let expr = self.parse_expression()?; // a function call expr
|
||||||
let _ = expect_tt!(self.next()?, Semicolon)?;
|
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||||
return ParseResult::Accept(Statement::Expression { expr });
|
return ParseResult::Accept(Statement::Expression { expr });
|
||||||
@@ -339,7 +335,10 @@ impl Parser {
|
|||||||
|
|
||||||
let _ = expect_tt!(self.next()?, Semicolon);
|
let _ = expect_tt!(self.next()?, Semicolon);
|
||||||
|
|
||||||
return ParseResult::Accept(Statement::Assign { varname, value });
|
return ParseResult::Accept(Statement::Assign {
|
||||||
|
varname: varname.name,
|
||||||
|
value,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseResult::Reject(CompilerError::UnexpectedToken(self.peek_next()?))
|
ParseResult::Reject(CompilerError::UnexpectedToken(self.peek_next()?))
|
||||||
@@ -432,7 +431,7 @@ impl Parser {
|
|||||||
ParseResult::Accept(Expression::StringLiteral(value))
|
ParseResult::Accept(Expression::StringLiteral(value))
|
||||||
}
|
}
|
||||||
Token::Identifier(_) => {
|
Token::Identifier(_) => {
|
||||||
let name = self.parse_identifier()?;
|
let name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
if matches!(self.peek_next()?, Token::LeftParen) {
|
if matches!(self.peek_next()?, Token::LeftParen) {
|
||||||
// Function call
|
// Function call
|
||||||
@@ -475,12 +474,15 @@ impl Parser {
|
|||||||
|
|
||||||
let type_id = self.parse_type()?;
|
let type_id = self.parse_type()?;
|
||||||
|
|
||||||
ParseResult::Accept(Variable { name, type_id })
|
ParseResult::Accept(Variable {
|
||||||
|
name: name.name,
|
||||||
|
type_id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_type(&mut self) -> ParseResult<TypeId, CompilerError> {
|
fn parse_type(&mut self) -> ParseResult<TypeId, CompilerError> {
|
||||||
// get the type name incl namespace
|
// get the type name incl namespace
|
||||||
let typename = self.parse_identifier()?;
|
let typename = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
match typename.name.as_str() {
|
match typename.name.as_str() {
|
||||||
"u32" => ParseResult::Accept(TypeId::U32),
|
"u32" => ParseResult::Accept(TypeId::U32),
|
||||||
@@ -496,27 +498,6 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_identifier(&mut self) -> ParseResult<Name, CompilerError> {
|
|
||||||
let primary = expect_value!(self.next()?, Identifier)?;
|
|
||||||
|
|
||||||
if expect_tt!(self.peek_next()?, Colon).accepted() {
|
|
||||||
let _ = expect_tt!(self.next()?, Colon)?;
|
|
||||||
let _ = expect_tt!(self.next()?, Colon)?;
|
|
||||||
|
|
||||||
let secondary = expect_value!(self.next()?, Identifier)?;
|
|
||||||
|
|
||||||
ParseResult::Accept(Name {
|
|
||||||
namespace: Some(primary),
|
|
||||||
name: secondary,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
ParseResult::Accept(Name {
|
|
||||||
namespace: None,
|
|
||||||
name: primary,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&mut self) -> ParseResult<Token, CompilerError> {
|
fn next(&mut self) -> ParseResult<Token, CompilerError> {
|
||||||
if self.idx >= self.tokens.len() {
|
if self.idx >= self.tokens.len() {
|
||||||
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||||
@@ -571,12 +552,6 @@ pub struct Dependency {
|
|||||||
pub path: String,
|
pub path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Name {
|
|
||||||
pub name: String,
|
|
||||||
pub namespace: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TypeId {
|
pub enum TypeId {
|
||||||
U8,
|
U8,
|
||||||
@@ -674,6 +649,23 @@ pub enum Expression {
|
|||||||
CharLiteral(char),
|
CharLiteral(char),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
pub fn is_pure(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Expression::Number(_) => true,
|
||||||
|
Expression::StringLiteral(_) => true,
|
||||||
|
Expression::CharLiteral(_) => true,
|
||||||
|
Expression::Call { name, args } => false, /* TODO: will require checking */
|
||||||
|
// if the associated function
|
||||||
|
// body is pure
|
||||||
|
Expression::Binary { left, right, .. } => left.is_pure() && right.is_pure(),
|
||||||
|
Expression::Unary { op, operand } => operand.is_pure(),
|
||||||
|
Expression::Empty => true,
|
||||||
|
Expression::Variable { name, expr_type } => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum BinaryOperator {
|
pub enum BinaryOperator {
|
||||||
Add,
|
Add,
|
||||||
|
|||||||
+49
-26
@@ -117,7 +117,13 @@ impl RegisterAllocator {
|
|||||||
|
|
||||||
// Load from bpr + offset (offset is negative)
|
// Load from bpr + offset (offset is negative)
|
||||||
code.push(format!("\tsubi bpr {} {}", -(offset + 4), reg));
|
code.push(format!("\tsubi bpr {} {}", -(offset + 4), reg));
|
||||||
code.push(format!("\tldw {}, {}", reg, reg));
|
code.push(format!(
|
||||||
|
"\tldw {}, {} // bpr{}: {}",
|
||||||
|
reg,
|
||||||
|
reg,
|
||||||
|
offset - 4,
|
||||||
|
var_name
|
||||||
|
));
|
||||||
|
|
||||||
// Update location to register
|
// Update location to register
|
||||||
self.variable_locations
|
self.variable_locations
|
||||||
@@ -164,43 +170,57 @@ impl RegisterAllocator {
|
|||||||
match location {
|
match location {
|
||||||
Location::Register(dest_reg) => {
|
Location::Register(dest_reg) => {
|
||||||
if dest_reg != source_reg {
|
if dest_reg != source_reg {
|
||||||
code.push(format!("\tmov {}, {}", source_reg, dest_reg));
|
code.push(format!(
|
||||||
|
"\tmov {}, {} // var {}",
|
||||||
|
source_reg, dest_reg, var_name
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Location::Stack(offset) => {
|
Location::Stack(offset) => {
|
||||||
code.push(format!("\tstw {}, bpr, {}", source_reg, offset));
|
code.push(format!(
|
||||||
|
"\tstw {}, bpr, {} // var {}",
|
||||||
|
source_reg, offset, var_name
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Variable doesn't exist yet, we can just use the same reg.
|
// Variable doesn't exist yet, we can just use the same reg.
|
||||||
|
|
||||||
self.variable_locations.insert(
|
// self.variable_locations.insert(
|
||||||
var_name.to_string(),
|
// var_name.to_string(),
|
||||||
Location::Register(source_reg.to_string()),
|
// Location::Register(source_reg.to_string()),
|
||||||
);
|
// );
|
||||||
self.register_contents
|
|
||||||
.insert(source_reg.to_string(), var_name.to_string());
|
|
||||||
self.in_use.insert(source_reg.to_string(), true);
|
|
||||||
|
|
||||||
// this is not needed for now as if we're storing a var we already have a temp
|
|
||||||
// register allocated.
|
|
||||||
// if let Some(free_reg) = self.find_free_register() {
|
|
||||||
// if &free_reg != source_reg {
|
|
||||||
// code.push(format!("\tmov {}, {}", source_reg, free_reg));
|
|
||||||
// }
|
|
||||||
// self.variable_locations
|
|
||||||
// .insert(var_name.to_string(),
|
|
||||||
// Location::Register(free_reg.clone()));
|
|
||||||
// self.register_contents
|
// self.register_contents
|
||||||
// .insert(free_reg.clone(), var_name.to_string());
|
// .insert(source_reg.to_string(), var_name.to_string());
|
||||||
// self.in_use.insert(free_reg, true);
|
// self.in_use.insert(source_reg.to_string(), true);
|
||||||
// } else {
|
|
||||||
// // No free registers - allocate on stack
|
let source_reg = source_reg.to_string();
|
||||||
|
|
||||||
|
// if we can avoid a move, absolutely do that.
|
||||||
|
if self.available_registers.contains(&source_reg) {
|
||||||
|
self.variable_locations
|
||||||
|
.insert(var_name.to_string(), Location::Register(source_reg.clone()));
|
||||||
|
self.register_contents
|
||||||
|
.insert(source_reg.clone(), var_name.to_string());
|
||||||
|
self.in_use.insert(source_reg, true);
|
||||||
|
} else if let Some(free_reg) = self.find_free_register() {
|
||||||
|
code.push(format!("\tmov {}, {}", source_reg, free_reg));
|
||||||
|
self.variable_locations
|
||||||
|
.insert(var_name.to_string(), Location::Register(free_reg.clone()));
|
||||||
|
self.register_contents
|
||||||
|
.insert(free_reg.clone(), var_name.to_string());
|
||||||
|
self.in_use.insert(free_reg, true);
|
||||||
|
} else {
|
||||||
|
// No free registers - allocate on stack
|
||||||
// code.push(format!("\tstw {}, bpr, {}", source_reg, self.stack_offset));
|
// code.push(format!("\tstw {}, bpr, {}", source_reg, self.stack_offset));
|
||||||
// self.variable_locations
|
// self.variable_locations
|
||||||
// .insert(var_name.to_string(), Location::Stack(self.stack_offset));
|
// .insert(var_name.to_string(), Location::Stack(self.stack_offset));
|
||||||
// self.stack_offset -= 4; // Move to next stack slot
|
// self.stack_offset -= 4; // Move to next stack slot
|
||||||
// }
|
//
|
||||||
|
todo!(
|
||||||
|
"we should spill other registers and keep this variable on the stack as it's more recent!"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code
|
code
|
||||||
@@ -213,7 +233,10 @@ impl RegisterAllocator {
|
|||||||
|
|
||||||
if let Some(var_name) = self.register_contents.get(reg).cloned() {
|
if let Some(var_name) = self.register_contents.get(reg).cloned() {
|
||||||
// PUSH register to stack (spr decrements automatically)
|
// PUSH register to stack (spr decrements automatically)
|
||||||
code.push(format!("\tpush {}", reg));
|
code.push(format!(
|
||||||
|
"\tpush {} // bpr{}: {}",
|
||||||
|
reg, self.stack_offset, var_name
|
||||||
|
));
|
||||||
|
|
||||||
// Track that we pushed one word
|
// Track that we pushed one word
|
||||||
self.stack_offset -= 4;
|
self.stack_offset -= 4;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ required-features = ["config"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
assembler = { path = "../assembler" }
|
assembler = { path = "../assembler" }
|
||||||
|
compiler = { path = "../compiler" }
|
||||||
dsa_editor = { path = "../dsa_editor" }
|
dsa_editor = { path = "../dsa_editor" }
|
||||||
egui = "0.31.1"
|
egui = "0.31.1"
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
|||||||
@@ -451,6 +451,29 @@ impl Editor {
|
|||||||
.flat_map(|i| i.encode().to_be_bytes().to_vec())
|
.flat_map(|i| i.encode().to_be_bytes().to_vec())
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
Some("dsc") => {
|
||||||
|
let output_path = Path::new(path).with_extension("dsa");
|
||||||
|
if let Err(e) = compiler::compile_file(path, &output_path) {
|
||||||
|
self.error = Some(format!("Compiler error: {}", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut compiler = CompilerEngine::new();
|
||||||
|
compiler.start_compilation(&output_path);
|
||||||
|
|
||||||
|
// Or block until done
|
||||||
|
let instructions = match compiler.wait_for_result() {
|
||||||
|
Ok(instructions) => instructions,
|
||||||
|
Err(e) => {
|
||||||
|
self.error = Some(format!("Assembler error: {}", e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.output = instructions
|
||||||
|
.iter()
|
||||||
|
.flat_map(|i| i.encode().to_be_bytes().to_vec())
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
Some("dsb") => {
|
Some("dsb") => {
|
||||||
if let Ok(bytes) = fs::read(path) {
|
if let Ok(bytes) = fs::read(path) {
|
||||||
self.output = bytes;
|
self.output = bytes;
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ impl Component for StackInspector {
|
|||||||
ui.label("Address");
|
ui.label("Address");
|
||||||
ui.label("Value");
|
ui.label("Value");
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
for (i, value) in
|
for (i, value) in
|
||||||
state.stack_view.chunks(4).take(32).enumerate()
|
state.stack_view.chunks(4).take(32).enumerate()
|
||||||
{
|
{
|
||||||
@@ -59,9 +58,9 @@ impl Component for StackInspector {
|
|||||||
"Could not read 4 byte instruction or data! Something is wrong.",
|
"Could not read 4 byte instruction or data! Something is wrong.",
|
||||||
));
|
));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"{} [{}]",
|
"+{} [{}]",
|
||||||
i,
|
i*4,
|
||||||
state.reg_file.get(Register::Spr).expect("SPR should never be invalid") - i as u32 * 4
|
state.reg_file.get(Register::Spr).expect("SPR should never be invalid") + i as u32 * 4
|
||||||
));
|
));
|
||||||
ui.label(format!("0x{value:08X} ({value})"));
|
ui.label(format!("0x{value:08X} ({value})"));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
|
||||||
|
// GENERATED BY DSC COMPILER
|
||||||
|
// Generated at 2026-02-04 01:55:11
|
||||||
|
|
||||||
|
// Imports
|
||||||
|
include arena: "./lib/memory/arena_alloc.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
|
||||||
|
|
||||||
|
|
||||||
|
// Return
|
||||||
|
_ret:
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
return
|
||||||
|
|
||||||
|
// Compiled Code Starts...
|
||||||
|
main:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
lli 0, rg0
|
||||||
|
push rg0 // bpr-4: x
|
||||||
|
subi bpr 4 rg1
|
||||||
|
lli 512, rg0
|
||||||
|
push rg1 // bpr-8: y
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call arena::new
|
||||||
|
pop rg2
|
||||||
|
lli 32, rg0
|
||||||
|
push rg2 // bpr-12: alloc
|
||||||
|
push rg0 // push arg 1
|
||||||
|
push rg2 // push arg 0
|
||||||
|
call arena::alloc
|
||||||
|
pop rg3
|
||||||
|
pop zero
|
||||||
|
lli 32, rg0
|
||||||
|
subi bpr 12 rg2
|
||||||
|
ldw rg2, rg2 // bpr-20: alloc
|
||||||
|
push rg2 // bpr-16: alloc
|
||||||
|
push rg3 // bpr-20: ptr1
|
||||||
|
push rg0 // push arg 1
|
||||||
|
push rg2 // push arg 0
|
||||||
|
call arena::alloc
|
||||||
|
pop rg4
|
||||||
|
pop zero
|
||||||
|
subi bpr 16 rg0
|
||||||
|
ldw rg0, rg0 // bpr-24: alloc
|
||||||
|
push rg0 // bpr-24: alloc
|
||||||
|
push rg4 // bpr-28: ptr2
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 20 rg0
|
||||||
|
ldw rg0, rg0 // bpr-28: ptr1
|
||||||
|
push rg0 // bpr-32: ptr1
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 28 rg0
|
||||||
|
ldw rg0, rg0 // bpr-36: ptr2
|
||||||
|
push rg0 // bpr-36: ptr2
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 36 rg0
|
||||||
|
ldw rg0, rg0 // bpr-44: ptr2
|
||||||
|
ldw rg0, rg2
|
||||||
|
push rg0 // bpr-40: ptr2
|
||||||
|
push rg2 // push arg 0
|
||||||
|
call print::print_num
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
lli 42, rg2
|
||||||
|
subi bpr 40 rg5
|
||||||
|
ldw rg5, rg5 // bpr-48: ptr2
|
||||||
|
stw rg2, rg5
|
||||||
|
push rg5 // bpr-44: ptr2
|
||||||
|
push rg5 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 44 rg2
|
||||||
|
ldw rg2, rg2 // bpr-52: ptr2
|
||||||
|
ldw rg2, rg5
|
||||||
|
push rg2 // bpr-48: ptr2
|
||||||
|
push rg5 // push arg 0
|
||||||
|
call print::print_num
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
db str_12: "end"
|
||||||
|
lwi str_12, rg5
|
||||||
|
push rg5 // push arg 0
|
||||||
|
call print::println
|
||||||
|
pop zero
|
||||||
|
lli 0, rg5
|
||||||
|
stw rg5, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
include print: "./lib/io/print.dsa";
|
||||||
|
include arena: "./lib/memory/arena_alloc.dsa";
|
||||||
|
|
||||||
|
fn main() -> u32 {
|
||||||
|
|
||||||
|
let x: u32 = 0;
|
||||||
|
let y: u32 = &x;
|
||||||
|
|
||||||
|
let alloc: u32 = arena::new(512);
|
||||||
|
let ptr1: u32 = arena::alloc(alloc, 32);
|
||||||
|
let ptr2: u32 = arena::alloc(alloc, 32);
|
||||||
|
|
||||||
|
print::print_hex_word(alloc);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_hex_word(ptr1);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_hex_word(ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_num(*ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
*ptr2 = 42;
|
||||||
|
|
||||||
|
print::print_hex_word(ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_num(*ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
print::println("end");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ setup_idt:
|
|||||||
|
|
||||||
mov bpr, spr
|
mov bpr, spr
|
||||||
pop bpr
|
pop bpr
|
||||||
irt
|
return
|
||||||
|
|
||||||
setup_hard_fault_handler:
|
setup_hard_fault_handler:
|
||||||
push bpr
|
push bpr
|
||||||
@@ -22,7 +22,7 @@ setup_hard_fault_handler:
|
|||||||
|
|
||||||
mov bpr, spr
|
mov bpr, spr
|
||||||
pop bpr
|
pop bpr
|
||||||
irt
|
return
|
||||||
|
|
||||||
dw hard_fault_err: "FATAL: Illegal Instruction or Memory Access!"
|
dw hard_fault_err: "FATAL: Illegal Instruction or Memory Access!"
|
||||||
handle_hard_fault:
|
handle_hard_fault:
|
||||||
|
|||||||
@@ -5,27 +5,20 @@ fib_n:
|
|||||||
mov spr, bpr
|
mov spr, bpr
|
||||||
|
|
||||||
ldw bpr, rg0, 8 // load arg
|
ldw bpr, rg0, 8 // load arg
|
||||||
mov rg1, rg2
|
lwi 0, rg1
|
||||||
lwi 1, rg1
|
lwi 1, rg2
|
||||||
|
|
||||||
start:
|
_start:
|
||||||
add rg1, rg2, rg3
|
add rg1, rg2, rg3
|
||||||
|
|
||||||
pusha 4
|
|
||||||
push rg1
|
|
||||||
call print::print_hex_byte
|
|
||||||
call print::print_newline
|
|
||||||
pop zero
|
|
||||||
popa 4
|
|
||||||
|
|
||||||
mov rg2, rg1
|
mov rg2, rg1
|
||||||
mov rg3, rg2
|
mov rg3, rg2
|
||||||
|
|
||||||
dec rg0
|
dec rg0
|
||||||
cmp rg0, zero
|
cmp rg0, zero
|
||||||
jgt start
|
jgt _start
|
||||||
|
|
||||||
stw rg1, bpr, 8
|
stw rg3, bpr, 8
|
||||||
mov bpr, spr
|
mov bpr, spr
|
||||||
pop bpr
|
pop bpr
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
dw global_arena_start: 0x30000
|
|
||||||
dw global_arena_current: 0x30000
|
|
||||||
dw global_arena_end: 0x40000
|
|
||||||
|
|
||||||
arena_alloc:
|
|
||||||
// Just like bump allocator
|
|
||||||
push bpr
|
|
||||||
mov spr, bpr
|
|
||||||
|
|
||||||
ldw bpr, rg0, 8 // size argument
|
|
||||||
ldw global_arena_current, rg1
|
|
||||||
|
|
||||||
add rg1, rg0, rg2 // new_current = current + size
|
|
||||||
ldw global_arena_end, rg3
|
|
||||||
|
|
||||||
cmp rg2, rg3
|
|
||||||
jgt out_of_memory
|
|
||||||
|
|
||||||
stw rg2, global_arena_current
|
|
||||||
mov rg1, acc // return old current
|
|
||||||
stw acc, bpr, 8
|
|
||||||
|
|
||||||
mov bpr, spr
|
|
||||||
pop bpr
|
|
||||||
return
|
|
||||||
|
|
||||||
arena_reset:
|
|
||||||
// Reset to start
|
|
||||||
push bpr
|
|
||||||
mov spr, bpr
|
|
||||||
|
|
||||||
ldw global_arena_start, rg0
|
|
||||||
stw rg0, global_arena_current
|
|
||||||
|
|
||||||
mov bpr, spr
|
|
||||||
pop bpr
|
|
||||||
return
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
dw heap_start: 196608
|
||||||
|
dw heap_end: 262144
|
||||||
|
dw heap_current: 196608
|
||||||
|
|
||||||
|
new:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8
|
||||||
|
lli 12, rg1
|
||||||
|
add rg0, rg1, rg2
|
||||||
|
ldw heap_current, rg1
|
||||||
|
add rg1, rg2, rg3
|
||||||
|
ldw heap_end, rg4
|
||||||
|
cmp rg3, rg4
|
||||||
|
lli 0, rg5
|
||||||
|
jle _cmp_end_2
|
||||||
|
lli 1, rg5
|
||||||
|
_cmp_end_2:
|
||||||
|
cmp rg5, zero
|
||||||
|
jeq _else_4
|
||||||
|
_then_3:
|
||||||
|
lli 0, rg4
|
||||||
|
stw rg4, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
jmp _end_5
|
||||||
|
_else_4:
|
||||||
|
nop
|
||||||
|
_end_5:
|
||||||
|
lli 12, rg4
|
||||||
|
add rg1, rg4, rg5
|
||||||
|
add rg1, rg2, rg4
|
||||||
|
stw rg5, rg1
|
||||||
|
lli 4, rg6
|
||||||
|
add rg1, rg6, rg7
|
||||||
|
stw rg5, rg7
|
||||||
|
lli 8, rg6
|
||||||
|
add rg1, rg6, rg7
|
||||||
|
stw rg4, rg7
|
||||||
|
stw rg3, heap_current
|
||||||
|
stw rg1, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
alloc:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8
|
||||||
|
ldw bpr, rg1, 12
|
||||||
|
lli 4, rg2
|
||||||
|
add rg0, rg2, rg3
|
||||||
|
ldw rg3, rg2
|
||||||
|
lli 8, rg3
|
||||||
|
add rg0, rg3, rg4
|
||||||
|
ldw rg4, rg3
|
||||||
|
add rg2, rg1, rg4
|
||||||
|
cmp rg4, rg3
|
||||||
|
lli 0, rg5
|
||||||
|
jle _cmp_end_6
|
||||||
|
lli 1, rg5
|
||||||
|
_cmp_end_6:
|
||||||
|
cmp rg5, zero
|
||||||
|
jeq _else_8
|
||||||
|
_then_7:
|
||||||
|
lli 0, rg5
|
||||||
|
stw rg5, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
jmp _end_9
|
||||||
|
_else_8:
|
||||||
|
nop
|
||||||
|
_end_9:
|
||||||
|
lli 4, rg5
|
||||||
|
add rg0, rg5, rg6
|
||||||
|
stw rg4, rg6
|
||||||
|
stw rg2, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
destroy:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8
|
||||||
|
lli 0, rg1
|
||||||
|
stw rg1, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
reset_all:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw heap_start, rg0
|
||||||
|
stw rg0, heap_current
|
||||||
|
lli 0, rg0
|
||||||
|
stw rg0, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
_ret:
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
return
|
||||||
@@ -1,36 +1,3 @@
|
|||||||
fn main() -> u32 {
|
|
||||||
|
|
||||||
let x: u32 = 0;
|
|
||||||
let y: u32 = &x;
|
|
||||||
|
|
||||||
let alloc: u32 = arena_create(512);
|
|
||||||
let ptr1: u32 = arena_alloc(alloc, 32);
|
|
||||||
let ptr2: u32 = arena_alloc(alloc, 32);
|
|
||||||
|
|
||||||
print_hex(alloc);
|
|
||||||
print_newline();
|
|
||||||
print_hex(ptr1);
|
|
||||||
print_newline();
|
|
||||||
print_hex(ptr2);
|
|
||||||
print_newline();
|
|
||||||
printnum(*ptr2);
|
|
||||||
print_newline();
|
|
||||||
*ptr2 = 42;
|
|
||||||
|
|
||||||
print_hex(ptr2);
|
|
||||||
print_newline();
|
|
||||||
printnum(*ptr2);
|
|
||||||
print_newline();
|
|
||||||
println("end");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Arena Allocator
|
// Arena Allocator
|
||||||
// Supports multiple arenas that can be destroyed independently
|
// Supports multiple arenas that can be destroyed independently
|
||||||
// Much more practical than a simple bump allocator
|
// Much more practical than a simple bump allocator
|
||||||
+43
-37
@@ -1,45 +1,51 @@
|
|||||||
include fib: "./lib/maths/fib.dsa"
|
|
||||||
include maths: "./lib/maths/core.dsa"
|
|
||||||
include print: "./lib/io/print.dsa"
|
|
||||||
|
|
||||||
dw idt: 0xFFFF0000
|
// GENERATED BY DSC COMPILER
|
||||||
|
// Generated at 2026-02-04 01:44:06
|
||||||
|
|
||||||
|
// Imports
|
||||||
|
include print: "./lib/io/print.dsa"
|
||||||
|
include fib: "./lib/maths/fib.dsa"
|
||||||
|
|
||||||
|
// Globals & Reserved Memory
|
||||||
|
|
||||||
|
|
||||||
|
// Entry Point
|
||||||
dw stack: 0x10000
|
dw stack: 0x10000
|
||||||
init:
|
db message: "Process Exited with code:"
|
||||||
// setup interrupt handlers
|
_init:
|
||||||
ldw idt, idr
|
|
||||||
lwi handle_hard_fault, rg0
|
|
||||||
stw rg0, idr, 4
|
|
||||||
// set up a stack.
|
|
||||||
ldw stack, bpr
|
ldw stack, bpr
|
||||||
mov bpr, spr
|
mov bpr, spr
|
||||||
|
push zero
|
||||||
dw string: "hello world"
|
call main
|
||||||
start:
|
call print::print_newline
|
||||||
lwi 100, rg0
|
lwi message, rg0
|
||||||
lwi 10, rg1
|
|
||||||
|
|
||||||
push rg1
|
|
||||||
push rg0
|
|
||||||
call maths::new_divide
|
|
||||||
pop rg0
|
|
||||||
pop rg1
|
|
||||||
hlt
|
|
||||||
|
|
||||||
pop rg0
|
|
||||||
pop zero
|
|
||||||
push rg0
|
|
||||||
call print::print_num
|
|
||||||
pop zero
|
|
||||||
|
|
||||||
hlt
|
|
||||||
|
|
||||||
// fault handler in case we fail DSA.
|
|
||||||
dw hard_fault_err: "FATAL: Illegal Instruction or Memory Access!"
|
|
||||||
handle_hard_fault:
|
|
||||||
call print::clear
|
|
||||||
call print::reset
|
|
||||||
lwi hard_fault_err, rg0
|
|
||||||
push rg0
|
push rg0
|
||||||
call print::print
|
call print::print
|
||||||
pop zero
|
pop zero
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
hlt
|
hlt
|
||||||
|
|
||||||
|
|
||||||
|
// Return
|
||||||
|
_ret:
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
return
|
||||||
|
|
||||||
|
// Compiled Code Starts...
|
||||||
|
main:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
lli 6, rg0
|
||||||
|
push rg0 // bpr-4: x
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call fib::fib_n
|
||||||
|
pop rg1
|
||||||
|
push rg1 // bpr-8: y
|
||||||
|
push rg1 // push arg 0
|
||||||
|
call print::print_num
|
||||||
|
pop zero
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
include print: "./lib/io/print.dsa";
|
||||||
|
include fib: "./lib/maths/fib.dsa";
|
||||||
|
|
||||||
|
fn main() -> u32 {
|
||||||
|
let x: u32 = 6;
|
||||||
|
|
||||||
|
let y: u32 = fib::fib_n(x);
|
||||||
|
print::print_num(y);
|
||||||
|
}
|
||||||
+172
-25
@@ -1,12 +1,14 @@
|
|||||||
|
|
||||||
// GENERATED BY DSC COMPILER
|
// GENERATED BY DSC COMPILER
|
||||||
// Generated at 2026-02-03 02:08:02
|
// Generated at 2026-02-03 23:37:16
|
||||||
|
|
||||||
// Imports
|
// Imports
|
||||||
include print: "./lib/io/print.dsa"
|
include print: "./lib/io/print.dsa"
|
||||||
|
|
||||||
// Globals & Reserved Memory
|
// Globals & Reserved Memory
|
||||||
|
dw heap_start: 196608
|
||||||
|
dw heap_end: 262144
|
||||||
|
dw heap_current: 196608
|
||||||
|
|
||||||
// Entry Point
|
// Entry Point
|
||||||
dw stack: 0x10000
|
dw stack: 0x10000
|
||||||
@@ -37,31 +39,176 @@ main:
|
|||||||
push bpr
|
push bpr
|
||||||
mov spr, bpr
|
mov spr, bpr
|
||||||
|
|
||||||
lli 5, rg0
|
lli 0, rg0
|
||||||
db str_1: "Hello world"
|
push rg0 // bpr-4: x
|
||||||
lwi str_1, rg1
|
subi bpr 4 rg1
|
||||||
db str_2: "test"
|
lli 512, rg0
|
||||||
lwi str_2, rg2
|
push rg1 // bpr-8: y
|
||||||
push rg0
|
push rg0 // push arg 0
|
||||||
push rg1
|
call arena_create
|
||||||
push rg2
|
|
||||||
db str_3: "hello world 2 electric boogaloo"
|
|
||||||
lwi str_3, rg3
|
|
||||||
push rg3
|
|
||||||
call print::println
|
|
||||||
pop zero
|
|
||||||
pop rg2
|
pop rg2
|
||||||
pop rg1
|
lli 32, rg0
|
||||||
pop rg0
|
push rg2 // bpr-12: alloc
|
||||||
push rg0
|
push rg0 // push arg 1
|
||||||
push rg1
|
push rg2 // push arg 0
|
||||||
push rg2
|
call arena_alloc
|
||||||
lli 213, rg3
|
pop rg3
|
||||||
push rg3
|
pop zero
|
||||||
|
lli 32, rg0
|
||||||
|
subi bpr 12 rg2
|
||||||
|
ldw rg2, rg2 // bpr-20: alloc
|
||||||
|
push rg3 // bpr-16: ptr1
|
||||||
|
push rg2 // bpr-20: alloc
|
||||||
|
push rg0 // push arg 1
|
||||||
|
push rg2 // push arg 0
|
||||||
|
call arena_alloc
|
||||||
|
pop rg4
|
||||||
|
pop zero
|
||||||
|
subi bpr 20 rg0
|
||||||
|
ldw rg0, rg0 // bpr-28: alloc
|
||||||
|
push rg4 // bpr-24: ptr2
|
||||||
|
push rg0 // bpr-28: alloc
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 16 rg0
|
||||||
|
ldw rg0, rg0 // bpr-24: ptr1
|
||||||
|
push rg0 // bpr-32: ptr1
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 24 rg0
|
||||||
|
ldw rg0, rg0 // bpr-32: ptr2
|
||||||
|
push rg0 // bpr-36: ptr2
|
||||||
|
push rg0 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 36 rg0
|
||||||
|
ldw rg0, rg0 // bpr-44: ptr2
|
||||||
|
ldw rg0, rg2
|
||||||
|
push rg0 // bpr-40: ptr2
|
||||||
|
push rg2 // push arg 0
|
||||||
call print::print_num
|
call print::print_num
|
||||||
pop zero
|
pop zero
|
||||||
pop rg2
|
call print::print_newline
|
||||||
pop rg1
|
lli 42, rg2
|
||||||
pop rg0
|
subi bpr 40 rg5
|
||||||
|
ldw rg5, rg5 // bpr-48: ptr2
|
||||||
|
stw rg2, rg5
|
||||||
|
push rg5 // bpr-44: ptr2
|
||||||
|
push rg5 // push arg 0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
subi bpr 44 rg2
|
||||||
|
ldw rg2, rg2 // bpr-52: ptr2
|
||||||
|
ldw rg2, rg5
|
||||||
|
push rg2 // bpr-48: ptr2
|
||||||
|
push rg5 // push arg 0
|
||||||
|
call print::print_num
|
||||||
|
pop zero
|
||||||
|
call print::print_newline
|
||||||
|
db str_1: "end"
|
||||||
|
lwi str_1, rg5
|
||||||
|
push rg5 // push arg 0
|
||||||
|
call print::println
|
||||||
|
pop zero
|
||||||
|
lli 0, rg5
|
||||||
|
stw rg5, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
arena_create:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8
|
||||||
|
lli 12, rg1
|
||||||
|
add rg0, rg1, rg2
|
||||||
|
ldw heap_current, rg1
|
||||||
|
add rg1, rg2, rg3
|
||||||
|
ldw heap_end, rg4
|
||||||
|
cmp rg3, rg4
|
||||||
|
lli 0, rg5
|
||||||
|
jle _cmp_end_2
|
||||||
|
lli 1, rg5
|
||||||
|
_cmp_end_2:
|
||||||
|
cmp rg5, zero
|
||||||
|
jeq _else_4
|
||||||
|
_then_3:
|
||||||
|
lli 0, rg4
|
||||||
|
stw rg4, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
jmp _end_5
|
||||||
|
_else_4:
|
||||||
|
nop
|
||||||
|
_end_5:
|
||||||
|
lli 12, rg4
|
||||||
|
add rg1, rg4, rg5
|
||||||
|
add rg1, rg2, rg4
|
||||||
|
stw rg5, rg1
|
||||||
|
lli 4, rg6
|
||||||
|
add rg1, rg6, rg7
|
||||||
|
stw rg5, rg7
|
||||||
|
lli 8, rg6
|
||||||
|
add rg1, rg6, rg7
|
||||||
|
stw rg4, rg7
|
||||||
|
stw rg3, heap_current
|
||||||
|
stw rg1, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
arena_alloc:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8
|
||||||
|
ldw bpr, rg1, 12
|
||||||
|
lli 4, rg2
|
||||||
|
add rg0, rg2, rg3
|
||||||
|
ldw rg3, rg2
|
||||||
|
lli 8, rg3
|
||||||
|
add rg0, rg3, rg4
|
||||||
|
ldw rg4, rg3
|
||||||
|
add rg2, rg1, rg4
|
||||||
|
cmp rg4, rg3
|
||||||
|
lli 0, rg5
|
||||||
|
jle _cmp_end_6
|
||||||
|
lli 1, rg5
|
||||||
|
_cmp_end_6:
|
||||||
|
cmp rg5, zero
|
||||||
|
jeq _else_8
|
||||||
|
_then_7:
|
||||||
|
lli 0, rg5
|
||||||
|
stw rg5, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
jmp _end_9
|
||||||
|
_else_8:
|
||||||
|
nop
|
||||||
|
_end_9:
|
||||||
|
lli 4, rg5
|
||||||
|
add rg0, rg5, rg6
|
||||||
|
stw rg4, rg6
|
||||||
|
stw rg2, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
arena_destroy:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8
|
||||||
|
lli 0, rg1
|
||||||
|
stw rg1, bpr, 8
|
||||||
|
jmp _ret
|
||||||
|
|
||||||
|
reset_all:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw heap_start, rg0
|
||||||
|
stw rg0, heap_current
|
||||||
|
lli 0, rg0
|
||||||
|
stw rg0, bpr, 8
|
||||||
jmp _ret
|
jmp _ret
|
||||||
|
|
||||||
|
|||||||
@@ -268,6 +268,8 @@
|
|||||||
- [ ] Error recovery mechanisms
|
- [ ] Error recovery mechanisms
|
||||||
- [ ] Comprehensive parser tests
|
- [ ] Comprehensive parser tests
|
||||||
- [ ] Syntax error message quality testing
|
- [ ] Syntax error message quality testing
|
||||||
|
- [ ] Implement C frontend by moving lexer/parser from `c_compiler` to the new `compiler` project structure
|
||||||
|
- [ ] Evaluate possible memory management strategies (e.g., keep all variables on the stack vs spill only when calling functions)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -530,6 +532,20 @@
|
|||||||
|
|
||||||
#### 4.1.3 Symbol Table Loader in Emulator
|
#### 4.1.3 Symbol Table Loader in Emulator
|
||||||
|
|
||||||
|
### Pre-Debugger Editor Integration Tasks
|
||||||
|
|
||||||
|
- **Integrate compiler into editor**
|
||||||
|
- Add a build command that invokes the full compiler pipeline (lexer → parser → codegen).
|
||||||
|
- Show compilation output and errors in the console panel.
|
||||||
|
|
||||||
|
- **DSC language support**
|
||||||
|
- Enable syntax highlighting and auto‑completion for DSC files within the editor.
|
||||||
|
- Provide a dedicated “Build DSC” command that uses the integrated compiler.
|
||||||
|
|
||||||
|
- **Editor diagnostics**
|
||||||
|
- Wire compiler error messages to the editor’s gutter so users can click to jump to source lines.
|
||||||
|
|
||||||
|
|
||||||
**Estimate: 2 days**
|
**Estimate: 2 days**
|
||||||
**Dependencies:** 4.1.2
|
**Dependencies:** 4.1.2
|
||||||
**Deliverable:** Symbol loading in emulator crate
|
**Deliverable:** Symbol loading in emulator crate
|
||||||
@@ -679,17 +695,19 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 4.3.4 Integrate Build Tools in Editor
|
#### 4.3.4 Integrate Build Tools and Compiler into Editor
|
||||||
|
|
||||||
**Estimate: 1 day**
|
Estimate: 1 day
|
||||||
**Dependencies:** 4.3.1, 3.1.2
|
Dependencies: 4.3.1, 3.1.2, 2.1.2
|
||||||
**Deliverable:** Integrated build experience
|
Deliverable: Integrated build experience with compiler support
|
||||||
|
|
||||||
- [ ] Build button/command in UI
|
- [ ] Build button/command in UI that invokes the full compiler pipeline
|
||||||
- [ ] Show build output in console panel
|
- [ ] Show build output and compilation errors in console panel
|
||||||
- [ ] Error navigation (click to jump to source)
|
- [ ] Error navigation (click to jump to source)
|
||||||
- [ ] Hot reload on successful build
|
- [ ] Hot reload on successful build
|
||||||
- [ ] Build status indicator
|
- [ ] Build status indicator
|
||||||
|
- [ ] Hook DSC language support into editor for syntax highlighting and auto‑completion
|
||||||
|
- [ ] Provide dedicated DSC build command that uses the new compiler integration
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user