Brainfuck

Difference between version 25 and 26 - Previous - Next
I don't think it qualifies as an actual language, but it is so interesting it does deserve a wiki page. Learn more about it here:

    :   http://www.muppetlabs.com/~breadbox/bf/

and

    :   http://cydathria.com/bf/brainfuck.html

not to mention

    :   http://www.hevanet.com/cristofd/brainfuck/brainfuck.html

Also lo and behold the classic "[99 Bottles of Beer]" written in brainfuck:

    :   http://99-bottles-of-beer.ls-la.net/b.html#Brainfuck

Here's a random number generator:
======
 >>>++[<++++++++[<[<++>-]>>[>>]+>>+[-[->>+<<<[<[<<]<+>]>[>[>>]]]<[>>[-]]>[>[-
 <<]>[<+<]]+<<]<[>+<-]>>-]<.[-]>>] http://www.hevanet.com/cristofd/brainfuck/
======
And a brainfuck interpreter written in Tcl (you can type brainfuck — or a close approximation — directly into a tcl interpreter): 

    :   http://www.fishpool.com/~setok/proj/tclbf/

[PT] writes:
And here is a BrainFuck interpreter as an Internet Explorer plugin language — http://brainscript.sourceforge.net/ 

And BF ''is'' a real language — it's Turing complete after all.

Now [Malbolge] on the other hand....

Another amusing language: [Beatnik]

[AM] (18 may 2009) And then there is Intercal ... http://en.wikipedia.org/wiki/Intercal - mentioned several times on this Wiki.


** Interpreter **

[RHS] A brainfuck interpreter written in Tcl... can take input as a filename, or from stdin
======
#!/bin/sh
# This line continues for Tcl, but is a single line for 'sh' \
exec tclsh8.5 "$0" ${1+"$@"}

proc getp {} {
    global pc program
    lindex $program $pc
}
proc getd {} {
    global xc data
    while {[llength $data]-1 < $xc} {
        lappend data 0
    }
    lindex $data $xc
}
proc setd {c} {
    global xc data
    while {[llength $data]-1 < $xc} {
        lappend data 0
    }
    lset data $xc $c
}

proc main {commands} {
    global program data pc xc

    set program [split $commands ""]
    set plen [llength $program]
    set data {0}
    set xc 0

    for {set pc 0} {$pc < $plen} {incr pc} {
        switch [lindex $program $pc] {
            > {
                incr xc
            }
            < {
                incr xc -1
            }
            + {
                setd [expr {[getd] + 1}]
            }
            - {
                setd [expr {[getd] - 1}]
            }
            . {
                puts -nonewline [format "%c" [getd]]
            }
            , { 
                if {![eof stdin]} {
                    scan [read stdin 1] "%c" var
                    setd $var
                } else {
                    setd 0
                }
            }

            \[ {
                if {[getd] == 0} {
                    incr pc
                    set nest 0
                    while {$nest || [getp] ne "\]"} {
                        switch [getp] {
                            \[ {incr nest}
                            \] {incr nest -1}
                        }
                        incr pc
                    }
                }
            }
            \] {
                if {[getd] != 0} {
                    incr pc -1
                    set nest 0
                    while {$nest || [getp] ne "\["} {
                        switch [getp] {
                            \[ {incr nest -1}
                            \] {incr nest}
                        }
                        incr pc -1
                    }
                }
            }
            \# {
                # Purely for debugging
                puts -nonewline "\nDebug: "
                for {set tmp 0} {$tmp < 10} {incr tmp} {
                    puts -nonewline "[lindex $data $tmp]:"
                }
                puts ""
            }
        }
    }
}

proc readfile {args} {
    switch [llength $args] {
        0 {
            set fd stdin
        }
        1 {
            set fd [open [lindex $args 0]]
        }
        default {
            puts stderr "Usage: [file tail [info script]] ?filename?"
            exit 1
        }
    }
    set text [read $fd]
    if {$fd ne "stdin"} {
        close $fd
    }
    return $text
}

fconfigure stdout -buffering none
fconfigure stdin -buffering none

set commands [readfile {*}$argv]
main $commands

