Last active
August 30, 2019 12:17
-
-
Save Tosainu/367e35aed49a2590d4f78fbca9e805c9 to your computer and use it in GitHub Desktop.
Brainf**k compiler (bf -> LLVM IR) and interpreter.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::VecDeque; | |
use std::io::Read; | |
struct Emitter { | |
variable_idx: u32, | |
label_idx: u32, | |
loop_stack: VecDeque<u32>, | |
} | |
impl Emitter { | |
fn new() -> Emitter { | |
Emitter { | |
variable_idx: 1, | |
label_idx: 1, | |
loop_stack: VecDeque::new(), | |
} | |
} | |
fn emit_move_ptr(&mut self, n: i32) { | |
println!( | |
r#" | |
; emit_move_ptr({2}) | |
%{0} = load i32*, i32** %ptr, align 8 | |
%{1} = getelementptr inbounds i32, i32* %{0}, i32 {2} | |
store i32* %{1}, i32** %ptr, align 8"#, | |
self.variable_idx, | |
self.variable_idx + 1, | |
n | |
); | |
self.variable_idx += 2; | |
} | |
fn emit_add(&mut self, n: i32) { | |
println!( | |
r#" | |
; emit_add({3}) | |
%{0} = load i32*, i32** %ptr, align 8 | |
%{1} = load i32, i32* %{0}, align 4 | |
%{2} = add nsw i32 %{1}, {3} | |
store i32 %{2}, i32* %{0}, align 4"#, | |
self.variable_idx, | |
self.variable_idx + 1, | |
self.variable_idx + 2, | |
n | |
); | |
self.variable_idx += 3; | |
} | |
fn emit_putchar(&mut self) { | |
println!( | |
r#" | |
; emit_putchar() | |
%{0} = load i32*, i32** %ptr, align 8 | |
%{1} = load i32, i32* %{0}, align 4 | |
%{2} = call i32 @putchar(i32 %{1})"#, | |
self.variable_idx, | |
self.variable_idx + 1, | |
self.variable_idx + 2, | |
); | |
self.variable_idx += 3; | |
} | |
fn emit_getchar(&mut self) { | |
println!( | |
r#" | |
; emit_getchar() | |
%{0} = load i32*, i32** %ptr, align 8 | |
%{1} = call i32 @getchar() | |
store i32 %{1}, i32* %{0}, align 4"#, | |
self.variable_idx, | |
self.variable_idx + 1, | |
); | |
self.variable_idx += 2; | |
} | |
fn emit_loop_begin(&mut self) { | |
println!( | |
r#" | |
; emit_loop_begin() | |
br label %loop{3}_cond | |
loop{3}_cond: | |
%{0} = load i32*, i32** %ptr, align 8 | |
%{1} = load i32, i32* %{0}, align 4 | |
%{2} = icmp ne i32 %{1}, 0 | |
br i1 %{2}, label %loop{3}_body, label %loop{3}_end | |
loop{3}_body:"#, | |
self.variable_idx, | |
self.variable_idx + 1, | |
self.variable_idx + 2, | |
self.label_idx | |
); | |
self.loop_stack.push_back(self.label_idx); | |
self.variable_idx += 3; | |
self.label_idx += 1; | |
} | |
fn emit_loop_end(&mut self) { | |
if let Some(n) = self.loop_stack.pop_back() { | |
println!( | |
r#" | |
; emit_loop_end() | |
br label %loop{0}_cond | |
loop{0}_end:"#, | |
n | |
); | |
} | |
} | |
fn emit_header(&self) { | |
println!("; emit_header()"); | |
println!("define i32 @main() {{"); | |
println!(" %heap_i8 = call i8* @calloc(i64 30000, i64 4)"); | |
println!(" %heap_i32 = bitcast i8* %heap_i8 to i32*"); | |
println!(" %heap = alloca i32*, align 8"); | |
println!(" %ptr = alloca i32*, align 8"); | |
println!(" store i32* %heap_i32, i32** %heap, align 8"); | |
println!(" store i32* %heap_i32, i32** %ptr, align 8"); | |
} | |
fn emit_footer(&self) { | |
println!(); | |
println!(" ; emit_footer()"); | |
println!(" %{} = load i32*, i32** %heap, align 8", self.variable_idx); | |
println!( | |
" %{} = bitcast i32* %{} to i8*", | |
self.variable_idx + 1, | |
self.variable_idx | |
); | |
println!(" call void @free(i8* %{})", self.variable_idx + 1); | |
println!(" ret i32 0"); | |
println!("}}"); | |
println!("declare i8* @calloc(i64, i64)"); | |
println!("declare void @free(i8*)"); | |
println!("declare i32 @getchar()"); | |
println!("declare i32 @putchar(i32)"); | |
} | |
} | |
fn main() -> std::io::Result<()> { | |
let mut src = Vec::new(); | |
std::io::stdin().read_to_end(&mut src)?; | |
let mut e = Emitter::new(); | |
e.emit_header(); | |
for c in src.iter() { | |
match c { | |
b'>' => e.emit_move_ptr(1), | |
b'<' => e.emit_move_ptr(-1), | |
b'+' => e.emit_add(1), | |
b'-' => e.emit_add(-1), | |
b'.' => e.emit_putchar(), | |
b',' => e.emit_getchar(), | |
b'[' => e.emit_loop_begin(), | |
b']' => e.emit_loop_end(), | |
_ => (), | |
} | |
} | |
e.emit_footer(); | |
Ok(()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::VecDeque; | |
use std::fs::File; | |
use std::io::{Read, Write}; | |
fn getchar() -> u8 { | |
std::io::stdin() | |
.bytes() | |
.next() | |
.and_then(|r| r.ok()) | |
.unwrap() | |
} | |
fn putchar(c: u8) { | |
std::io::stdout().write_all(&[c]).unwrap(); | |
} | |
fn main() { | |
let src = std::env::args().nth(1).and_then(|filename| { | |
let mut file = File::open(filename).ok()?; | |
let mut buf = Vec::new(); | |
file.read_to_end(&mut buf).ok()?; | |
Some(buf) | |
}).unwrap_or( | |
b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".to_vec() | |
); | |
let mut stack = VecDeque::new(); | |
let mut heap = vec![0u32; 30000]; | |
let mut ptr = 0; | |
let mut pc = 0; | |
let mut eval = true; | |
while pc < src.len() { | |
let mut pc_next = None; | |
match (eval, src[pc]) { | |
(true, b'>') => ptr += 1, | |
(true, b'<') => ptr -= 1, | |
(true, b'+') => heap[ptr] += 1, | |
(true, b'-') => heap[ptr] -= 1, | |
(true, b'.') => putchar(heap[ptr] as u8), | |
(true, b',') => heap[ptr] = u32::from(getchar()), | |
(_, b'[') => { | |
stack.push_back((eval, pc)); | |
eval = eval && heap[ptr] > 0; | |
} | |
(_, b']') => { | |
if let Some((e, p)) = stack.pop_back() { | |
if eval { | |
pc_next = Some(p); | |
} else { | |
eval = e; | |
} | |
} | |
} | |
_ => (), | |
} | |
pc = pc_next.unwrap_or(pc + 1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment