There already is a Brainfuck interpreter question, but it is from 2011 and not up to date with current site standards and vague in places. I have proposed reposting this in chat, and it was well-received. Here goes.


Given the Brainfuck code and the program's input as input, run the Brainfuck code. As there are various variants of Brainfuck, here is the one I will use for the challenge:

  • There are least 128 cells.
  • Cell values are positive integers, having a maximum value of at least 128.
  • Going into negative cells in undefined.
  • Going into negative numbers is undefined.
  • Going above the maximum cell value is undefined.
  • Going above the maximum cell is undefined.
  • EOF is 0

You may assume that the code will conform to the above constraints and that brackets will be balanced. You may also assume that all input code will be nothing but <>,.+-[].


"+[+[<<<+>>>>]+<-<-<<<+<++]<<.<++.<++..+++.<<++.<---.>>.>.+++.------.>-.>>--." -> "Hello, World!"

",[.,]", "abc" -> "abc"


As this is , shortest answer in bytes wins.

  • 7
    \$\begingroup\$ Maybe link to a brainfuck specification? \$\endgroup\$
    – mousetail
    Aug 31 at 19:13
  • 2
    \$\begingroup\$ You say "going into negative numbers is undefined" but the hello world depends on a wrapping implementation for overflow \$\endgroup\$
    – mousetail
    Aug 31 at 19:34
  • 2
    \$\begingroup\$ @emanresuA "Array size: 30000 bytes (not circled)" <-- This means that trying to go to cell 30001 will do nothing, on the other post. On this one, you're expected to from 30000 to 0, when you try to access 30001. This is a HUGE difference. EOL being 0 is also a HUGE difference. The other one also requires you to handle comments, while this lets you assume that there's no comments. (Newlines are considered comments.) These are REALLY HUGE differences. I doubt that that many answers can be re-posted without some work. \$\endgroup\$ Aug 31 at 20:54
  • 2
    \$\begingroup\$ Yes, that is completely missing, since you only mention that "negative cells [are] undefined". Going past the 128th cell doesn't mean it is a negative cell. The next cell can be the 129th cell (no overflow at 128) or the 0th (cyclic "tape", with overflow to positive) cell or the -127th (cyclic tape, with overflow into negative). You do have "Going above the maximum cell value is undefined", but that's for the cell value, not the pointer value. However, a few lines above, you said the cell values must be integers. The spec does need some tightening. \$\endgroup\$ Aug 31 at 21:26
  • 8
    \$\begingroup\$ I think the spec is far too loose and unopinionated. Maybe there's room for another BF challenge, but IMO it should change things in some meaningful way, like unbounded cells + tape or following the spec precisely. As it is now this challenge is just a downgrade from the existing one in every way except the I/O, which nobody really pays attention to anyway. It also should've been sandboxed for longer, as challenges go this is a pretty poorly specified one. Also, typical practice is to ask on meta before redoing an old challenge, something which should definitely have been done here. \$\endgroup\$ Aug 31 at 22:17

3 Answers 3


simply, 425 bytes

This big boy defines an anonymous function which takes an input string and returns the output of the Brainfuck program.

This anonymous function has to be stored into a variable, and receives the code as the 1st argument ($C), and the input as the 2nd argument ($T).

This is a language being interpreted by JavaScript, which then interprets Brainfuck to generate JavaScript code. 🤯

fn($C$I){if run&empty($I)$I=''$T=&json_decode(<<<J
J)$N=''each$X in$C if$T[$X]$N=&str_concat($N$T[$X])$F=run$argv->map->constructor('X'run&str_concat("var I=Array.from(X+''),O='',T=[],P=0;"$N"return O"))send run$F($I);}

It is an absolute mess!

Ungolfed - to plain English

