Skip to content

Instantly share code, notes, and snippets.

@hillerstorm
Forked from 140bytes/LICENSE.txt
Last active December 23, 2015 10:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hillerstorm/6622750 to your computer and use it in GitHub Desktop.
Save hillerstorm/6622750 to your computer and use it in GitHub Desktop.
Brainfuck interpreter

Brainfuck interpreter

Fully featured with loops, input, output and comments.

139bytes!

Chrome and Firefox only

(
a, // brainfuck input and later used as brainfuck "register"
b, // function to use for user input and output
// if any data is passed, it is printed out,
// otherwise b returns user input
c // placeholder index argument
) => eval(
// replace every char
a.replace(
// use regex as register
a = /./g,
// mapping table from identifiers to javascript
d => '1=-~101=~-10while1{0}0c++0c--0b101=b()'
// saving some space
.replace(/1/g, '(a[c])')
.split(0)['+-[]><.,'.indexOf(d)]
// add semicolon to separate statements
+ ';',
// reset index
c = 0)
)
(a,b,c)=>eval(a.replace(a=/./g,d=>'1=-~101=~-10while1{0}0c++0c--0b101=b()'.replace(/1/g,'(a[c])').split(0)['+-[]><.,'.indexOf(d)]+';',c=0))
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2013 Johan Hillerström <https://github.com/hillerstorm>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "brainfuck",
"description": "Executes a brainfuck program.",
"keywords": [
"brainfuck",
"interpreter",
"compiler"
]
}
<!DOCTYPE html>
<title>Brainfuck interpreter</title>
<div>Expected Value: <b>Hello World!
</b></div>
<div>Actual value: <b id="ret"></b></div>
<script>
var brainfuck = (a,b,c)=>eval(a.replace(a=/./g,d=>'1=-~101=~-10while1{0}0c++0c--0b101=b()'.replace(/1/g,'(a[c])').split(0)['+-[]><.,'.indexOf(d)]+';',c=0));
brainfuck(
'++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.',
function (output) {
// if no output is given, read user input instead
if (typeof output === 'undefined') {
return prompt().charCodeAt();
}
// else buffer the char
document.getElementById('ret').innerHTML += String.fromCharCode(output);
});
</script>
@hillerstorm
Copy link
Author

Went from buffering output and writing in batches to just console.log-ing every char... ugly but much shorter.
Alerting every char would be even shorter but annoying as hell! :D

@atk
Copy link

atk commented Sep 20, 2013

Maybe we should externalize the input/output to a callback each. While ugly, this wouldn't break the rules of 140byt.es. In addition, the initialisation outside of a string allows us to reuse the coerced command string "c" as counter, saving another char, resulting in 165chars - 25 to go:

function(a,i,o,b,c){for(b=c='';a[b];c+=['a[b]=-~a[b]0a[b]=~-a[b]0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'.split(0)['+-[]><.,'.indexOf(a[b++])]]+';');a=[b=0];eval(c)}

@atk
Copy link

atk commented Sep 20, 2013

2 more bytes saved by a for-in-loop:

function(a,i,o,b,c){c='';for(b in a)c+=['a[b]=-~a[b]0a[b]=~-a[b]0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'.split(0)['+-[]><.,'.indexOf(a[b])]]+';');a=[b=0];eval(c)}

@atk
Copy link

atk commented Sep 20, 2013

Since undefined; doesn't usually do anything in modern JS engines (except searching the scope for an undefined global variable), you could lose the array2string coercion, saving 2 more bytes:

function(a,i,o,b,c){c='';for(b in a)c+='a[b]=-~a[b]0a[b]=~-a[b]0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'.split(0)['+-[]><.,'.indexOf(a[b])]+';');a=[b=0];eval(c)}

@hillerstorm
Copy link
Author

Brilliant!
Changing right away

@atk
Copy link

atk commented Sep 20, 2013

Spoken as your Caddy on this code golf: Thank you!

@hillerstorm
Copy link
Author

