首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Brainfuck解释器改进

Brainfuck解释器改进
EN

Code Review用户
提问于 2019-04-03 13:22:39
回答 2查看 67关注 0票数 2

我用javascript创建了一个brainfuck解释器。它对我很好,因为我在我的编译器中没有看到任何bug。我想从您那里得到一个建议和回顾,这个编译器应该改进什么。

下面是简短的代码:

代码语言:javascript
复制
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;

}
EN

回答 2

Code Review用户

发布于 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)
  • 不确定为什么在不抛出相同的错误>时抛出"<“溢出。<应该循环到内存中的最高字节。
  • 对不改变的变量使用"const“。
  • 使用===!==而不是==!=
  • 不要重复空间(每一行都是空的)。它使代码难以阅读,因为它将不适合一个屏幕。
  • 命名相当差。-result可以作为“输出”更好
    • str可能是commandsprogram
    • i,可能是programCounter或者commandIndex。我是个老派,所以会用pc

  • 与其像数组我一样一遍又一遍地对数组进行索引,不如将其存储在变量const command = array[i];中,然后if(command === ">") {使代码更紧凑,更易于跟踪。

设计.

您可以使用查找来获取每个命令所需的操作。(参见示例commandList),而不是一个长if() {}else if()...。这要快得多,因为您不需要跨过else if语句来找到匹配的块。此外,还可以方便地添加或更改命令,更容易阅读。

The Bug

正如我在评论中指出的,有一个错误。您没有正确地实现"[“命令。这是一个大问题,因为许多项目都会失败。对于解决方案是不平凡的。

有两种解决办法。

  1. 当您需要跳过前面搜索匹配关闭。这可能会花费大量的性能。
  2. 编译代码并找到匹配、打开和关闭[],这样您就可以快速找到匹配的位置,并在需要时移动到它。

易用性

作为字符串而不是数组,输入和输出会更好。使高炉更容易使用。

停止问题.

有一个基本的要求你已经错过了。您永远不可能知道您正在运行的程序是否会退出。

由于JavaScript是阻塞的,这意味着如果bf代码进入无限循环,就无法停止执行。你应该防止这个问题。

最简单的方法是限制指令的数量,并在到达时抛出错误。

或者可以让命令在计时器上执行。

示例

函数名为bfVM,用于brainFuckVirtualMachine,需要编译程序。(因为我就是这样修复你的错误的)。

我包括了编译器,它所做的就是找到匹配的块并将它们映射到彼此之间。返回已编译的对象。

和两个测试饲料的程序。

代码语言:javascript
复制
// 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);
票数 1
EN

Code Review用户

发布于 2019-04-03 14:47:03

代码中的一些注释可能会有很大帮助。

在这里,开关语句可能比if/else链更具可读性。

如果可能,将'bf‘函数重命名为更具可读性的函数,对于'str’参数也是如此。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/216779

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档