mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-12 17:27:50 +08:00
combine Error and error into an error in eval functions
This commit is contained in:
parent
c2507ad73b
commit
c1b7701a68
53
eval/eval.go
53
eval/eval.go
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
11
eval/exec.go
11
eval/exec.go
|
@ -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
29
main.go
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user