Set $fn to the anonymous function($code, $input) {
    If call &empty($input) then {
        Set $input to "".

    Set $tokens to the result of calling &json_decode(<<<JSON
    ".": "output += String.fromCharCode(tape[pointer]);",
    ",": "tape[pointer] = input.length ? input.shift().charCodeAt(0) : 0;",
    "<": "pointer = pointer ? pointer - 1 : 255;",
    ">": "pointer = (pointer + 1) % 256;",
    "-": "tape[pointer] = tape[pointer] ? tape[pointer] - 1 : 255;",
    "+": "tape[pointer] = tape[pointer] ? (tape[pointer] + 1) % 256 : 1;",
    "[": "while(tape[pointer]){",
    "]": "}"
    Set $inner_code to "".
    Foreach $char in $code {
        If $tokens[$char] then {
            Set $inner_code to the result of calling &str_concat($inner_code, $tokens[$char], "\n").
    Set $js_code to the result of calling &str_concat(
        "input = Array.from(input + '');\nvar output = '';\nvar tape = [];\nvar pointer = 0;\n",
        "return output;"
    Set $fn to the result of calling $argv["map"]["constructor"]("input", $js_code).
    Return the result of calling $fn($input).

Now it is a lot more readable!

How does it work?

This will simply read the input code, character by character, and replace the valid tokens into JavaScript code.

Using the array["filter"]["constructor"](code) (but using "map" to save bytes) trick in JSFuck, I can receive a JavaScript function which I can execute normally!

This is FAAAAAAAAAAAAR from the intended way of doing things, but ... it works!

Example output

This is the JavaScript code generated for the test cases.

Hello world: +[+[<<<+>>>>]+<-<-<<<+<++]<<.<++.<++..+++.<<++.<---.>>.>.+++.------.>-.>>--.

var fn = function anonymous(X
) {
var I=Array.from(X+''),O='',T=[],P=0;T[P]=T[P]?(T[P]+1)%256:1;while(T[P]){T[P]=T[P]?(T[P]+1)%256:1;while(T[P]){P=P?P-1:255;P=P?P-1:255;P=P?P-1:255;T[P]=T[P]?(T[P]+1)%256:1;P=(P+1)%256;P=(P+1)%256;P=(P+1)%256;P=(P+1)%256;}T[P]=T[P]?(T[P]+1)%256:1;P=P?P-1:255;T[P]=T[P]?T[P]-1:255;P=P?P-1:255;T[P]=T[P]?T[P]-1:255;P=P?P-1:255;P=P?P-1:255;P=P?P-1:255;T[P]=T[P]?(T[P]+1)%256:1;P=P?P-1:255;T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;}P=P?P-1:255;P=P?P-1:255;O+=String.fromCharCode(T[P]);P=P?P-1:255;T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;O+=String.fromCharCode(T[P]);P=P?P-1:255;T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;O+=String.fromCharCode(T[P]);O+=String.fromCharCode(T[P]);T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;O+=String.fromCharCode(T[P]);P=P?P-1:255;P=P?P-1:255;T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;O+=String.fromCharCode(T[P]);P=P?P-1:255;T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;O+=String.fromCharCode(T[P]);P=(P+1)%256;P=(P+1)%256;O+=String.fromCharCode(T[P]);P=(P+1)%256;O+=String.fromCharCode(T[P]);T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;T[P]=T[P]?(T[P]+1)%256:1;O+=String.fromCharCode(T[P]);T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;O+=String.fromCharCode(T[P]);P=(P+1)%256;T[P]=T[P]?T[P]-1:255;O+=String.fromCharCode(T[P]);P=(P+1)%256;P=(P+1)%256;T[P]=T[P]?T[P]-1:255;T[P]=T[P]?T[P]-1:255;O+=String.fromCharCode(T[P]);return O


Cat program: ,[.,]

var fn = function anonymous(X
) {
var I=Array.from(X+''),O='',T=[],P=0;T[P]=I[0]?I.shift().charCodeAt(0):0;while(T[P]){O+=String.fromCharCode(T[P]);T[P]=I[0]?I.shift().charCodeAt(0):0;}return O

console.log(fn(prompt("What does the cat say?", "")));


Factor + brainfuck, 13 bytes


Try it online!

Try it online! (with input)


Rust + -C overflow-checks=off, 370 bytes

|p:&[u8]|{let(mut o,mut c,mut t,mut k)=(0,0,[0;128],0);loop{match p[o]{62=>c+=1,60=>c-=1,43=>t[c]+=1,45=>t[c]-=1,46=>print!("{}",t[c]as char),44=>t[c]=stdin().bytes().next().unwrap_or(Ok(0)).unwrap(),91=>if t[c]<1{k=1;while k>0{o+=1;if p[o]==91{k+=1};if p[o]==93{k-=1}}},93=>if t[c]>0{k=1;while k>0{o-=1;if p[o]==91{k-=1}if p[o]==93{k+=1}}},_=>()}o+=1}};use std::io::*;

Attempt This Online! (Hello World)

Attempt This Online (Cat)

Will crash when it reaches the end of the program.


Not the answer you're looking for? Browse other questions tagged or ask your own question.