Brainfuck interpreter in JavaScript, take 2
I think I said I'd refrain from reviewing, since I reviewed the first take, but I can't help myself.

Again: I like it! It's very neat and tidy. I really can't fault it.


It's well-formatted
It's readable
It's efficient


Hell, it even taught me Brainfuck! Kudos!

If I were to be super nit-picky (and I'll have to be, to find something to write), it's that it'd be nice to use plural form for loop and operation (i.e. loops and operations). I know, I know, loops will ruin the nice 4-letter-aligned formatting that going on, but still.

next should perhaps be called current or similar, since that's what it actually is.

I guess you could skip the split(''), and just loop through the string, but either way works.

I might also prefer a stricter typeof brainfuck.end === 'function' check at the, uh, end, but that's just me being pedantic. OTOH, you could just stub it to a no-op function, and not have a check.

It might be nice to initialize data to [0], and have &lt; and &gt; initialize blank cells instead of doing it lazily in the +/- operations. It'd be a little more correct, and you'd avoid a program like . spitting out undefined instead of 0. You can do it in one (admittedly tricky) assignment:

'&gt;': function () { data[++cell] = data[cell] || 0; }


Lastly, it might be nice to check input from read, since you could basically return whatever you want from your own read function. Worth it? Nah, not really.

I do have some other ideas, though. Less review, and more "what if...".

findJumps (formerly findLoops)

We talked about doing syntax-checking in the comments (and you came up with a nice implementation), so I'll skip that here. Instead, this is just an alternative to the code in the question.

function findJumps(code) {
  var jumps = [],
      stack = [],
      character, i, j;

  for(i = 0 ; character = code[i] ; i++) {
    if(character === '[') {
      stack.push(i);
    } else if(character === ']') {
      j = stack.pop();
      jumps[i] = j;
      jumps[j] = i;
    }
  }

  return jumps;
}


Instead of returning an array of objects, it's enough to just return an array of indices. It doesn't really matter what's start and end.

You could then change the [ and ] instructions to

'[': function () { data[cell] || (next = jumps[next]); },
']': function () { data[cell] &amp;&amp; (next = jumps[next]); }


which seems nicely "symmetrical" :)

switch

Yeah, yeah, using a switch statement is boring. Still, it has some (minor) maybe-benefits:


Illegal characters can be absorbed by the default: case (or no case at all) - no need to sanitize the code string first (though depending on how loop-heavy the code is, it may be more efficient to sanitize it anyway).
It avoids declaring new operation functions on each call to run, and the calling overhead of those functions (though the impact of those things is in all likelihood zero; it's all optimized away).


Looks ok, I think (works too)

function run(code) {
  var jumps = findJumps(code),
      data = [],
      cell = 0,
      instruction, curr;

  for(curr = 0 ; instruction = code[curr] ; curr++) {
    switch(instruction) {
      case '&gt;': ++cell;                              break;
      case '&lt;': --cell;                              break;
      case '+': data[cell] = (data[cell] || 0) + 1;  break;
      case '-': data[cell] = (data[cell] || 0) - 1;  break;
      case '[': data[cell] || (curr = jumps[curr]);  break;
      case ']': data[cell] &amp;&amp; (curr = jumps[curr]);  break;
      case ',': data[cell] = this.read();            break;
      case '.': this.write(data[cell]);              break;
    }
  }

  this.end();
}


The above assumes that this.end is (at least) stubbed. I've left out the cell initialization, but it works fine with that too.

Anyway, this is just me having some fun with it. Again, I can't really fault your code.

Now, how about tackling Befunge? Or maybe just Huh?