mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
Use specific error types for parse, compile and eval.
Generic error types in util are ditched.
This commit is contained in:
parent
88b912bda4
commit
ee81f0680c
|
@ -168,14 +168,16 @@ func (ed *Editor) refresh(fullRefresh bool, tips bool) error {
|
|||
if tips && !atEnd(err, len(src)) {
|
||||
ed.addTip("compiler error: %s", err)
|
||||
}
|
||||
if err, ok := err.(*util.PosError); ok {
|
||||
p := err.Begin
|
||||
if err, ok := err.(*eval.CompilationError); ok {
|
||||
p := err.Context.Begin
|
||||
for i, token := range ed.tokens {
|
||||
if token.Node.Begin() <= p && p < token.Node.End() {
|
||||
ed.tokens[i].MoreStyle = joinStyles(ed.tokens[i].MoreStyle, styleForCompilerError)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logger.Printf("Compile returned error of type %T", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,16 +189,17 @@ func (ed *Editor) refresh(fullRefresh bool, tips bool) error {
|
|||
|
||||
func atEnd(e error, n int) bool {
|
||||
switch e := e.(type) {
|
||||
case *util.PosError:
|
||||
return e.Begin == n
|
||||
case *util.Errors:
|
||||
for _, child := range e.Errors {
|
||||
if !atEnd(child, n) {
|
||||
case *eval.CompilationError:
|
||||
return e.Context.Begin == n
|
||||
case *parse.ParseError:
|
||||
for _, entry := range e.Entries {
|
||||
if entry.Context.Begin != n {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
Logger.Printf("atEnd called with error type %T", e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
29
eval/compilation-error.go
Normal file
29
eval/compilation-error.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package eval
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/elves/elvish/util"
|
||||
)
|
||||
|
||||
// CompilationError represents a compilation error and can pretty print it.
|
||||
type CompilationError struct {
|
||||
Message string
|
||||
Context util.SourceContext
|
||||
}
|
||||
|
||||
func (ce *CompilationError) Error() string {
|
||||
return fmt.Sprintf("compilation error: %d-%d in %s: %s",
|
||||
ce.Context.Begin, ce.Context.End, ce.Context.Name, ce.Message)
|
||||
}
|
||||
|
||||
func (ce *CompilationError) Pprint() string {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
fmt.Fprintf(buf, "Compilation error: \033[31;1m%s\033[m\n", ce.Message)
|
||||
fmt.Fprint(buf, " ")
|
||||
ce.Context.Pprint(buf, " ")
|
||||
|
||||
return buf.String()
|
||||
}
|
|
@ -36,7 +36,8 @@ func (cp *compiler) compiling(n parse.Node) {
|
|||
}
|
||||
|
||||
func (cp *compiler) errorpf(begin, end int, format string, args ...interface{}) {
|
||||
throw(&util.PosError{fmt.Errorf(format, args...), "Compile error", util.Traceback{cp.name, cp.text, begin, end, nil}})
|
||||
throw(&CompilationError{fmt.Sprintf(format, args...),
|
||||
util.SourceContext{cp.name, cp.text, begin, end, nil}})
|
||||
}
|
||||
|
||||
func (cp *compiler) errorf(format string, args ...interface{}) {
|
||||
|
|
15
eval/eval.go
15
eval/eval.go
|
@ -55,7 +55,7 @@ type EvalCtx struct {
|
|||
verdict bool
|
||||
|
||||
begin, end int
|
||||
traceback *util.Traceback
|
||||
traceback *util.SourceContext
|
||||
|
||||
background bool
|
||||
}
|
||||
|
@ -280,9 +280,9 @@ func catch(perr *error, ec *EvalCtx) {
|
|||
}
|
||||
if exc, ok := r.(util.Exception); ok {
|
||||
err := exc.Error
|
||||
if _, ok := err.(*util.TracebackError); !ok {
|
||||
if _, ok := err.(*Exception); !ok {
|
||||
if _, ok := err.(flow); !ok {
|
||||
err = ec.makeTracebackError(err)
|
||||
err = ec.makeException(err)
|
||||
}
|
||||
}
|
||||
*perr = err
|
||||
|
@ -291,12 +291,13 @@ func catch(perr *error, ec *EvalCtx) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ec *EvalCtx) makeTracebackError(e error) *util.TracebackError {
|
||||
return &util.TracebackError{Cause: e, Traceback: ec.addTraceback()}
|
||||
// makeException turns an error into an Exception by adding traceback.
|
||||
func (ec *EvalCtx) makeException(e error) *Exception {
|
||||
return &Exception{e, ec.addTraceback()}
|
||||
}
|
||||
|
||||
func (ec *EvalCtx) addTraceback() *util.Traceback {
|
||||
return &util.Traceback{
|
||||
func (ec *EvalCtx) addTraceback() *util.SourceContext {
|
||||
return &util.SourceContext{
|
||||
Name: ec.srcName, Source: ec.src,
|
||||
Begin: ec.begin, End: ec.end, Next: ec.traceback,
|
||||
}
|
||||
|
|
32
eval/exception.go
Normal file
32
eval/exception.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package eval
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/elves/elvish/util"
|
||||
)
|
||||
|
||||
// Exception represents an elvish exception.
|
||||
type Exception struct {
|
||||
Cause error
|
||||
Traceback *util.SourceContext
|
||||
}
|
||||
|
||||
func (exc *Exception) Error() string {
|
||||
return exc.Cause.Error()
|
||||
}
|
||||
|
||||
func (exc *Exception) Pprint() string {
|
||||
buf := new(bytes.Buffer)
|
||||
// Error message
|
||||
fmt.Fprintf(buf, "Exception: \033[31;1m%s\033[m\n", exc.Cause.Error())
|
||||
buf.WriteString("Traceback:")
|
||||
|
||||
for tb := exc.Traceback; tb != nil; tb = tb.Next {
|
||||
buf.WriteString("\n ")
|
||||
tb.Pprint(buf, " ")
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
66
parse/parse-error.go
Normal file
66
parse/parse-error.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package parse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/elves/elvish/util"
|
||||
)
|
||||
|
||||
// ParseErrorEntry represents one parse error.
|
||||
type ParseErrorEntry struct {
|
||||
Message string
|
||||
Context util.SourceContext
|
||||
}
|
||||
|
||||
// ParseError stores multiple ParseErrorEntry's and can pretty print them.
|
||||
type ParseError struct {
|
||||
Entries []*ParseErrorEntry
|
||||
}
|
||||
|
||||
func (pe *ParseError) Add(msg string, ctx util.SourceContext) {
|
||||
pe.Entries = append(pe.Entries, &ParseErrorEntry{msg, ctx})
|
||||
}
|
||||
|
||||
func (pe *ParseError) Error() string {
|
||||
switch len(pe.Entries) {
|
||||
case 0:
|
||||
return "no parse error"
|
||||
case 1:
|
||||
e := pe.Entries[0]
|
||||
return fmt.Sprintf("parse error: %d-%d in %s: %s",
|
||||
e.Context.Begin, e.Context.End, e.Context.Name, e.Message)
|
||||
default:
|
||||
buf := new(bytes.Buffer)
|
||||
// Contexts of parse error entries all have the same name
|
||||
fmt.Fprintf(buf, "multiple parse errors in %s: ", pe.Entries[0].Context.Name)
|
||||
for i, e := range pe.Entries {
|
||||
if i > 0 {
|
||||
fmt.Fprint(buf, "; ")
|
||||
}
|
||||
fmt.Fprintf(buf, "%d-%d: %s", e.Context.Begin, e.Context.End, e.Message)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (pe *ParseError) Pprint() string {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
switch len(pe.Entries) {
|
||||
case 0:
|
||||
return "no parse error"
|
||||
case 1:
|
||||
e := pe.Entries[0]
|
||||
fmt.Fprintf(buf, "Parse error: \033[31;1m%s\033[m\n ", e.Message)
|
||||
e.Context.Pprint(buf, " ")
|
||||
default:
|
||||
fmt.Fprint(buf, "Multiple parse errors:\n")
|
||||
for _, e := range pe.Entries {
|
||||
fmt.Fprintf(buf, " \033[31;1m%s\033[m\n ", e.Message)
|
||||
e.Context.Pprint(buf, " ")
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
|
@ -13,14 +13,14 @@ import (
|
|||
|
||||
// Parse parses elvish source.
|
||||
func Parse(srcname, src string) (*Chunk, error) {
|
||||
ps := &parser{srcname, src, 0, 0, []map[rune]int{{}}, 0, nil}
|
||||
ps := &parser{srcname, src, 0, 0, []map[rune]int{{}}, 0, ParseError{}}
|
||||
bn := parseChunk(ps)
|
||||
if ps.pos != len(src) {
|
||||
ps.error(errUnexpectedRune)
|
||||
}
|
||||
var err error
|
||||
if ps.errors != nil {
|
||||
err = ps.errors
|
||||
if len(ps.errors.Entries) > 0 {
|
||||
err = &ps.errors
|
||||
}
|
||||
return bn, err
|
||||
}
|
||||
|
@ -263,10 +263,11 @@ func (fn *Form) tryAssignment(ps *parser) bool {
|
|||
}
|
||||
|
||||
pos := ps.pos
|
||||
errors := ps.errors
|
||||
errorEntries := ps.errors.Entries
|
||||
an := parseAssignment(ps)
|
||||
if ps.errors != errors {
|
||||
ps.errors = errors
|
||||
// If errors were added, revert
|
||||
if len(ps.errors.Entries) > len(errorEntries) {
|
||||
ps.errors.Entries = errorEntries
|
||||
ps.pos = pos
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/elves/elvish/util"
|
||||
)
|
||||
|
||||
func a(c ...interface{}) ast {
|
||||
|
@ -369,9 +367,9 @@ func TestParseError(t *testing.T) {
|
|||
t.Errorf("Parse(%q) returns no error", tc.src)
|
||||
continue
|
||||
}
|
||||
posErr0 := err.(*util.Errors).Errors[0].(*util.PosError)
|
||||
if posErr0.Begin != tc.pos {
|
||||
t.Errorf("Parse(%q) first error begins at %d, want %d. Errors are:%s\n", tc.src, posErr0.Begin, tc.pos, err)
|
||||
posErr0 := err.(*ParseError).Entries[0]
|
||||
if posErr0.Context.Begin != tc.pos {
|
||||
t.Errorf("Parse(%q) first error begins at %d, want %d. Errors are:%s\n", tc.src, posErr0.Context.Begin, tc.pos, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ type parser struct {
|
|||
overEOF int
|
||||
cutsets []map[rune]int
|
||||
controls int
|
||||
errors *util.Errors
|
||||
errors ParseError
|
||||
}
|
||||
|
||||
const eof rune = -1
|
||||
|
@ -93,10 +93,7 @@ func (ps *parser) advance(c int) {
|
|||
}
|
||||
|
||||
func (ps *parser) errorp(begin, end int, e error) {
|
||||
if ps.errors == nil {
|
||||
ps.errors = &util.Errors{}
|
||||
}
|
||||
ps.errors.Append(&util.PosError{e, "Parse error", util.Traceback{ps.srcName, ps.src, begin, end, nil}})
|
||||
ps.errors.Add(e.Error(), util.SourceContext{ps.srcName, ps.src, begin, end, nil})
|
||||
}
|
||||
|
||||
func (ps *parser) error(e error) {
|
||||
|
|
34
run/run.go
34
run/run.go
|
@ -129,18 +129,18 @@ func source(ev *eval.Evaler, fname string, notexistok bool) bool {
|
|||
func sourceText(ev *eval.Evaler, name, src string) bool {
|
||||
n, err := parse.Parse(name, src)
|
||||
if err != nil {
|
||||
printError(err, "Parse error")
|
||||
printError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
op, err := ev.Compile(n, name, src)
|
||||
if err != nil {
|
||||
printError(err, "Compile error")
|
||||
printError(err)
|
||||
return false
|
||||
}
|
||||
err = ev.Eval(op, name, src)
|
||||
if err != nil {
|
||||
printError(err, "Exception")
|
||||
printError(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -258,27 +258,15 @@ func newEvalerAndStore() (*eval.Evaler, *store.Store) {
|
|||
return eval.NewEvaler(st, dataDir), st
|
||||
}
|
||||
|
||||
func printError(err error, errtype string) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
switch err := err.(type) {
|
||||
case *util.Errors:
|
||||
for _, e := range err.Errors {
|
||||
printError(e, errtype)
|
||||
}
|
||||
case *util.PosError:
|
||||
fmt.Fprintln(os.Stderr, err.Pprint())
|
||||
case *util.TracebackError:
|
||||
fmt.Fprintln(os.Stderr, err.Pprint())
|
||||
default:
|
||||
printErrorString(errtype, err.Error())
|
||||
}
|
||||
type Pprinter interface {
|
||||
Pprint() string
|
||||
}
|
||||
|
||||
func printErrorString(errtype, s string) {
|
||||
if sys.IsATTY(2) {
|
||||
s = "\033[1;31m" + s + "\033[m"
|
||||
func printError(err error) {
|
||||
switch err := err.(type) {
|
||||
case Pprinter:
|
||||
fmt.Fprintln(os.Stderr, err.Pprint())
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "\033[31;1m%s\033[m", err.Error())
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, errtype+": "+s)
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PosError is an error associated with a position range.
|
||||
type PosError struct {
|
||||
Err error
|
||||
Type string
|
||||
Traceback
|
||||
}
|
||||
|
||||
func (pe *PosError) Error() string {
|
||||
return fmt.Sprintf("%d-%d: %s", pe.Traceback.Begin, pe.Traceback.End, pe.msg())
|
||||
}
|
||||
|
||||
// Pprint pretty-prints a PosError.
|
||||
func (pe *PosError) Pprint() string {
|
||||
buf := new(bytes.Buffer)
|
||||
// Error message
|
||||
fmt.Fprintf(buf, "%s: \033[31;1m%s\033[m\n", pe.Type, pe.msg())
|
||||
// Position
|
||||
pe.Traceback.Pprint(buf, " ")
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (pe *PosError) msg() string {
|
||||
if pe.Err != nil {
|
||||
return pe.Err.Error()
|
||||
} else {
|
||||
return "<nil>"
|
||||
}
|
||||
}
|
|
@ -6,26 +6,26 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type Traceback struct {
|
||||
type SourceContext struct {
|
||||
Name string
|
||||
Source string
|
||||
Begin int
|
||||
End int
|
||||
Next *Traceback
|
||||
Next *SourceContext
|
||||
}
|
||||
|
||||
var CulpritStyle = "1;4"
|
||||
|
||||
func (te *Traceback) Pprint(w io.Writer, sourceIndent string) {
|
||||
if te.Begin == -1 {
|
||||
fmt.Fprintf(w, "%s, unknown position", te.Name)
|
||||
func (sc *SourceContext) Pprint(w io.Writer, sourceIndent string) {
|
||||
if sc.Begin == -1 {
|
||||
fmt.Fprintf(w, "%s, unknown position", sc.Name)
|
||||
return
|
||||
} else if te.Begin < 0 || te.End > len(te.Source) || te.Begin > te.End {
|
||||
fmt.Fprintf(w, "%s, invalid position %d-%d", te.Name, te.Begin, te.End)
|
||||
} else if sc.Begin < 0 || sc.End > len(sc.Source) || sc.Begin > sc.End {
|
||||
fmt.Fprintf(w, "%s, invalid position %d-%d", sc.Name, sc.Begin, sc.End)
|
||||
return
|
||||
}
|
||||
|
||||
before, culprit, after := bca(te.Source, te.Begin, te.End)
|
||||
before, culprit, after := bca(sc.Source, sc.Begin, sc.End)
|
||||
// Find the part of "before" that is on the same line as the culprit.
|
||||
lineBefore := lastLine(before)
|
||||
// Find on which line the culprit begins.
|
||||
|
@ -44,9 +44,9 @@ func (te *Traceback) Pprint(w io.Writer, sourceIndent string) {
|
|||
endLine := beginLine + strings.Count(culprit, "\n")
|
||||
|
||||
if beginLine == endLine {
|
||||
fmt.Fprintf(w, "%s, line %d:\n", te.Name, beginLine)
|
||||
fmt.Fprintf(w, "%s, line %d:\n", sc.Name, beginLine)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s, line %d-%d:\n", te.Name, beginLine, endLine)
|
||||
fmt.Fprintf(w, "%s, line %d-%d:\n", sc.Name, beginLine, endLine)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s%s", sourceIndent, lineBefore)
|
|
@ -1,29 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type TracebackError struct {
|
||||
Cause error
|
||||
Traceback *Traceback
|
||||
}
|
||||
|
||||
func (te *TracebackError) Error() string {
|
||||
return te.Cause.Error()
|
||||
}
|
||||
|
||||
func (te *TracebackError) Pprint() string {
|
||||
buf := new(bytes.Buffer)
|
||||
// Error message
|
||||
fmt.Fprintf(buf, "Exception: \033[31;1m%s\033[m\n", te.Cause.Error())
|
||||
buf.WriteString("Traceback:")
|
||||
|
||||
for tb := te.Traceback; tb != nil; tb = tb.Next {
|
||||
buf.WriteString("\n ")
|
||||
tb.Pprint(buf, " ")
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
Loading…
Reference in New Issue
Block a user