Skip to content

Commit

Permalink
Expose file.Error type for better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
antonmedv committed Mar 20, 2020
1 parent 546dfc9 commit 0f43d1f
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 26 deletions.
2 changes: 1 addition & 1 deletion checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func Check(tree *parser.Tree, config *conf.Config) (reflect.Type, error) {
}

if v.err != nil {
return t, fmt.Errorf("%v", v.err.Format(tree.Source))
return t, v.err.Bind(tree.Source)
}

return t, nil
Expand Down
2 changes: 1 addition & 1 deletion expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
err = optimizer.Optimize(&tree.Node, config)
if err != nil {
if fileError, ok := err.(*file.Error); ok {
return nil, fmt.Errorf("%v", fileError.Format(tree.Source))
return nil, fileError.Bind(tree.Source)
}
return nil, err
}
Expand Down
37 changes: 37 additions & 0 deletions expr_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package expr_test

import (
"encoding/json"
"fmt"
"github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/file"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -1017,6 +1019,41 @@ func TestPatch_length(t *testing.T) {
require.Equal(t, true, output)
}

func TestCompile_exposed_error(t *testing.T) {
_, err := expr.Compile(`1 == true`)
require.Error(t, err)

fileError, ok := err.(*file.Error)
require.True(t, ok, "error should be of type *file.Error")
require.Equal(t, "invalid operation: == (mismatched types int and bool) (1:3)\n | 1 == true\n | ..^", fileError.Error())
require.Equal(t, 2, fileError.Column)
require.Equal(t, 1, fileError.Line)

b, err := json.Marshal(err)
require.NoError(t, err)
require.Equal(t, `{"Line":1,"Column":2,"Message":"invalid operation: == (mismatched types int and bool)","Snippet":"\n | 1 == true\n | ..^"}`, string(b))
}

func TestAsBool_exposed_error_(t *testing.T) {
_, err := expr.Compile(`42`, expr.AsBool())
require.Error(t, err)

_, ok := err.(*file.Error)
require.False(t, ok, "error must not be of type *file.Error")
require.Equal(t, "expected bool, but got int", err.Error())
}

func TestEval_exposed_error(t *testing.T) {
_, err := expr.Eval(`1/0`, nil)
require.Error(t, err)

fileError, ok := err.(*file.Error)
require.True(t, ok, "error should be of type *file.Error")
require.Equal(t, "runtime error: integer divide by zero (1:2)\n | 1/0\n | .^", fileError.Error())
require.Equal(t, 1, fileError.Column)
require.Equal(t, 1, fileError.Line)
}

//
// Mock types
//
Expand Down
40 changes: 20 additions & 20 deletions file/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,14 @@ import (
type Error struct {
Location
Message string
Snippet string
}

const (
dot = "."
ind = "^"
)

func (e *Error) Error() string {
return e.Message
return e.format()
}

func (e *Error) Format(source *Source) string {
if e.Location.Empty() {
return e.Message
}
var result = fmt.Sprintf(
"%s (%d:%d)",
e.Message,
e.Location.Line,
e.Location.Column+1, // add one to the 0-based column for display
)
func (e *Error) Bind(source *Source) *Error {
if snippet, found := source.Snippet(e.Location.Line); found {
snippet := strings.Replace(snippet, "\t", " ", -1)
srcLine := "\n | " + snippet
Expand All @@ -41,18 +28,31 @@ func (e *Error) Format(source *Source) string {
if sz > 1 {
goto noind
} else {
indLine += dot
indLine += "."
}
}
if _, sz := utf8.DecodeRune(bytes); sz > 1 {
goto noind
} else {
indLine += ind
indLine += "^"
}
srcLine += indLine

noind:
result += srcLine
e.Snippet = srcLine
}
return result
return e
}

func (e *Error) format() string {
if e.Location.Empty() {
return e.Message
}
return fmt.Sprintf(
"%s (%d:%d)%s",
e.Message,
e.Line,
e.Column+1, // add one to the 0-based column for display
e.Snippet,
)
}
2 changes: 1 addition & 1 deletion parser/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func Lex(source *file.Source) ([]Token, error) {
}

if l.err != nil {
return nil, fmt.Errorf("%v", l.err.Format(source))
return nil, l.err.Bind(source)
}

return l.tokens, nil
Expand Down
2 changes: 1 addition & 1 deletion parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func Parse(input string) (*Tree, error) {
}

if p.err != nil {
return nil, fmt.Errorf("%v", p.err.Format(source))
return nil, p.err.Bind(source)
}

return &Tree{
Expand Down
4 changes: 2 additions & 2 deletions vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ func Run(program *Program, env interface{}) (out interface{}, err error) {

defer func() {
if r := recover(); r != nil {
h := file.Error{
f := &file.Error{
Location: program.Locations[vm.pp],
Message: fmt.Sprintf("%v", r),
}
err = fmt.Errorf("%v", h.Format(program.Source))
err = f.Bind(program.Source)
}
}()

Expand Down

0 comments on commit 0f43d1f

Please sign in to comment.