main.go (3120B)
1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "flag" 7 "fmt" 8 "io" 9 "log" 10 "os" 11 12 "github.com/abtrout/gbfy" 13 ) 14 15 var ( 16 codeFile = flag.String("f", "", "Path to Brainfuck program to execute") 17 dataFile = flag.String("d", "", "Path to file containing input data for program") 18 launchRepl = flag.Bool("i", false, "Launch interactive interpreter before exit") 19 ) 20 21 func main() { 22 flag.Parse() 23 24 var in, out bytes.Buffer 25 if *dataFile != "" { 26 if input, err := os.ReadFile(*dataFile); err != nil { 27 log.Fatalf("Failed to read input file: %v", err) 28 } else { 29 in = *bytes.NewBuffer(input) 30 } 31 } 32 bf := gbfy.New(&in, &out) 33 34 if *codeFile != "" { 35 if err := runCodeFile(bf); err != nil { 36 log.Fatalf("Execution failed: %v", err) 37 } 38 } else if err := runPiped(bf); err != nil { 39 log.Fatalf("Execution failed: %v", err) 40 } 41 42 if *launchRepl { 43 log.Println("Starting interactive REPL ...") 44 repl(bf, &in, &out) 45 } else { 46 log.Printf("Output from execution: %v\n", out.Bytes()) 47 } 48 } 49 50 const welcomeMsg = `Go Brainfuck Yourself! 51 :d[ump] to dump interpreter state 52 :f[uck] to reset interpreter state 53 :q[uit] to exit REPL loop` 54 55 func repl(bf *gbfy.Brainfuck, in, out *bytes.Buffer) { 56 fmt.Println(welcomeMsg) 57 58 f, err := os.Open("/dev/tty") 59 if err != nil { 60 log.Fatalf("Failed to open TTY: %v", err) 61 } 62 stdIn := bufio.NewReader(f) 63 64 // TODO: Switch interpreter input/output here? Continue 65 // reading from original `input` (i.e. from dataFile) 66 // until EOF -- then switch to TTY? 67 68 for { 69 fmt.Print("gbfy> ") 70 line, err := stdIn.ReadBytes('\n') 71 if err == io.EOF { 72 break 73 } else if err != nil { 74 log.Fatalf("Error! %v", err) 75 } 76 77 if len(line) > 0 && line[0] == ':' { 78 switch line[1] { 79 case 'd': 80 formatDump(bf, out.Bytes()) 81 case 'f': 82 out.Reset() // reset output buffer! 83 bf = gbfy.New(in, out) 84 fmt.Println("Reset interpreter!") 85 case 'q': 86 break 87 } 88 } else { 89 r := bufio.NewReader(bytes.NewReader(line)) 90 if err := runCommands(bf, r); err != nil { 91 log.Fatalf("Execution failed: %v", err) 92 } 93 } 94 } 95 } 96 97 func runCodeFile(bf *gbfy.Brainfuck) error { 98 f, err := os.Open(*codeFile) 99 if err != nil { 100 return err 101 } 102 in := bufio.NewReader(f) 103 if err := runCommands(bf, in); err != nil { 104 return err 105 } 106 return nil 107 } 108 109 func runPiped(bf *gbfy.Brainfuck) error { 110 stat, err := os.Stdin.Stat() 111 if err != nil { 112 return err 113 } 114 if (stat.Mode() & os.ModeCharDevice) == 0 { 115 in := bufio.NewReader(os.Stdin) 116 if err := runCommands(bf, in); err != nil { 117 return err 118 } 119 } 120 return nil 121 } 122 123 func runCommands(bf *gbfy.Brainfuck, r *bufio.Reader) error { 124 for { 125 cmds, err := r.ReadString('\n') 126 if err == io.EOF { 127 break 128 } else if err != nil { 129 return fmt.Errorf("failed to ReadByte: %v", err) 130 } 131 if err := bf.Eval(cmds); err != nil { 132 return fmt.Errorf("failed to Eval %q: %v", cmds, err) 133 } 134 } 135 return nil 136 } 137 138 func formatDump(bf *gbfy.Brainfuck, out []byte) { 139 d, cells, i, cmds := bf.Dump() 140 fmt.Printf("CELLS d: %3d, current cell value: %x\n", d, cells[d]) 141 fmt.Printf("CMDS i: %3d, current command: %q\n", i, cmds[i-1]) 142 fmt.Printf("OUT %v\n", out) 143 }