brainfuck-macro-rs / src / lib.rs

#![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);
    }
}