JavaScript
Node
pegjs
brainfuck
AsyncAwait

Node.jsでのBrainf*ckインタプリタ実装。PEG.js使用。
PEG.jsの勉強に作ってみました。PEG.js便利です。JSに馴染むぞぉ〜。

BF処理系として、工夫といえば工夫なのは、ループブロック構文[と]の対応を、実行時に命令列からスキャンするのではなく、パース/AST構築の段階でシンタックスの問題として解決するところ。まあ、普通に考えればそうなるべきな話ではあるのですが。

苦労したのは、1文字入力で、標準ライブラリを呼べばいいのかなと思ったら、見付けられなかったので作りました(別記事)。

require('./parser')しているところは、PEG.js onlineで以下から生成させたパーサをparser.jsというファイル名でダウンロードし、同じディレクトリに保存してください。

code = (normal_insn / block_insn / otherchar) *

normal_insn = ch:[><+-.,] { return ch }
block_insn = '[' brk:block ']' { return brk  }

block = cod:code {
  return cod;
}

otherchar = [^><+-.,\[\]] {
  return undefined
}

実行例:

bfjs.mov.gif

以下、main.js

const readline = require('readline');
const rl = readline.createInterface({input:process.stdin});
let buf = "";

function getchar() {
  if (buf.length == 0) {
    if (process.stdin.AtEndOfStream) {
      return Promise.resolve(null); // EOF
    }
    return new Promise((resolve, reject) => {
      rl.once('line', (line) => {
        buf = buf + line + "\n";
        resolve(getchar());
      });
    });
  } else {
    const result = buf.charCodeAt(0);
    buf = buf.substring(1);
    return Promise.resolve(result);
  }
}

/*
  code = (normal_insn / block_insn / otherchar) *

  normal_insn = ch:[><+-.,] { return ch }
  block_insn = '[' brk:block ']' { return brk  }

  block = cod:code {
  return cod;
  }

  otherchar = [^><+-.,\[\]] {
  return undefined
  }
*/
// generated with PEG.js from above peg syntax.
const parser = require("./parser.js"); 


const source = `
>>++++++++[->++++++++<]>>>>+++++++++[->++++++++++<]>[<<,[->+<<+<<+>>>]<<<[
->>>+<<<]>>>>>[->+>>+<<<]>[<<[->+>>+<<<]>>>[-<<<+>>>]<<[[-]<->]>-]>>[-<<<+
>>>]<<<<<<<[-<+<<+>>>]<[>>[-<+<<+>>>]<<<[->>>+<<<]>>[[-]>-<]<-]<<[->>>+<<<
]>>>>><[[-]>++++++++++++++++++++++++++++++++>[[-]<------------------------
-------->]<<]>>[-]<.>>]`;

const ast = parser.parse(source);

const memSize = 20;
let ptr = 0;
const memory = Array(memSize).fill(0);

async function run(code, prefix) {
  // console.log("run code=",code,'mem=',memory,'ptr=',ptr);
  for (const i of code) {
    // console.log("  ins=",i);
    if (Array.isArray(i)) {
      while (memory[ptr] !== 0) {
        await run(i, prefix+" ");
      }
    }
    else if (i === '>'){
      ptr += 1;
    }
    else if (i === '<'){
      ptr -= 1;
    }
    else if (i === '+'){
      memory[ptr] += 1;
    }
    else if (i === '-'){
      memory[ptr] -= 1;
    }
    else if (i === '.'){
      process.stdout.write(String.fromCharCode(memory[ptr]));
    }
    else if (i === ','){
      memory[ptr] = await getchar();
    }
  }
}

run(ast, "").then(()=>{
  console.log(memory);
  process.exit()
});