brainfuck_rs 2.0.0

Brainfuck parser and interpreter using nom
Documentation
use crate::ast::{Block, Node};

use std::io::{self, Read};

#[derive(Debug, PartialEq)]
pub struct Context {
    lbuf:  Vec<i8>,
    rbuf:  Vec<i8>,
    index: i64,
}

impl Context {
    pub fn new() -> Context {
        Context {
            lbuf:  Vec::new(),
            rbuf:  vec![0],
            index: 0,
        }
    }

    #[cfg(test)]
    pub fn new_with_data(lbuf: Vec<i8>, rbuf: Vec<i8>, index: i64) -> Context {
        Context {
            lbuf:  lbuf,
            rbuf:  rbuf,
            index: index,
        }
    }

    fn get(&self) -> Option<&i8> {
        if self.index < 0 {
            self.lbuf.get((-self.index - 1) as usize)
        } else {
            self.rbuf.get(self.index as usize)
        }
    }

    fn get_mut(&mut self) -> Option<&mut i8> {
        if self.index < 0 {
            self.lbuf.get_mut((-self.index - 1) as usize)
        } else {
            self.rbuf.get_mut(self.index as usize)
        }
    }

    fn loop_cond(&self) -> bool {
        self.get().map(|e| *e != 0).unwrap_or(false)
    }

    fn run_node(&mut self, node: &Node) {
        match *node {
            Node::LShift => {
                self.index -= 1;
                if self.index < 0 {
                    while self.lbuf.len() <= ((-self.index - 1) as usize) {
                        self.lbuf.push(0);
                    }
                }
            },
            Node::RShift => {
                self.index += 1;
                if self.index >= 0 {
                    while self.rbuf.len() <= (self.index as usize) {
                        self.rbuf.push(0);
                    }
                }
            },
            Node::Inc => {
                if let Some(elem) = self.get_mut() {
                    *elem += 1;
                }
            },
            Node::Dec => {
                if let Some(elem) = self.get_mut() {
                    *elem -= 1;
                }
            },
            Node::PutCh => {
                if let Some(elem) = self.get_mut() {
                    print!("{}", (*elem as u8) as char);
                }
            },
            Node::GetCh => {
                let mut buffer = [0;1];
                io::stdin().read_exact(&mut buffer).expect("Failed to read from stdin");
                if let Some(elem) = self.get_mut() {
                    *elem = buffer[0] as i8;
                }
            },
            Node::Loop(ref block) => {
                while self.loop_cond() {
                    self.run(block);
                }
            },
        }
    }

    pub fn run(&mut self, block: &Block) {
        for node in block.into_iter() {
            self.run_node(node);
        }
    }
}

impl Default for Context {
    fn default() -> Self {
        Context::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::ast::Node;

    #[test]
    fn test_lshift() {
        let mut ctx = Context::new();
        ctx.run_node(&Node::LShift);
        assert_eq!(ctx, Context::new_with_data(vec![0], vec![0], -1));
    }

    #[test]
    fn test_rshift() {
        let mut ctx = Context::new();
        ctx.run_node(&Node::RShift);
        assert_eq!(ctx, Context::new_with_data(Vec::new(), vec![0, 0], 1));
    }

    #[test]
    fn test_inc() {
        let mut ctx = Context::new();
        ctx.run_node(&Node::Inc);
        assert_eq!(ctx, Context::new_with_data(Vec::new(), vec![1], 0));
    }

    #[test]
    fn test_dec() {
        let mut ctx = Context::new();
        ctx.run_node(&Node::Dec);
        assert_eq!(ctx, Context::new_with_data(Vec::new(), vec![-1], 0));
    }

    #[test]
    fn test_putch() {
        let mut ctx = Context::new();
        ctx.run_node(&Node::PutCh);
        assert_eq!(ctx, Context::new());
    }

    #[test]
    fn test_block() {
        let mut ctx = Context::new();
        ctx.run(&Block::default());
        ctx.run(&From::from(vec![Node::Inc, Node::RShift, Node::Inc, Node::LShift, Node::LShift, Node::LShift, Node::Dec]));
        assert_eq!(ctx, Context::new_with_data(vec![0, -1], vec![1, 1], -2));
    }

    #[test]
    fn test_loop() {
        let mut ctx = Context::new();
        ctx.run_node(&Node::Inc);
        ctx.run_node(&Node::Inc);
        ctx.run_node(&Node::Loop(From::from(vec![Node::Dec, Node::RShift, Node::Inc, Node::LShift])));
        assert_eq!(ctx, Context::new_with_data(Vec::new(), vec![0, 2], 0));
    }

    #[test]
    fn test_left_loop() {
        let mut ctx = Context::default();
        ctx.run_node(&Node::LShift);
        ctx.run_node(&Node::Inc);
        ctx.run_node(&Node::Inc);
        ctx.run_node(&Node::Loop(From::from(vec![Node::Dec, Node::LShift, Node::Inc, Node::RShift])));
        assert_eq!(ctx, Context::new_with_data(vec![0, 2], vec![0], -1));
    }
}