Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
115 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} |