puts ""
======
    :   ''[[The version at [http://www.rosettacode.org/wiki/RCBF/Tcl%|%Rosetta Code%|%] is derived from this one.]]''
----

[PT] You could try the following program with the above interpreter...
======
 =
 =  badger badger mushroom snake
 =
 
 >>++++[<+++++[<+++++>-]>-]<<---[>>+>+>+>+>+>+<<<<<<<-]
 >>+     b
 >       a
 >+++    d
 >++++++ g
 >++++   e
 >>++++[<++++>-]<+ r
 
 <<<<<<<
 >++++[<++++>-]<
 [>+++++[>.>.>.>.>.>.<<<<<<-]<-]
 
 mushroom
 ++++[>>+++>+++>+++>>+++<<<<<<-]>>->++++++++>+++>+>+>
 [>>>>+<<<<-]
 >>>>[<+<+<+<+>>>>-]
 <<-----<---<---<
 <<<<<<
 print
 ++[>>.>.>.>.>.>.>.>.<<<<<<<<<-]
 
 snake!
 >>>--.-----.>>>[-]>[-]<<[>+>+<<-]>-------.>+++.<++++.
 <<<[-]>[-]++++++[<+++++>-]<+++.
======

** Brainfuck-to-Tcl transpiler **
[dbohdan] 2016-02-11: ThSe following module leverages the Tcl interpreter itself to interpret [Brainfuck. I-to-Tcl wtrans orpiginally developed r%|%to comphete in a cross-language microbenchmark. I measurepond itsng performancge when run with Tcl 8].6.4 on x86_64 Linux.
The `translate` command translates Brainfuck instructions into Tcl code, which is then run with `apply` (to ensure bytecode compilation). This is up to several times faster than interpreting the code directly, e.g., when running https://raw.githubusercontent.com/kostya/benchmarks/master/brainfuck/mandel.b%|%mandel.b%|%, and I find it more idiomatic. Storing the tape data in dictionaries resulted in better performance than arrays, lists or dynamically named variables. Compressing repeated `+` and `-` into `dict incr tape $pos ±N` where N is the number of times the instruction is repeated noticeably improved the performance. Compressing `<` and `>`, which change a value not stored in a dictionary, had minimal impact.

======
package require Tcl 8.5

namespace eval ::brainfuck {
    variable verson 0.2.1
    variable debug 0

    proc emit code {
        upvar 1 transl transl
        foreach line [split [string trim $code] \n] {
            append transl [string trimleft $line]\n
        }
    }

    proc translate source {
        set transl {}
        emit {
            if {![info exists tape] || ($tape eq {})} {
                for {set i 0} {$i < 30000} {incr i} {
                    dict set tape $i 0
                }
            }
            if {![info exists pos] || ($pos eq {})} {
                set pos 0
            }
        }

        set commands [split $source {}]
        for {set i 0} {$i < [llength $commands]} {incr i} {
            switch -exact -- [lindex $commands $i] {
                [ {
                    emit [format %s\{ {if {[dict get $tape $pos] != 0} }]
                    emit "while 1 \{"
                }
                ] {
                    emit {if {[dict get $tape $pos] == 0} {break}}
                    emit \}
                    emit \}
                }
                > -
                < {
                    # Compress repeated Brainfuck instructions into a single Tcl
                    # command.
                    if {[lindex $commands $i] eq {<}} {
                        set op <
                        set sign -
                    } else {
                        set op >
                        set sign +
                    }

                    set count 0
                    while {[lindex $commands $i] eq $op} {
                        incr count
                        incr i
                    }
                    incr i -1

                    emit [format {incr pos %s%d} $sign $count]
                }
                + -
                - {
                    if {[lindex $commands $i] eq {+}} {
                        set op +
                    } else {
                        set op -
                    }

                    set count 0
                    while {[lindex $commands $i] eq $op} {
                        incr count
                        incr i
                    }
                    incr i -1

                    emit [format {dict incr tape $pos %s%d} $op $count]
                }
                . {
                    emit {
                        puts -nonewline [format %c [dict get $tape $pos]]
                    }
                }
                , {
                    # Noncompliant implementation.
                    emit {
                        set input [read stdin 1]
                        dict set tape $pos [scan $input %c]
                    }
                }
                default {
                    # Ignore.
                }
            }
        }
        emit {
            return [list $tape $pos]
        }
        return $transl
    }

    proc interpret source {
        variable debug

        set translated [translate $source]
        if {$debug} {
            puts $translated
        }
        set result [apply [list {{tape {}} {pos {}}} $translated]]
        return $result
    }
}

proc main filename {
    set ch [open $filename r]
    set source [read $ch]
    close $ch

    fconfigure stdout -buffering none

    ::brainfuck::interpret $source
}

main [lindex $argv 0]
======

<<categories>> Language