diff --git a/expr_test.go b/expr_test.go index 13cb55c8e..441985319 100644 --- a/expr_test.go +++ b/expr_test.go @@ -1160,7 +1160,7 @@ func TestExpr_call_floatarg_func_with_int(t *testing.T) { } } -func TestConstExpr_error(t *testing.T) { +func TestConstExpr_error_panic(t *testing.T) { env := map[string]interface{}{ "divide": func(a, b int) int { return a / b }, } @@ -1174,6 +1174,31 @@ func TestConstExpr_error(t *testing.T) { require.Equal(t, "compile error: integer divide by zero (1:5)\n | 1 + divide(1, 0)\n | ....^", err.Error()) } +type divideError struct{ Message string } + +func (e divideError) Error() string { + return e.Message +} +func TestConstExpr_error_as_error(t *testing.T) { + env := map[string]interface{}{ + "divide": func(a, b int) (int, error) { + if b == 0 { + return 0, divideError{"integer divide by zero"} + } + return a / b, nil + }, + } + + _, err := expr.Compile( + `1 + divide(1, 0)`, + expr.Env(env), + expr.ConstExpr("divide"), + ) + require.Error(t, err) + require.Equal(t, "integer divide by zero", err.Error()) + require.IsType(t, divideError{}, err) +} + func TestConstExpr_error_wrong_type(t *testing.T) { env := map[string]interface{}{ "divide": 0, diff --git a/optimizer/const_expr.go b/optimizer/const_expr.go index 85fcc337f..95dd2d4b4 100644 --- a/optimizer/const_expr.go +++ b/optimizer/const_expr.go @@ -2,12 +2,15 @@ package optimizer import ( "fmt" - . "github.com/antonmedv/expr/ast" - "github.com/antonmedv/expr/file" "reflect" "strings" + + . "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/file" ) +var errorType = reflect.TypeOf((*error)(nil)).Elem() + type constExpr struct { applied bool err error @@ -70,7 +73,12 @@ func (c *constExpr) Exit(node *Node) { } out := fn.Call(in) - constNode := &ConstantNode{Value: out[0].Interface()} + value := out[0].Interface() + if len(out) == 2 && out[1].Type() == errorType && !out[1].IsNil() { + c.err = out[1].Interface().(error) + return + } + constNode := &ConstantNode{Value: value} patch(constNode) } }