Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions pkg/compiler/compiler_func_ns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
39 changes: 39 additions & 0 deletions pkg/compiler/compiler_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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) {
Expand Down
40 changes: 39 additions & 1 deletion pkg/compiler/compiler_logical_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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(`
Expand Down
20 changes: 20 additions & 0 deletions pkg/compiler/compiler_member_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package compiler_test
import (
"context"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime/core"
"testing"

"github.com/MontFerret/ferret/pkg/compiler"
Expand Down Expand Up @@ -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`)
})
})
})
}
Expand Down
15 changes: 12 additions & 3 deletions pkg/compiler/visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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()

Expand Down
5 changes: 4 additions & 1 deletion pkg/drivers/cdp/events/loop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func NewBufferedTestEventStream(buffer int) *TestEventStream {
es := new(TestEventStream)
es.ready = make(chan struct{}, buffer)
es.message = make(chan interface{}, buffer)

return es
}

Expand Down Expand Up @@ -394,7 +395,7 @@ func TestLoop(t *testing.T) {
// Stop the loop
cancel()

time.Sleep(time.Duration(100) * time.Millisecond)
time.Sleep(time.Duration(500) * time.Millisecond)

onLoad.Emit(&page.LoadEventFiredReply{})

Expand All @@ -404,6 +405,8 @@ func TestLoop(t *testing.T) {
onLoad.Emit(&page.LoadEventFiredReply{})
}

time.Sleep(time.Duration(500) * time.Millisecond)

So(counter.Value(), ShouldEqual, eventsToFire)
})
}
Expand Down
14 changes: 9 additions & 5 deletions pkg/parser/antlr/FqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -264,18 +264,22 @@ memberExpression
memberExpressionSource
: variable
| param
| functionCallExpression
| arrayLiteral
| objectLiteral
| functionCall
;

memberExpressionPath
: QuestionMark? Dot propertyName
| (QuestionMark Dot)? computedPropertyName
functionCall
: namespace functionIdentifier arguments
;

functionCallExpression
: namespace functionIdentifier arguments
: functionCall QuestionMark?
;

memberExpressionPath
: QuestionMark? Dot propertyName
| (QuestionMark Dot)? computedPropertyName
;

functionIdentifier
Expand Down
5 changes: 3 additions & 2 deletions pkg/parser/fql/FqlParser.interp

Large diffs are not rendered by default.

Loading