go-brainfuck-yourself

A Brainfuck interpreter that I wrote myself in Go
git clone git@abtrout.com:go-brainfuck-yourself.git
Log | Files | Refs | README

commit 28c1e49c0224de7ae71fcce230995bd612a1b5b0
parent 573382e878c41f7684b2544e9862b69b89071aae
Author: david cochran <about.trout@gmail.com>
Date:   Mon, 11 Dec 2023 15:16:34 -0800

fixes #4 - execute commands from unix pipes

Diffstat:
Mcmd/gbfy/main.go | 124++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 79 insertions(+), 45 deletions(-)

diff --git a/cmd/gbfy/main.go b/cmd/gbfy/main.go @@ -13,71 +13,59 @@ import ( ) var ( - codeFile = flag.String("f", "", "Path to Brainfuck file to interpret") - inputFile = flag.String("i", "", "Path to file containing input data for program") - launchREPL = flag.Bool("repl", false, "Launch interactive interpreter before exit") + codeFile = flag.String("f", "", "Path to Brainfuck program to execute") + dataFile = flag.String("d", "", "Path to file containing input data for program") + endInteractive = flag.Bool("i", false, "Launch interactive interpreter before exit") ) func main() { flag.Parse() var ( - err error - cmds []byte - input []byte + input []byte + output bytes.Buffer ) - if *codeFile != "" { - cmds, err = os.ReadFile(*codeFile) - if err != nil { - log.Fatalf("Failed to read code file: %v", err) - } - } - if *inputFile != "" { - input, err = os.ReadFile(*inputFile) - if err != nil { + if *dataFile != "" { + var err error + if input, err = os.ReadFile(*dataFile); err != nil { log.Fatalf("Failed to read input file: %v", err) } } - var output bytes.Buffer bf := gbfy.New(bytes.NewBuffer(input), &output) - if err != nil { - log.Fatalf("Failed to initialize interpreter: %v", err) - } - - if len(cmds) > 0 { - for _, cmd := range cmds { - if err := bf.Eval(cmd); err != nil { - log.Fatalf("Failed to Eval command %q: %v", cmd, err) - } + if *codeFile != "" { + if err := runCodeFile(bf); err != nil { + log.Fatalf("Execution failed: %v", err) } - log.Printf("Output from execution: %v\n", output.Bytes()) + } else if err := runPiped(bf); err != nil { + log.Fatalf("Execution failed: %v", err) } - if *launchREPL { - log.Println("Starting interactive REPL") + if *endInteractive { + log.Println("Starting interactive REPL ...") repl(bf) } } -const ( - welcomeMsg = `Go Brainfuck Yourself! +const welcomeMsg = `Go Brainfuck Yourself! :q[uit] to exit REPL loop :d[ump] to dump interpreter state :f[uck] to reset interpreter state` - prompt = "gbfy> " -) - func repl(bf *gbfy.Brainfuck) { fmt.Println(welcomeMsg) - in := bufio.NewReader(os.Stdin) + + f, err := os.Open("/dev/tty") + if err != nil { + log.Fatalf("Failed to open TTY: %v", err) + } + in := bufio.NewReader(f) + for { - fmt.Print(prompt) + fmt.Print("gbfy> ") line, err := in.ReadString('\n') if err == io.EOF { - fmt.Println("") - return + break } else if err != nil { log.Fatalf("Error! %v", err) } @@ -85,25 +73,71 @@ func repl(bf *gbfy.Brainfuck) { if len(line) > 0 && line[0] == ':' { switch line[1] { case 'd': - replDump(bf) + formatDump(bf.Dump()) case 'f': bf.Reset() fmt.Println("Reset interpreter!") case 'q': - return + break } } else { - for _, cmd := range []byte(line) { - if err := bf.Eval(cmd); err != nil { - log.Fatalf("Failed to Eval command %q: %v", cmd, err) - } + r := bufio.NewReader(bytes.NewReader([]byte(line))) + if err := runCommands(bf, r); err != nil { + log.Fatalf("Execution failed: %v", err) } } } } -func replDump(bf *gbfy.Brainfuck) { - d, cells, i, cmds, out := bf.Dump() +func runCodeFile(bf *gbfy.Brainfuck) error { + f, err := os.Open(*codeFile) + if err != nil { + return err + } + in := bufio.NewReader(f) + if err := runCommands(bf, in); err != nil { + return err + } + logOutput(bf) + return nil +} + +func runPiped(bf *gbfy.Brainfuck) error { + stat, err := os.Stdin.Stat() + if err != nil { + return err + } + if (stat.Mode() & os.ModeCharDevice) == 0 { + in := bufio.NewReader(os.Stdin) + if err := runCommands(bf, in); err != nil { + return err + } + } + logOutput(bf) + return nil +} + +func runCommands(bf *gbfy.Brainfuck, r *bufio.Reader) error { + for { + cmd, err := r.ReadByte() + if err == io.EOF { + break + } else if err != nil { + return fmt.Errorf("failed to ReadByte: %v", err) + } + if err := bf.Eval(cmd); err != nil { + return fmt.Errorf("failed to Eval %q: %v", cmd, err) + } + } + return nil +} + +func logOutput(bf *gbfy.Brainfuck) { + _, _, _, _, out := bf.Dump() + log.Printf("Output from execution: %v\n", out) +} + +func formatDump(d int, cells []byte, i int, cmds []byte, out []byte) { fmt.Printf("Cells; d: %d, current cell value: %x\n", d, cells[d]) fmt.Printf("Cmds: i: %d, current command: %q\n", i, cmds[i-1]) fmt.Printf("Out: %v\n", out)