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
}
実行例:
以下、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()
});