From f673534c53d6123d3efc1dfa5e8db344de4fa611 Mon Sep 17 00:00:00 2001 From: Mio Date: Mon, 4 Nov 2019 22:04:53 +0800 Subject: [PATCH] Initial commit --- debugger.go | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 154 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100755 debugger.go create mode 100755 main.go diff --git a/debugger.go b/debugger.go new file mode 100755 index 0000000..138f40e --- /dev/null +++ b/debugger.go @@ -0,0 +1,161 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" + + "gitlab.lazymio.cn/mio/miniplc0/vm" +) + +var debugHelp = `Simple miniplc0 debugger. +You can use the abbreviation of a command. +[H]elp -- Show this message. +[N]ext -- Run a single instruction. +[L]ist n -- List n instructions. +[S]tack n -- Show n stack elemets. +[I]formation -- Show current information. +[Q]uit -- Quit the debugger. +` + +// [R]estart -- Restart the debugging. Not impleneted + +type cmd int32 + +const ( + cNext cmd = iota + cList + cStack + cInformation + cRestart + cHelp + cQuit +) + +type debuggerCommand struct { + C cmd + X int32 +} + +func newCommandFromString(line string) *debuggerCommand { + ln := strings.TrimSpace(line) + tokens := strings.Split(ln, " ") + if len(tokens) == 0 { + return nil + } + if len(tokens) == 1 { + switch strings.ToLower(tokens[0]) { + case "h": + fallthrough + case "help": + return &debuggerCommand{C: cHelp} + case "q": + fallthrough + case "quit": + return &debuggerCommand{C: cQuit} + case "r": + fallthrough + case "restart": + //return &debuggerCommand{C: cRestart} + return nil + case "i": + fallthrough + case "info": + fallthrough + case "infomation": + return &debuggerCommand{C: cInformation} + case "l": + fallthrough + case "list": + return &debuggerCommand{C: cList, X: 10} + case "s": + fallthrough + case "stack": + return &debuggerCommand{C: cStack, X: 20} + case "n": + fallthrough + case "next": + return &debuggerCommand{C: cNext} + } + return nil + } + if len(tokens) == 2 { + x, err := strconv.ParseInt(tokens[1], 10, 32) + if err != nil { + return nil + } + switch strings.ToLower(tokens[0]) { + case "l": + fallthrough + case "list": + return &debuggerCommand{C: cList, X: int32(x)} + case "s": + fallthrough + case "stack": + return &debuggerCommand{C: cStack, X: int32(x)} + } + return nil + } + return nil +} + +// Debug debugs the epf file. +func Debug(file *os.File) { + epf, err := vm.NewEPFv1FromFile(file) + if err != nil { + panic(err) + } + v := vm.NewVMDefault(len(epf.GetInstructions()), epf.GetEntry()) + v.Load(epf.GetInstructions()) + scanner := bufio.NewScanner(os.Stdin) + fmt.Print(debugHelp) + for true { + fmt.Print(">") + scanner.Scan() + cmd := newCommandFromString(scanner.Text()) + quit := false + if cmd == nil { + fmt.Println("Wrong format.\nType 'help' to see more.") + continue + } + switch cmd.C { + case cHelp: + fmt.Print(debugHelp) + case cQuit: + quit = true + break + case cRestart: + quit = true + break + case cNext: + if err = v.RunSingle(); err != nil { + if err == vm.ErrIllegalInstruction { + fmt.Println("The program has stopped.") + } else { + // Should have better user experience. + fmt.Println(err) + fmt.Println(*v) + os.Exit(0) + } + } + fmt.Printf("Next instruction: %v\n", *(v.NextInstruction())) + case cInformation: + fmt.Printf("IP=%v SP=%v\nInstructions[IP]:%v\n", v.IP, v.SP, *(v.NextInstruction())) + stackvalue := v.GetStackTop() + if stackvalue != nil { + fmt.Printf("Stack[SP-1]=%v\n", *stackvalue) + } else { + fmt.Println("Stack[SP-1]=[Invalid]") + } + case cList: + fmt.Println(v.InstructionGraph(cmd.X)) + case cStack: + fmt.Println(v.StackGraph(cmd.X)) + } + if quit { + break + } + } +} diff --git a/main.go b/main.go new file mode 100755 index 0000000..8678ff3 --- /dev/null +++ b/main.go @@ -0,0 +1,154 @@ +package main + +import ( + "bufio" + "fmt" + "os" + + "github.com/BUAA-SE-Compiling/vm" + + flag "github.com/spf13/pflag" +) + +// Run runs the epf file. +func Run(file *os.File) { + epf, err := vm.NewEPFv1FromFile(file) + if err != nil { + panic(err) + } + vm := vm.NewVMDefault(len(epf.GetInstructions()), epf.GetEntry()) + vm.Load(epf.GetInstructions()) + if err := vm.Run(); err != nil { + fmt.Println(vm) + panic(err) + } +} + +// Interprete interprets the input file directly. +func Interprete(file *os.File) { + scanner := bufio.NewScanner(file) + instructions := []vm.Instruction{} + linenum := 0 + for scanner.Scan() { + linenum++ + single := vm.ParseInstruction(scanner.Text()) + if single == nil { + fmt.Fprintf(os.Stderr, "Line %v: Bad instruction", linenum) + } + instructions = append(instructions, *single) + } + vm := vm.NewVMDefault(len(instructions), 0) + vm.Load(instructions) + if err := vm.Run(); err != nil { + fmt.Println(vm) + panic(err) + } +} + +// Decompile decompiles the epf file. +func Decompile(file *os.File) { + epf, err := vm.NewEPFv1FromFile(file) + if err != nil { + panic(err) + } + for _, i := range epf.GetInstructions() { + fmt.Println(i) + } + return +} + +// Assemble assembles the text file to an epf file. +func Assemble(in *os.File, out *os.File) { + scanner := bufio.NewScanner(in) + instructions := []vm.Instruction{} + linenum := 0 + for scanner.Scan() { + linenum++ + single := vm.ParseInstruction(scanner.Text()) + if single == nil { + fmt.Fprintf(os.Stderr, "Line %v: Bad instruction", linenum) + } + instructions = append(instructions, *single) + } + epf := vm.NewEPFv1FromInstructions(instructions, 0) + epf.WriteFile(out) + return +} + +// We don't know whehter a flag is unset or set with an empty string if its default value is an empty string. +func isFlagPassed(name string) bool { + found := false + flag.Visit(func(f *flag.Flag) { + if f.Name == name { + found = true + } + }) + return found +} + +func main() { + var input string + var output string + var decompile bool + var run bool + var debug bool + var help bool + var assemble bool + var interprete bool + flag.CommandLine.Init("Default", flag.ContinueOnError) + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "A vm implementation for mini plc0.\n") + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flag.PrintDefaults() + } + flag.StringVarP(&input, "input", "i", "-", "The input file. The default is os.Stdin.") + flag.StringVarP(&output, "output", "o", "", "The output file.") + flag.BoolVarP(&decompile, "decompile", "D", false, "Decompile without running.") + flag.BoolVarP(&run, "run", "R", false, "Run the file.") + flag.BoolVarP(&interprete, "interprete", "I", false, "Interprete the file.") + flag.BoolVarP(&debug, "debug", "d", false, "Debug the file.") + flag.BoolVarP(&help, "help", "h", false, "Show this message.") + flag.BoolVarP(&assemble, "assemble", "A", false, "Assemble a text file to an EPFv1 file.") + if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { + fmt.Println(err) + fmt.Fprintf(os.Stderr, "Run with --help for details.\n") + os.Exit(2) + } + if help || (decompile && !isFlagPassed("output")) { + flag.Usage() + os.Exit(2) + } + if !debug && !run && !decompile && !assemble && !interprete { + fmt.Fprintf(os.Stderr, "You must choose to decomple, run, assemble, interprete or debug.\n") + fmt.Fprintf(os.Stderr, "Run with --help for details.\n") + os.Exit(2) + } + var file *os.File + var err error + if input != "-" { + file, err = os.Open(input) + if err != nil { + panic(err) + } + defer file.Close() + } else { + file = os.Stdin + } + if debug { + Debug(file) + } else if decompile { + Decompile(file) + } else if run { + Run(file) + } else if assemble { + out, err := os.OpenFile(output, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660) + if err != nil { + panic(err) + } + defer out.Close() + Assemble(file, out) + } else if interprete { + Interprete(file) + } + os.Exit(0) +}