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
  • Select an option

  • Save hillerstorm/6622750 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
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

atk commented Sep 20, 2013

Copy link
Copy Markdown

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

atk commented Sep 20, 2013

Copy link
Copy Markdown

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

atk commented Sep 20, 2013

Copy link
Copy Markdown

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
Copy Markdown
Author

Brilliant!
Changing right away

@atk

atk commented Sep 20, 2013

Copy link
Copy Markdown

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

@hillerstorm

Copy link
Copy Markdown
Author

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

@atk

atk commented Sep 20, 2013

Copy link
Copy Markdown

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
Copy Markdown
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

tsaniel commented Sep 20, 2013

Copy link
Copy Markdown

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

@hillerstorm

Copy link
Copy Markdown
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

atk commented Sep 23, 2013

Copy link
Copy Markdown

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
Copy Markdown
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

atk commented Sep 23, 2013

Copy link
Copy Markdown

The last few bytes are always the hardest.

@hillerstorm

Copy link
Copy Markdown
Author

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

@atk

atk commented Sep 23, 2013

Copy link
Copy Markdown

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
Copy Markdown
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

atk commented Sep 24, 2013

Copy link
Copy Markdown

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
Copy Markdown
Author

:D ooooonly 12!

@atk

atk commented Sep 24, 2013

Copy link
Copy Markdown

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
Copy Markdown
Author

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

@atk

atk commented Sep 24, 2013

Copy link
Copy Markdown

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
Copy Markdown
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

atk commented Sep 24, 2013

Copy link
Copy Markdown

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

@atk

atk commented Sep 24, 2013

Copy link
Copy Markdown

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
Copy Markdown
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
Copy Markdown
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

atk commented Sep 24, 2013

Copy link
Copy Markdown

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
Copy Markdown
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

atk commented Sep 25, 2013

Copy link
Copy Markdown

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