在Rust

时间:2016-09-06 16:24:06

标签: parsing rust brainfuck

我正在尝试在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,但我不太清楚如何使用它。

处理[命令的分支可能完全错误,但我不确定应该怎么做。它将包含向量的LoopToken枚举的变体)推送到tokens向量。问题是然后获得Loopif let语句应该执行的向量的可变借用。

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

2 个答案:

答案 0 :(得分: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;(我认为这或多或少是你的意图)。这允许您操纵Vec内的Loop

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

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

答案 1 :(得分: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
}

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