package main import ( "bufio" "errors" "fmt" "io/ioutil" "os" "strings" "github.com/fatih/color" "github.com/forestgiant/sliceutil" flag "github.com/spf13/pflag" ) func main() { bf := BrainfuckEmulator{} var err error err = bf.New() if err != nil { if strings.HasSuffix(err.Error(), "::ignore") { return } panic(err) } err = bf.Run() if err != nil { if strings.HasSuffix(err.Error(), "::ignore") { return } panic(err) } } // BrainfuckEmulator is used to emulate brainfuck. type BrainfuckEmulator struct { code string pairs [][]int memory []int execution int pointer int debug bool filename string } // New instance of BrainfuckEmulator. func (bf *BrainfuckEmulator) New() error { var err error err = bf.args() if err != nil { return err } err = bf.readScript(bf.filename) if err != nil { return err } err = bf.getBracketPairs() if err != nil { return err } return nil } func (bf *BrainfuckEmulator) args() error { debugptr := flag.BoolP("debug", "d", false, "Enable debug mode") flag.Parse() args := flag.Args() if len(args) == 1 { bf.filename = args[0] bf.debug = *debugptr } else { fmt.Fprintln(os.Stdout, "Brainfuck help") fmt.Fprintln(os.Stdout, "usage: brainfuck ") flag.PrintDefaults() return errors.New("bf: invalid arguments ::ignore") } return nil } func (bf *BrainfuckEmulator) fancyoutput() { for i := 0; i < len(bf.memory); i++ { if bf.pointer == i { color.Set(color.FgRed) fmt.Fprintf(os.Stdout, fmt.Sprint(bf.memory[i])) color.Unset() } else { fmt.Fprintf(os.Stdout, fmt.Sprint(bf.memory[i])) } if i < len(bf.memory)-1 { fmt.Fprintf(os.Stdout, ",") } } fmt.Fprintln(os.Stdout, "") for i := 0; i < len(bf.code); i++ { if bf.execution == i { color.Set(color.FgRed) fmt.Fprintf(os.Stdout, string(bf.code[i])) color.Unset() } else { fmt.Fprintf(os.Stdout, string(bf.code[i])) } } fmt.Fprintln(os.Stdout, "") } // Run the brainfuck program. func (bf *BrainfuckEmulator) Run() error { bf.pointer = 0 bf.execution = 0 bf.memory = []int{0} for true { if bf.debug { bf.fancyoutput() } if bf.execution >= len(bf.code) { break } switch bf.code[bf.execution] { case '+': bf.memory[bf.pointer]++ if bf.memory[bf.pointer] == 256 { bf.memory[bf.pointer] = 0 } case '-': bf.memory[bf.pointer]-- if bf.memory[bf.pointer] == -1 { bf.memory[bf.pointer] = 255 } case '>': bf.pointer++ if len(bf.memory) <= bf.pointer { bf.memory = append(bf.memory, 0) } case '<': bf.pointer-- if bf.pointer < 0 { fmt.Fprintln(os.Stdout, "Memory underflow: pointer can't be less than 0") return errors.New("bf: memory underflow ::ignore") } case '[': if bf.memory[bf.pointer] == 0 { for i := 0; i < len(bf.pairs[0]); i++ { if bf.pairs[0][i] == bf.execution { bf.execution = bf.pairs[1][i] break } } } case ']': if bf.memory[bf.pointer] != 0 { for i := 0; i < len(bf.pairs[1]); i++ { if bf.pairs[1][i] == bf.execution { bf.execution = bf.pairs[0][i] break } } } case '@': bf.pointer = 0 case '$': bf.fancyoutput() case ',': char := make([]byte, 1) os.Stdin.Read(char) bf.memory[bf.pointer] = int(char[0]) case '.': b := byte(bf.memory[bf.pointer]) os.Stdout.Write([]byte{b}) } bf.execution++ } return nil } func (bf *BrainfuckEmulator) readScript(filename string) error { filebuffer, err := ioutil.ReadFile(filename) if err != nil { if strings.HasSuffix(err.Error(), "no such file or directory") { fmt.Fprintln(os.Stdout, "IO Error: no such file or directory") return errors.New("bf: file missing ::ignore") } return err } inputdata := string(filebuffer) data := bufio.NewScanner(strings.NewReader(inputdata)) data.Split(bufio.ScanRunes) allowed := []string{"+", "-", "<", ">", "[", "]", ",", ".", "@"} final := "" for data.Scan() { if sliceutil.Contains(allowed, data.Text()) { final += data.Text() } } bf.code = final return nil } func (bf *BrainfuckEmulator) getBracketPairs() error { pairs := [][]int{{}, {}} temp := []int{} var err error for i := 0; i < len(bf.code); i++ { if bf.code[i] == '[' { temp = append(temp, i) } else if bf.code[i] == ']' { if len(temp) > 0 { pairs[0] = append(pairs[0], temp[len(temp)-1]) pairs[1] = append(pairs[1], i) temp = temp[:len(temp)-1] } else { fmt.Fprintln(os.Stdout, "Syntax error: missing opening bracket") return errors.New("bf: missing opening bracket ::ignore") } } } if len(temp) != 0 { fmt.Fprintln(os.Stdout, "Syntax error: spare opening bracket") return errors.New("bf: sqare opening brackets ::ignore") } bf.pairs = pairs return err }