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
|
2012-11-14 01:14:14 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
2019-10-20 11:22:40 +00:00
|
|
|
"io"
|
2012-11-14 01:14:14 +00:00
|
|
|
"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" }
|
|
|
|
|
2012-11-14 01:14:14 +00:00
|
|
|
type Machine struct {
|
|
|
|
array []byte
|
|
|
|
ptr int
|
|
|
|
reader *bufio.Reader
|
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
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a new machine with standard memory size
|
2019-10-20 11:22:40 +00:00
|
|
|
func NewStdin() *Machine {
|
|
|
|
return New(os.Stdin)
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(in io.Reader) *Machine {
|
2012-11-14 01:14:14 +00:00
|
|
|
bytes := make([]byte, MEM_STD)
|
|
|
|
return &Machine{
|
|
|
|
array: bytes,
|
|
|
|
ptr: 0,
|
2019-10-20 11:22:40 +00:00
|
|
|
reader: bufio.NewReader(in),
|
2019-10-20 11:46:17 +00:00
|
|
|
InstructionLimit: 0,
|
|
|
|
MemMax: 3000000,
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements '>'
|
2019-10-20 11:46:17 +00:00
|
|
|
func (m *Machine) PtrIncr() error {
|
2012-11-14 01:14:14 +00:00
|
|
|
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{}
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
2019-10-20 11:46:17 +00:00
|
|
|
return nil
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Implements '<'
|
2019-10-20 11:46:17 +00:00
|
|
|
func (m *Machine) PtrDecr() error {
|
2012-11-14 01:14:14 +00:00
|
|
|
if m.ptr == 0 {
|
2019-10-20 11:46:17 +00:00
|
|
|
return ErrMemoryUnderflow{}
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
|
|
|
m.ptr -= 1
|
2019-10-20 11:46:17 +00:00
|
|
|
return nil
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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() {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-14 03:23:58 +00:00
|
|
|
func (m *Machine) value() byte {
|
|
|
|
return m.array[m.ptr]
|
|
|
|
}
|
|
|
|
|
2012-11-14 01:14:14 +00:00
|
|
|
// Run the whole program specified by input
|
2019-10-20 11:46:17 +00:00
|
|
|
func (m *Machine) Run(input []byte) error {
|
|
|
|
instrCount := 0
|
2012-11-14 03:23:58 +00:00
|
|
|
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{}
|
|
|
|
}
|
2012-11-14 03:23:58 +00:00
|
|
|
instr := input[ip]
|
2012-11-14 03:27:18 +00:00
|
|
|
// 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
|
2012-11-14 03:23:58 +00:00
|
|
|
}
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
2012-11-14 03:27:18 +00:00
|
|
|
} else if instr == ']' && m.value() != 0 {
|
2012-11-14 03:23:58 +00:00
|
|
|
// if *ptr != 0, go back to [
|
2012-11-14 03:27:18 +00:00
|
|
|
for lc := 1; lc > 0; {
|
|
|
|
ip -= 1
|
|
|
|
if input[ip] == ']' {
|
|
|
|
lc += 1
|
|
|
|
} else if input[ip] == '[' {
|
|
|
|
lc -= 1
|
2012-11-14 03:23:58 +00:00
|
|
|
}
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|
|
|
|
} else if instr == '>' {
|
2019-10-20 11:46:17 +00:00
|
|
|
if err := m.PtrIncr(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2012-11-14 01:14:14 +00:00
|
|
|
} else if instr == '<' {
|
2019-10-20 11:46:17 +00:00
|
|
|
if err := m.PtrDecr(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2012-11-14 01:14:14 +00:00
|
|
|
} 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
|
2012-11-14 01:14:14 +00:00
|
|
|
}
|