Add brainfuck script in go
This commit is contained in:
commit
62398ca2ed
|
@ -0,0 +1 @@
|
|||
brainfuck
|
|
@ -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 <filename>")
|
||||
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
|
||||
}
|
Reference in New Issue