commit 62398ca2edfd1598bc5a16189ab9bb686c3e4e78 Author: MrMelon Date: Tue Oct 20 19:46:07 2020 +0100 Add brainfuck script in go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3b7ee7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +brainfuck diff --git a/brainfuck.go b/brainfuck.go new file mode 100644 index 0000000..a2849fd --- /dev/null +++ b/brainfuck.go @@ -0,0 +1,227 @@ +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 +} diff --git a/build b/build new file mode 100755 index 0000000..7d80b72 --- /dev/null +++ b/build @@ -0,0 +1,3 @@ +#!/bin/bash +go build -o brainfuck +sudo cp ./brainfuck /usr/bin/brainfuck