1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-07-17 01:32:22 +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
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`) 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() { Convey("RETURN foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)", t, func() {
c := compiler.New() c := compiler.New()
@ -48,6 +60,26 @@ func TestForTernaryExpression(t *testing.T) {
So(string(out2), ShouldEqual, `[1,2,3,4,5]`) 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() { Convey("LET res = foo ? TRUE : (FOR i IN 1..5 RETURN i*2)", t, func() {
c := compiler.New() c := compiler.New()
@ -91,6 +123,26 @@ func TestForTernaryExpression(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(string(out2), ShouldEqual, `[1,2,3,4,5]`) 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) { func BenchmarkForTernary(b *testing.B) {

View File

@ -22,6 +22,18 @@ func TestForTernaryWhileExpression(t *testing.T) {
So(string(out1), ShouldEqual, `[]`) 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() { Convey("RETURN foo ? TRUE : (FOR i WHILE F() < 10 RETURN i*2)", t, func() {
c := compiler.New() c := compiler.New()

View File

@ -104,6 +104,22 @@ func TestFunctionCall(t *testing.T) {
So(string(out), ShouldEqual, `null`) 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() { Convey("Should return NONE when error is handled", t, func() {
c := compiler.New() c := compiler.New()
c.RegisterFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) { 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`) 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) { func BenchmarkFunctionCallArg1(b *testing.B) {

View File

@ -4,6 +4,8 @@ import (
"context" "context"
"github.com/MontFerret/ferret/pkg/compiler" "github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime" "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" . "github.com/smartystreets/goconvey/convey"
"testing" "testing"
) )
@ -179,11 +181,35 @@ func TestLet(t *testing.T) {
So(string(out), ShouldEqual, "[1,2,3]") 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() c := compiler.New()
p, err := c.Compile(` 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 RETURN i
`) `)
@ -193,7 +219,30 @@ func TestLet(t *testing.T) {
out, err := p.Run(context.Background()) out, err := p.Run(context.Background())
So(err, ShouldBeNil) 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() { 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(err, ShouldBeNil)
So(string(out), ShouldEqual, `"data"`) 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 out core.Expression
var err error var err error
if exp := ctx.ForExpression(); exp != nil { if exp := ctx.Expression(); 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 {
out, err = v.doVisitExpression(exp.(*fql.ExpressionContext), scope) out, err = v.doVisitExpression(exp.(*fql.ExpressionContext), scope)
} else { } else {
return nil, core.Error(ErrInvalidToken, ctx.GetText()) 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) 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) { func (v *visitor) doVisitForExpression(ctx *fql.ForExpressionContext, scope *scope) (core.Expression, error) {
var err error var err error
var valVarName string var valVarName string
@ -599,6 +625,7 @@ func (v *visitor) doVisitCollectAggregateSelector(ctx *fql.CollectAggregateSelec
if fnCtx != nil { if fnCtx != nil {
exp, err := v.doVisitFunctionCallExpression(fnCtx.(*fql.FunctionCallExpressionContext), scope) exp, err := v.doVisitFunctionCallExpression(fnCtx.(*fql.FunctionCallExpressionContext), scope)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -945,7 +972,7 @@ func (v *visitor) doVisitMemberExpressionSource(ctx *fql.MemberExpressionSourceC
} }
if fnCall := ctx.FunctionCall(); fnCall != nil { 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 { if objectLiteral := ctx.ObjectLiteral(); objectLiteral != nil {
@ -1113,10 +1140,6 @@ func (v *visitor) doVisitVariableDeclaration(ctx *fql.VariableDeclarationContext
if exp := ctx.Expression(); exp != nil { if exp := ctx.Expression(); exp != nil {
init, err = v.doVisitExpression(ctx.Expression().(*fql.ExpressionContext), scope) 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 { 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) args := make([]core.Expression, 0, 5)
argsCtx := context.Arguments() argsCtx := context.Arguments()
@ -1186,17 +1209,25 @@ func (v *visitor) doVisitFunctionCall(context *fql.FunctionCallContext, ignoreEr
return expressions.NewFunctionCallExpression( return expressions.NewFunctionCallExpression(
v.getSourceMap(context), v.getSourceMap(context),
fun, fun,
ignoreErrors,
args..., args...,
) )
} }
func (v *visitor) doVisitFunctionCallExpression(context *fql.FunctionCallExpressionContext, scope *scope) (core.Expression, error) { func (v *visitor) doVisitFunctionCallExpression(context *fql.FunctionCallExpressionContext, scope *scope) (core.Expression, error) {
return v.doVisitFunctionCall( exp, err := v.doVisitFunctionCall(
context.FunctionCall().(*fql.FunctionCallContext), context.FunctionCall().(*fql.FunctionCallContext),
context.QuestionMark() != nil,
scope, 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) { 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) { 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 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) { 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)) 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 { if exp := ctx.RangeOperator(); exp != nil {
return v.doVisitRangeOperator(exp.(*fql.RangeOperatorContext), scope) 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) return v.doVisitParamContext(param.(*fql.ParamContext), scope)
} }
if exp := ctx.InlineHighLevelExpression(); exp != nil {
return v.doVisitInlineHighLevelExpression(exp.(*fql.InlineHighLevelExpressionContext), scope)
}
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }

View File

@ -40,13 +40,20 @@ bodyExpression
; ;
variableDeclaration variableDeclaration
: Let Identifier Assign OpenParen (forExpression | waitForExpression) CloseParen : Let Identifier Assign expression
| Let Identifier Assign expression
; ;
returnExpression returnExpression
: Return Distinct? OpenParen (forExpression | waitForExpression) CloseParen : Return Distinct? expression
| Return Distinct? expression ;
inlineHighLevelExpression
: OpenParen highLevelExpression CloseParen errorOperator?
;
highLevelExpression
: forExpression
| waitForExpression
; ;
forExpression forExpression
@ -223,10 +230,6 @@ noneLiteral
| None | None
; ;
expressionGroup
: OpenParen expression CloseParen
;
expression expression
: unaryOperator expression : unaryOperator expression
| expression multiplicativeOperator expression | expression multiplicativeOperator expression
@ -238,9 +241,6 @@ expression
| expression regexpOperator expression | expression regexpOperator expression
| expression logicalAndOperator expression | expression logicalAndOperator expression
| expression logicalOrOperator 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 | expression QuestionMark expression? Colon expression
| rangeOperator | rangeOperator
| stringLiteral | stringLiteral
@ -255,6 +255,11 @@ expression
| variable | variable
| noneLiteral | noneLiteral
| expressionGroup | expressionGroup
| inlineHighLevelExpression
;
expressionGroup
: OpenParen expression CloseParen errorOperator?
; ;
memberExpression memberExpression
@ -274,7 +279,7 @@ functionCall
; ;
functionCallExpression functionCallExpression
: functionCall QuestionMark? : functionCall errorOperator?
; ;
memberExpressionPath memberExpressionPath
@ -282,6 +287,10 @@ memberExpressionPath
| (QuestionMark Dot)? computedPropertyName | (QuestionMark Dot)? computedPropertyName
; ;
errorOperator
: QuestionMark
;
functionIdentifier functionIdentifier
: Identifier : Identifier
| And | 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. // ExitReturnExpression is called when production returnExpression is exited.
func (s *BaseFqlParserListener) ExitReturnExpression(ctx *ReturnExpressionContext) {} 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. // EnterForExpression is called when production forExpression is entered.
func (s *BaseFqlParserListener) EnterForExpression(ctx *ForExpressionContext) {} func (s *BaseFqlParserListener) EnterForExpression(ctx *ForExpressionContext) {}
@ -284,18 +298,18 @@ func (s *BaseFqlParserListener) EnterNoneLiteral(ctx *NoneLiteralContext) {}
// ExitNoneLiteral is called when production noneLiteral is exited. // ExitNoneLiteral is called when production noneLiteral is exited.
func (s *BaseFqlParserListener) ExitNoneLiteral(ctx *NoneLiteralContext) {} 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. // EnterExpression is called when production expression is entered.
func (s *BaseFqlParserListener) EnterExpression(ctx *ExpressionContext) {} func (s *BaseFqlParserListener) EnterExpression(ctx *ExpressionContext) {}
// ExitExpression is called when production expression is exited. // ExitExpression is called when production expression is exited.
func (s *BaseFqlParserListener) ExitExpression(ctx *ExpressionContext) {} 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. // EnterMemberExpression is called when production memberExpression is entered.
func (s *BaseFqlParserListener) EnterMemberExpression(ctx *MemberExpressionContext) {} func (s *BaseFqlParserListener) EnterMemberExpression(ctx *MemberExpressionContext) {}
@ -326,6 +340,12 @@ func (s *BaseFqlParserListener) EnterMemberExpressionPath(ctx *MemberExpressionP
// ExitMemberExpressionPath is called when production memberExpressionPath is exited. // ExitMemberExpressionPath is called when production memberExpressionPath is exited.
func (s *BaseFqlParserListener) ExitMemberExpressionPath(ctx *MemberExpressionPathContext) {} 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. // EnterFunctionIdentifier is called when production functionIdentifier is entered.
func (s *BaseFqlParserListener) EnterFunctionIdentifier(ctx *FunctionIdentifierContext) {} func (s *BaseFqlParserListener) EnterFunctionIdentifier(ctx *FunctionIdentifierContext) {}

View File

@ -47,6 +47,14 @@ func (v *BaseFqlParserVisitor) VisitReturnExpression(ctx *ReturnExpressionContex
return v.VisitChildren(ctx) 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{} { func (v *BaseFqlParserVisitor) VisitForExpression(ctx *ForExpressionContext) interface{} {
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
@ -183,11 +191,11 @@ func (v *BaseFqlParserVisitor) VisitNoneLiteral(ctx *NoneLiteralContext) interfa
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
func (v *BaseFqlParserVisitor) VisitExpressionGroup(ctx *ExpressionGroupContext) interface{} { func (v *BaseFqlParserVisitor) VisitExpression(ctx *ExpressionContext) interface{} {
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
func (v *BaseFqlParserVisitor) VisitExpression(ctx *ExpressionContext) interface{} { func (v *BaseFqlParserVisitor) VisitExpressionGroup(ctx *ExpressionGroupContext) interface{} {
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
@ -211,6 +219,10 @@ func (v *BaseFqlParserVisitor) VisitMemberExpressionPath(ctx *MemberExpressionPa
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
func (v *BaseFqlParserVisitor) VisitErrorOperator(ctx *ErrorOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitFunctionIdentifier(ctx *FunctionIdentifierContext) interface{} { func (v *BaseFqlParserVisitor) VisitFunctionIdentifier(ctx *FunctionIdentifierContext) interface{} {
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }

View File

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

View File

@ -37,6 +37,12 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#returnExpression. // Visit a parse tree produced by FqlParser#returnExpression.
VisitReturnExpression(ctx *ReturnExpressionContext) interface{} 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. // Visit a parse tree produced by FqlParser#forExpression.
VisitForExpression(ctx *ForExpressionContext) interface{} VisitForExpression(ctx *ForExpressionContext) interface{}
@ -139,12 +145,12 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#noneLiteral. // Visit a parse tree produced by FqlParser#noneLiteral.
VisitNoneLiteral(ctx *NoneLiteralContext) interface{} VisitNoneLiteral(ctx *NoneLiteralContext) interface{}
// Visit a parse tree produced by FqlParser#expressionGroup.
VisitExpressionGroup(ctx *ExpressionGroupContext) interface{}
// Visit a parse tree produced by FqlParser#expression. // Visit a parse tree produced by FqlParser#expression.
VisitExpression(ctx *ExpressionContext) interface{} VisitExpression(ctx *ExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#expressionGroup.
VisitExpressionGroup(ctx *ExpressionGroupContext) interface{}
// Visit a parse tree produced by FqlParser#memberExpression. // Visit a parse tree produced by FqlParser#memberExpression.
VisitMemberExpression(ctx *MemberExpressionContext) interface{} VisitMemberExpression(ctx *MemberExpressionContext) interface{}
@ -160,6 +166,9 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#memberExpressionPath. // Visit a parse tree produced by FqlParser#memberExpressionPath.
VisitMemberExpressionPath(ctx *MemberExpressionPathContext) interface{} VisitMemberExpressionPath(ctx *MemberExpressionPathContext) interface{}
// Visit a parse tree produced by FqlParser#errorOperator.
VisitErrorOperator(ctx *ErrorOperatorContext) interface{}
// Visit a parse tree produced by FqlParser#functionIdentifier. // Visit a parse tree produced by FqlParser#functionIdentifier.
VisitFunctionIdentifier(ctx *FunctionIdentifierContext) interface{} 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) { 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 // if it's Post conditional execution, step in always
// Otherwise, it's not the first iteration // 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) doNext, err := iterator.predicate(ctx, scope)
if err != nil { 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++ iterator.pos++
nextScope := scope.Fork() nextScope := scope.Fork()

View File

@ -7,23 +7,21 @@ import (
) )
type FunctionCallExpression struct { type FunctionCallExpression struct {
src core.SourceMap src core.SourceMap
fun core.Function fun core.Function
ignoreErrors bool args []core.Expression
args []core.Expression
} }
func NewFunctionCallExpression( func NewFunctionCallExpression(
src core.SourceMap, src core.SourceMap,
fun core.Function, fun core.Function,
ignoreErrors bool,
args ...core.Expression, args ...core.Expression,
) (*FunctionCallExpression, error) { ) (*FunctionCallExpression, error) {
if fun == nil { if fun == nil {
return nil, core.Error(core.ErrMissedArgument, "function") 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 { func (e *FunctionCallExpression) Arguments() []core.Expression {
@ -31,18 +29,6 @@ func (e *FunctionCallExpression) Arguments() []core.Expression {
} }
func (e *FunctionCallExpression) Function() core.Function { 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 return e.fun
} }
@ -63,7 +49,7 @@ func (e *FunctionCallExpression) Exec(ctx context.Context, scope *core.Scope) (c
out, err := arg.Exec(ctx, scope) out, err := arg.Exec(ctx, scope)
if err != nil { if err != nil {
return values.None, e.maybeError(core.SourceError(e.src, err)) return values.None, core.SourceError(e.src, err)
} }
args[idx] = out args[idx] = out
@ -73,17 +59,9 @@ func (e *FunctionCallExpression) Exec(ctx context.Context, scope *core.Scope) (c
} }
if err != nil { if err != nil {
return values.None, e.maybeError(core.SourceError(e.src, err)) return values.None, core.SourceError(e.src, err)
} }
return out, nil 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 return values.True, nil
}, },
false,
) )
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -48,7 +47,6 @@ func TestFunctionCallExpression(t *testing.T) {
return values.True, nil return values.True, nil
}, },
false,
args..., args...,
) )
@ -75,7 +73,6 @@ func TestFunctionCallExpression(t *testing.T) {
return values.True, nil return values.True, nil
}, },
false,
args..., args...,
) )
@ -96,11 +93,15 @@ func TestFunctionCallExpression(t *testing.T) {
func(ctx context.Context, args ...core.Value) (value core.Value, e error) { func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return values.NewString("booo"), core.ErrNotImplemented return values.NewString("booo"), core.ErrNotImplemented
}, },
true,
) )
So(err, ShouldBeNil) 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(err, ShouldBeNil)
So(out.Type().String(), ShouldEqual, types.None.String()) 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
}