Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
wtdcode committed Nov 4, 2019
0 parents commit f673534
Show file tree
Hide file tree
Showing 2 changed files with 315 additions and 0 deletions.
161 changes: 161 additions & 0 deletions debugger.go
Original file line number Diff line number Diff line change
@@ -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
}
}
}
154 changes: 154 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit f673534

Please sign in to comment.