1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-11-25 22:01:39 +02:00

Added support of optional ignoring of errors to function calls (#652)

* Added support of optional ignoring of errors to function calls

* Added support of handling of source failure to optional chaining

* Updated unit tests
This commit is contained in:
Tim Voronov
2021-09-06 22:21:20 -04:00
committed by GitHub
parent 0cb7623a7f
commit 5f361e9e1b
16 changed files with 1211 additions and 893 deletions

View File

@@ -80,6 +80,21 @@ func TestFunctionNSCall(t *testing.T) {
So(err, ShouldNotBeNil)
})
Convey("T::FAIL()? should return NONE", t, func() {
c := compiler.New()
p, err := c.Compile(`
RETURN T::FAIL()?
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `null`)
})
Convey("Should use keywords", t, func() {
c := compiler.New()

View File

@@ -5,6 +5,7 @@ import (
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/pkg/errors"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
@@ -83,6 +84,44 @@ func TestFunctionCall(t *testing.T) {
So(string(out), ShouldEqual, `[2,4,6,8]`)
})
Convey("Should handle errors when ? is used", t, func() {
c := compiler.New()
c.RegisterFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return values.None, errors.New("test error")
})
p, err := c.Compile(`
RETURN ERROR()?
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `null`)
})
Convey("Should return NONE when error is handled", t, func() {
c := compiler.New()
c.RegisterFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return values.NewString("booo"), errors.New("test error")
})
p, err := c.Compile(`
RETURN ERROR()?
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `null`)
})
}
func BenchmarkFunctionCallArg1(b *testing.B) {

View File

@@ -2,8 +2,10 @@ package compiler_test
import (
"context"
"errors"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/runtime/core"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
@@ -71,7 +73,43 @@ func TestLogicalOperators(t *testing.T) {
So(string(out), ShouldEqual, `"foo"`)
})
Convey("NONE && true should return null", t, func() {
Convey("ERROR()? || 'boo' should return 'boo'", t, func() {
c := compiler.New()
c.RegisterFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return nil, errors.New("test")
})
p, err := c.Compile(`
RETURN ERROR()? || 'boo'
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `"boo"`)
})
Convey("!ERROR()? && TRUE should return false", t, func() {
c := compiler.New()
c.RegisterFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return nil, errors.New("test")
})
p, err := c.Compile(`
RETURN !ERROR()? && TRUE
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("NONE && true should return null", t, func() {
c := compiler.New()
p, err := c.Compile(`

View File

@@ -3,6 +3,7 @@ package compiler_test
import (
"context"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime/core"
"testing"
"github.com/MontFerret/ferret/pkg/compiler"
@@ -495,6 +496,25 @@ RETURN o1.first["second"][o2.prop].fourth["fifth"]["bottom"]
So(string(out), ShouldEqual, `"bar"`)
})
Convey("When function returns error", func() {
c := compiler.New()
c.RegisterFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return nil, core.ErrNotImplemented
})
p, err := c.Compile(`
RETURN ERROR()?.foo
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `null`)
})
})
})
}

View File

@@ -928,8 +928,8 @@ func (v *visitor) doVisitMemberExpressionSource(ctx *fql.MemberExpressionSourceC
return v.doVisitParamContext(param.(*fql.ParamContext), scope)
}
if fnCall := ctx.FunctionCallExpression(); fnCall != nil {
return v.doVisitFunctionCallExpression(fnCall.(*fql.FunctionCallExpressionContext), scope)
if fnCall := ctx.FunctionCall(); fnCall != nil {
return v.doVisitFunctionCall(fnCall.(*fql.FunctionCallContext), false, scope)
}
if objectLiteral := ctx.ObjectLiteral(); objectLiteral != nil {
@@ -1134,7 +1134,7 @@ func (v *visitor) doVisitRangeOperator(ctx *fql.RangeOperatorContext, scope *sco
)
}
func (v *visitor) doVisitFunctionCallExpression(context *fql.FunctionCallExpressionContext, scope *scope) (core.Expression, error) {
func (v *visitor) doVisitFunctionCall(context *fql.FunctionCallContext, ignoreErrors bool, scope *scope) (core.Expression, error) {
args := make([]core.Expression, 0, 5)
argsCtx := context.Arguments()
@@ -1170,10 +1170,19 @@ func (v *visitor) doVisitFunctionCallExpression(context *fql.FunctionCallExpress
return expressions.NewFunctionCallExpression(
v.getSourceMap(context),
fun,
ignoreErrors,
args...,
)
}
func (v *visitor) doVisitFunctionCallExpression(context *fql.FunctionCallExpressionContext, scope *scope) (core.Expression, error) {
return v.doVisitFunctionCall(
context.FunctionCall().(*fql.FunctionCallContext),
context.QuestionMark() != nil,
scope,
)
}
func (v *visitor) doVisitParamContext(context *fql.ParamContext, s *scope) (core.Expression, error) {
name := context.Identifier().GetText()