We're actually at 160bytes!
There was a leftover ')' after the semicolon-string that gave me a syntax error :)

@atk
Copy link

atk commented Sep 20, 2013

Removed stray bracket in the code (160bytes):

function(a,i,o,b,c){c='';for(b in a)c+='a[b]=-~a[b]0a[b]=~-a[b]0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'.split(0)['+-[]><.,'.indexOf(a[b])]+';';a=[b=0];eval(c)}

Update: same thought at almost the same time... 20bytes to go, though I have to admit I'm currently out of ideas. Not to worry, though; I'll come back to this gist after the weekend :)

@hillerstorm
Copy link
Author

:D yea there's alot in there.
Some examples I've found either skips loops or input which is just a bad excuse for being lazy!
I wrote mine fully functional as a fun side project 2 weeks ago and minified it from there :)

@tsaniel
Copy link

tsaniel commented Sep 20, 2013

There was another 140bytes entry for Brainf**k interpreter, have a look:
https://gist.github.com/Kambfhase/995255

@hillerstorm
Copy link
Author

Yea but it skips user input (,) and comments, it'll break on any bf code with any other char than +-[]><.
:) my crazy dream is to manage a fully featured interpreter within 140bytes :P browser-independent!

@atk
Copy link

atk commented Sep 23, 2013

Interestingly, the same approach that Kambfhase used imposed on this version would result in 3 bytes more code than we have now:

function f(a,i,o,b){return++i?'a[b]=-~a[b]0a[b]=~-a[b]0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'.split(0)['+-[]><.,'.indexOf(a)+';':a=[b=0],eval(a.replace(/./g,f))}

@hillerstorm
Copy link
Author

Yep, tried that this weekend too with no luck... I did however shave off 5 bytes by moving the added semicolon to the start instead of the end and skipped setting c="".
That way the code always starts with "undefined;" but that doesn't really matter :) still works!

function(a,i,o,b,c){for(b in a)c+=';'+'a[b]=-~a[b]0a[b]=~-a[b]0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'.split(0)['+-[]><.,'.indexOf(a[b])];a=[b=0];eval(c)}

@atk
Copy link

atk commented Sep 23, 2013

The last few bytes are always the hardest.

@hillerstorm
Copy link
Author

And in this case we're talking 15 bytes. Not an easy task!

@atk
Copy link

atk commented Sep 23, 2013

If we used a buffer class "b" which extended arrays in a way that always returned 0 if not set, we could reduce the code by another 4 bytes:

function(a,i,o,b,c,d){for(d in a)c+=';'+'a[b]++0a[b]--0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'.split(0)['+-[]><.,'.indexOf(a[d])];a=new b;b=0;eval(c)}

I'm not yet sure how the buffer class would look like and we'd still have 11 bytes to eliminate.

@hillerstorm
Copy link
Author

I managed to shave 2 bytes by using string replacement.

At first I tried to just substitute all instances of a[b] with 1 and do a string replace but it turned out that

'1=-~101=~-10while(1){0}0b++0b--0o(1)01=i()'.replace(/1/g,'a[b]')

is just as long as

'a[b]=-~a[b]0a[b]=~-a[b]0while(a[b]){0}0b++0b--0o(a[b])0a[b]=i()'

but broadening the replace to (a[b]) is still valid and saves 2 bytes from the while/output :)

so the final script for tonight is 153bytes:

function(a,i,o,b,c){for(b in a)c+=';'+'1=-~101=~-10while1{0}0b++0b--0o101=i()'.replace(/1/g,'(a[b])').split(0)['+-[]><.,'.indexOf(a[b])];a=[b=0];eval(c)}

@atk
Copy link

atk commented Sep 24, 2013

We can save another byte by giving our input method i() an unused argument by using i1 instead of i():

function(a,i,o,b,c){for(b in a)c+=';'+'1=-~101=~-10while1{0}0b++0b--0o101=i1'.replace(/1/g,'(a[b])').split(0)['+-[]><.,'.indexOf(a[b])];a=[b=0];eval(c)}

