diff options
Diffstat (limited to 'cmd/ethereum/repl')
-rw-r--r-- | cmd/ethereum/repl/console_colors_windows.go | 80 | ||||
-rw-r--r-- | cmd/ethereum/repl/repl.go | 85 | ||||
-rw-r--r-- | cmd/ethereum/repl/repl_darwin.go | 127 | ||||
l--------- | cmd/ethereum/repl/repl_linux.go | 1 | ||||
-rw-r--r-- | cmd/ethereum/repl/repl_windows.go | 75 |
5 files changed, 368 insertions, 0 deletions
diff --git a/cmd/ethereum/repl/console_colors_windows.go b/cmd/ethereum/repl/console_colors_windows.go new file mode 100644 index 000000000..1f517bd8c --- /dev/null +++ b/cmd/ethereum/repl/console_colors_windows.go @@ -0,0 +1,80 @@ +/* Inspired by https://github.com/xuyu/logging/blob/master/colorful_win.go */ + +package ethrepl + +import ( + "syscall" + "unsafe" +) + +type color uint16 + +const ( + green = color(0x0002) + red = color(0x0004) + yellow = color(0x000E) +) + +const ( + mask = uint16(yellow | green | red) +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetStdHandle = kernel32.NewProc("GetStdHandle") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + hStdout uintptr + initScreenInfo *consoleScreenBufferInfo +) + +func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { + ret, _, _ := procSetConsoleTextAttribute.Call(hConsoleOutput, uintptr(wAttributes)) + return ret != 0 +} + +type coord struct { + X, Y int16 +} + +type smallRect struct { + Left, Top, Right, Bottom int16 +} + +type consoleScreenBufferInfo struct { + DwSize coord + DwCursorPosition coord + WAttributes uint16 + SrWindow smallRect + DwMaximumWindowSize coord +} + +func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo { + var csbi consoleScreenBufferInfo + ret, _, _ := procGetConsoleScreenBufferInfo.Call(hConsoleOutput, uintptr(unsafe.Pointer(&csbi))) + if ret == 0 { + return nil + } + return &csbi +} + +const ( + stdOutputHandle = uint32(-11 & 0xFFFFFFFF) +) + +func init() { + hStdout, _, _ = procGetStdHandle.Call(uintptr(stdOutputHandle)) + initScreenInfo = getConsoleScreenBufferInfo(hStdout) +} + +func resetColorful() { + if initScreenInfo == nil { + return + } + setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes) +} + +func changeColor(c color) { + attr := uint16(0) & ^mask | uint16(c) + setConsoleTextAttribute(hStdout, attr) +} diff --git a/cmd/ethereum/repl/repl.go b/cmd/ethereum/repl/repl.go new file mode 100644 index 000000000..a40a8874e --- /dev/null +++ b/cmd/ethereum/repl/repl.go @@ -0,0 +1,85 @@ +package ethrepl + +import ( + "bufio" + "fmt" + "io" + "os" + "path" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/ethlog" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/javascript" +) + +var logger = ethlog.NewLogger("REPL") + +type Repl interface { + Start() + Stop() +} + +type JSRepl struct { + re *javascript.JSRE + + prompt string + + history *os.File + + running bool +} + +func NewJSRepl(ethereum *eth.Ethereum) *JSRepl { + hist, err := os.OpenFile(path.Join(ethutil.Config.ExecPath, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + panic(err) + } + + return &JSRepl{re: javascript.NewJSRE(ethereum), prompt: "> ", history: hist} +} + +func (self *JSRepl) Start() { + if !self.running { + self.running = true + logger.Infoln("init JS Console") + reader := bufio.NewReader(self.history) + for { + line, err := reader.ReadString('\n') + if err != nil && err == io.EOF { + break + } else if err != nil { + fmt.Println("error reading history", err) + break + } + + addHistory(line[:len(line)-1]) + } + self.read() + } +} + +func (self *JSRepl) Stop() { + if self.running { + self.running = false + self.re.Stop() + logger.Infoln("exit JS Console") + self.history.Close() + } +} + +func (self *JSRepl) parseInput(code string) { + defer func() { + if r := recover(); r != nil { + fmt.Println("[native] error", r) + } + }() + + value, err := self.re.Run(code) + if err != nil { + fmt.Println(err) + return + } + + self.PrintValue(value) +} diff --git a/cmd/ethereum/repl/repl_darwin.go b/cmd/ethereum/repl/repl_darwin.go new file mode 100644 index 000000000..ba7dae996 --- /dev/null +++ b/cmd/ethereum/repl/repl_darwin.go @@ -0,0 +1,127 @@ +package ethrepl + +// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include +// #cgo darwin LDFLAGS: -L/usr/local/opt/readline/lib +// #cgo LDFLAGS: -lreadline +// #include <stdio.h> +// #include <stdlib.h> +// #include <readline/readline.h> +// #include <readline/history.h> +import "C" +import ( + "fmt" + "os" + "os/signal" + "strings" + "syscall" + "unsafe" +) + +func initReadLine() { + C.rl_catch_sigwinch = 0 + C.rl_catch_signals = 0 + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGWINCH) + signal.Notify(c, os.Interrupt) + go func() { + for sig := range c { + switch sig { + case syscall.SIGWINCH: + C.rl_resize_terminal() + + case os.Interrupt: + C.rl_cleanup_after_signal() + default: + + } + } + }() +} + +func readLine(prompt *string) *string { + var p *C.char + + //readline allows an empty prompt(NULL) + if prompt != nil { + p = C.CString(*prompt) + } + + ret := C.readline(p) + + if p != nil { + C.free(unsafe.Pointer(p)) + } + + if ret == nil { + return nil + } //EOF + + s := C.GoString(ret) + C.free(unsafe.Pointer(ret)) + return &s +} + +func addHistory(s string) { + p := C.CString(s) + C.add_history(p) + C.free(unsafe.Pointer(p)) +} + +var indentCount = 0 +var str = "" + +func (self *JSRepl) setIndent() { + open := strings.Count(str, "{") + open += strings.Count(str, "(") + closed := strings.Count(str, "}") + closed += strings.Count(str, ")") + indentCount = open - closed + if indentCount <= 0 { + self.prompt = "> " + } else { + self.prompt = strings.Join(make([]string, indentCount*2), "..") + self.prompt += " " + } +} + +func (self *JSRepl) read() { + initReadLine() +L: + for { + switch result := readLine(&self.prompt); true { + case result == nil: + break L + + case *result != "": + str += *result + "\n" + + self.setIndent() + + if indentCount <= 0 { + if *result == "exit" { + self.Stop() + break L + } + + hist := str[:len(str)-1] + addHistory(hist) //allow user to recall this line + self.history.WriteString(str) + + self.parseInput(str) + + str = "" + } + } + } +} + +func (self *JSRepl) PrintValue(v interface{}) { + method, _ := self.re.Vm.Get("prettyPrint") + v, err := self.re.Vm.ToValue(v) + if err == nil { + val, err := method.Call(method, v) + if err == nil { + fmt.Printf("%v", val) + } + } +} diff --git a/cmd/ethereum/repl/repl_linux.go b/cmd/ethereum/repl/repl_linux.go new file mode 120000 index 000000000..276f135d7 --- /dev/null +++ b/cmd/ethereum/repl/repl_linux.go @@ -0,0 +1 @@ +repl_darwin.go
\ No newline at end of file diff --git a/cmd/ethereum/repl/repl_windows.go b/cmd/ethereum/repl/repl_windows.go new file mode 100644 index 000000000..bfae57088 --- /dev/null +++ b/cmd/ethereum/repl/repl_windows.go @@ -0,0 +1,75 @@ +package ethrepl + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func (self *JSRepl) read() { + reader := bufio.NewReader(os.Stdin) + for { + fmt.Printf(self.prompt) + str, _, err := reader.ReadLine() + if err != nil { + fmt.Println("Error reading input", err) + } else { + if (string(str) == "exit") { + self.Stop() + break + } else { + self.parseInput(string(str)) + } + } + } +} + +func addHistory(s string) { +} + +func printColored(outputVal string) { + for ; outputVal != "" ; { + codePart := "" + if (strings.HasPrefix(outputVal, "\033[32m")) { + codePart = "\033[32m" + changeColor(2) + } + if (strings.HasPrefix(outputVal, "\033[1m\033[30m")) { + codePart = "\033[1m\033[30m" + changeColor(8) + } + if (strings.HasPrefix(outputVal, "\033[31m")) { + codePart = "\033[31m" + changeColor(red) + } + if (strings.HasPrefix(outputVal, "\033[35m")) { + codePart = "\033[35m" + changeColor(5) + } + if (strings.HasPrefix(outputVal, "\033[0m")) { + codePart = "\033[0m" + resetColorful() + } + textPart := outputVal[len(codePart):len(outputVal)] + index := strings.Index(textPart, "\033") + if index == -1 { + outputVal = "" + } else { + outputVal = textPart[index:len(textPart)] + textPart = textPart[0:index] + } + fmt.Printf("%v", textPart) + } +} + +func (self *JSRepl) PrintValue(v interface{}) { + method, _ := self.re.Vm.Get("prettyPrint") + v, err := self.re.Vm.ToValue(v) + if err == nil { + val, err := method.Call(method, v) + if err == nil { + printColored(fmt.Sprintf("%v", val)) + } + } +} |