diff --git a/eval/eval.go b/eval/eval.go index 2a094af3..1a966029 100644 --- a/eval/eval.go +++ b/eval/eval.go @@ -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)) } diff --git a/eval/eval_test.go b/eval/eval_test.go index c69d95fd..d8cab7d7 100644 --- a/eval/eval_test.go +++ b/eval/eval_test.go @@ -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 := "" 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) diff --git a/eval/exec.go b/eval/exec.go index d44244eb..30857862 100644 --- a/eval/exec.go +++ b/eval/exec.go @@ -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. diff --git a/main.go b/main.go index 775b1487..286fb3f4 100644 --- a/main.go +++ b/main.go @@ -42,12 +42,14 @@ func newEvalerAndStore() (*eval.Evaler, *store.Store) { } func printError(err error) { - if err != nil { - if ce, ok := err.(*errutil.ContextualError); ok { - fmt.Fprint(os.Stderr, ce.Pprint()) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - } + if err == nil { + return + } + if ce, ok := err.(*errutil.ContextualError); ok { + fmt.Fprint(os.Stderr, ce.Pprint()) + } else { + 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) - printError(err) - eval.PprintBadError(ex) - if err != nil || !ex.Bool() { + err := ev.Source(fname) + if err != nil { + printError(err) os.Exit(1) } }