combine Error and error into an error in eval functions

This commit is contained in:
Qi Xiao 2016-01-31 01:40:49 +01:00
parent c2507ad73b
commit c1b7701a68
4 changed files with 53 additions and 80 deletions

View File

@ -70,8 +70,8 @@ func NewEvaler(st *store.Store) *Evaler {
return &Evaler{global, map[string]ns{}, searchPaths, st, nil}
}
func pprintError(e Error) {
switch e := e.inner.(type) {
func PprintError(e error) {
switch e := e.(type) {
case nil:
fmt.Print("\033[32mok\033[m")
case multiError:
@ -80,7 +80,7 @@ func pprintError(e Error) {
if i > 0 {
fmt.Print(" | ")
}
pprintError(c)
PprintError(c.inner)
}
fmt.Print(")")
case flow:
@ -90,15 +90,6 @@ func pprintError(e Error) {
}
}
func PprintBadError(ex Error) {
if ex.Bool() {
return
}
fmt.Print("⤇ ")
pprintError(ex)
fmt.Println()
}
const (
outChanSize = 32
outChanLeader = "▶ "
@ -169,36 +160,36 @@ func makeScope(s ns) scope {
// Eval evaluates a chunk node n. The supplied name and text are used in
// diagnostic messages.
func (ev *Evaler) Eval(name, text string, n *parse.Chunk) (Error, error) {
func (ev *Evaler) Eval(name, text string, n *parse.Chunk) error {
return ev.evalWithOut(name, text, n, nil)
}
func (ev *Evaler) evalWithOut(name, text string, n *parse.Chunk, out *port) (Error, error) {
func (ev *Evaler) evalWithOut(name, text string, n *parse.Chunk, out *port) error {
op, err := compile(name, text, makeScope(ev.global), n)
if err != nil {
return GenericFailure, err
return err
}
ec, outdone := newTopEvalCtx(ev, name, text)
if out != nil {
outdone = nil
ec.ports[1] = out
}
ex, err := ec.eval(op)
if err == nil && outdone != nil {
// XXX maybe the out channel is always closed regardless of the error? need some checking
ex := ec.peval(op)
ec.closePorts()
if outdone != nil {
<-outdone
}
return ex, err
return ex.inner
}
// eval evaluates an Op.
func (ec *evalCtx) eval(op exitusOp) (ex Error, err error) {
if op == nil {
return OK, nil
}
defer ec.closePorts()
defer errutil.Catch(&err)
return op(ec), nil
// peval evaluates an exitusOp in a protected environment so that calls to
// errorf are wrapped in an Error.
func (ec *evalCtx) peval(op exitusOp) (ex Error) {
// defer ec.closePorts()
defer errutil.Catch(&ex.inner)
return op(ec)
}
// errorf stops the ec.eval immediately by panicking with a diagnostic message.
@ -223,10 +214,10 @@ func (ec *evalCtx) mustSingleString(vs []Value, what string, p int) String {
}
// SourceText evaluates a chunk of elvish source.
func (ev *Evaler) SourceText(name, src, dir string) (Error, error) {
func (ev *Evaler) SourceText(name, src, dir string) error {
n, err := parse.Parse(name, src)
if err != nil {
return GenericFailure, err
return err
}
return ev.Eval(name, src, n)
}
@ -243,10 +234,10 @@ func readFileUTF8(fname string) (string, error) {
}
// Source evaluates the content of a file.
func (ev *Evaler) Source(fname string) (Error, error) {
func (ev *Evaler) Source(fname string) error {
src, err := readFileUTF8(fname)
if err != nil {
return GenericFailure, err
return err
}
return ev.SourceText(fname, src, path.Dir(fname))
}

View File

@ -1,6 +1,7 @@
package eval
import (
"errors"
"os"
"reflect"
"strconv"
@ -26,10 +27,11 @@ func strs(ss ...string) []Value {
return vs
}
var anyerror = errors.New("")
type more struct {
wantBytesOut []byte
wantExit Error
wantError bool
wantError error
}
var nomore more
@ -45,7 +47,7 @@ var evalTests = []struct {
// Outputs of pipelines in a chunk are concatenated
{"put x; put y; put z", strs("x", "y", "z"), nomore},
// A failed pipeline cause the whole chunk to fail
{"put a; false; put b", strs("a"), more{wantExit: NewFailure("1")}},
{"put a; false; put b", strs("a"), more{wantError: errors.New("1")}},
// Pipelines
// Pure byte pipeline
@ -142,7 +144,7 @@ func mustParse(t *testing.T, name, text string) *parse.Chunk {
return n
}
func evalAndCollect(t *testing.T, texts []string, chsize int) ([]Value, []byte, Error, error) {
func evalAndCollect(t *testing.T, texts []string, chsize int) ([]Value, []byte, error) {
name := "<eval test>"
ev := NewEvaler(nil)
@ -166,7 +168,7 @@ func evalAndCollect(t *testing.T, texts []string, chsize int) ([]Value, []byte,
outs := []Value{}
// Exit. Only the exit of the last text is saved.
var ex Error
var ex error
for _, text := range texts {
n := mustParse(t, name, text)
@ -180,21 +182,19 @@ func evalAndCollect(t *testing.T, texts []string, chsize int) ([]Value, []byte,
exhausted <- struct{}{}
}()
var err error
ex, err = ev.evalWithOut(name, text, n, &port{ch: out, closeCh: true, f: pw})
if err != nil {
return outs, outBytes, ex, err
}
ex = ev.evalWithOut(name, text, n, &port{ch: out, closeCh: true, f: pw})
<-exhausted
}
pw.Close()
<-bytesExhausted
return outs, outBytes, ex, nil
return outs, outBytes, ex
}
func TestEval(t *testing.T) {
for _, tt := range evalTests {
out, bytesOut, ex, err := evalAndCollect(t, []string{tt.text}, len(tt.wantOut))
// fmt.Printf("eval %q\n", tt.text)
out, bytesOut, err := evalAndCollect(t, []string{tt.text}, len(tt.wantOut))
good := true
errorf := func(format string, args ...interface{}) {
@ -205,21 +205,11 @@ func TestEval(t *testing.T) {
t.Errorf(format, args...)
}
if tt.wantError != (err != nil) {
wantError := "nil"
if tt.wantError {
wantError = "non-nil"
}
errorf("got err=%v, want %s", err, wantError)
}
if tt.wantBytesOut != nil && !reflect.DeepEqual(tt.wantBytesOut, bytesOut) {
errorf("got bytesOut=%q, want %q", bytesOut, tt.wantBytesOut)
}
if tt.wantExit != OK && !reflect.DeepEqual(tt.wantExit, ex) {
errorf("got exitus=%v, want %v", ex, tt.wantExit)
}
if tt.wantExit == OK && !ex.Bool() {
errorf("got exitus=%v, want all ok", ex)
if !(tt.wantError == anyerror && err != nil) && !reflect.DeepEqual(tt.wantError, err) {
errorf("got err=%v, want %v", err, tt.wantError)
}
if !reflect.DeepEqual(tt.wantOut, out) {
errorf("got out=%v, want %v", out, tt.wantOut)
@ -231,7 +221,7 @@ func TestEval(t *testing.T) {
}
func TestMultipleEval(t *testing.T) {
outs, _, _, err := evalAndCollect(t, []string{"set x = hello", "put $x"}, 1)
outs, _, err := evalAndCollect(t, []string{"set x = hello", "put $x"}, 1)
wanted := strs("hello")
if err != nil {
t.Errorf("eval %q => %v, want nil", err)

View File

@ -4,8 +4,6 @@ import (
"fmt"
"os"
"syscall"
"github.com/elves/elvish/errutil"
)
const (
@ -76,13 +74,10 @@ func (c *Closure) Call(ec *evalCtx, args []Value) Error {
// TODO(xiaq): Also change ec.name and ec.text since the closure being
// called can come from another source.
ex, err := ec.eval(c.Op)
if err != nil {
fmt.Print(err.(*errutil.ContextualError).Pprint())
return evalFailure
}
ex := ec.peval(c.Op)
ec.closePorts()
return ex
// return ec.peval(c.Op)
}
// waitStatusToError converts syscall.WaitStatus to an Error.

21
main.go
View File

@ -42,12 +42,14 @@ func newEvalerAndStore() (*eval.Evaler, *store.Store) {
}
func printError(err error) {
if err != nil {
if err == nil {
return
}
if ce, ok := err.(*errutil.ContextualError); ok {
fmt.Fprint(os.Stderr, ce.Pprint())
} else {
fmt.Fprintln(os.Stderr, err.Error())
}
eval.PprintError(err)
fmt.Println()
}
}
@ -58,13 +60,10 @@ func interact() {
printError(err)
if err == nil {
// XXX
ex, err := ev.Source(datadir + "/rc.elv")
err := ev.Source(datadir + "/rc.elv")
if err != nil && !os.IsNotExist(err) {
printError(err)
}
if !os.IsNotExist(err) {
eval.PprintBadError(ex)
}
}
cmdNum := 0
@ -110,19 +109,17 @@ func interact() {
printError(err)
if err == nil {
ex, err := ev.Eval(name, lr.Line, n)
err := ev.Eval(name, lr.Line, n)
printError(err)
eval.PprintBadError(ex)
}
}
}
func script(fname string) {
ev, _ := newEvalerAndStore()
ex, err := ev.Source(fname)
err := ev.Source(fname)
if err != nil {
printError(err)
eval.PprintBadError(ex)
if err != nil || !ex.Bool() {
os.Exit(1)
}
}