#![allow(unused_mut)]
#[derive(Debug)]
pub struct Machine {
pos: usize,
mem: Vec<u8>,
}
fn putc(c: u8) {
print!("{}", c as char);
}
fn getc() -> u8 {
0 // TODO
}
impl Machine {
pub fn new(n: usize) -> Machine { Machine { pos: 0, mem: vec![0; n] } }
pub fn r(&mut self) { /* > */ self.pos = self.pos.checked_add(1).expect("Brainfuck out of memory"); }
pub fn l(&mut self) { /* < */ self.pos = self.pos.checked_sub(1).expect("Brainfuck underflow"); }
pub fn i(&mut self) { /* + */ let x = &mut self.mem[self.pos]; *x = x.wrapping_add(1); }
pub fn d(&mut self) { /* - */ let x = &mut self.mem[self.pos]; *x = x.wrapping_sub(1); }
pub fn p(&mut self) { /* . */ putc(self.mem[self.pos]); }
pub fn g(&mut self) { /* , */ self.mem[self.pos] = getc(); }
pub fn nz(&self) -> bool { self.mem[self.pos] > 0 }
}
#[macro_export]
macro_rules! _bf_insn {
( $m:expr; ) => { };
( $m:expr; > $($t:tt)* ) => { $m.r(); _bf_insn!($m; $($t)*); };
( $m:expr; >> $($t:tt)* ) => { $m.r(); $m.r(); _bf_insn!($m; $($t)*); };
( $m:expr; < $($t:tt)* ) => { $m.l(); _bf_insn!($m; $($t)*); };
( $m:expr; << $($t:tt)* ) => { $m.l(); $m.l(); _bf_insn!($m; $($t)*); };
( $m:expr; + $($t:tt)* ) => { $m.i(); _bf_insn!($m; $($t)*); };
( $m:expr; - $($t:tt)* ) => { $m.d(); _bf_insn!($m; $($t)*); };
( $m:expr; <- $($t:tt)* ) => { $m.l(); $m.d(); _bf_insn!($m; $($t)*); };
( $m:expr; -> $($t:tt)* ) => { $m.d(); $m.r(); _bf_insn!($m; $($t)*); };
( $m:expr; . $($t:tt)* ) => { $m.p(); _bf_insn!($m; $($t)*); };
( $m:expr; .. $($t:tt)* ) => { $m.p(); $m.p(); _bf_insn!($m; $($t)*); };
( $m:expr; ... $($t:tt)* ) => { $m.p(); $m.p(); $m.p(); _bf_insn!($m; $($t)*); };
( $m:expr; , $($t:tt)* ) => { $m.g(); _bf_insn!($m; $($t)*); };
( $m:expr; [ ] $($t:tt)* ) => { while $m.nz() { } _bf_insn!($m; $($t)*); };
( $m:expr; [ $($h:tt)* ] $($t:tt)* ) => { while $m.nz() { _bf_insn!($m; $($h)*); } _bf_insn!($m; $($t)*); };
}
#[macro_export]
macro_rules! brainfuck {
( $e:expr; $($code:tt)+ ) => {
{
let mut m = $crate::Machine::new($e);
_bf_insn!(m; $($code)*);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn weird_tokens() {
brainfuck!(2; ..);
brainfuck!(2; ...);
brainfuck!(2; ....);
brainfuck!(2; > <-);
brainfuck!(2; ->);
brainfuck!(2; > <--);
brainfuck!(2; -->);
brainfuck!(2; --);
brainfuck!(2; ++);
brainfuck!(2; ,,);
brainfuck!(2; ><);
brainfuck!(2; > <>);
brainfuck!(2; []);
brainfuck!(2; [.]);
}
#[test]
fn hello_with_macro() {
brainfuck!(10;
++++++++
[>++++ [>++>+++>+++>+<<<<-] >+>+>->>+ [<] <-]
>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
);
}
#[test]
fn hello_without_macro() {
let mut m = Machine::new(10);
m.i(); m.i(); m.i(); m.i(); m.i(); m.i(); m.i(); m.i();
while m.nz() {
m.r(); m.i(); m.i(); m.i(); m.i();
while m.nz() {
m.r(); m.i(); m.i();
m.r(); m.i(); m.i(); m.i();
m.r(); m.i(); m.i(); m.i();
m.r(); m.i();
m.l(); m.l(); m.l(); m.l(); m.d();
}
m.r(); m.i();
m.r(); m.i();
m.r(); m.d();
m.r(); m.r(); m.i();
while m.nz() { m.l(); }
m.l(); m.d();
}
m.r(); m.r(); m.p();
m.r(); m.d(); m.d(); m.d(); m.p();
m.i(); m.i(); m.i(); m.i(); m.i(); m.i(); m.i(); m.p();
m.p();
m.i(); m.i(); m.i(); m.p();
m.r(); m.r(); m.p();
m.l(); m.d(); m.p();
m.l(); m.p();
m.i(); m.i(); m.i(); m.p();
m.d(); m.d(); m.d(); m.d(); m.d(); m.d(); m.p();
m.d(); m.d(); m.d(); m.d(); m.d(); m.d(); m.d(); m.d(); m.p();
m.r(); m.r(); m.i(); m.p();
m.r(); m.i(); m.i(); m.p();
println!("{:?}", m);
}
}