1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-02-09 13:38:35 +02:00

Added support of error supression to inline expressions (#671)

* Added support of error supression to inline expressions
This commit is contained in:
Tim Voronov 2021-09-20 00:53:00 -04:00 committed by GitHub
parent 658e929c5a
commit 2f399c669e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1793 additions and 1485 deletions

View File

@ -28,6 +28,18 @@ func TestForTernaryExpression(t *testing.T) {
So(string(out2), ShouldEqual, `true`)
})
Convey("RETURN foo ? TRUE : (FOR i IN 1..5 T::FAIL() RETURN i*2)?", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
LET foo = FALSE
RETURN foo ? TRUE : (FOR i IN 1..5 T::FAIL() RETURN i*2)?
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `null`)
})
Convey("RETURN foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)", t, func() {
c := compiler.New()
@ -48,6 +60,26 @@ func TestForTernaryExpression(t *testing.T) {
So(string(out2), ShouldEqual, `[1,2,3,4,5]`)
})
Convey("RETURN foo ? (FOR i IN 1..5 RETURN T::FAIL())? : (FOR i IN 1..5 RETURN T::FAIL())?", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
LET foo = FALSE
RETURN foo ? (FOR i IN 1..5 RETURN T::FAIL()) : (FOR i IN 1..5 RETURN T::FAIL())?
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `null`)
out2, err := c.MustCompile(`
LET foo = TRUE
RETURN foo ? (FOR i IN 1..5 RETURN T::FAIL())? : (FOR i IN 1..5 RETURN T::FAIL())
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out2), ShouldEqual, `null`)
})
Convey("LET res = foo ? TRUE : (FOR i IN 1..5 RETURN i*2)", t, func() {
c := compiler.New()
@ -91,6 +123,26 @@ func TestForTernaryExpression(t *testing.T) {
So(err, ShouldBeNil)
So(string(out2), ShouldEqual, `[1,2,3,4,5]`)
})
Convey("LET res = (FOR i IN 1..5 RETURN T::FAIL())? ? TRUE : FALSE", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
LET res = (FOR i IN 1..5 RETURN T::FAIL())? ? TRUE : FALSE
RETURN res
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `false`)
out2, err := c.MustCompile(`
LET res = (FOR i IN 1..5 RETURN i)? ? TRUE : FALSE
RETURN res
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out2), ShouldEqual, `true`)
})
}
func BenchmarkForTernary(b *testing.B) {

View File

@ -22,6 +22,18 @@ func TestForTernaryWhileExpression(t *testing.T) {
So(string(out1), ShouldEqual, `[]`)
})
Convey("RETURN foo ? TRUE : (FOR i WHILE T::FAIL() RETURN i*2)?", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
LET foo = FALSE
RETURN foo ? TRUE : (FOR i WHILE T::FAIL() RETURN i*2)?
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `null`)
})
Convey("RETURN foo ? TRUE : (FOR i WHILE F() < 10 RETURN i*2)", t, func() {
c := compiler.New()

View File

@ -104,6 +104,22 @@ func TestFunctionCall(t *testing.T) {
So(string(out), ShouldEqual, `null`)
})
Convey("Should handle errors when ? is used within a group", t, func() {
c := compiler.New()
p, err := c.Compile(`
RETURN (FALSE OR T::FAIL())?
`)
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) {
@ -122,6 +138,38 @@ func TestFunctionCall(t *testing.T) {
So(string(out), ShouldEqual, `null`)
})
Convey("Should be able to use FOR as an argument", t, func() {
c := compiler.New()
p, err := c.Compile(`
RETURN FIRST((FOR i IN 1..10 RETURN i * 2))
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `2`)
})
Convey("Should be able to use FOR as arguments", t, func() {
c := compiler.New()
p, err := c.Compile(`
RETURN UNION((FOR i IN 0..5 RETURN i), (FOR i IN 6..10 RETURN i))
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[0,1,2,3,4,5,6,7,8,9,10]`)
})
}
func BenchmarkFunctionCallArg1(b *testing.B) {

View File

@ -4,6 +4,8 @@ import (
"context"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
@ -179,11 +181,35 @@ func TestLet(t *testing.T) {
So(string(out), ShouldEqual, "[1,2,3]")
})
Convey("Should compile LET i = (FOR i WHILE 0 > 1 RETURN i) RETURN i", t, func() {
Convey("Should compile LET src = NONE LET i = (FOR i IN NONE RETURN i)? RETURN i == NONE", t, func() {
c := compiler.New()
p, err := c.Compile(`
LET i = (FOR i WHILE 0 > 1 RETURN i)
LET src = NONE
LET i = (FOR i IN src RETURN i)?
RETURN i == NONE
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "true")
})
Convey("Should compile LET i = (FOR i WHILE COUNTER() < 5 RETURN i) RETURN i", t, func() {
c := compiler.New()
counter := -1
c.RegisterFunction("COUNTER", func(ctx context.Context, args ...core.Value) (core.Value, error) {
counter++
return values.NewInt(counter), nil
})
p, err := c.Compile(`
LET i = (FOR i WHILE COUNTER() < 5 RETURN i)
RETURN i
`)
@ -193,7 +219,30 @@ func TestLet(t *testing.T) {
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[]")
So(string(out), ShouldEqual, "[0,1,2,3,4]")
})
Convey("Should compile LET i = (FOR i WHILE COUNTER() < 5 T::FAIL() RETURN i)? RETURN i == NONE", t, func() {
c := compiler.New()
counter := -1
c.RegisterFunction("COUNTER", func(ctx context.Context, args ...core.Value) (core.Value, error) {
counter++
return values.NewInt(counter), nil
})
p, err := c.Compile(`
LET i = (FOR i WHILE COUNTER() < 5 T::FAIL() RETURN i)?
RETURN i == NONE
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "true")
})
Convey("Should compile LET i = { items: [1,2,3]} FOR el IN i.items RETURN i", t, func() {
@ -262,4 +311,30 @@ func TestLet(t *testing.T) {
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `"data"`)
})
Convey("Should handle error from WAITFOR EVENT", t, func() {
out, err := newCompilerWithObservable().MustCompile(`
LET obj = X::CREATE()
LET res = (WAITFOR EVENT "event" IN obj 100)?
RETURN res == NONE
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("Should compare result of handled error", t, func() {
out, err := newCompilerWithObservable().MustCompile(`
LET obj = X::CREATE()
LET res = (WAITFOR EVENT "event" IN obj 100)? != NONE
RETURN res
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
}

View File

@ -185,11 +185,7 @@ func (v *visitor) doVisitReturnExpression(ctx *fql.ReturnExpressionContext, scop
var out core.Expression
var err error
if exp := ctx.ForExpression(); exp != nil {
out, err = v.doVisitForExpression(exp.(*fql.ForExpressionContext), scope.Fork())
} else if exp := ctx.WaitForExpression(); exp != nil {
out, err = v.doVisitWaitForExpressionContext(exp.(*fql.WaitForExpressionContext), scope)
} else if exp := ctx.Expression(); exp != nil {
if exp := ctx.Expression(); exp != nil {
out, err = v.doVisitExpression(exp.(*fql.ExpressionContext), scope)
} else {
return nil, core.Error(ErrInvalidToken, ctx.GetText())
@ -202,6 +198,36 @@ func (v *visitor) doVisitReturnExpression(ctx *fql.ReturnExpressionContext, scop
return expressions.NewReturnExpression(v.getSourceMap(ctx), out)
}
func (v *visitor) doVisitInlineHighLevelExpression(ctx *fql.InlineHighLevelExpressionContext, scope *scope) (core.Expression, error) {
hlexpCtx := ctx.HighLevelExpression()
if hlexpCtx == nil {
return nil, ErrInvalidToken
}
exp, err := v.doVisitHighLevelExpression(hlexpCtx.(*fql.HighLevelExpressionContext), scope.Fork())
if err != nil {
return nil, err
}
if ctx.ErrorOperator() == nil {
return exp, nil
}
return expressions.SuppressErrors(exp)
}
func (v *visitor) doVisitHighLevelExpression(ctx *fql.HighLevelExpressionContext, scope *scope) (core.Expression, error) {
if exp := ctx.ForExpression(); exp != nil {
return v.doVisitForExpression(exp.(*fql.ForExpressionContext), scope.Fork())
} else if exp := ctx.WaitForExpression(); exp != nil {
return v.doVisitWaitForExpressionContext(exp.(*fql.WaitForExpressionContext), scope)
}
return nil, ErrInvalidToken
}
func (v *visitor) doVisitForExpression(ctx *fql.ForExpressionContext, scope *scope) (core.Expression, error) {
var err error
var valVarName string
@ -599,6 +625,7 @@ func (v *visitor) doVisitCollectAggregateSelector(ctx *fql.CollectAggregateSelec
if fnCtx != nil {
exp, err := v.doVisitFunctionCallExpression(fnCtx.(*fql.FunctionCallExpressionContext), scope)
if err != nil {
return nil, err
}
@ -945,7 +972,7 @@ func (v *visitor) doVisitMemberExpressionSource(ctx *fql.MemberExpressionSourceC
}
if fnCall := ctx.FunctionCall(); fnCall != nil {
return v.doVisitFunctionCall(fnCall.(*fql.FunctionCallContext), false, scope)
return v.doVisitFunctionCall(fnCall.(*fql.FunctionCallContext), scope)
}
if objectLiteral := ctx.ObjectLiteral(); objectLiteral != nil {
@ -1113,10 +1140,6 @@ func (v *visitor) doVisitVariableDeclaration(ctx *fql.VariableDeclarationContext
if exp := ctx.Expression(); exp != nil {
init, err = v.doVisitExpression(ctx.Expression().(*fql.ExpressionContext), scope)
} else if exp := ctx.ForExpression(); exp != nil {
init, err = v.doVisitForExpression(exp.(*fql.ForExpressionContext), scope)
} else if exp := ctx.WaitForExpression(); exp != nil {
init, err = v.doVisitWaitForExpressionContext(exp.(*fql.WaitForExpressionContext), scope)
}
if err != nil {
@ -1150,7 +1173,7 @@ func (v *visitor) doVisitRangeOperator(ctx *fql.RangeOperatorContext, scope *sco
)
}
func (v *visitor) doVisitFunctionCall(context *fql.FunctionCallContext, ignoreErrors bool, scope *scope) (core.Expression, error) {
func (v *visitor) doVisitFunctionCall(context *fql.FunctionCallContext, scope *scope) (core.Expression, error) {
args := make([]core.Expression, 0, 5)
argsCtx := context.Arguments()
@ -1186,17 +1209,25 @@ func (v *visitor) doVisitFunctionCall(context *fql.FunctionCallContext, ignoreEr
return expressions.NewFunctionCallExpression(
v.getSourceMap(context),
fun,
ignoreErrors,
args...,
)
}
func (v *visitor) doVisitFunctionCallExpression(context *fql.FunctionCallExpressionContext, scope *scope) (core.Expression, error) {
return v.doVisitFunctionCall(
exp, err := v.doVisitFunctionCall(
context.FunctionCall().(*fql.FunctionCallContext),
context.QuestionMark() != nil,
scope,
)
if err != nil {
return nil, err
}
if context.ErrorOperator() == nil {
return exp, nil
}
return expressions.SuppressErrors(exp)
}
func (v *visitor) doVisitParamContext(context *fql.ParamContext, s *scope) (core.Expression, error) {
@ -1417,13 +1448,23 @@ func (v *visitor) doVisitArrayOperator(ctx *fql.ExpressionContext, scope *scope)
}
func (v *visitor) doVisitExpressionGroup(ctx *fql.ExpressionGroupContext, scope *scope) (core.Expression, error) {
exp := ctx.Expression()
expCtx := ctx.Expression()
if exp == nil {
if expCtx == nil {
return nil, ErrInvalidToken
}
return v.doVisitExpression(exp.(*fql.ExpressionContext), scope)
exp, err := v.doVisitExpression(expCtx.(*fql.ExpressionContext), scope)
if err != nil {
return nil, err
}
if ctx.ErrorOperator() == nil {
return exp, nil
}
return expressions.SuppressErrors(exp)
}
func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (core.Expression, error) {
@ -1515,19 +1556,6 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
return v.doVisitNoneLiteral(exp.(*fql.NoneLiteralContext))
}
if exp := ctx.QuestionMark(); exp != nil {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
return v.createTernaryOperator(
v.getSourceMap(ctx),
exps,
scope,
)
}
if exp := ctx.RangeOperator(); exp != nil {
return v.doVisitRangeOperator(exp.(*fql.RangeOperatorContext), scope)
}
@ -1536,6 +1564,10 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
return v.doVisitParamContext(param.(*fql.ParamContext), scope)
}
if exp := ctx.InlineHighLevelExpression(); exp != nil {
return v.doVisitInlineHighLevelExpression(exp.(*fql.InlineHighLevelExpressionContext), scope)
}
return nil, ErrNotImplemented
}

View File

@ -40,13 +40,20 @@ bodyExpression
;
variableDeclaration
: Let Identifier Assign OpenParen (forExpression | waitForExpression) CloseParen
| Let Identifier Assign expression
: Let Identifier Assign expression
;
returnExpression
: Return Distinct? OpenParen (forExpression | waitForExpression) CloseParen
| Return Distinct? expression
: Return Distinct? expression
;
inlineHighLevelExpression
: OpenParen highLevelExpression CloseParen errorOperator?
;
highLevelExpression
: forExpression
| waitForExpression
;
forExpression
@ -223,10 +230,6 @@ noneLiteral
| None
;
expressionGroup
: OpenParen expression CloseParen
;
expression
: unaryOperator expression
| expression multiplicativeOperator expression
@ -238,9 +241,6 @@ expression
| expression regexpOperator expression
| expression logicalAndOperator expression
| expression logicalOrOperator expression
| expression QuestionMark OpenParen (forExpression | waitForExpression) CloseParen Colon OpenParen (forExpression | waitForExpression) CloseParen
| expression QuestionMark expression Colon OpenParen (forExpression | waitForExpression) CloseParen
| expression QuestionMark OpenParen (forExpression | waitForExpression) CloseParen Colon expression
| expression QuestionMark expression? Colon expression
| rangeOperator
| stringLiteral
@ -255,6 +255,11 @@ expression
| variable
| noneLiteral
| expressionGroup
| inlineHighLevelExpression
;
expressionGroup
: OpenParen expression CloseParen errorOperator?
;
memberExpression
@ -274,7 +279,7 @@ functionCall
;
functionCallExpression
: functionCall QuestionMark?
: functionCall errorOperator?
;
memberExpressionPath
@ -282,6 +287,10 @@ memberExpressionPath
| (QuestionMark Dot)? computedPropertyName
;
errorOperator
: QuestionMark
;
functionIdentifier
: Identifier
| And

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -80,6 +80,20 @@ func (s *BaseFqlParserListener) EnterReturnExpression(ctx *ReturnExpressionConte
// ExitReturnExpression is called when production returnExpression is exited.
func (s *BaseFqlParserListener) ExitReturnExpression(ctx *ReturnExpressionContext) {}
// EnterInlineHighLevelExpression is called when production inlineHighLevelExpression is entered.
func (s *BaseFqlParserListener) EnterInlineHighLevelExpression(ctx *InlineHighLevelExpressionContext) {
}
// ExitInlineHighLevelExpression is called when production inlineHighLevelExpression is exited.
func (s *BaseFqlParserListener) ExitInlineHighLevelExpression(ctx *InlineHighLevelExpressionContext) {
}
// EnterHighLevelExpression is called when production highLevelExpression is entered.
func (s *BaseFqlParserListener) EnterHighLevelExpression(ctx *HighLevelExpressionContext) {}
// ExitHighLevelExpression is called when production highLevelExpression is exited.
func (s *BaseFqlParserListener) ExitHighLevelExpression(ctx *HighLevelExpressionContext) {}
// EnterForExpression is called when production forExpression is entered.
func (s *BaseFqlParserListener) EnterForExpression(ctx *ForExpressionContext) {}
@ -284,18 +298,18 @@ func (s *BaseFqlParserListener) EnterNoneLiteral(ctx *NoneLiteralContext) {}
// ExitNoneLiteral is called when production noneLiteral is exited.
func (s *BaseFqlParserListener) ExitNoneLiteral(ctx *NoneLiteralContext) {}
// EnterExpressionGroup is called when production expressionGroup is entered.
func (s *BaseFqlParserListener) EnterExpressionGroup(ctx *ExpressionGroupContext) {}
// ExitExpressionGroup is called when production expressionGroup is exited.
func (s *BaseFqlParserListener) ExitExpressionGroup(ctx *ExpressionGroupContext) {}
// EnterExpression is called when production expression is entered.
func (s *BaseFqlParserListener) EnterExpression(ctx *ExpressionContext) {}
// ExitExpression is called when production expression is exited.
func (s *BaseFqlParserListener) ExitExpression(ctx *ExpressionContext) {}
// EnterExpressionGroup is called when production expressionGroup is entered.
func (s *BaseFqlParserListener) EnterExpressionGroup(ctx *ExpressionGroupContext) {}
// ExitExpressionGroup is called when production expressionGroup is exited.
func (s *BaseFqlParserListener) ExitExpressionGroup(ctx *ExpressionGroupContext) {}
// EnterMemberExpression is called when production memberExpression is entered.
func (s *BaseFqlParserListener) EnterMemberExpression(ctx *MemberExpressionContext) {}
@ -326,6 +340,12 @@ func (s *BaseFqlParserListener) EnterMemberExpressionPath(ctx *MemberExpressionP
// ExitMemberExpressionPath is called when production memberExpressionPath is exited.
func (s *BaseFqlParserListener) ExitMemberExpressionPath(ctx *MemberExpressionPathContext) {}
// EnterErrorOperator is called when production errorOperator is entered.
func (s *BaseFqlParserListener) EnterErrorOperator(ctx *ErrorOperatorContext) {}
// ExitErrorOperator is called when production errorOperator is exited.
func (s *BaseFqlParserListener) ExitErrorOperator(ctx *ErrorOperatorContext) {}
// EnterFunctionIdentifier is called when production functionIdentifier is entered.
func (s *BaseFqlParserListener) EnterFunctionIdentifier(ctx *FunctionIdentifierContext) {}

View File

@ -47,6 +47,14 @@ func (v *BaseFqlParserVisitor) VisitReturnExpression(ctx *ReturnExpressionContex
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitInlineHighLevelExpression(ctx *InlineHighLevelExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitHighLevelExpression(ctx *HighLevelExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitForExpression(ctx *ForExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
@ -183,11 +191,11 @@ func (v *BaseFqlParserVisitor) VisitNoneLiteral(ctx *NoneLiteralContext) interfa
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitExpressionGroup(ctx *ExpressionGroupContext) interface{} {
func (v *BaseFqlParserVisitor) VisitExpression(ctx *ExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitExpression(ctx *ExpressionContext) interface{} {
func (v *BaseFqlParserVisitor) VisitExpressionGroup(ctx *ExpressionGroupContext) interface{} {
return v.VisitChildren(ctx)
}
@ -211,6 +219,10 @@ func (v *BaseFqlParserVisitor) VisitMemberExpressionPath(ctx *MemberExpressionPa
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitErrorOperator(ctx *ErrorOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitFunctionIdentifier(ctx *FunctionIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@ -37,6 +37,12 @@ type FqlParserListener interface {
// EnterReturnExpression is called when entering the returnExpression production.
EnterReturnExpression(c *ReturnExpressionContext)
// EnterInlineHighLevelExpression is called when entering the inlineHighLevelExpression production.
EnterInlineHighLevelExpression(c *InlineHighLevelExpressionContext)
// EnterHighLevelExpression is called when entering the highLevelExpression production.
EnterHighLevelExpression(c *HighLevelExpressionContext)
// EnterForExpression is called when entering the forExpression production.
EnterForExpression(c *ForExpressionContext)
@ -139,12 +145,12 @@ type FqlParserListener interface {
// EnterNoneLiteral is called when entering the noneLiteral production.
EnterNoneLiteral(c *NoneLiteralContext)
// EnterExpressionGroup is called when entering the expressionGroup production.
EnterExpressionGroup(c *ExpressionGroupContext)
// EnterExpression is called when entering the expression production.
EnterExpression(c *ExpressionContext)
// EnterExpressionGroup is called when entering the expressionGroup production.
EnterExpressionGroup(c *ExpressionGroupContext)
// EnterMemberExpression is called when entering the memberExpression production.
EnterMemberExpression(c *MemberExpressionContext)
@ -160,6 +166,9 @@ type FqlParserListener interface {
// EnterMemberExpressionPath is called when entering the memberExpressionPath production.
EnterMemberExpressionPath(c *MemberExpressionPathContext)
// EnterErrorOperator is called when entering the errorOperator production.
EnterErrorOperator(c *ErrorOperatorContext)
// EnterFunctionIdentifier is called when entering the functionIdentifier production.
EnterFunctionIdentifier(c *FunctionIdentifierContext)
@ -235,6 +244,12 @@ type FqlParserListener interface {
// ExitReturnExpression is called when exiting the returnExpression production.
ExitReturnExpression(c *ReturnExpressionContext)
// ExitInlineHighLevelExpression is called when exiting the inlineHighLevelExpression production.
ExitInlineHighLevelExpression(c *InlineHighLevelExpressionContext)
// ExitHighLevelExpression is called when exiting the highLevelExpression production.
ExitHighLevelExpression(c *HighLevelExpressionContext)
// ExitForExpression is called when exiting the forExpression production.
ExitForExpression(c *ForExpressionContext)
@ -337,12 +352,12 @@ type FqlParserListener interface {
// ExitNoneLiteral is called when exiting the noneLiteral production.
ExitNoneLiteral(c *NoneLiteralContext)
// ExitExpressionGroup is called when exiting the expressionGroup production.
ExitExpressionGroup(c *ExpressionGroupContext)
// ExitExpression is called when exiting the expression production.
ExitExpression(c *ExpressionContext)
// ExitExpressionGroup is called when exiting the expressionGroup production.
ExitExpressionGroup(c *ExpressionGroupContext)
// ExitMemberExpression is called when exiting the memberExpression production.
ExitMemberExpression(c *MemberExpressionContext)
@ -358,6 +373,9 @@ type FqlParserListener interface {
// ExitMemberExpressionPath is called when exiting the memberExpressionPath production.
ExitMemberExpressionPath(c *MemberExpressionPathContext)
// ExitErrorOperator is called when exiting the errorOperator production.
ExitErrorOperator(c *ErrorOperatorContext)
// ExitFunctionIdentifier is called when exiting the functionIdentifier production.
ExitFunctionIdentifier(c *FunctionIdentifierContext)

View File

@ -37,6 +37,12 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#returnExpression.
VisitReturnExpression(ctx *ReturnExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#inlineHighLevelExpression.
VisitInlineHighLevelExpression(ctx *InlineHighLevelExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#highLevelExpression.
VisitHighLevelExpression(ctx *HighLevelExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#forExpression.
VisitForExpression(ctx *ForExpressionContext) interface{}
@ -139,12 +145,12 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#noneLiteral.
VisitNoneLiteral(ctx *NoneLiteralContext) interface{}
// Visit a parse tree produced by FqlParser#expressionGroup.
VisitExpressionGroup(ctx *ExpressionGroupContext) interface{}
// Visit a parse tree produced by FqlParser#expression.
VisitExpression(ctx *ExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#expressionGroup.
VisitExpressionGroup(ctx *ExpressionGroupContext) interface{}
// Visit a parse tree produced by FqlParser#memberExpression.
VisitMemberExpression(ctx *MemberExpressionContext) interface{}
@ -160,6 +166,9 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#memberExpressionPath.
VisitMemberExpressionPath(ctx *MemberExpressionPathContext) interface{}
// Visit a parse tree produced by FqlParser#errorOperator.
VisitErrorOperator(ctx *ErrorOperatorContext) interface{}
// Visit a parse tree produced by FqlParser#functionIdentifier.
VisitFunctionIdentifier(ctx *FunctionIdentifierContext) interface{}

View File

@ -46,11 +46,9 @@ func NewDefaultWhileIterator(mode WhileMode, predicate WhilePredicate) (Iterator
}
func (iterator *WhileIterator) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) {
counter := values.NewInt(iterator.pos)
// if it's Post conditional execution, step in always
// Otherwise, it's not the first iteration
if iterator.mode == WhileModePost || counter > 0 {
if iterator.mode == WhileModePost || iterator.pos > 0 {
doNext, err := iterator.predicate(ctx, scope)
if err != nil {
@ -62,6 +60,7 @@ func (iterator *WhileIterator) Next(ctx context.Context, scope *core.Scope) (*co
}
}
counter := values.NewInt(iterator.pos)
iterator.pos++
nextScope := scope.Fork()

View File

@ -7,23 +7,21 @@ import (
)
type FunctionCallExpression struct {
src core.SourceMap
fun core.Function
ignoreErrors bool
args []core.Expression
src core.SourceMap
fun core.Function
args []core.Expression
}
func NewFunctionCallExpression(
src core.SourceMap,
fun core.Function,
ignoreErrors bool,
args ...core.Expression,
) (*FunctionCallExpression, error) {
if fun == nil {
return nil, core.Error(core.ErrMissedArgument, "function")
}
return &FunctionCallExpression{src, fun, ignoreErrors, args}, nil
return &FunctionCallExpression{src, fun, args}, nil
}
func (e *FunctionCallExpression) Arguments() []core.Expression {
@ -31,18 +29,6 @@ func (e *FunctionCallExpression) Arguments() []core.Expression {
}
func (e *FunctionCallExpression) Function() core.Function {
if e.ignoreErrors {
return func(ctx context.Context, args ...core.Value) (core.Value, error) {
out, err := e.fun(ctx, args...)
if err != nil {
return values.None, nil
}
return out, nil
}
}
return e.fun
}
@ -63,7 +49,7 @@ func (e *FunctionCallExpression) Exec(ctx context.Context, scope *core.Scope) (c
out, err := arg.Exec(ctx, scope)
if err != nil {
return values.None, e.maybeError(core.SourceError(e.src, err))
return values.None, core.SourceError(e.src, err)
}
args[idx] = out
@ -73,17 +59,9 @@ func (e *FunctionCallExpression) Exec(ctx context.Context, scope *core.Scope) (c
}
if err != nil {
return values.None, e.maybeError(core.SourceError(e.src, err))
return values.None, core.SourceError(e.src, err)
}
return out, nil
}
}
func (e *FunctionCallExpression) maybeError(err error) error {
if !e.ignoreErrors {
return err
}
return nil
}

View File

@ -22,7 +22,6 @@ func TestFunctionCallExpression(t *testing.T) {
return values.True, nil
},
false,
)
So(err, ShouldBeNil)
@ -48,7 +47,6 @@ func TestFunctionCallExpression(t *testing.T) {
return values.True, nil
},
false,
args...,
)
@ -75,7 +73,6 @@ func TestFunctionCallExpression(t *testing.T) {
return values.True, nil
},
false,
args...,
)
@ -96,11 +93,15 @@ func TestFunctionCallExpression(t *testing.T) {
func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return values.NewString("booo"), core.ErrNotImplemented
},
true,
)
So(err, ShouldBeNil)
out, err := f.Exec(context.Background(), rootScope.Fork())
fse, err := expressions.SuppressErrors(f)
So(err, ShouldBeNil)
out, err := fse.Exec(context.Background(), rootScope.Fork())
So(err, ShouldBeNil)
So(out.Type().String(), ShouldEqual, types.None.String())

View File

@ -0,0 +1,32 @@
package expressions
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
type SuppressibleExpression struct {
exp core.Expression
}
func SuppressErrors(exp core.Expression) (core.Expression, error) {
if exp == nil {
return nil, core.Error(core.ErrMissedArgument, "expression")
}
return &SuppressibleExpression{exp}, nil
}
func (exp *SuppressibleExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
return exp.Maybe(exp.exp.Exec(ctx, scope))
}
func (exp *SuppressibleExpression) Maybe(value core.Value, err error) (core.Value, error) {
if err != nil {
return values.None, nil
}
return value, nil
}