#[cfg(test)]
mod tests;
use super::BrainfuckInstr;
const ALLOCATE_AND_START: &str = include_str!("compiler/alloc_start.asm");
const EXIT_SYSCALL: &str = include_str!("compiler/exit.asm");
pub fn compile(code: &[BrainfuckInstr]) -> String {
let mut output = String::new();
output.push_str(ALLOCATE_AND_START);
let mut total_loops = 0;
let mut loop_no_stack = Vec::new();
for instruction in code {
translate_instruction(
instruction,
&mut total_loops,
&mut loop_no_stack,
&mut output
);
}
output.push_str(EXIT_SYSCALL);
output
}
fn translate_instruction(instruction: &BrainfuckInstr, total_loops: &mut u32, loop_no_stack: &mut Vec<u32>, output: &mut String) {
use BrainfuckInstr::*;
output.push_str(match instruction {
PointerDec => "dec rsi",
PointerInc => "inc rsi",
DataDec => "dec byte [rsi]",
DataInc => "inc byte [rsi]",
GetByte => "mov rdx,1\nmov rdi,0\nmov rax,0\nsyscall",
PutByte => "mov rdx,1\nmov rdi,1\nmov rax,1\nsyscall",
WhileNonzero => {
*total_loops += 1;
loop_no_stack.push(*total_loops);
let asm = format!(
"cmp byte [rsi],0\nje .end_{x}\n.while_{x}:\n",
x = total_loops
);
output.push_str(&asm);
return },
EndWhile => {
let asm = format!(
"cmp byte [rsi],0\njne .while_{x}\n.end_{x}:\n",
x = loop_no_stack.pop().expect("There should be a loop number on the stack at this point.")
);
output.push_str(&asm);
return },
BrainfuckInstr::PointerSub(x) => {
let asm = format!("sub rsi,{x}\n", x = x);
output.push_str(&asm);
return
},
BrainfuckInstr::PointerAdd(x) => {
let asm = format!("add rsi,{x}\n", x = x);
output.push_str(&asm);
return
},
BrainfuckInstr::DataSub(x) => {
let asm = format!("sub byte [rsi],{x}\n", x = x);
output.push_str(&asm);
return
},
BrainfuckInstr::DataAdd(x) => {
let asm = format!("add byte [rsi],{x}\n", x = x);
output.push_str(&asm);
return
},
BrainfuckInstr::Print(x) => {
let asm = format!("mov rdx,{x}\nmov rdi,1\nmov rax,1\nsyscall\n", x = x);
output.push_str(&asm);
return
}
});
output.push('\n');
}