"use strict"; function lookForMatchingBracket(program, pc, level) { if (pc >= program.length) throw "Error: Unbalanced brackets in the BF program, too many opening brackets"; switch(program[pc]) { case '[': return lookForMatchingBracket(program, pc+1, level+1); case ']': if (level == 1) return pc; return lookForMatchingBracket(program, pc+1, level-1); default: return lookForMatchingBracket(program, pc+1, level); } } // (leftTape, tapeCursor, rightTape) form a zipper: // leftTape is the (infinite) list of all values to the left of the cursor (from right to left), // while rightTape is the (infinite) list of all values to the right of the cursor (from left to right). // These lists are represented as functions that return an object with the first value, and a function for the rest of the list. function evalRec(program, pc, input, output, leftTape, tapeCursor, rightTape, loopContinuation) { if (pc >= program.length) return output; switch(program[pc]) { case '.': const newOutput = output.concat(String.fromCharCode(tapeCursor)); return evalRec(program, pc+1, input, newOutput, leftTape, tapeCursor, rightTape, loopContinuation); case ',': return evalRec(program, pc+1, input.slice(1), output, leftTape, input.charCodeAt(0), rightTape, loopContinuation); case '+': return evalRec(program, pc+1, input, output, leftTape, tapeCursor+1, rightTape, loopContinuation); case '-': return evalRec(program, pc+1, input, output, leftTape, tapeCursor-1, rightTape, loopContinuation); case '>': const evaluatedRightTape = rightTape(); return evalRec(program, pc+1, input, output, ()=>({cursor: tapeCursor, rest: leftTape}), evaluatedRightTape.cursor, evaluatedRightTape.rest, loopContinuation); case '<': const evaluatedLeftTape = leftTape(); return evalRec(program, pc+1, input, output, evaluatedLeftTape.rest, evaluatedLeftTape.cursor, ()=>({cursor: tapeCursor, rest: rightTape}), loopContinuation); case '[': const matchingPC = lookForMatchingBracket(program, pc, 0); if (tapeCursor == 0) return evalRec(program, matchingPC+1, input, output, leftTape, tapeCursor, rightTape, loopContinuation); return evalRec(program, pc+1, input, output, leftTape, tapeCursor, rightTape, (...varargs) => evalRec(program, pc, ...varargs, loopContinuation)); case ']': return loopContinuation(input, output, leftTape, tapeCursor, rightTape); default: throw "Unsupported character: " + program[pc] + " at pc " + pc; } } function infiniteTape() { return {cursor: 0, rest: infiniteTape}; } function evalShort(program, input) { return evalRec(program, 0, input, "", infiniteTape, 0, infiniteTape, ()=>{throw "Error: Unbalanced brackets in the BF program, too many closing brackets";}); } function test(program, input, expectedOutput) { const result = evalShort(program, input); if (result != expectedOutput) throw ("Wrong result, program: " + program + " on input " + input + " had output " + result + " instead of " + expectedOutput); } for (var i = 0; i < 50000; ++i) { test(",...", "A", "AAA"); test(",..+..--.", "C", "CCDDB"); test(",<,>..<..", "EF", "EEFF"); // The following program is taken from the Wikipedia brainfuck page: test("++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.", "", "Hello World!\n") }