assembler changes & brainf##k compiler lmao
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
++++++++++++++++++++++++++++++++++++++++++++ c1v44 : ASCII code of comma
|
||||
>++++++++++++++++++++++++++++++++ c2v32 : ASCII code of space
|
||||
>++++++++++++++++ c3v11 : quantity of numbers to be calculated
|
||||
> c4v0 : zeroth Fibonacci number (will not be printed)
|
||||
>+ c5v1 : first Fibonacci number
|
||||
<< c3 : loop counter
|
||||
[ block : loop to print (i)th number and calculate next one
|
||||
>> c5 : the number to be printed
|
||||
|
||||
block : divide c5 by 10 (preserve c5)
|
||||
> c6v0 : service zero
|
||||
>++++++++++ c7v10 : divisor
|
||||
<< c5 : back to dividend
|
||||
[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<] c5v0 : divmod algo; results in 0 n d_n%d n%d n/d
|
||||
>[<+>-] c5 : move dividend back to c5 and clear c6
|
||||
>[-] c7v0 : clear c7
|
||||
|
||||
>> block : c9 can have two digits; divide it by ten again
|
||||
>++++++++++ c10v10: divisor
|
||||
< c9 : back to dividend
|
||||
[->-[>+>>]>[+[-<+>]>+>>]<<<<<] c9v0 : another divmod algo; results in 0 d_n%d n%d n/d
|
||||
>[-] c10v0 : clear c10
|
||||
>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]c12v0 : print nonzero n/d (first digit) and clear c12
|
||||
<[++++++++++++++++++++++++++++++++++++++++++++++++.[-]] c11v0 : print nonzero n%d (second digit) and clear c11
|
||||
|
||||
<<<++++++++++++++++++++++++++++++++++++++++++++++++.[-] c8v0 : print any n%d (last digit) and clear c8
|
||||
<<<<<<<.>. c1c2 : print comma and space
|
||||
block : actually calculate next Fibonacci in c6
|
||||
>>[>>+<<-] c4v0 : move c4 to c6 (don't need to preserve it)
|
||||
>[>+<<+>-] c5v0 : move c5 to c6 and c4 (need to preserve it)
|
||||
>[<+>-] c6v0 : move c6 with sum to c5
|
||||
<<<- c3 : decrement loop counter
|
||||
]
|
||||
<<++... c1 : output three dots
|
||||
Binary file not shown.
@@ -0,0 +1,205 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs,
|
||||
path::{self, Path, PathBuf},
|
||||
sync::{Arc, Mutex},
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
use crate::assembler::{AssembleError, Token, expand_pseudo_ops, lexer, quick_hash};
|
||||
use crate::assembler::{Node, Parser, resolve_dependencies};
|
||||
use crate::util::logging::Logger;
|
||||
|
||||
// pub fn new_assemble(path: &Path) {
|
||||
// let program = Program::new();
|
||||
// let program_ref = ProgramRef::new(program);
|
||||
|
||||
// let task = Module::build(path.to_path_buf(), program_ref.clone());
|
||||
// program_ref.add_task(task);
|
||||
|
||||
// // wait on all tasks to finish
|
||||
// for task in program_ref.get_tasks() {
|
||||
// let module = task.module.join().unwrap();
|
||||
// program_ref.add_module(module);
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct Program {
|
||||
pub main_path: PathBuf,
|
||||
registry: HashSet<u64>,
|
||||
modules: Vec<Module>,
|
||||
tasks: Vec<Task>,
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
registry: HashSet::new(),
|
||||
modules: Vec::new(),
|
||||
tasks: Vec::new(),
|
||||
main_path: PathBuf::new(),
|
||||
logger: Logger::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_task(&mut self, task: Task) {
|
||||
self.tasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Program {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
struct ProgramRef {
|
||||
program: Arc<Mutex<Program>>,
|
||||
}
|
||||
|
||||
impl ProgramRef {
|
||||
pub fn new(program: Program) -> Self {
|
||||
Self {
|
||||
program: Arc::new(Mutex::new(program)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(&self, path: &Path) {
|
||||
self.program
|
||||
.lock()
|
||||
.unwrap()
|
||||
.registry
|
||||
.insert(quick_hash(path));
|
||||
}
|
||||
|
||||
pub fn is_registered(&self, path: &Path) -> bool {
|
||||
self.program
|
||||
.lock()
|
||||
.unwrap()
|
||||
.registry
|
||||
.contains(&quick_hash(path))
|
||||
}
|
||||
|
||||
// pub fn get_tasks(&self) -> Vec<&Task> {
|
||||
// self.program.lock().unwrap().tasks.iter().collect()
|
||||
// }
|
||||
|
||||
pub fn add_task(&self, task: Task) {
|
||||
self.program.lock().unwrap().add_task(task);
|
||||
}
|
||||
|
||||
pub fn add_module(&self, module: Module) {
|
||||
self.program.lock().unwrap().modules.push(module);
|
||||
}
|
||||
|
||||
pub fn log(&self, message: &str) {
|
||||
self.program.lock().unwrap().logger.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ProgramRef {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
program: self.program.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Module {
|
||||
pub path: PathBuf,
|
||||
pub hash: u64,
|
||||
pub nodes: Vec<Node>,
|
||||
program: ProgramRef,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(path: PathBuf, hash: u64, nodes: Vec<Node>, program: ProgramRef) -> Self {
|
||||
Self {
|
||||
path,
|
||||
hash,
|
||||
nodes,
|
||||
program,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(path: PathBuf, program: ProgramRef) -> Task {
|
||||
// spawn a thread that creates the main function and executes the lexer and parser.
|
||||
let handle = thread::spawn(move || {
|
||||
let mut module =
|
||||
Module::new(path.clone(), quick_hash(&path), Vec::new(), program.clone());
|
||||
|
||||
let tokens = module.lex();
|
||||
module.parse(tokens);
|
||||
module.expand();
|
||||
module.prepare_dependencies();
|
||||
module
|
||||
});
|
||||
|
||||
Task { module: handle }
|
||||
}
|
||||
|
||||
fn lex(&mut self) -> Vec<Token> {
|
||||
if let Ok(path) = self.path.canonicalize() {
|
||||
self.program.log(&format!(
|
||||
"{:20} {:20} [{}]",
|
||||
"Building",
|
||||
self.get_filename(),
|
||||
path.display()
|
||||
));
|
||||
}
|
||||
|
||||
let src = fs::read_to_string(&self.path)
|
||||
.map_err(|_| AssembleError::InvalidFile(self.path.to_path_buf()))
|
||||
.unwrap();
|
||||
let file_hash = quick_hash(&self.path);
|
||||
|
||||
self.program
|
||||
.log(&format!("{:20} {:20}", "Tokenising", self.get_filename()));
|
||||
lexer::lexer(src, file_hash).unwrap()
|
||||
}
|
||||
|
||||
fn parse(&mut self, tokens: Vec<Token>) {
|
||||
self.program
|
||||
.log(&format!("{:20} {:20}", "Parsing", self.get_filename()));
|
||||
let parsed = Parser::parse_nodes(tokens).unwrap();
|
||||
self.nodes = parsed;
|
||||
}
|
||||
|
||||
fn expand(&mut self) {
|
||||
self.program
|
||||
.log(&format!("{:20} {:20}", "Expanding", self.get_filename()));
|
||||
let expanded = expand_pseudo_ops(self.nodes.clone(), self.hash).unwrap();
|
||||
self.nodes = expanded;
|
||||
}
|
||||
|
||||
fn prepare_dependencies(&mut self) {
|
||||
let nodes = resolve_dependencies(self.nodes.clone(), self.path.parent().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let dependencies = Parser::get_dependencies(&nodes, &self.path).unwrap();
|
||||
|
||||
for dep in dependencies {
|
||||
if self.program.is_registered(&dep) {
|
||||
// we have already built this module!
|
||||
continue;
|
||||
}
|
||||
|
||||
self.program.register(&dep);
|
||||
|
||||
// create new module
|
||||
// add the task to the program
|
||||
let task = Module::build(dep, self.program.clone());
|
||||
self.program.add_task(task);
|
||||
}
|
||||
}
|
||||
|
||||
// helper functions
|
||||
fn get_filename(&self) -> &str {
|
||||
self.path.file_name().unwrap().to_str().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
module: JoinHandle<Module>,
|
||||
}
|
||||
@@ -3,14 +3,23 @@ use common::{args, prelude::*};
|
||||
use crate::assembler::model::{Node, Opcode};
|
||||
use crate::{assembler::AssembleError, expect_token};
|
||||
|
||||
fn log(message: &str) {
|
||||
println!("\x1b[32mINFO:\x1b[0m {message}");
|
||||
}
|
||||
|
||||
pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||
let mut instructions = vec![];
|
||||
|
||||
for node in nodes {
|
||||
println!("node {node}");
|
||||
instructions.push(build_instruction(node)?);
|
||||
instructions.push(
|
||||
build_instruction(node.clone())
|
||||
.expect(format!("Failed to build instruction: {:?}", node).as_str()),
|
||||
);
|
||||
}
|
||||
|
||||
println!("------------------------");
|
||||
log("Compilation Success ✅");
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,14 @@ macro_rules! node {
|
||||
)
|
||||
};
|
||||
|
||||
($symbol: expr, $opcode: expr) => {
|
||||
$crate::assembler::model::Node::new(
|
||||
$symbol.clone(),
|
||||
$opcode.clone(),
|
||||
Vec::new()
|
||||
)
|
||||
};
|
||||
|
||||
(@convert_token $token: literal) => {
|
||||
$crate::assembler::model::Token::Immediate($token)
|
||||
};
|
||||
|
||||
@@ -5,7 +5,8 @@ use std::{
|
||||
fmt, fs,
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
path::{Path, PathBuf},
|
||||
sync::mpsc,
|
||||
sync::{Arc, Mutex, mpsc},
|
||||
thread,
|
||||
};
|
||||
|
||||
use common::prelude::Instruction;
|
||||
@@ -19,6 +20,7 @@ fn log(message: &str) {
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod assembler;
|
||||
pub mod codegen;
|
||||
pub mod expand;
|
||||
pub mod lexer;
|
||||
@@ -39,72 +41,95 @@ pub use self::{
|
||||
use crate::util::logging::{Entry, Logger};
|
||||
|
||||
pub struct CompilerEngine {
|
||||
modules: HashSet<u64>,
|
||||
program: Program,
|
||||
logger: Option<Logger>,
|
||||
receiver: Option<mpsc::Receiver<Entry>>,
|
||||
result: Option<Result<Vec<Instruction>, AssembleError>>,
|
||||
result_tx: mpsc::Sender<Result<Vec<Instruction>, AssembleError>>,
|
||||
result_rx: Option<mpsc::Receiver<Result<Vec<Instruction>, AssembleError>>>,
|
||||
is_running: bool,
|
||||
}
|
||||
|
||||
impl CompilerEngine {
|
||||
pub fn new() -> CompilerEngine {
|
||||
let (tx, rx) = mpsc::channel::<Entry>();
|
||||
CompilerEngine {
|
||||
program: Program::new(),
|
||||
modules: HashSet::new(),
|
||||
logger: Some(Logger::new(tx)),
|
||||
receiver: Some(rx),
|
||||
result: None,
|
||||
pub fn new() -> Self {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
Self {
|
||||
result_tx: tx,
|
||||
result_rx: Some(rx),
|
||||
is_running: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_log() -> Option<Entry> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_logs() -> Vec<Entry> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.result.is_some()
|
||||
}
|
||||
|
||||
pub fn result(&self) -> Option<Result<Vec<Instruction>, AssembleError>> {
|
||||
self.result.clone()
|
||||
}
|
||||
|
||||
pub fn assemble(&mut self, src: &Path) -> Result<(), AssembleError> {
|
||||
let hash = quick_hash(src);
|
||||
|
||||
if self.modules.contains(&hash) {
|
||||
return Ok(());
|
||||
/// Start the compilation process in a separate thread
|
||||
pub fn start_compilation(&mut self, src: &Path) {
|
||||
if self.is_running {
|
||||
return;
|
||||
}
|
||||
|
||||
prepare_dependency(src, &mut self.modules, &mut self.program)?;
|
||||
let src = src.to_path_buf();
|
||||
let tx = self.result_tx.clone();
|
||||
|
||||
self.result = Some(self.build());
|
||||
thread::spawn(move || {
|
||||
let result = assemble(&src);
|
||||
tx.send(result).unwrap();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
self.is_running = true;
|
||||
}
|
||||
|
||||
fn load_module(&mut self, path: &Path) -> Result<(), AssembleError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build(&self) -> Result<Vec<Instruction>, AssembleError> {
|
||||
let mut nodes = self.program.nodes.clone();
|
||||
|
||||
create_sections(&mut nodes)?;
|
||||
resolve_symbols(&mut nodes)?;
|
||||
|
||||
let instructions = codegen(nodes)?;
|
||||
for inst in instructions.iter() {
|
||||
println!("{inst}");
|
||||
/// Check if compilation is complete and get the result
|
||||
pub fn try_get_result(&mut self) -> Option<Result<Vec<Instruction>, AssembleError>> {
|
||||
if !self.is_running {
|
||||
return None;
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
match self.result_rx.as_ref().unwrap().try_recv() {
|
||||
Ok(result) => {
|
||||
self.is_running = false;
|
||||
Some(result)
|
||||
}
|
||||
Err(mpsc::TryRecvError::Empty) => None,
|
||||
Err(mpsc::TryRecvError::Disconnected) => {
|
||||
self.is_running = false;
|
||||
Some(Err(AssembleError::Generic))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block until compilation is complete and return the result
|
||||
pub fn wait_for_result(&mut self) -> Result<Vec<Instruction>, AssembleError> {
|
||||
if !self.is_running {
|
||||
return Err(AssembleError::Generic);
|
||||
}
|
||||
|
||||
match self.result_rx.take().unwrap().recv() {
|
||||
Ok(result) => {
|
||||
self.is_running = false;
|
||||
result
|
||||
}
|
||||
Err(_) => {
|
||||
self.is_running = false;
|
||||
Err(AssembleError::Generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
||||
let mut modules = HashSet::new();
|
||||
let mut program = Program::new();
|
||||
|
||||
let hash = quick_hash(src);
|
||||
|
||||
if modules.contains(&hash) {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
prepare_dependency(src, &mut modules, &mut program)?;
|
||||
|
||||
let mut nodes = program.nodes.clone();
|
||||
|
||||
create_sections(&mut nodes)?;
|
||||
resolve_symbols(&mut nodes)?;
|
||||
|
||||
let instructions = codegen(nodes)?;
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
impl Default for CompilerEngine {
|
||||
@@ -143,7 +168,8 @@ fn prepare_dependency(
|
||||
let base_dir = path
|
||||
.parent()
|
||||
.ok_or_else(|| AssembleError::InvalidFile(path.to_path_buf()))?;
|
||||
let nodes = resolve_dependencies(parsed, base_dir)?;
|
||||
let mut nodes = expand_pseudo_ops(parsed, file_hash)?;
|
||||
nodes = resolve_dependencies(nodes, base_dir)?;
|
||||
|
||||
let deps = Parser::get_dependencies(&nodes, path)?;
|
||||
|
||||
@@ -151,7 +177,6 @@ fn prepare_dependency(
|
||||
"{:20} {:20}",
|
||||
"Expanding PseudoInstructions", filename
|
||||
));
|
||||
let mut nodes = expand_pseudo_ops(nodes, file_hash)?;
|
||||
|
||||
// add a section instruction
|
||||
nodes.insert(
|
||||
@@ -181,19 +206,6 @@ fn prepare_dependency(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _build(_src: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
/// TODO: disassembling functionality
|
||||
/// - We probably don't need to implement this for a while yet.
|
||||
/// - This method should recover symbols such as labels and variables from the human
|
||||
/// written assembly, recognising sequences that are expansions of pseudo-instructions
|
||||
/// and reversing this to produce near enough the original source code.
|
||||
pub fn disassemble(_: Vec<Instruction>) -> String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AssembleError {
|
||||
Generic,
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{self, Path},
|
||||
};
|
||||
|
||||
use common::prelude::{Instruction, Register};
|
||||
|
||||
use crate::{
|
||||
assembler::{
|
||||
Module, Node, Opcode, Symbol, Token, codegen, create_sections, expand_pseudo_ops,
|
||||
resolve_symbols,
|
||||
},
|
||||
node,
|
||||
};
|
||||
|
||||
pub fn build(src: &Path) -> Vec<Instruction> {
|
||||
let src = fs::read_to_string(src).unwrap();
|
||||
let mut nodes = parse(src);
|
||||
|
||||
// we need to expand pseudoinstructions etc now
|
||||
nodes = expand_pseudo_ops(nodes, 0).unwrap();
|
||||
|
||||
create_sections(&mut nodes).unwrap();
|
||||
|
||||
for n in nodes.iter() {
|
||||
println!("{}", n);
|
||||
}
|
||||
|
||||
resolve_symbols(&mut nodes).unwrap();
|
||||
|
||||
codegen(nodes).unwrap()
|
||||
}
|
||||
|
||||
pub fn parse(src: String) -> Vec<Node> {
|
||||
let stack = Token::Immediate(0x10000);
|
||||
let acc = Token::Register(Register::Acc);
|
||||
let rga = Token::Register(Register::Rga);
|
||||
|
||||
let bpr = Token::Register(Register::Bpr);
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let mut nodes = Vec::<Node>::new();
|
||||
|
||||
// Define symbols
|
||||
let print_start = Symbol {
|
||||
name: "print".to_string(),
|
||||
module: Module::Resolved(0),
|
||||
};
|
||||
|
||||
let tokens = lex(src);
|
||||
|
||||
let mut id = 0;
|
||||
let mut idstack = Vec::<u32>::new();
|
||||
|
||||
nodes.extend(vec![
|
||||
// set up a stack
|
||||
node!(None, Opcode::Lwi, stack, bpr),
|
||||
node!(None, Opcode::Mov, bpr, spr),
|
||||
// set up the data pointer
|
||||
node!(
|
||||
Some(Symbol {
|
||||
name: "main".to_string(),
|
||||
module: Module::Resolved(0)
|
||||
}),
|
||||
Opcode::Lwi,
|
||||
Token::Immediate(0x30000),
|
||||
rga
|
||||
),
|
||||
]);
|
||||
|
||||
for (id, tok) in tokens.iter().enumerate() {
|
||||
match tok {
|
||||
BfToken::Inc => {
|
||||
// inc acc
|
||||
nodes.extend(vec![node!(None, Opcode::Inc, acc)]);
|
||||
}
|
||||
BfToken::Dec => {
|
||||
// dec acc
|
||||
nodes.extend(vec![node!(None, Opcode::Dec, acc)]);
|
||||
}
|
||||
BfToken::IncPtr => {
|
||||
// stb acc, rga
|
||||
// add rga, 4
|
||||
// ldb rga, acc
|
||||
nodes.extend(vec![
|
||||
node!(None, Opcode::Stw, acc, rga, 0),
|
||||
node!(None, Opcode::AddI, rga, 4, rga),
|
||||
node!(None, Opcode::Ldw, rga, acc, 0),
|
||||
]);
|
||||
}
|
||||
BfToken::DecPtr => {
|
||||
// stb acc, rga
|
||||
// sub rga, 4
|
||||
// ldb rga, acc
|
||||
nodes.extend(vec![
|
||||
node!(None, Opcode::Stw, acc, rga, 0),
|
||||
node!(None, Opcode::SubI, rga, 4, rga),
|
||||
node!(None, Opcode::Ldw, rga, acc, 0),
|
||||
]);
|
||||
}
|
||||
BfToken::Out => {
|
||||
// push rga
|
||||
// call print
|
||||
// pop zero
|
||||
nodes.extend(vec![
|
||||
node!(None, Opcode::Push, acc),
|
||||
node!(None, Opcode::Call, Token::Symbol(print_start.clone())),
|
||||
node!(None, Opcode::Pop, Token::Register(Register::Zero)),
|
||||
]);
|
||||
}
|
||||
BfToken::In => {
|
||||
// Read a byte from input and store it at the current data pointer
|
||||
// Assuming we have an input function mapped to a specific memory location or I/O port
|
||||
nodes.extend(vec![
|
||||
// Read input (assuming input is mapped to memory address 0x40000)
|
||||
node!(None, Opcode::Ldw, Token::Immediate(0x40000), acc, 0),
|
||||
// Store the input byte at the current data pointer
|
||||
]);
|
||||
}
|
||||
BfToken::Forward => {
|
||||
// Start of loop [
|
||||
let loop_start = format!("loop_start_{}", id);
|
||||
let loop_end = format!("loop_end_{}", id);
|
||||
|
||||
// Push the current position for the matching ]
|
||||
idstack.push(id as u32);
|
||||
|
||||
// Load current cell value and check if zero
|
||||
nodes.extend(vec![
|
||||
// Compare with zero
|
||||
node!(None, Opcode::Cmp, acc, Token::Register(Register::Zero)),
|
||||
// If zero, jump to end of loop
|
||||
node!(
|
||||
None,
|
||||
Opcode::Jeq,
|
||||
Token::Symbol(Symbol {
|
||||
name: loop_end,
|
||||
module: Module::Resolved(0),
|
||||
}),
|
||||
Token::Register(Register::Zero)
|
||||
),
|
||||
]);
|
||||
|
||||
// Add label for loop start
|
||||
nodes.push(node!(
|
||||
Some(Symbol {
|
||||
name: loop_start,
|
||||
module: Module::Resolved(0),
|
||||
}),
|
||||
Opcode::Nop
|
||||
));
|
||||
}
|
||||
BfToken::Back => {
|
||||
// End of loop ]
|
||||
if let Some(start_id) = idstack.pop() {
|
||||
let loop_start = format!("loop_start_{}", start_id);
|
||||
let loop_end = format!("loop_end_{}", start_id);
|
||||
|
||||
// Jump back to the start of the loop
|
||||
nodes.extend(vec![
|
||||
// Compare with zero
|
||||
node!(None, Opcode::Cmp, acc, Token::Register(Register::Zero)),
|
||||
// If not zero, jump back to start of loop
|
||||
node!(
|
||||
None,
|
||||
Opcode::Jne,
|
||||
Token::Symbol(Symbol {
|
||||
name: loop_start,
|
||||
module: Module::Resolved(0),
|
||||
}),
|
||||
Token::Register(Register::Zero)
|
||||
),
|
||||
// Add label for loop end
|
||||
node!(
|
||||
Some(Symbol {
|
||||
name: loop_end,
|
||||
module: Module::Resolved(0),
|
||||
}),
|
||||
Opcode::Nop
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
// Unmatched closing bracket - could add error handling here
|
||||
eprintln!("Warning: Unmatched ']' at position {}", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes.push(node!(None, Opcode::Hlt));
|
||||
|
||||
insert_lib(&mut nodes);
|
||||
|
||||
nodes
|
||||
}
|
||||
|
||||
fn insert_lib(nodes: &mut Vec<Node>) {
|
||||
let bpr = Token::Register(Register::Bpr);
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let rg0 = Token::Register(Register::Rg0);
|
||||
let rg1 = Token::Register(Register::Rg1);
|
||||
|
||||
let print_start = Symbol {
|
||||
name: "print".to_string(),
|
||||
module: Module::Resolved(0),
|
||||
};
|
||||
let current = Symbol {
|
||||
name: "current".to_string(),
|
||||
module: Module::Resolved(0),
|
||||
};
|
||||
// set up the program framework.
|
||||
nodes.extend(vec![
|
||||
// set display to 0x20000
|
||||
node!(
|
||||
None,
|
||||
Opcode::Dw,
|
||||
Token::Symbol(current.clone()),
|
||||
Token::Immediate(0x20000)
|
||||
),
|
||||
// print function
|
||||
// initialisation
|
||||
node!(Some(print_start.clone()), Opcode::Push, bpr.clone()),
|
||||
node!(None, Opcode::Mov, spr.clone(), bpr.clone()),
|
||||
// function body
|
||||
node!(
|
||||
None,
|
||||
Opcode::Ldw,
|
||||
bpr.clone(),
|
||||
rg0.clone(),
|
||||
Token::Immediate(8)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Ldw,
|
||||
Token::Symbol(current.clone()), // Load address of current
|
||||
rg1.clone(),
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stb,
|
||||
rg0.clone(),
|
||||
rg1.clone(),
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::AddI,
|
||||
rg1.clone(),
|
||||
Token::Immediate(1),
|
||||
rg1.clone()
|
||||
),
|
||||
// function return according to spec.
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stw,
|
||||
rg1.clone(),
|
||||
Token::Symbol(current.clone()), // Store back to current
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(None, Opcode::Mov, bpr.clone(), spr.clone()),
|
||||
node!(None, Opcode::Pop, bpr.clone()),
|
||||
node!(None, Opcode::Return),
|
||||
]);
|
||||
}
|
||||
|
||||
enum BfToken {
|
||||
Inc,
|
||||
Dec,
|
||||
IncPtr,
|
||||
DecPtr,
|
||||
Out,
|
||||
In,
|
||||
Forward,
|
||||
Back,
|
||||
}
|
||||
|
||||
fn lex(src: String) -> Vec<BfToken> {
|
||||
src.chars()
|
||||
.filter_map(|c| match c {
|
||||
'+' => Some(BfToken::Inc),
|
||||
'-' => Some(BfToken::Dec),
|
||||
'>' => Some(BfToken::IncPtr),
|
||||
'<' => Some(BfToken::DecPtr),
|
||||
'.' => Some(BfToken::Out),
|
||||
',' => Some(BfToken::In),
|
||||
'[' => Some(BfToken::Forward),
|
||||
']' => Some(BfToken::Back),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn create_symbol(id: u32) -> Symbol {
|
||||
Symbol {
|
||||
name: format!("label_{}", id),
|
||||
module: Module::Resolved(0),
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
pub mod assembler;
|
||||
pub mod brainf;
|
||||
pub mod tooling;
|
||||
mod util;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::assembler::CompilerEngine;
|
||||
pub use crate::assembler::disassemble;
|
||||
pub use crate::tooling::project;
|
||||
}
|
||||
|
||||
+31
-34
@@ -1,4 +1,4 @@
|
||||
use assembler::prelude::*;
|
||||
use assembler::{brainf, prelude::*};
|
||||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
fn main() {
|
||||
@@ -6,7 +6,29 @@ fn main() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
|
||||
if args.len() == 2 && args[1] == "init" {
|
||||
assembler::tooling::project::tool_libcreate();
|
||||
project::tool_libcreate();
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
if args.len() == 2 && args[1] == "brainf" {
|
||||
let src = PathBuf::from("brainf.bf");
|
||||
let result = brainf::build(&src);
|
||||
|
||||
let mut file = match fs::File::create("brainf.dsb") {
|
||||
Err(e) => {
|
||||
eprintln!("Failed to create output file: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
||||
for instruction in result {
|
||||
if let Err(e) = file.write(&instruction.encode().to_be_bytes()) {
|
||||
eprintln!("Failed to write to output file: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
@@ -19,41 +41,16 @@ fn main() {
|
||||
let output_path = &args[4];
|
||||
let src = PathBuf::from(input_path);
|
||||
|
||||
// Create the output file
|
||||
let mut output_file = match fs::File::create(output_path) {
|
||||
Ok(file) => file,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to create output file: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize the compiler engine
|
||||
let mut engine = CompilerEngine::new();
|
||||
let mut compiler = CompilerEngine::new();
|
||||
compiler.start_compilation(&src);
|
||||
|
||||
// Assemble the source file
|
||||
if let Err(e) = engine.assemble(&src) {
|
||||
eprintln!("Assembly error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
// Or block until done
|
||||
let result = compiler.wait_for_result().unwrap();
|
||||
|
||||
// Build and write the output
|
||||
match engine.result() {
|
||||
Some(Ok(instructions)) => {
|
||||
for instruction in instructions {
|
||||
if let Err(e) = output_file.write_all(&instruction.encode().to_le_bytes())
|
||||
{
|
||||
eprintln!("Failed to write to output file: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
eprintln!("Build error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
None => {
|
||||
eprintln!("Build error: No result available");
|
||||
for instruction in result {
|
||||
if let Err(e) = fs::write(output_path, instruction.encode().to_be_bytes()) {
|
||||
eprintln!("Failed to write to output file: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,75 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused)]
|
||||
use std::{fmt, sync::mpsc::Sender};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct Logger {
|
||||
pub sender: Sender<Entry>,
|
||||
}
|
||||
pub struct Logger {}
|
||||
|
||||
impl Logger {
|
||||
pub fn new(sender: Sender<Entry>) -> Self {
|
||||
Self { sender }
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn debug<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Debug,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn info<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Info,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn warn<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Warn,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn error<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Error,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn fatal<T: fmt::Display>(&self, message: T) {
|
||||
self.sender
|
||||
.send(Entry {
|
||||
etype: EntryType::Fatal,
|
||||
message: message.to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
pub fn log(&self, message: &str) {
|
||||
println!("\x1b[32mINFO:\x1b[0m {message}");
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Debug)]=
|
||||
// pub struct Logger {
|
||||
// pub sender: Sender<Entry>,
|
||||
// }
|
||||
|
||||
// impl Logger {
|
||||
// pub fn new(sender: Sender<Entry>) -> Self {
|
||||
// Self { sender }
|
||||
// }
|
||||
|
||||
// pub fn debug<T: fmt::Display>(&self, message: T) {
|
||||
// self.sender
|
||||
// .send(Entry {
|
||||
// etype: EntryType::Debug,
|
||||
// message: message.to_string(),
|
||||
// })
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
// pub fn info<T: fmt::Display>(&self, message: T) {
|
||||
// self.sender
|
||||
// .send(Entry {
|
||||
// etype: EntryType::Info,
|
||||
// message: message.to_string(),
|
||||
// })
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
// pub fn warn<T: fmt::Display>(&self, message: T) {
|
||||
// self.sender
|
||||
// .send(Entry {
|
||||
// etype: EntryType::Warn,
|
||||
// message: message.to_string(),
|
||||
// })
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
// pub fn error<T: fmt::Display>(&self, message: T) {
|
||||
// self.sender
|
||||
// .send(Entry {
|
||||
// etype: EntryType::Error,
|
||||
// message: message.to_string(),
|
||||
// })
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
// pub fn fatal<T: fmt::Display>(&self, message: T) {
|
||||
// self.sender
|
||||
// .send(Entry {
|
||||
// etype: EntryType::Fatal,
|
||||
// message: message.to_string(),
|
||||
// })
|
||||
// .unwrap();
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct Entry {
|
||||
etype: EntryType,
|
||||
pub message: String,
|
||||
|
||||
Reference in New Issue
Block a user