Brainfuck is quite a brilliant little programming language. As noted above and elsewhere, it operates in a similar manner to a basic Turing machine. A note on the name: understandably, Urban Müller now wishes he'd called it something different.

Below is the code for a Brainfuck interpreter written in Java. The usage is:

	java Brainfuck yourCode.b

Where yourCode.b is a file containing Brainfuck. Standard input and standard output are used for the ',' and '.'. To pipe a file into your program (or anyone else's), you can do this at a shell:

	cat yourFile | java Brainfuck yourCode.b 1

...I think.

The code could be better, but it could also be worse. It was written to be understandable, rather than terse - for that, see the writeup below. There you can see an interpreter done in 3 lines of obfuscated C. (Which is cool, but consider which one is easier to debug or maintain :-P) This would probably make for a good tournament of Perl Golf.

That said:

import java.io.InputStream;
import java.io.PrintStream;
import java.io.File;
import java.io.FileInputStream;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Stack;

class Brainfuck {

    InputStream input;
    PrintStream output;

    ArrayList infiniteTape;
    int headPosition;

    Brainfuck( InputStream in, PrintStream out ) {
        input = in;
        output = out;

        infiniteTape = new ArrayList();

        for( int i = 0; i != 50; i++ ) infiniteTape.add( new Byte( (byte)0x00 ) );

        headPosition = 0;
    }

    void interpret( byte[] code ) throws IOException {

        int instructionPointer = 0;

        Stack nestedLoops = new Stack();

        while( instructionPointer != code.length ) switch( code[instructionPointer] ) {

            case '>' :
                headPosition++;
                if( headPosition >= infiniteTape.size() ) stretchTapeEnd();
                instructionPointer++;
                break;
            case '<' :
                headPosition--;
                if( headPosition <= 0 ) stretchTapeStart();
                instructionPointer++;
                break;
            case '+' :
                setSymbol( (byte)(getSymbol() + 0x01) );
                instructionPointer++;
                break;
            case '-' :
                setSymbol( (byte)(getSymbol() - 0x01) );
                instructionPointer++;
                break;
            case ',' :
                setSymbol( (byte)input.read() );
                instructionPointer++;
                break;
            case '.' :
                output.print( (char)getSymbol() );
                instructionPointer++;
                break;
            case '[' :
                if( getSymbol() != 0x00 ) {
                    nestedLoops.push( new Integer( ++instructionPointer ) );
                } else instructionPointer = skipLoop( code, instructionPointer );
                break;
            case ']' :
                if( nestedLoops.size() == 0 )
                  throw new IOException ("mismatched ]");

                if( getSymbol() != (byte)0x00 ) {
                    instructionPointer = ((Integer)nestedLoops.peek()).intValue();
                } else {
                    nestedLoops.pop();
                    instructionPointer++;
                }
                break;
            default : instructionPointer++; break;
        }
    }

    byte getSymbol() {
        Byte symbol = (Byte)infiniteTape.get( headPosition );
        return symbol.byteValue();
    }

    void setSymbol( byte value ) {
        Byte symbol = new Byte( value );
        infiniteTape.set( headPosition, symbol );
    }

    void stretchTapeEnd() {
        for( int i = 0; i != 20; i++ )
          infiniteTape.add( new Byte( (byte)0x00 ) );
    }

    void stretchTapeStart() {
        for( int i = 0; i != 20; i++ )
          infiniteTape.add( 0, new Byte( (byte)0x00 ) );
        headPosition += 20;
    }

    int skipLoop( byte[] code, int from ) {
        int subLoops = 0;

        do switch( code[++from] ) {
            case '[' : subLoops++; break;
            case ']' : subLoops--; break;
            default  : break;
        } while( subLoops != -1 );

        return ++from;
    }

    public static void main( String[] args ) throws IOException {

        byte[] code;

        if( args.length < 1 ) {
            System.out.println( "Usage: java Brainfuck file.b" );
            return;
        }

        File brainfuckFile = new File( args[0] );

        if( !brainfuckFile.exists() ) {
            System.out.println( args[0] + " does not exist" );
            return;
        }

        code = new byte[(int)brainfuckFile.length()];

        FileInputStream fileIn = new FileInputStream( brainfuckFile );

        for( int i = 0; i != code.length; i++ ) code[i] = (byte)fileIn.read();

        fileIn.close();

        Brainfuck interpreter = new Brainfuck( System.in, System.out );
        interpreter.interpret( code );

    }

}

And there you go. Now, you too can marvel at ''hello, world!'', and wonder just how on earth ccunning checked for the difference between singular and plural when counting bottles of beer on a wall. This is just the first step, of course; the logical progression would be to write a Brainfuck interpreter entirely in Brainfuck...

...but someone's already beaten me to it:

http://home.wxs.nl/~faase009/Ha_bf_inter.html

1: Thanks to ivarneli for the correction.