patternjavascriptMinor
Brainfuck interpreter in JavaScript
Viewed 0 times
brainfuckjavascriptinterpreter
Problem
Just what it says on the tin: a brainfuck interpreter in JavaScript.
Just call
Notice the references to
function brainfuck(source) {
var code = source.replace(/[^-+<>.,[\]]/g, '').split(''); // program code
var loop = []; // stack of loops created by bracket operators
var data = []; // array of data cells stored by the program code
var cell = 0; // index in the data array representing one "cell" of data
var next = 0; // index in the code array of the next instruction to run
var operation = {
'>': function () {
if (~loop[0]) {
++cell;
}
},
'<': function () {
if (~loop[0]) {
--cell;
}
},
'+': function () {
if (~loop[0]) {
data[cell] = (data[cell] || 0) + 1;
}
},
'-': function () {
if (~loop[0]) {
data[cell] = (data[cell] || 0) - 1;
}
},
'.': function () {
if (~loop[0]) {
brainfuck.write(data[cell]);
}
},
',': function () {
if (~loop[0]) {
data[cell] = brainfuck.read();
}
},
'[': function () {
loop.unshift(data[cell] ? next : -1);
},
']': function () {
if (~loop[0] && data[cell]) {
next = loop[0];
} else {
loop.shift();
}
}
};
while (next < code.length) {
operation[code[next++]]();
}
if (brainfuck.end) {
brainfuck.end();
}
}Just call
brainfuck(source), where source is some brainfuck source code, to run the interpreter.Notice the references to
brainfuck.read, brainfuck.write and brainfuck.end. Since input and output are heavily dependent on the host environment, it's up to the implementation to provide these. Here's some code for a browser-based implementation, using `consoSolution
It's nice and clean; I like it. I won't even try to figure out if its interpretation is correct, since I don't know Brainfuck, so I'll take your word for that and just look at the JS :)
As you yourself pointed out, the
For instance:
Not super clean by itself, but it'll clean up the operator functions. Anyway, that's low-hanging fruit, and there are more - and no doubt cleaner - ways to solve it.
However, I'd be more concerned about the fact that there's only the single interpreter. I'd seem cleaner to me to:
-
Return an object from
-
And that
I imagine something like:
(if this was for Node-only, it might be fun to base it off of EventEmitter, and use the usual event handling for IO)
Or run the code immediately, but provide the read/write functions as arguments, e.g.:
Point is just to avoid having one single
Ok, ok, you're not going to be running interpreters in parallel or anything, but it just seems cleaner to me to let each run have clean slate.
But really, I can't find much to comment on. It's pretty clean as it is.
As you yourself pointed out, the
[ and ] operators might be handled differently, which would also allow you avoid the if(~loop[0]), since that's used in all the other operators.For instance:
switch(code[next++]) {
case '[':
loop.unshift(data[cell] ? next : -1);
break;
case ']':
if (~loop[0] && data[cell]) {
next = loop[0];
} else {
loop.shift();
}
break;
default:
if(~loop[0]) operations[op]();
}Not super clean by itself, but it'll clean up the operator functions. Anyway, that's low-hanging fruit, and there are more - and no doubt cleaner - ways to solve it.
However, I'd be more concerned about the fact that there's only the single interpreter. I'd seem cleaner to me to:
-
Return an object from
brainfuck (it doesn't have to be a construtor function; returning an object literal would work fine) that allows you to attach the read, write and end functions to just that instance. This would of course also necessitate a run function or something, to kick off the interpretation.-
And that
read, write and end are stubbed out/have default implementations, so I don't have to supply 'em, if I don't care. E.g. maybe I just want to see if the code runs but the output can go to /dev/null for all I care (or, in this case, a no-op function).I imagine something like:
var interpreter = brainfuck(source);
interpreter.read = ...
interpreter.write = ...
interpreter.run();(if this was for Node-only, it might be fun to base it off of EventEmitter, and use the usual event handling for IO)
Or run the code immediately, but provide the read/write functions as arguments, e.g.:
brainfuck(source, reader, writer [, end]).Point is just to avoid having one single
brainfuck function that will operate on any source code you pass, but has a single, "global" set of I/O functions.Ok, ok, you're not going to be running interpreters in parallel or anything, but it just seems cleaner to me to let each run have clean slate.
But really, I can't find much to comment on. It's pretty clean as it is.
Code Snippets
switch(code[next++]) {
case '[':
loop.unshift(data[cell] ? next : -1);
break;
case ']':
if (~loop[0] && data[cell]) {
next = loop[0];
} else {
loop.shift();
}
break;
default:
if(~loop[0]) operations[op]();
}var interpreter = brainfuck(source);
interpreter.read = ...
interpreter.write = ...
interpreter.run();Context
StackExchange Code Review Q#58086, answer score: 6
Revisions (0)
No revisions yet.