于是结合前面看到的语法,再加上 Google 的帮助,用 Rust 来写一个 Brainfuck 解释器吧~
这一篇与另一篇 post 联动,Brainfuck Interpreter in C++17——A Modern Approach to Kill Your Brain
其实在有了 C++ 写的经历之后,用 Rust 来重写一次,几乎就是熟悉一下 Rust 的基本语法,然后一些小的地方(比如 std::vector, std::stack, std::map 在 Rust 中的等价的类是什么)就靠着 Google 和 Rust 官方文档基本就可以写出来了
另外 Brainfuck 解释器的话,与别的很多东西比起来,似乎没有那么困难,写起来还是蛮快的
当然,有些 C++ 里的就没法依葫芦画瓢放到 Rust 里了,但终归只是一些小的修改~
![](/wp-content/uploads/2019/09/rust-brainfuck-interpreter.webp)
顺便也放一份到 GitHub 上~rust-brainfuck
use std::io::{self, BufReader, BufRead, Read}; use std::collections::HashMap; use std::vec::Vec; /// The `BrainfuckOp` type. enum BrainfuckOp { IncrementValueOp, /// + DecrementValueOp, /// - IncrementPtrOp, /// > DecrementPtrOp, /// < PrintOp, /// . ReadOp, /// , LoopStartOp, /// [ LoopEndOp, /// ] MonoStateOp } /// Brainfuck virtual machine status struct BrainfuckVMStatus { /// virtual infinity length tape tape: HashMap<i32, i32>, /// current cell of the tape tape_ptr: i32, /// used for keeping track of all valid brainfuck_op instruction: Vec<char>, /// current brainfuck_op index instruction_ptr_current: i32, /// keeping track of loops instruction_loop_ptr: Vec<i32>, /// flag of skipping loop, e.g /// +-[[[------------++++++++++-.>>[>]>>>--<<<<<<--]]]++++ /// ^skipping from, but we need all ^end of skipping /// instructions inside. jump_loop: i32 } /// Returns a new brainfuck VM status. /// /// # Example /// /// ``` /// let status = new_brainfuck_status(); /// ``` fn new_brainfuck_status() -> BrainfuckVMStatus { BrainfuckVMStatus { tape: HashMap::new(), tape_ptr: 0, instruction: Vec::new(), instruction_ptr_current: -1, instruction_loop_ptr: Vec::new(), jump_loop: 0 } } /// Returns next corresponding BrainfuckOp of given `character`. /// /// # Arguments /// /// * `status` - A mutable var that holds the status of current brainfuck VM status /// * `character` - char type op (may be invalid brainfuck op) /// * `via_loop` - Due to the way I wrote, a flag is needed to avoid re-adding ops /// /// # Example /// /// ``` /// let status = new_brainfuck_status(); /// let next_op = next_op(&mut status, '+', false); /// ``` fn next_op(status: &mut BrainfuckVMStatus, character: char, via_loop: bool) -> BrainfuckOp { // match BrainfuckOp for character let op = match character { '+' => BrainfuckOp::IncrementValueOp, '-' => BrainfuckOp::DecrementValueOp, '>' => BrainfuckOp::IncrementPtrOp, '<' => BrainfuckOp::DecrementPtrOp, '.' => BrainfuckOp::PrintOp, ',' => BrainfuckOp::ReadOp, '[' => BrainfuckOp::LoopStartOp, ']' => BrainfuckOp::LoopEndOp, // invaild char for brainfuck // monostate is returned _ => BrainfuckOp::MonoStateOp, }; // do not append the char_op if we're retriving the next op inside a loop_op if !via_loop { match op { BrainfuckOp::MonoStateOp => (), _ => { // save char_op to instruction status.instruction.push(character); // increse the ptr of current instruction status.instruction_ptr_current += 1; } }; } // return next op op } /// Run brainfuck VM /// /// # Arguments /// /// * `status` - A mutable var that holds the status of current brainfuck VM status /// * `char_op` - char type op (may be invalid brainfuck op) /// * `via_loop` - Due to the way I wrote, a flag is needed to avoid re-adding ops /// /// # Example /// /// ``` /// let status = new_brainfuck_status(); /// run_vm(&mut status, '+', false); /// ``` fn run_vm(status: &mut BrainfuckVMStatus, char_op: char, via_loop: bool) { // get next op from char_op let op = next_op(status, char_op, via_loop); match op { BrainfuckOp::IncrementValueOp => { // skip actual action if we're skipping loop if status.jump_loop == 0 { let count = status.tape.entry(status.tape_ptr).or_insert(0); *count += 1; } }, BrainfuckOp::DecrementValueOp => { // skip actual action if we're skipping loop if status.jump_loop == 0 { let count = status.tape.entry(status.tape_ptr).or_insert(0); *count -= 1; } }, BrainfuckOp::IncrementPtrOp => { // skip actual action if we're skipping loop if status.jump_loop == 0 { status.tape_ptr += 1; } }, BrainfuckOp::DecrementPtrOp => { // skip actual action if we're skipping loop if status.jump_loop == 0 { status.tape_ptr -= 1; } }, BrainfuckOp::PrintOp => { // skip actual action if we're skipping loop if status.jump_loop == 0 { // take cell from tape let out = *status.tape.entry(status.tape_ptr).or_insert(0); // print as char print!("{}", (out % 255) as u8 as char); } }, BrainfuckOp::ReadOp => { // skip actual action if we're skipping loop if status.jump_loop == 0 { // Rust read a single char from stdin // https://stackoverflow.com/a/30679861 let input: Option<i32> = std::io::stdin() .bytes() .next() .and_then(|result| result.ok()) .map(|byte| byte as i32); // store in tape let out = status.tape.entry(status.tape_ptr).or_insert(0); *out = input.unwrap(); } }, BrainfuckOp::LoopStartOp => { // if and only if 1) `current_cell_value != 0` // 2) and we're not do the skipping // we can record the starting index of the if instruction // besides, if we're in condition 1) // the if statement should be also skipped let cell = status.tape.entry(status.tape_ptr).or_insert(0); if *cell != 0 && status.jump_loop == 0 { status.instruction_loop_ptr.push(status.instruction_ptr_current); } else { status.jump_loop += 1; } }, BrainfuckOp::LoopEndOp => { // decrease the jump_loop value if we encounter the `]` // and we were previously doing the skip if status.jump_loop != 0 { status.jump_loop -= 1; } else { // if we were not in skipping // then we need to check the loop condition, `current_cell_value != 0` let cell = *status.tape.entry(status.tape_ptr).or_insert(0); if cell != 0 { // loop the instruction until condition satisfies no more while *status.tape.entry(status.tape_ptr).or_insert(0) != 0 { // save current instruction pointer let current = status.instruction_ptr_current; // start the loop right after the index of `[` if let Some(last) = status.instruction_loop_ptr.last().cloned() { status.instruction_ptr_current = last + 1; while status.instruction_ptr_current < current { // run one op at a time // until the next op is the corresponding `]` let current_op = status.instruction[status.instruction_ptr_current as usize]; run_vm(status, current_op, true); status.instruction_ptr_current += 1; } // restore the current instruction pointer status.instruction_ptr_current = current; } } // pop current loop starting index status.instruction_loop_ptr.pop(); } } }, BrainfuckOp::MonoStateOp => () } } fn main() -> io::Result<()> { // the brainfuck vm let mut status = new_brainfuck_status(); // read from stdin let buffer = BufReader::new(io::stdin()); for line in buffer.lines() { for c in line?.chars() { // handle every character run_vm(&mut status, c, false); } } Ok(()) }
都是看The Rust Programming Language的,为什么差距那么大?
难得见到神儿说汉语,合影!
哪有这么难?
话说神儿又是什么鬼(╯°□°)╯︵ ┻━┻