diff --git a/README.md b/README.md index dc94ed9..7bfde76 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ for performance comparison see no_optimisation branch. dump parsed program -eight eight bit execution + -print + pretty print parsed program -version display version diff --git a/cmd/bfg/main.go b/cmd/bfg/main.go index cd05c4f..1df7c8a 100644 --- a/cmd/bfg/main.go +++ b/cmd/bfg/main.go @@ -25,6 +25,7 @@ func main() { version := flag.Bool("version", false, "display version") eight := flag.Bool("eight", false, "eight bit execution") dump := flag.Bool("dump", false, "dump parsed program") + print := flag.Bool("print", false, "pretty print parsed program") flag.Usage = func() { fmt.Printf("Usage:\n %s [option] source.bf [input]\n", os.Args[0]) @@ -35,6 +36,7 @@ func main() { flag.Parse() if *version { fmt.Printf("Version: v%s%s\n", Version, VersionPrerelease) + fmt.Println("https://github.com/tristanmorgan/bfg\n") os.Exit(0) } @@ -61,6 +63,8 @@ func main() { } if *dump { parser.Dump(program, outputBuf) + } else if *print { + parser.Print(program, outputBuf) } else if *eight { data := make([]byte, parser.DataSize) parser.Execute(data, program, inputBuf, outputBuf) diff --git a/parser/print.go b/parser/print.go new file mode 100644 index 0000000..87d452d --- /dev/null +++ b/parser/print.go @@ -0,0 +1,83 @@ +package parser + +import ( + "bufio" + "fmt" + "strings" +) + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} + +func repeatDirection(neg, pos string, vect int) string { + if vect > 0 { + return strings.Repeat(pos, vect) + } + return strings.Repeat(neg, abs(vect)) +} + +func instPrint(inst, lastInst Instruction) string { + switch inst.operator { + case opAddDp: + return repeatDirection("<", ">", inst.operand) + case opAddVal: + return repeatDirection("-", "+", inst.operand) + case opSetVal: + prefix := "[-]" + if lastInst.IsZeroOp() && inst.operand != 0 { + prefix = "" + } + return prefix + repeatDirection("-", "+", inst.operand) + case opOut: + return "." + case opIn: + return "," + case opJmpZ: + return "[" + case opJmpNz: + return "]" + case opMove: + return "[-" + repeatDirection("<", ">", inst.operand) + "+" + repeatDirection(">", "<", inst.operand) + "]" + case opSkip: + return "[" + repeatDirection("<", ">", inst.operand) + "]" + case opMulVal: + return "" + case opNoop: + if lastInst.operator == opMulVal { + multiplier := strings.Repeat("+", abs(inst.operand)) + if inst.operand < 0 { + multiplier = strings.Repeat("-", abs(inst.operand)) + } + if lastInst.operand > 0 { + return "[-" + strings.Repeat(">", lastInst.operand) + multiplier + strings.Repeat("<", lastInst.operand) + "]" + } + return "[-" + strings.Repeat("<", abs(lastInst.operand)) + multiplier + strings.Repeat(">", abs(lastInst.operand)) + "]" + } + return "" + default: + return "" + } +} + +// Print pretty prints out the parsed program. +func Print(program []Instruction, writer *bufio.Writer) { + depth := 0 + startLoop := NewInstruction('[') + endLoop := NewInstruction(']') + lastInst := NewInstruction('!') + for _, inst := range program { + if inst.SameOp(endLoop) { + depth-- + } + fmt.Fprintln(writer, strings.Repeat("\t", depth), instPrint(inst, lastInst)) + if inst.SameOp(startLoop) { + depth++ + } + lastInst = inst + } + writer.Flush() +} diff --git a/parser/print_test.go b/parser/print_test.go new file mode 100644 index 0000000..feb11e1 --- /dev/null +++ b/parser/print_test.go @@ -0,0 +1,31 @@ +package parser + +import ( + "bufio" + "bytes" + "reflect" + "testing" +) + +func TestPrint(t *testing.T) { + program := []Instruction{ + {opNoop, 0}, + {opAddDp, 5}, + {opSetVal, 0}, + {opAddVal, 5}, + {opMove, 2}, + {opJmpZ, 7}, + {opIn, 1}, + {opJmpNz, 5}, + {opAddDp, 2}, + } + var buf bytes.Buffer + outputBuf := bufio.NewWriter(&buf) + Print(program, outputBuf) + got := buf.String() + want := " \n >>>>>\n [-]\n +++++\n [->>+<<]\n [\n\t ,\n ]\n >>\n" + + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v want %v", got, want) + } +} diff --git a/parser/tokenise.go b/parser/tokenise.go index e71f988..6957a65 100644 --- a/parser/tokenise.go +++ b/parser/tokenise.go @@ -57,6 +57,9 @@ func Tokenise(input io.ByteReader) (program []Instruction, err error) { pc-- } else if pc-jmpPc == 2 && program[pc-1].SameOp(NewInstruction('+')) { pc = jmpPc + if program[jmpPc-1].SameOp(NewInstruction('+')) { + pc-- + } program = program[:pc] program = append(program, Instruction{opSetVal, 0}) } else if pc-jmpPc == 2 && program[pc-1].SameOp(NewInstruction('>')) { diff --git a/parser/tokenise_test.go b/parser/tokenise_test.go index 3cd459d..d0cff37 100644 --- a/parser/tokenise_test.go +++ b/parser/tokenise_test.go @@ -16,7 +16,7 @@ func TestTokenise(t *testing.T) { }{ { "small_prog", - ">>>>>[-]zero+++++>+++++[->>+<<]move>>[>>+<<-]move", + ">>>>>++[-]zero+++++>+++++[->>+<<]move>>[>>+<<-]move", []Instruction{ {opNoop, 0}, {opAddDp, 5}, diff --git a/sample/rot13.bf b/sample/rot13.bf index 3b057ac..a6f53be 100755 --- a/sample/rot13.bf +++ b/sample/rot13.bf @@ -1,5 +1,5 @@ #!/usr/bin/env bf ,+[-[>>++++[>++++++++<-]<+<-[>+>+>-[>>>]<[[>+<-]>>+>]<<<<<-]]>>>[-]+>-- -[-[<->+++[-]]]<[++++++++++++<[>-[>+>>]>[+[<+>-]>+>>]<<<<<-]>>[<+>-]>[-[ +[-[<->[-]]]<[++++++++++++<[>-[>+>>]>[+[<+>-]>+>>]<<<<<-]>>[<+>-]>[-[ -<<[-]>>]<<[<<->>-]>>]<<[<<+>>-]]<[-]<.[-]<,+]