gofuck/brainfuck.go

153 lines
2.9 KiB
Go
Raw Permalink Normal View History

2012-11-14 03:33:57 +00:00
// gofuck is a simple brainfuck interpreter written in Go
2012-11-14 01:18:55 +00:00
package gofuck
import (
"bufio"
"fmt"
"io"
"os"
)
const (
MEM_STD = 30000
)
2019-10-20 11:46:17 +00:00
type ErrMemoryOverflow struct {}
func (e ErrMemoryOverflow) Error() string { return "Memory Overflow" }
type ErrMemoryUnderflow struct {}
func (e ErrMemoryUnderflow) Error() string { return "Memory Underflow" }
type ErrInstructionLimit struct {}
func (e ErrInstructionLimit) Error() string { return "Instruction Limit Reached" }
type Machine struct {
array []byte
ptr int
reader *bufio.Reader
2019-10-20 12:16:46 +00:00
writer io.Writer
2019-10-20 11:46:17 +00:00
// InstructionLimit prevents a runaway execution if running under a controlled environment
InstructionLimit int
// MemMax is the limit of how large memory can expand
MemMax int
}
// Returns a new machine with standard memory size
func NewStdin() *Machine {
2019-10-20 12:16:46 +00:00
return New(os.Stdin, os.Stdout)
}
2019-10-20 12:16:46 +00:00
func New(in io.Reader, out io.Writer) *Machine {
bytes := make([]byte, MEM_STD)
return &Machine{
array: bytes,
ptr: 0,
reader: bufio.NewReader(in),
2019-10-20 12:16:46 +00:00
writer: out,
2019-10-20 11:46:17 +00:00
InstructionLimit: 0,
MemMax: 3000000,
}
}
// Implements '>'
2019-10-20 11:46:17 +00:00
func (m *Machine) PtrIncr() error {
if m.ptr >= len(m.array)-1 {
m.array = append(m.array, 0)
}
m.ptr += 1
2019-10-20 11:46:17 +00:00
if m.ptr > m.MemMax {
return ErrMemoryOverflow{}
}
2019-10-20 11:46:17 +00:00
return nil
}
// Implements '<'
2019-10-20 11:46:17 +00:00
func (m *Machine) PtrDecr() error {
if m.ptr == 0 {
2019-10-20 11:46:17 +00:00
return ErrMemoryUnderflow{}
}
m.ptr -= 1
2019-10-20 11:46:17 +00:00
return nil
}
// Implements '+'
func (m *Machine) ByteIncr() {
m.array[m.ptr] += 1
}
// Implements '-'
func (m *Machine) ByteDecr() {
m.array[m.ptr] -= 1
}
// Implements the '.' command
func (m *Machine) Output() {
2019-10-20 12:16:46 +00:00
fmt.Fprintf(m.writer, "%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
}
}
func (m *Machine) value() byte {
return m.array[m.ptr]
}
// Run the whole program specified by input
2019-10-20 11:46:17 +00:00
func (m *Machine) Run(input []byte) error {
instrCount := 0
for ip := 0; ip < len(input); ip++ {
2019-10-20 11:46:17 +00:00
instrCount++
if m.InstructionLimit > 0 && instrCount > m.InstructionLimit {
return ErrInstructionLimit{}
}
instr := input[ip]
// if *ptr == 0, jump to ]
if instr == '[' && m.value() == 0 {
for lc := 1; lc > 0; {
ip += 1
if input[ip] == ']' {
lc -= 1
} else if input[ip] == '[' {
lc += 1
}
}
} else if instr == ']' && m.value() != 0 {
// if *ptr != 0, go back to [
for lc := 1; lc > 0; {
ip -= 1
if input[ip] == ']' {
lc += 1
} else if input[ip] == '[' {
lc -= 1
}
}
} else if instr == '>' {
2019-10-20 11:46:17 +00:00
if err := m.PtrIncr(); err != nil {
return err
}
} else if instr == '<' {
2019-10-20 11:46:17 +00:00
if err := m.PtrDecr(); err != nil {
return err
}
} else if instr == '+' {
m.ByteIncr()
} else if instr == '-' {
m.ByteDecr()
} else if instr == '.' {
m.Output()
} else if instr == ',' {
m.Input()
}
}
2019-10-20 11:46:17 +00:00
return nil
}