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::model::{Node, Opcode};
|
||||||
use crate::{assembler::AssembleError, expect_token};
|
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> {
|
pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||||
let mut instructions = vec![];
|
let mut instructions = vec![];
|
||||||
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
println!("node {node}");
|
instructions.push(
|
||||||
instructions.push(build_instruction(node)?);
|
build_instruction(node.clone())
|
||||||
|
.expect(format!("Failed to build instruction: {:?}", node).as_str()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("------------------------");
|
||||||
|
log("Compilation Success ✅");
|
||||||
|
|
||||||
Ok(instructions)
|
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) => {
|
(@convert_token $token: literal) => {
|
||||||
$crate::assembler::model::Token::Immediate($token)
|
$crate::assembler::model::Token::Immediate($token)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use std::{
|
|||||||
fmt, fs,
|
fmt, fs,
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::mpsc,
|
sync::{Arc, Mutex, mpsc},
|
||||||
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use common::prelude::Instruction;
|
use common::prelude::Instruction;
|
||||||
@@ -19,6 +20,7 @@ fn log(message: &str) {
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
|
||||||
|
pub mod assembler;
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
pub mod expand;
|
pub mod expand;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
@@ -39,72 +41,95 @@ pub use self::{
|
|||||||
use crate::util::logging::{Entry, Logger};
|
use crate::util::logging::{Entry, Logger};
|
||||||
|
|
||||||
pub struct CompilerEngine {
|
pub struct CompilerEngine {
|
||||||
modules: HashSet<u64>,
|
result_tx: mpsc::Sender<Result<Vec<Instruction>, AssembleError>>,
|
||||||
program: Program,
|
result_rx: Option<mpsc::Receiver<Result<Vec<Instruction>, AssembleError>>>,
|
||||||
logger: Option<Logger>,
|
is_running: bool,
|
||||||
receiver: Option<mpsc::Receiver<Entry>>,
|
|
||||||
result: Option<Result<Vec<Instruction>, AssembleError>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompilerEngine {
|
impl CompilerEngine {
|
||||||
pub fn new() -> CompilerEngine {
|
pub fn new() -> Self {
|
||||||
let (tx, rx) = mpsc::channel::<Entry>();
|
let (tx, rx) = mpsc::channel();
|
||||||
CompilerEngine {
|
Self {
|
||||||
program: Program::new(),
|
result_tx: tx,
|
||||||
modules: HashSet::new(),
|
result_rx: Some(rx),
|
||||||
logger: Some(Logger::new(tx)),
|
is_running: false,
|
||||||
receiver: Some(rx),
|
|
||||||
result: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_log() -> Option<Entry> {
|
/// Start the compilation process in a separate thread
|
||||||
None
|
pub fn start_compilation(&mut self, src: &Path) {
|
||||||
|
if self.is_running {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_logs() -> Vec<Entry> {
|
let src = src.to_path_buf();
|
||||||
vec![]
|
let tx = self.result_tx.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let result = assemble(&src);
|
||||||
|
tx.send(result).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.is_running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_ready(&self) -> bool {
|
/// Check if compilation is complete and get the result
|
||||||
self.result.is_some()
|
pub fn try_get_result(&mut self) -> Option<Result<Vec<Instruction>, AssembleError>> {
|
||||||
|
if !self.is_running {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn result(&self) -> Option<Result<Vec<Instruction>, AssembleError>> {
|
match self.result_rx.as_ref().unwrap().try_recv() {
|
||||||
self.result.clone()
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(&mut self, src: &Path) -> Result<(), AssembleError> {
|
/// 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);
|
let hash = quick_hash(src);
|
||||||
|
|
||||||
if self.modules.contains(&hash) {
|
if modules.contains(&hash) {
|
||||||
return Ok(());
|
return Ok(vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare_dependency(src, &mut self.modules, &mut self.program)?;
|
prepare_dependency(src, &mut modules, &mut program)?;
|
||||||
|
|
||||||
self.result = Some(self.build());
|
let mut nodes = program.nodes.clone();
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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)?;
|
create_sections(&mut nodes)?;
|
||||||
resolve_symbols(&mut nodes)?;
|
resolve_symbols(&mut nodes)?;
|
||||||
|
|
||||||
let instructions = codegen(nodes)?;
|
let instructions = codegen(nodes)?;
|
||||||
for inst in instructions.iter() {
|
|
||||||
println!("{inst}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(instructions)
|
Ok(instructions)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CompilerEngine {
|
impl Default for CompilerEngine {
|
||||||
@@ -143,7 +168,8 @@ fn prepare_dependency(
|
|||||||
let base_dir = path
|
let base_dir = path
|
||||||
.parent()
|
.parent()
|
||||||
.ok_or_else(|| AssembleError::InvalidFile(path.to_path_buf()))?;
|
.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)?;
|
let deps = Parser::get_dependencies(&nodes, path)?;
|
||||||
|
|
||||||
@@ -151,7 +177,6 @@ fn prepare_dependency(
|
|||||||
"{:20} {:20}",
|
"{:20} {:20}",
|
||||||
"Expanding PseudoInstructions", filename
|
"Expanding PseudoInstructions", filename
|
||||||
));
|
));
|
||||||
let mut nodes = expand_pseudo_ops(nodes, file_hash)?;
|
|
||||||
|
|
||||||
// add a section instruction
|
// add a section instruction
|
||||||
nodes.insert(
|
nodes.insert(
|
||||||
@@ -181,19 +206,6 @@ fn prepare_dependency(
|
|||||||
Ok(())
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AssembleError {
|
pub enum AssembleError {
|
||||||
Generic,
|
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 assembler;
|
||||||
|
pub mod brainf;
|
||||||
pub mod tooling;
|
pub mod tooling;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::assembler::CompilerEngine;
|
pub use crate::assembler::CompilerEngine;
|
||||||
pub use crate::assembler::disassemble;
|
pub use crate::tooling::project;
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-33
@@ -1,4 +1,4 @@
|
|||||||
use assembler::prelude::*;
|
use assembler::{brainf, prelude::*};
|
||||||
use std::{fs, io::Write, path::PathBuf};
|
use std::{fs, io::Write, path::PathBuf};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -6,7 +6,29 @@ fn main() {
|
|||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|
||||||
if args.len() == 2 && args[1] == "init" {
|
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);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,42 +41,17 @@ fn main() {
|
|||||||
let output_path = &args[4];
|
let output_path = &args[4];
|
||||||
let src = PathBuf::from(input_path);
|
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
|
// Initialize the compiler engine
|
||||||
let mut engine = CompilerEngine::new();
|
let mut compiler = CompilerEngine::new();
|
||||||
|
compiler.start_compilation(&src);
|
||||||
|
|
||||||
// Assemble the source file
|
// Or block until done
|
||||||
if let Err(e) = engine.assemble(&src) {
|
let result = compiler.wait_for_result().unwrap();
|
||||||
eprintln!("Assembly error: {}", e);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build and write the output
|
for instruction in result {
|
||||||
match engine.result() {
|
if let Err(e) = fs::write(output_path, instruction.encode().to_be_bytes()) {
|
||||||
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);
|
eprintln!("Failed to write to output file: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Some(Err(e)) => {
|
|
||||||
eprintln!("Build error: {}", e);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
eprintln!("Build error: No result available");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +1,75 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(unused)]
|
||||||
use std::{fmt, sync::mpsc::Sender};
|
use std::{fmt, sync::mpsc::Sender};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub struct Logger {}
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Logger {
|
|
||||||
pub sender: Sender<Entry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Logger {
|
impl Logger {
|
||||||
pub fn new(sender: Sender<Entry>) -> Self {
|
pub fn new() -> Self {
|
||||||
Self { sender }
|
Self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug<T: fmt::Display>(&self, message: T) {
|
pub fn log(&self, message: &str) {
|
||||||
self.sender
|
println!("\x1b[32mINFO:\x1b[0m {message}");
|
||||||
.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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[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 {
|
pub struct Entry {
|
||||||
etype: EntryType,
|
etype: EntryType,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user