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

View File

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

View File

@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"os" "os"
"syscall" "syscall"
"github.com/elves/elvish/errutil"
) )
const ( 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 // TODO(xiaq): Also change ec.name and ec.text since the closure being
// called can come from another source. // called can come from another source.
ex, err := ec.eval(c.Op) ex := ec.peval(c.Op)
if err != nil { ec.closePorts()
fmt.Print(err.(*errutil.ContextualError).Pprint())
return evalFailure
}
return ex return ex
// return ec.peval(c.Op)
} }
// waitStatusToError converts syscall.WaitStatus to an Error. // waitStatusToError converts syscall.WaitStatus to an Error.

29
main.go
View File

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