0

我正在尝试用 Rust 编写一个优化的 Brainfuck 编译器。目前它将标记存储在一个平面向量中,这可以工作,但我无法将其更改为使用语法树:

#[derive(Clone, PartialEq, Eq)]
pub enum Token {
    Output,
    Input,
    Loop(Vec<Token>),
    Move(i32),
    Add(i32, i32),
    LoadOut(i32, i32),
}
use Token::*;

pub fn parse(code: &str) -> Vec<Token> {
    let mut alltokens = Vec::new();
    let mut tokens = &mut alltokens;
    let mut tokvecs: Vec<&mut Vec<Token>> = Vec::new();
    for i in code.chars() {
        match i {
            '+' => tokens.push(Add(0, 1)),
            '-' => tokens.push(Add(0, -1)),
            '>' => tokens.push(Move(1)),
            '<' => tokens.push(Move(-1)),
            '[' => {
                tokens.push(Loop(Vec::new()));
                tokvecs.push(&mut tokens);
                if let &mut Loop(mut newtokens) = tokens.last_mut().unwrap() {
                    tokens = &mut newtokens;
                }
            },
            ']' => {
                tokens = tokvecs.pop().unwrap();
            },
            ',' => tokens.push(Input),
            '.' => {
                tokens.push(LoadOut(0, 0));
                tokens.push(Output);
            }
            _ => (),
        };
    }

    alltokens
}

我无法弄清楚如何处理[命令。代码中的当前实现是我尝试过的几个之一,但都失败了。我认为它可能需要使用 Rust Box,但我不太明白它是如何使用的。

处理命令的分支[可能完全错误,但我不确定应该如何完成。它将包含向量Loop的(枚举的变体)推送到向量。问题是然后在 that 中获得向量的可变借用,这是语句应该做的。TokentokensLoopif let

该代码无法编译,因为newtokens它没有超过块的末尾if let。是否可以获得对内部向量的可变引用Loop并设置tokens为它?如果没有,可以做些什么呢?

4

2 回答 2

1

好的,上次我很接近;看起来我错过了ref关键字:

if let &mut Loop(ref mut newtokens) = (&mut tokens).last_mut().unwrap()

我错过了它,因为到处都有其他借用检查器错误。我决定简化你的代码来解决它们:

pub fn parse(code: &str) -> Vec<Token> {
    let mut tokens = Vec::new();
    for i in code.chars() {
        match i {
            '+' => tokens.push(Add(0, 1)),
            '-' => tokens.push(Add(0, -1)),
            '>' => tokens.push(Move(1)),
            '<' => tokens.push(Move(-1)),
            '[' => {
                tokens.push(Loop(Vec::new()));
                if let &mut Loop(ref mut newtokens) = (&mut tokens).last_mut().unwrap() {
                    let bracket_tokens: &mut Vec<Token> = newtokens;
                }
            },
            ']' => {
                ()
            },
            ',' => tokens.push(Input),
            '.' => {
                tokens.push(LoadOut(0, 0));
                tokens.push(Output);
            }
            _ => unreachable!(),
        };
    }

    tokens
}

我合并了所有令牌变量(您并不真的需要它们)并更改tokens = &mut newtokens;let bracket_tokens: &mut Vec<Token> = newtokens;(我认为这或多或少是您的意图)。这允许您VecLoop.

但是,这段代码仍然有问题,不会解析brainf*ck 的循环;我想让它发挥作用,但它需要对方法进行重大改变。欢迎您尝试进一步扩展此变体,但这可能是一个痛苦的经历,尤其是如果您还不太熟悉借用检查器的规则。

我建议查看其他人的brainf*ck 解释器实现(例如这个)(虽然不是太旧,因为Rust 的语法在1.0 上线之前已经改变)以了解如何做到这一点。

于 2016-09-06T18:45:02.400 回答
0

我已经通过使其成为递归函数来使代码工作:

#[derive(Clone, PartialEq, Eq)]
pub enum Token {
    Output,
    Input,
    Loop(Vec<Token>),
    Move(i32),
    Add(i32, i32),
    LoadOut(i32, i32),
}
use Token::*;

pub fn parse(code: &str) -> Vec<Token> {
    _parse(&mut code.chars())
}

fn _parse(chars: &mut std::str::Chars) -> Vec<Token> {
    let mut tokens = Vec::new();
    while let Some(i) = chars.next() {
        match i {
            '+' => tokens.push(Add(0, 1)),
            '-' => tokens.push(Add(0, -1)),
            '>' => tokens.push(Move(1)),
            '<' => tokens.push(Move(-1)),
            '[' => tokens.push(Loop(_parse(chars))),
            ']' => { break; }
            ',' => tokens.push(Input),
            '.' => {
                tokens.push(LoadOut(0, 0));
                tokens.push(Output);
            }
            _ => (),
        };
    }

    tokens
}

它似乎有效,并且相当简单和优雅(我仍然有兴趣看到不使用递归的解决方案)。

于 2016-09-06T22:06:21.727 回答