Down to 152bytes, 12 to go :)

@hillerstorm
Copy link
Author

:D ooooonly 12!

@atk
Copy link

atk commented Sep 24, 2013

On the other hand, if io was the same function (output with and input without arguments), we can revert that change (+1byte) and save one argument (-2bytes):

function(a,i,b,c){for(b in a)c+=';'+'1=-~101=~-10while1{0}0b++0b--0i101=i()'.replace(/1/g,'(a[b])').split(0)['+-[]><.,'.indexOf(a[b])];a=[b=0];eval(c)}

function i(b,u){return b===u?prompt().charCodeAt():output+=String.fromCharCode(b)}

151bytes, 11 to go.

@hillerstorm
Copy link
Author

Great :) now if only replace, split and indexOf was shorter!

@atk
Copy link

atk commented Sep 24, 2013

We could save quite some chars if we could use the key characters to split the command string (but alas, then the command string could not use + and - at least for the first two commands :-( )

'+inc a[b]+-dec a[b]-[start loop[]stop loop]>inc b><dec b<.output.,input'.split(a[b])[1]

@hillerstorm
Copy link
Author

That could (or more likely will absolutely) result in errors parsing bf code with "comments" :) aka any char other than the valid identifiers.

@atk
Copy link

atk commented Sep 24, 2013

Only if one of those chars were in our command array, but it's a valid point anyway.

@atk
Copy link

atk commented Sep 24, 2013

The only other way I can imagine to save space would be to use clever mathematics with the charcode (my current approach to do so is woefully as long as the previous implementation):

';'+'1=-~101=~-10while1{0}0b++0b--0i101=i()'.replace(/1/g,'(a[b])').split(0)['+-[]><.,'.indexOf(a[b])]
';'+'while1{00}001=-~101=~-100b++0b--00i101=i()'.replace(/1/g,'(a[b])').split(0)[a[b].charCodeAt()%13]

Maybe we'll find a way to reduce this approach further.

@hillerstorm
Copy link
Author

Interesting...

I tried (and failed) with this crazy attempt (163b):

function(a,i,b){eval(eval(a.replace(/./g,"'1=-~101=~-10while1{0}0b++0b--0i101=i()'.replace(/1/g,'(a[b])').split(0)['+-[]><.,'.indexOf('$&')]+';'+")+"''"),a=[b=0])}

every char gets replaced with the whole lookup table and evaled twice :)

@hillerstorm
Copy link
Author

I guess I can give up my dream to be able to parse commented code and instead go for 140b by restricting the input to valid BF identifiers :)

@atk
Copy link

atk commented Sep 24, 2013

We could reluctantly externalize the main command string to go below 140bytes (114, to be precise). It's a bit of a gray area that I have used for my JS syntax highlighter and base64 decoder, too.

function(a,i,s,b,c){for(b in a)c+=';'+s.replace(/1/g,'(a[b])').split(0)['+-[]><.,'.indexOf(a[b])];a=[b=0];eval(c)}
// i=function(b,u){return b==u?prompt().charCodeAt():document.getElementById('output').innerHTML+=String.fromCharCode(b)}, 
// s='1=-~101=~-10while1{0}0b++0b--0i101=i()'

Let's keep the version with 151bytes as a great learning example, anyway.

@hillerstorm
Copy link
Author

It could even be 82b if the command string is the complete array, with the 1's already replaced with a[b]:

function(a,i,s,b,c){for(b in a)c+=';'+s['+-[]><.,'.indexOf(a[b])];a=[b=0];eval(c)}
// s=['a[b]=-~a[b]','a[b]=~-a[b]','while(a[b]){','}','b++','b--','i(a[b])','a[b]=i()']

but that actually feels like too much of a gray area for my taste :D
not at all plug-and-play! as much code outside as there is inside the function

@atk
Copy link

atk commented Sep 25, 2013

I wholeheartedly agree. Maybe we'll find a solution after all. Until then, we do have a gray area solution and another one that's too big.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment