Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(repl): improve support of multi-line statements #1129

Merged
merged 6 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 34 additions & 53 deletions gnovm/cmd/gno/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import (
"bufio"
"bytes"
"context"
"errors"
"flag"
"fmt"
"go/scanner"
"os"
"strings"

Expand Down Expand Up @@ -88,91 +89,71 @@
// gno> import "gno.land/p/demo/avl" // import the p/demo/avl package
// gno> func a() string { return "a" } // declare a new function named a
// gno> /src // print current generated source
// gno> /editor // enter in editor mode to add several lines
// gno> /reset // remove all previously inserted code
// gno> println(a()) // print the result of calling a()
// gno> /exit
// gno> /exit // alternative to <Ctrl-D>

Check warning on line 94 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L94

Added line #L94 was not covered by tests
`)
}

return runRepl(cfg)
}

func runRepl(cfg *replCfg) error {
// init repl state
r := repl.NewRepl()

if cfg.initialCommand != "" {
handleInput(r, cfg.initialCommand)
}

var multiline bool
for {
fmt.Fprint(os.Stdout, "gno> ")
fmt.Fprint(os.Stdout, "gno> ")

Check warning on line 108 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L108

Added line #L108 was not covered by tests

input, err := getInput(multiline)
if err != nil {
return err
prevline := ""
thehowl marked this conversation as resolved.
Show resolved Hide resolved
liner := bufio.NewScanner(os.Stdin)

for liner.Scan() {
line := liner.Text()
if prevline != "" {
line = prevline + "\n" + line
prevline = ""
}

Check warning on line 118 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L110-L118

Added lines #L110 - L118 were not covered by tests

if err := handleInput(r, line); err != nil {
var goScanError scanner.ErrorList
if errors.As(err, &goScanError) {
// We assume that a Go scanner error indicates an incomplete Go statement.
// Append next line and retry.
prevline = line
} else {
fmt.Fprintln(os.Stderr, err)
}

Check warning on line 128 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L120-L128

Added lines #L120 - L128 were not covered by tests
}

multiline = handleInput(r, input)
if prevline == "" {
fmt.Fprint(os.Stdout, "gno> ")
} else {
fmt.Fprint(os.Stdout, "... ")
thehowl marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 135 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L131-L135

Added lines #L131 - L135 were not covered by tests
}
return nil

Check warning on line 137 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L137

Added line #L137 was not covered by tests
}

// handleInput reads the input string and parses it depending if it
// is a specific command, or source code. It returns true if the following
// input is expected to be on more than one line.
func handleInput(r *repl.Repl, input string) bool {
// handleInput executes specific "/" commands, or evaluates input as Gno source code.
func handleInput(r *repl.Repl, input string) error {

Check warning on line 141 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L141

Added line #L141 was not covered by tests
switch strings.TrimSpace(input) {
case "/reset":
r.Reset()
case "/src":
fmt.Fprintln(os.Stdout, r.Src())
case "/exit":
os.Exit(0)
case "/editor":
fmt.Fprintln(os.Stdout, "// Entering editor mode (^D to finish)")
return true
case "":
// avoid to increase the repl execution counter if sending empty content
fmt.Fprintln(os.Stdout, "")
return false
// Avoid to increase the repl execution counter if no input.
default:
out, err := r.Process(input)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return err

Check warning on line 154 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L154

Added line #L154 was not covered by tests
}
fmt.Fprintln(os.Stdout, out)
}

return false
}

const (
inputBreaker = "^D"
nl = "\n"
)

func getInput(ml bool) (string, error) {
s := bufio.NewScanner(os.Stdin)
var mlOut bytes.Buffer
for s.Scan() {
line := s.Text()
if !ml {
return line, nil
}

if line == inputBreaker {
break
}

mlOut.WriteString(line)
mlOut.WriteString(nl)
}

if err := s.Err(); err != nil {
return "", err
}

return mlOut.String(), nil
return nil

Check warning on line 158 in gnovm/cmd/gno/repl.go

View check run for this annotation

Codecov / codecov/patch

gnovm/cmd/gno/repl.go#L158

Added line #L158 was not covered by tests
}
2 changes: 1 addition & 1 deletion gnovm/pkg/repl/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (r *Repl) Process(input string) (out string, err error) {
return r.handleExpression(exp)
}

return "", fmt.Errorf("error parsing code:\n\t- as expression (error: %q)\n\t- as declarations (error: %q)", expErr.Error(), declErr.Error())
return "", fmt.Errorf("error parsing code:\n\t- as expression: %w\n\t- as declarations: %w", expErr, declErr)
}

func (r *Repl) handleExpression(e *ast.File) (string, error) {
Expand Down
Loading