LoginSignup
3
2

More than 5 years have passed since last update.

Node.jsでPEG.jsでBrainf*ck

Last updated at Posted at 2017-12-09

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()
});
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2