我正在尝试在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
(Token
枚举的变体)推送到tokens
向量。问题是然后获得Loop
中if let
语句应该执行的向量的可变借用。
代码无法编译,因为newtokens
不会超过if let
块的结尾。是否有可能获得Loop
内的向量的可变引用,并将tokens
设置为它?如果没有,那可以做些什么呢?
答案 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
}
它似乎工作,并且相当简单和优雅(我仍然有兴趣看到一个不使用递归的解决方案)。