From a2a232f8b6a069b6102106b537141cff3bdf37d3 Mon Sep 17 00:00:00 2001 From: Chris Sexton Date: Tue, 13 Nov 2012 20:14:14 -0500 Subject: [PATCH] Initial version of a brainfuck interpreter --- brainfuck.go | 113 ++++++++++++++++++++++ examples/99botles.bf | 60 ++++++++++++ examples/HELLOBF2.BF | 1 + examples/SORT.BF | 6 ++ examples/cat.bf | 1 + examples/hello.bf | 21 ++++ examples/prime.bf | 224 +++++++++++++++++++++++++++++++++++++++++++ examples/rot13.bf | 29 ++++++ examples/test | 2 + examples/test2 | 2 + gofuck/main.go | 22 +++++ 11 files changed, 481 insertions(+) create mode 100644 brainfuck.go create mode 100644 examples/99botles.bf create mode 100644 examples/HELLOBF2.BF create mode 100644 examples/SORT.BF create mode 100644 examples/cat.bf create mode 100644 examples/hello.bf create mode 100644 examples/prime.bf create mode 100644 examples/rot13.bf create mode 100644 examples/test create mode 100644 examples/test2 create mode 100644 gofuck/main.go diff --git a/brainfuck.go b/brainfuck.go new file mode 100644 index 0000000..a89a0a3 --- /dev/null +++ b/brainfuck.go @@ -0,0 +1,113 @@ +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" +) + +const ( + MEM_MAX = 3000000 + MEM_STD = 30000 +) + +type Machine struct { + array []byte + ptr int + reader *bufio.Reader +} + +// Returns a new machine with standard memory size +func New() *Machine { + bytes := make([]byte, MEM_STD) + return &Machine{ + array: bytes, + ptr: 0, + reader: bufio.NewReader(os.Stdin), + } +} + +// Implements '>' +func (m *Machine) PtrIncr() { + if m.ptr >= len(m.array)-1 { + m.array = append(m.array, 0) + } + m.ptr += 1 + if m.ptr > MEM_MAX { + panic("Memory overflow") + } +} + +// Implements '<' +func (m *Machine) PtrDecr() { + if m.ptr == 0 { + panic("Memory underflow") + } + m.ptr -= 1 +} + +// Implements '+' +func (m *Machine) ByteIncr() { + if m.ptr > len(m.array)-1 { + fmt.Printf("Memory overflow, ptr=%d\n", m.ptr) + } + m.array[m.ptr] += 1 +} + +// Implements '-' +func (m *Machine) ByteDecr() { + m.array[m.ptr] -= 1 +} + +// Implements the '.' command +func (m *Machine) Output() { + fmt.Printf("%c", m.array[m.ptr]) +} + +// Implements the ',' command +func (m *Machine) Input() { + input, err := m.reader.ReadByte() + if err != nil { + m.array[m.ptr] = 0 + } else { + m.array[m.ptr] = input + } +} + +// Run the whole program specified by input +func (m *Machine) Run(input []byte) { + // execute the program + loop := -1 + discard := false + for i := 0; i < len(input); i++ { + instr := input[i] + if discard && instr != ']' { + continue + } else if instr == '[' { + loop = i + if m.array[m.ptr] == 0 { + discard = true + } + } else if instr == ']' { + if m.array[m.ptr] != 0 { + i = loop + } else { + discard = false + } + } else if instr == '>' { + m.PtrIncr() + } else if instr == '<' { + m.PtrDecr() + } else if instr == '+' { + m.ByteIncr() + } else if instr == '-' { + m.ByteDecr() + } else if instr == '.' { + m.Output() + } else if instr == ',' { + m.Input() + } + + } +} diff --git a/examples/99botles.bf b/examples/99botles.bf new file mode 100644 index 0000000..75fbba8 --- /dev/null +++ b/examples/99botles.bf @@ -0,0 +1,60 @@ +99 Bottles of Beer in Urban Mueller's BrainF*** (The actual +name is impolite) + +by Ben Olmstead + +ANSI C interpreter available on the internet; due to +constraints in comments the address below needs to have the +stuff in parenthesis replaced with the appropriate symbol: + +http://www(dot)cats(dash)eye(dot)com/cet/soft/lang/bf/ + +Believe it or not this language is indeed Turing complete! +Combines the speed of BASIC with the ease of INTERCAL and +the readability of an IOCCC entry! + +>+++++++++[<+++++++++++>-]<[>[-]>[-]<<[>+>+<<-]>>[<<+>>-]>>> +[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+< +-]<<-<-]+++++++++>[<->-]>>+>[<[-]<<+>>>-]>[-]+<<[>+>-<<-]<<< +[>>+>+<<<-]>>>[<<<+>>>-]>[<+>-]<<-[>[-]<[-]]>>+<[>[-]<-]<+++ ++++++[<++++++<++++++>>-]>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>- +]<<<<<<.>>[-]>[-]++++[<++++++++>-]<.>++++[<++++++++>-]<++.>+ +++++[<+++++++++>-]<.><+++++..--------.-------.>>[>>+>+<<<-]> +>>[<<<+>>>-]<[<<<<++++++++++++++.>>>>-]<<<<[-]>++++[<+++++++ ++>-]<.>+++++++++[<+++++++++>-]<--.---------.>+++++++[<------ +---->-]<.>++++++[<+++++++++++>-]<.+++..+++++++++++++.>++++++ +++[<---------->-]<--.>+++++++++[<+++++++++>-]<--.-.>++++++++ +[<---------->-]<++.>++++++++[<++++++++++>-]<++++.----------- +-.---.>+++++++[<---------->-]<+.>++++++++[<+++++++++++>-]<-. +>++[<----------->-]<.+++++++++++..>+++++++++[<---------->-]< +-----.---.>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>>+++ ++[<++++++>-]<--.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<. +><+++++..--------.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++ +++++++++++++.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<++ ++++++++>-]<--.---------.>+++++++[<---------->-]<.>++++++[<++ ++++++++++>-]<.+++..+++++++++++++.>++++++++++[<---------->-]< +-.---.>+++++++[<++++++++++>-]<++++.+++++++++++++.++++++++++. +------.>+++++++[<---------->-]<+.>++++++++[<++++++++++>-]<-. +-.---------.>+++++++[<---------->-]<+.>+++++++[<++++++++++>- +]<--.+++++++++++.++++++++.---------.>++++++++[<---------->-] +<++.>+++++[<+++++++++++++>-]<.+++++++++++++.----------.>++++ ++++[<---------->-]<++.>++++++++[<++++++++++>-]<.>+++[<-----> +-]<.>+++[<++++++>-]<..>+++++++++[<--------->-]<--.>+++++++[< +++++++++++>-]<+++.+++++++++++.>++++++++[<----------->-]<++++ +.>+++++[<+++++++++++++>-]<.>+++[<++++++>-]<-.---.++++++.---- +---.----------.>++++++++[<----------->-]<+.---.[-]<<<->[-]>[ +-]<<[>+>+<<-]>>[<<+>>-]>>>[-]<<<+++++++++<[>>>+<<[>+>[-]<<-] +>[<+>-]>[<<++++++++++>>>+<-]<<-<-]+++++++++>[<->-]>>+>[<[-]< +<+>>>-]>[-]+<<[>+>-<<-]<<<[>>+>+<<<-]>>>[<<<+>>>-]<>>[<+>-]< +<-[>[-]<[-]]>>+<[>[-]<-]<++++++++[<++++++<++++++>>-]>>>[>+>+ +<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>[-]>[-]++++[<++++++++> +-]<.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<.><+++++..--- +-----.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++++++++++++++ +.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<+++++++++>-]<- +-.---------.>+++++++[<---------->-]<.>++++++[<+++++++++++>-] +<.+++..+++++++++++++.>++++++++[<---------->-]<--.>+++++++++[ +<+++++++++>-]<--.-.>++++++++[<---------->-]<++.>++++++++[<++ +++++++++>-]<++++.------------.---.>+++++++[<---------->-]<+. +>++++++++[<+++++++++++>-]<-.>++[<----------->-]<.+++++++++++ +..>+++++++++[<---------->-]<-----.---.+++.---.[-]<<<] + diff --git a/examples/HELLOBF2.BF b/examples/HELLOBF2.BF new file mode 100644 index 0000000..586d17a --- /dev/null +++ b/examples/HELLOBF2.BF @@ -0,0 +1 @@ +>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.#>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++. diff --git a/examples/SORT.BF b/examples/SORT.BF new file mode 100644 index 0000000..1f15eb5 --- /dev/null +++ b/examples/SORT.BF @@ -0,0 +1,6 @@ +Here is a Brainf*** program that bubblesorts its input and spits it out: +>>>>>,+[>>>,+]<<<[<<< +[>>>[-<<<-<+>[>]>>]<<<[<]>> +[>>>+<<<-]<[>+>>>+<<<<-] +<<]>>>[-.[-]]>>>[>>>]<<<] + diff --git a/examples/cat.bf b/examples/cat.bf new file mode 100644 index 0000000..05b49e2 --- /dev/null +++ b/examples/cat.bf @@ -0,0 +1 @@ +,[.,] diff --git a/examples/hello.bf b/examples/hello.bf new file mode 100644 index 0000000..5e304b1 --- /dev/null +++ b/examples/hello.bf @@ -0,0 +1,21 @@ ++++++ +++++ initialize counter (cell #0) to 10 +[ use loop to set the next four cells to 70/100/30/10 + > +++++ ++ add 7 to cell #1 + > +++++ +++++ add 10 to cell #2 + > +++ add 3 to cell #3 + > + add 1 to cell #4 + <<<< - decrement counter (cell #0) +] +> ++ . print 'H' +> + . print 'e' ++++++ ++ . print 'l' +. print 'l' ++++ . print 'o' +> ++ . print ' ' +<< +++++ +++++ +++++ . print 'W' +> . print 'o' ++++ . print 'r' +----- - . print 'l' +----- --- . print 'd' +> + . print '!' +> . print '\n' diff --git a/examples/prime.bf b/examples/prime.bf new file mode 100644 index 0000000..9e582a5 --- /dev/null +++ b/examples/prime.bf @@ -0,0 +1,224 @@ +compute prime numbers +to use type the max number then push Alt 1 0 +=================================================================== +======================== OUTPUT STRING ============================ +=================================================================== +>++++++++[<++++++++>-]<++++++++++++++++.[-] +>++++++++++[<++++++++++>-]<++++++++++++++.[-] +>++++++++++[<++++++++++>-]<+++++.[-] +>++++++++++[<++++++++++>-]<+++++++++.[-] +>++++++++++[<++++++++++>-]<+.[-] +>++++++++++[<++++++++++>-]<+++++++++++++++.[-] +>+++++[<+++++>-]<+++++++.[-] +>++++++++++[<++++++++++>-]<+++++++++++++++++.[-] +>++++++++++[<++++++++++>-]<++++++++++++.[-] +>+++++[<+++++>-]<+++++++.[-] +>++++++++++[<++++++++++>-]<++++++++++++++++.[-] +>++++++++++[<++++++++++>-]<+++++++++++.[-] +>+++++++[<+++++++>-]<+++++++++.[-] +>+++++[<+++++>-]<+++++++.[-] + +=================================================================== +======================== INPUT NUMBER ============================ +=================================================================== ++ cont=1 +[ + - cont=0 + >, + ======SUB10====== + ---------- + + [ not 10 + <+> cont=1 + =====SUB38====== + ---------- + ---------- + ---------- + -------- + + > + =====MUL10======= + [>+>+<<-]>>[<<+>>-]< dup + + >>>+++++++++ + [ + <<< + [>+>+<<-]>>[<<+>>-]< dup + [<<+>>-] + >>- + ] + <<<[-]< + ======RMOVE1====== + < + [>+<-] + ] + < +] +>>[<<+>>-]<< + +=================================================================== +======================= PROCESS NUMBER =========================== +=================================================================== + +==== ==== ==== ==== +numd numu teid teiu +==== ==== ==== ==== + +>+<- +[ + >+ + ======DUP====== + [>+>+<<-]>>[<<+>>-]< + + >+<-- + + >>>>>>>>+<<<<<<<< isprime=1 + + [ + >+ + + <- + + =====DUP3===== + <[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<<< + + =====DUP2===== + >[>>+>+<<<-]>>>[<<<+>>>-]<<< < + + + >>> + + + ====DIVIDES======= + [>+>+<<-]>>[<<+>>-]< DUP i=div + + << + [ + >>>>>+ bool=1 + <<< + [>+>+<<-]>>[<<+>>-]< DUP + [>>[-]<<-] IF i THEN bool=0 + >> + [ IF i=0 + <<<< + [>+>+<<-]>>[<<+>>-]< i=div + >>> + - bool=0 + ] + <<< + - DEC i + << + - + ] + + +>>[<<[-]>>-]<< + >[-]< CLR div + =====END DIVIDES==== + + + [>>>>>>[-]<<<<<<-] if divides then isprime=0 + + + << + + >>[-]>[-]<<< + ] + + >>>>>>>> + [ + - + <<<<<<<[-]<< + + [>>+>+<<<-]>>>[<<<+>>>-]<<< + + >> + + + + + =================================================================== + ======================== OUTPUT NUMBER =========================== + =================================================================== + [>+<-]> + + [ + ======DUP====== + [>+>+<<-]>>[<<+>>-]< + + + ======MOD10==== + >+++++++++< + [ + >>>+<< bool= 1 + [>+>[-]<<-] bool= ten==0 + >[<+>-] ten = tmp + >[<<++++++++++>>-] if ten=0 ten=10 + <<- dec ten + <- dec num + ] + +++++++++ num=9 + >[<->-]< dec num by ten + + =======RROT====== + [>+<-] + < [>+<-] + < [>+<-] + >>>[<<<+>>>-] + < + + =======DIV10======== + >+++++++++< + [ + >>>+<< bool= 1 + [>+>[-]<<-] bool= ten==0 + >[<+>-] ten = tmp + >[<<++++++++++>>>+<-] if ten=0 ten=10 inc div + <<- dec ten + <- dec num + ] + >>>>[<<<<+>>>>-]<<<< copy div to num + >[-]< clear ten + + =======INC1========= + <+> + ] + + < + [ + =======MOVER========= + [>+<-] + + =======ADD48======== + +++++++[<+++++++>-]<-> + + =======PUTC======= + <.[-]> + + ======MOVEL2======== + >[<<+>>-]< + + <- + ] + + >++++[<++++++++>-]<.[-] + + =================================================================== + =========================== END FOR =============================== + =================================================================== + + + >>>>>>> + ] + <<<<<<<< + + + + >[-]< + [-] + <<- +] + +======LF======== + +++++++++++.[-] +@ diff --git a/examples/rot13.bf b/examples/rot13.bf new file mode 100644 index 0000000..0967020 --- /dev/null +++ b/examples/rot13.bf @@ -0,0 +1,29 @@ + +-,+[ Read first character and start outer character reading loop + -[ Skip forward if character is 0 + >>++++[>++++++++<-] Set up divisor (32) for division loop + (MEMORY LAYOUT: dividend copy remainder divisor quotient zero zero) + <+<-[ Set up dividend (x minus 1) and enter division loop + >+>+>-[>>>] Increase copy and remainder / reduce divisor / Normal case: skip forward + <[[>+<-]>>+>] Special case: move remainder back to divisor and increase quotient + <<<<<- Decrement dividend + ] End division loop + ]>>>[-]+ End skip loop; zero former divisor and reuse space for a flag + >--[-[<->+++[-]]]<[ Zero that flag unless quotient was 2 or 3; zero quotient; check flag + ++++++++++++<[ If flag then set up divisor (13) for second division loop + (MEMORY LAYOUT: zero copy dividend divisor remainder quotient zero zero) + >-[>+>>] Reduce divisor; Normal case: increase remainder + >[+[<+>-]>+>>] Special case: increase remainder / move it back to divisor / increase quotient + <<<<<- Decrease dividend + ] End division loop + >>[<+>-] Add remainder back to divisor to get a useful 13 + >[ Skip forward if quotient was 0 + -[ Decrement quotient and skip forward if quotient was 1 + -<<[-]>> Zero quotient and divisor if quotient was 2 + ]<<[<<->>-]>> Zero divisor and subtract 13 from copy if quotient was 1 + ]<<[<<+>>-] Zero divisor and add 13 to copy if quotient was 0 + ] End outer skip loop (jump to here if ((character minus 1)/32) was not 2 or 3) + <[-] Clear remainder from first division if second division was skipped + <.[-] Output ROT13ed character from copy and clear it + <-,+ Read next character +] End character reading loop diff --git a/examples/test b/examples/test new file mode 100644 index 0000000..8ca4115 --- /dev/null +++ b/examples/test @@ -0,0 +1,2 @@ +++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.-- +------.>+.>. diff --git a/examples/test2 b/examples/test2 new file mode 100644 index 0000000..7bdabfc --- /dev/null +++ b/examples/test2 @@ -0,0 +1,2 @@ ++++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>.-.[>++>+<<--]>--.--.+.>>>++.<<.<------.+.+++++.>>- +.<++++.<--.>>>.<<---.<.-.-.>+.[+++++.---<]>>[.--.]<<.<+.++.++>+++[.<][.]<++. diff --git a/gofuck/main.go b/gofuck/main.go new file mode 100644 index 0000000..f6b198d --- /dev/null +++ b/gofuck/main.go @@ -0,0 +1,22 @@ +package main + +import "brainfuck" + +func main() { + m := New() + instructions := make([]byte, 0) + + // read in the instructions + if len(os.Args) < 2 { + panic("No input given") + } + + file, err := os.Open(os.Args[1]) + reader := bufio.NewReader(file) + instructions, err = ioutil.ReadAll(reader) + if err != nil { + panic(err) + } + + m.Run(instructions) +}