From 21565ef2e0d3ed912fa2cea556b1ebbfe9202262 Mon Sep 17 00:00:00 2001 From: deoxxa Date: Sat, 5 Dec 2015 22:45:57 +1100 Subject: [PATCH] add simple REPL --- repl/repl.go | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 repl/repl.go diff --git a/repl/repl.go b/repl/repl.go new file mode 100644 index 00000000..0d70cc05 --- /dev/null +++ b/repl/repl.go @@ -0,0 +1,115 @@ +// Package repl implements a REPL (read-eval-print loop) for otto. +package repl + +import ( + "fmt" + "io" + "strings" + "sync/atomic" + + "github.com/robertkrimen/otto" + "gopkg.in/readline.v1" +) + +var counter uint32 + +// DebuggerHandler implements otto's debugger handler signature, providing a +// simple drop-in debugger implementation. +func DebuggerHandler(vm *otto.Otto) { + i := atomic.AddUint32(&counter, 1) + + // purposefully ignoring the error here - we can't do anything useful with + // it except panicking, and that'd be pretty rude. it'd be easy enough for a + // consumer to define an equivalent function that _does_ panic if desired. + _ = RunWithPrompt(vm, fmt.Sprintf("DEBUGGER[%d]>", i)) +} + +// Run creates a REPL with the default prompt and no prelude. +func Run(vm *otto.Otto) error { + return RunWithPromptAndPrelude(vm, "", "") +} + +// RunWithPrompt runs a REPL with the given prompt and no prelude. +func RunWithPrompt(vm *otto.Otto, prompt string) error { + return RunWithPromptAndPrelude(vm, prompt, "") +} + +// RunWithPrelude runs a REPL with the default prompt and the given prelude. +func RunWithPrelude(vm *otto.Otto, prelude string) error { + return RunWithPromptAndPrelude(vm, "", prelude) +} + +// RunWithPromptAndPrelude runs a REPL with the given prompt and prelude. +func RunWithPromptAndPrelude(vm *otto.Otto, prompt, prelude string) error { + if prompt == "" { + prompt = ">" + } + + prompt = strings.Trim(prompt, " ") + prompt += " " + + rl, err := readline.New(prompt) + if err != nil { + return err + } + + if prelude != "" { + if _, err := io.Copy(rl.Stderr(), strings.NewReader(prelude+"\n")); err != nil { + return err + } + + rl.Refresh() + } + + var d []string + + for { + l, err := rl.Readline() + if err != nil { + if err == readline.ErrInterrupt { + if d != nil { + d = nil + + rl.SetPrompt(prompt) + rl.Refresh() + + continue + } + + break + } + + return err + } + + if l == "" { + continue + } + + d = append(d, l) + + s, err := vm.Compile("repl", strings.Join(d, "\n")) + if err != nil { + rl.SetPrompt(strings.Repeat(" ", len(prompt))) + } else { + rl.SetPrompt(prompt) + + d = nil + + v, err := vm.Eval(s) + if err != nil { + if oerr, ok := err.(*otto.Error); ok { + io.Copy(rl.Stdout(), strings.NewReader(oerr.String())) + } else { + io.Copy(rl.Stdout(), strings.NewReader(err.Error())) + } + } else { + rl.Stdout().Write([]byte(v.String() + "\n")) + } + } + + rl.Refresh() + } + + return rl.Close() +}