Skip to content

Commit

Permalink
repl (#515)
Browse files Browse the repository at this point in the history
  • Loading branch information
xushiwei authored Aug 2, 2020
1 parent 39c2c86 commit e0242e1
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 53 deletions.
89 changes: 89 additions & 0 deletions cmd/internal/repl/replcmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2020 The GoPlus Authors (goplus.org)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package repl implements the ``gop repl'' command.
package repl

import (
"fmt"
"io"

"github.com/goplus/gop/cmd/internal/base"
"github.com/goplus/gop/repl"
"github.com/peterh/liner"
)

// -----------------------------------------------------------------------------

// LinerUI implements repl.UI interface.
type LinerUI struct {
state *liner.State
prompt string
}

// SetPrompt is required by repl.UI interface.
func (u *LinerUI) SetPrompt(prompt string) {
u.prompt = prompt
}

// Printf is required by repl.UI interface.
func (u *LinerUI) Printf(format string, a ...interface{}) {
fmt.Printf(format, a...)
}

// -----------------------------------------------------------------------------

func init() {
Cmd.Run = runCmd
}

// Cmd - gop repl
var Cmd = &base.Command{
UsageLine: "repl",
Short: "Play Go+ in console",
}

const (
welcome string = "Welcome to Go+ console!"
)

func runCmd(cmd *base.Command, args []string) {
fmt.Println(welcome)
state := liner.NewLiner()
defer state.Close()

state.SetCtrlCAborts(true)
state.SetMultiLineMode(true)

ui := &LinerUI{state: state}
repl := repl.New()
repl.SetUI(ui)

for {
line, err := ui.state.Prompt(ui.prompt)
if err != nil {
if err == liner.ErrPromptAborted || err == io.EOF {
fmt.Printf("\n")
break
}
fmt.Printf("Problem reading line: %v\n", err)
continue
}
repl.Run(line)
}
}

// -----------------------------------------------------------------------------
85 changes: 32 additions & 53 deletions cmd/internal/repl/repl.go → repl/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,75 +14,54 @@
limitations under the License.
*/

// Package repl implements the ``gop repl'' command.
package repl

import (
"errors"
"fmt"
"io"
"strings"

"github.com/goplus/gop/cl"
"github.com/goplus/gop/cmd/internal/base"
"github.com/goplus/gop/parser"
"github.com/goplus/gop/token"
"github.com/peterh/liner"

exec "github.com/goplus/gop/exec/bytecode"
)

func init() {
Cmd.Run = runCmd
// UI represents the UserInterface interacting abstraction.
type UI interface {
SetPrompt(prompt string)
Printf(format string, a ...interface{})
}

// Cmd - gop repl
var Cmd = &base.Command{
UsageLine: "repl",
Short: "Play Go+ in console",
}

type repl struct {
// REPL type
type REPL struct {
src string // the whole source code from repl
preContext exec.Context // store the context after exec
ip int // store the ip after exec
prompt string // the prompt type in console
continueMode bool // switch to control the promot type
liner *liner.State // liner instance
term UI // liner instance
}

const (
continuePrompt string = "... "
standardPrompt string = ">>> "
welcome string = "welcome to Go+ console!"
// ContinuePrompt - the current code statement is not completed.
ContinuePrompt string = "... "
// NormalPrompt - start of a code statement.
NormalPrompt string = ">>> "
)

func runCmd(cmd *base.Command, args []string) {
fmt.Println(welcome)
liner := liner.NewLiner()
rInstance := repl{prompt: standardPrompt, liner: liner}
defer rInstance.liner.Close()

for {
l, err := rInstance.liner.Prompt(string(rInstance.prompt))
if err != nil {
if err == io.EOF {
fmt.Printf("\n")
break
}
fmt.Printf("Problem reading line: %v\n", err)
continue
}
rInstance.replOneLine(l)
}
// New creates a REPL object.
func New() *REPL {
return &REPL{}
}

// replOneLine handle one line
func (r *repl) replOneLine(line string) {
if line != "" {
// mainly for scroll up
r.liner.AppendHistory(line)
}
// SetUI initializes UI.
func (r *REPL) SetUI(term UI) {
r.term = term
term.SetPrompt(NormalPrompt)
}

// Run handles one line.
func (r *REPL) Run(line string) {
if r.continueMode {
r.continueModeByLine(line)
}
Expand All @@ -92,22 +71,22 @@ func (r *repl) replOneLine(line string) {
}

// continueModeByLine check if continue-mode should continue :)
func (r *repl) continueModeByLine(line string) {
func (r *REPL) continueModeByLine(line string) {
if line != "" {
r.src += line + "\n"
return
}
// input nothing means jump out continue mode
r.continueMode = false
r.prompt = standardPrompt
r.term.SetPrompt(NormalPrompt)
}

// run execute the input line
func (r *repl) run(newLine string) (err error) {
func (r *REPL) run(newLine string) (err error) {
src := r.src + newLine + "\n"
defer func() {
if errR := recover(); errR != nil {
replErr(newLine, errR)
r.dumpErr(newLine, errR)
err = errors.New("panic err")
// TODO: Need a better way to log and show the stack when crash
// It is too long to print stack on terminal even only print part of the them; not friendly to user
Expand All @@ -122,12 +101,12 @@ func (r *repl) run(newLine string) (err error) {
// check if into continue mode
if strings.Contains(err.Error(), `expected ')', found 'EOF'`) ||
strings.Contains(err.Error(), "expected '}', found 'EOF'") {
r.prompt = continuePrompt
r.term.SetPrompt(ContinuePrompt)
r.continueMode = true
err = nil
return
}
fmt.Println("ParseGopFiles err", err)
r.term.Printf("ParseGopFiles err: %v\n", err)
return
}
cl.CallBuiltinOp = exec.CallBuiltinOp
Expand All @@ -140,7 +119,7 @@ func (r *repl) run(newLine string) (err error) {
err = nil
return
}
fmt.Printf("NewPackage err %+v\n", err)
r.term.Printf("NewPackage err %+v\n", err)
return
}
code := b.Resolve()
Expand All @@ -157,7 +136,7 @@ func (r *repl) run(newLine string) (err error) {
return
}

func replErr(line string, err interface{}) {
fmt.Println("code run fail : ", line)
fmt.Println("repl err: ", err)
func (r *REPL) dumpErr(line string, err interface{}) {
r.term.Printf("code run fail : %v\n", line)
r.term.Printf("repl err: %v\n", err)
}

0 comments on commit e0242e1

Please sign in to comment.