我用javascript创建了一个brainfuck解释器。它对我很好,因为我在我的编译器中没有看到任何bug。我想从您那里得到一个建议和回顾,这个编译器应该改进什么。
下面是简短的代码:
function bf(str, input){
var array = str.split("");
var memory = [0];
var pointer = 0;
var result = [];
var open = [];
for(var i = 0; i < array.length; i++){
if(array[i] == ">"){
pointer++;
if(memory.length-1 < pointer){
memory.push(0);
}
}else if(array[i] == "<"){
if(pointer > 0){
pointer--;
}else{
throw "Overflow Error at " + i
}
}else if(array[i] == "+"){
if(memory[pointer] < 255){
memory[pointer]++;
}else{
memory[pointer] = 0;
}
}else if(array[i] == "-"){
if(memory[pointer] > 0){
memory[pointer]--;
}else{
memory[pointer] = 255;
}
}else if(array[i] == "."){
result.push(String.fromCharCode(memory[pointer]));
}else if(array[i] == ","){
memory[pointer] = input.shift().charCodeAt(0);
}else if(array[i] == "["){
if(open.length){
if(open[open.length-1] != i){
open.push(i);
}
}else{
open.push(i);
}
}else if(array[i] == "]"){
if(open.length){
if(memory[pointer]){
i = open[open.length - 1];
}else{
open.pop();
}
}
}
}
return result;
}发布于 2019-04-03 18:17:56
str拆分成一个array,您可以使用括号符号直接访问字符。eg str[i] === ">"%进行循环。示例添加1 memory[pointer] = (memory[pointer] + 1) % 255并减去memory[pointer] = (memory[pointer] + 254) % 255。但是,由于您希望将内存模拟为字节,您还可以使用一个具有无符号Char的类型化数组。数组中的字节的行为与运行BF命令的VM上的字节相同。示例const memory = new Uint8Array(1024)>时抛出"<“溢出。<应该循环到内存中的最高字节。===和!==而不是==和!=result可以作为“输出”更好str可能是commands或programi,可能是programCounter或者commandIndex。我是个老派,所以会用pcconst command = array[i];中,然后if(command === ">") {使代码更紧凑,更易于跟踪。您可以使用查找来获取每个命令所需的操作。(参见示例commandList),而不是一个长if() {}else if()...。这要快得多,因为您不需要跨过else if语句来找到匹配的块。此外,还可以方便地添加或更改命令,更容易阅读。
正如我在评论中指出的,有一个错误。您没有正确地实现"[“命令。这是一个大问题,因为许多项目都会失败。对于解决方案是不平凡的。
有两种解决办法。
[和],这样您就可以快速找到匹配的位置,并在需要时移动到它。作为字符串而不是数组,输入和输出会更好。使高炉更容易使用。
有一个基本的要求你已经错过了。您永远不可能知道您正在运行的程序是否会退出。
由于JavaScript是阻塞的,这意味着如果bf代码进入无限循环,就无法停止执行。你应该防止这个问题。
最简单的方法是限制指令的数量,并在到达时抛出错误。
或者可以让命令在计时器上执行。
函数名为bfVM,用于brainFuckVirtualMachine,需要编译程序。(因为我就是这样修复你的错误的)。
我包括了编译器,它所做的就是找到匹配的块并将它们映射到彼此之间。返回已编译的对象。
和两个测试饲料的程序。
// Run two comnpiled BF programes.
setTimeout(() => {
console.log(bfVM(helloWorld))
console.log(bfVM(addNums))
},0);
/* Old school naming pc is program counter, ptr is a pointer, and
inPtr is input pointer */
function bfVM(compiled, input) {
const program = compiled.program;
const blocks = compiled.blocks;
const RAM = compiled.RAM;
const memory = new Uint8Array(RAM);
const commandList = {
">"(){ ptr = (ptr + 1) % RAM },
"<"(){ ptr = (ptr + RAM - 1) % RAM },
"+"(){ memory[ptr]++ },
"-"(){ memory[ptr]-- },
"."(){ output += String.fromCharCode(memory[ptr]) },
","(){ memory[ptr] = input.charCodeAt(inPtr++) },
"["(){ pc = !memory[ptr] ? blocks.get(pc)[0] : pc },
"]"(){ pc = memory[ptr] ? blocks.get(pc)[0] : pc },
};
var cycles = 0, ptr = 0, pc = 0, inPtr = 0, output = "";
while (pc < program.length && cycles++ < MAX_CYCLES) {
const cmd = commandList[program[pc]];
cmd && cmd();
pc ++;
}
if (cycles=== MAX_CYCLES) { throw new Error("Cycle limit reached at PC: " + pc) }
return output;
}
const MAX_CYCLES = 1024 * 1024; // Limit of execution steps.
function compileBF(program, RAM) {
var pc = 0;
const blocks = new Map();
const blockStack = [];
while (pc < program.length) {
if (program[pc] === "[") {
blocks.set(pc, [])
blockStack.push(pc);
} else if(program[pc] === "]") {
const open = blockStack.pop();
const block = blocks.get(open);
if (block === undefined) {
throw new Error("Syntax error: No matching block for ']' at " + pc);
}
block[0] = pc;
blocks.set(pc, [open]);
}
pc++;
}
if (blockStack.length) {
throw new Error("Syntax error: Block missmatch at " + pc);
}
return {program, blocks, RAM};
}
const nums = ["", "+", "++", "+++", "++++", "+++++", "++++++", "+++++++", "++++++++", "+++++++++"]
const toASCIINum = "++++++++[<++++++>-]<"
const helloWorld = compileBF(("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."), 14)
const addNums = compileBF(nums[5]+">"+nums[4]+"[<+>-]"+toASCIINum+".",2);发布于 2019-04-03 14:47:03
代码中的一些注释可能会有很大帮助。
在这里,开关语句可能比if/else链更具可读性。
如果可能,将'bf‘函数重命名为更具可读性的函数,对于'str’参数也是如此。
https://codereview.stackexchange.com/questions/216779
复制相似问题