mirror of
https://github.com/MontFerret/ferret.git
synced 2025-04-13 11:50:30 +02:00
Feature/#291 param dot notation (#378)
* Added param to member expression * Improved path lookup * Added support for params as segments in member expression path
This commit is contained in:
commit
926cc305df
@ -120,6 +120,66 @@ func TestMember(t *testing.T) {
|
|||||||
So(string(out), ShouldEqual, `"wsx"`)
|
So(string(out), ShouldEqual, `"wsx"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Deep path", func() {
|
||||||
|
c := compiler.New()
|
||||||
|
|
||||||
|
p, err := c.Compile(`
|
||||||
|
LET obj = {
|
||||||
|
first: {
|
||||||
|
second: {
|
||||||
|
third: {
|
||||||
|
fourth: {
|
||||||
|
fifth: {
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN obj.first.second.third.fourth.fifth.bottom
|
||||||
|
`)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
out, err := p.Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
So(string(out), ShouldEqual, `true`)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Deep computed path", func() {
|
||||||
|
c := compiler.New()
|
||||||
|
|
||||||
|
p, err := c.Compile(`
|
||||||
|
LET obj = {
|
||||||
|
first: {
|
||||||
|
second: {
|
||||||
|
third: {
|
||||||
|
fourth: {
|
||||||
|
fifth: {
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN obj["first"]["second"]["third"]["fourth"]["fifth"].bottom
|
||||||
|
`)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
out, err := p.Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
So(string(out), ShouldEqual, `true`)
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Prop after a func call", func() {
|
Convey("Prop after a func call", func() {
|
||||||
c := compiler.New()
|
c := compiler.New()
|
||||||
|
|
||||||
@ -160,9 +220,9 @@ func TestMember(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkMemberArray(b *testing.B) {
|
func BenchmarkMemberArray(b *testing.B) {
|
||||||
p := compiler.New().MustCompile(`
|
p := compiler.New().MustCompile(`
|
||||||
LET arr = [1]
|
LET arr = [[[[1]]]]
|
||||||
|
|
||||||
RETURN arr[0]
|
RETURN arr[0][0][0][0]
|
||||||
`)
|
`)
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
@ -172,9 +232,21 @@ func BenchmarkMemberArray(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMemberObject(b *testing.B) {
|
func BenchmarkMemberObject(b *testing.B) {
|
||||||
p := compiler.New().MustCompile(`
|
p := compiler.New().MustCompile(`
|
||||||
LET obj = { "foo": "bar"}
|
LET obj = {
|
||||||
|
first: {
|
||||||
|
second: {
|
||||||
|
third: {
|
||||||
|
fourth: {
|
||||||
|
fifth: {
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RETURN obj.foo
|
RETURN obj.first.second.third.fourth.fifth.bottom
|
||||||
`)
|
`)
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
|
@ -63,4 +63,54 @@ func TestParam(t *testing.T) {
|
|||||||
So(string(out), ShouldEqual, `[1,2,3,4]`)
|
So(string(out), ShouldEqual, `[1,2,3,4]`)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Should be possible to use in member expression", t, func() {
|
||||||
|
prog := compiler.New().
|
||||||
|
MustCompile(`
|
||||||
|
RETURN @param.value
|
||||||
|
`)
|
||||||
|
|
||||||
|
out := prog.MustRun(
|
||||||
|
context.Background(),
|
||||||
|
runtime.WithParam("param", map[string]interface{}{
|
||||||
|
"value": "foobar",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
So(string(out), ShouldEqual, `"foobar"`)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should be possible to use in member expression as a computed property", t, func() {
|
||||||
|
prog := compiler.New().
|
||||||
|
MustCompile(`
|
||||||
|
LET obj = { foo: "bar" }
|
||||||
|
RETURN obj[@param]
|
||||||
|
`)
|
||||||
|
|
||||||
|
out := prog.MustRun(
|
||||||
|
context.Background(),
|
||||||
|
runtime.WithParam("param", "foo"),
|
||||||
|
)
|
||||||
|
|
||||||
|
So(string(out), ShouldEqual, `"bar"`)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should be possible to use in member expression as segments", t, func() {
|
||||||
|
prog := compiler.New().
|
||||||
|
MustCompile(`
|
||||||
|
LET doc = { foo: { bar: "baz" } }
|
||||||
|
|
||||||
|
RETURN doc.@attr.@subattr
|
||||||
|
`)
|
||||||
|
|
||||||
|
out := prog.MustRun(
|
||||||
|
context.Background(),
|
||||||
|
runtime.WithParam("attr", "foo"),
|
||||||
|
runtime.WithParam("subattr", "bar"),
|
||||||
|
)
|
||||||
|
|
||||||
|
So(string(out), ShouldEqual, `"baz"`)
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -736,39 +736,13 @@ func (v *visitor) doVisitForExpressionStatement(ctx *fql.ForExpressionStatementC
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scope *scope) (core.Expression, error) {
|
func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scope *scope) (core.Expression, error) {
|
||||||
var source core.Expression
|
member, err := v.doVisitMember(ctx.Member().(*fql.MemberContext), scope)
|
||||||
var children []antlr.Tree
|
|
||||||
|
|
||||||
identifier := ctx.Identifier()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
if identifier != nil {
|
|
||||||
varName := ctx.Identifier().GetText()
|
|
||||||
|
|
||||||
_, err := scope.GetVariable(varName)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
varExp, err := expressions.NewVariableExpression(v.getSourceMap(ctx), varName)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
source = varExp
|
|
||||||
children = ctx.GetChildren()
|
|
||||||
} else {
|
|
||||||
fcall, err := v.doVisitFunctionCallExpression(ctx.FunctionCallExpression().(*fql.FunctionCallExpressionContext), scope)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
source = fcall
|
|
||||||
children = ctx.GetChildren()[1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
children := ctx.MemberPath().GetChildren()
|
||||||
path := make([]core.Expression, 0, len(children))
|
path := make([]core.Expression, 0, len(children))
|
||||||
|
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
@ -808,9 +782,9 @@ func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scop
|
|||||||
path = append(path, exp)
|
path = append(path, exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
member, err := expressions.NewMemberExpression(
|
exp, err := expressions.NewMemberExpression(
|
||||||
v.getSourceMap(ctx),
|
v.getSourceMap(ctx),
|
||||||
source,
|
member,
|
||||||
path,
|
path,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -818,7 +792,51 @@ func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scop
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return member, nil
|
return exp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *visitor) doVisitMember(ctx *fql.MemberContext, scope *scope) (core.Expression, error) {
|
||||||
|
identifier := ctx.Identifier()
|
||||||
|
|
||||||
|
if identifier != nil {
|
||||||
|
varName := ctx.Identifier().GetText()
|
||||||
|
|
||||||
|
_, err := scope.GetVariable(varName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
exp, err := expressions.NewVariableExpression(v.getSourceMap(ctx), varName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return exp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fnCall := ctx.FunctionCallExpression()
|
||||||
|
|
||||||
|
if fnCall != nil {
|
||||||
|
exp, err := v.doVisitFunctionCallExpression(fnCall.(*fql.FunctionCallExpressionContext), scope)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return exp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
param := ctx.Param()
|
||||||
|
|
||||||
|
exp, err := v.doVisitParamContext(param.(*fql.ParamContext), scope)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return exp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *visitor) doVisitObjectLiteral(ctx *fql.ObjectLiteralContext, scope *scope) (core.Expression, error) {
|
func (v *visitor) doVisitObjectLiteral(ctx *fql.ObjectLiteralContext, scope *scope) (core.Expression, error) {
|
||||||
@ -871,7 +889,7 @@ func (v *visitor) doVisitObjectLiteral(ctx *fql.ObjectLiteralContext, scope *sco
|
|||||||
return literals.NewObjectLiteralWith(props...), nil
|
return literals.NewObjectLiteralWith(props...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *visitor) doVisitPropertyNameContext(ctx *fql.PropertyNameContext, _ *scope) (core.Expression, error) {
|
func (v *visitor) doVisitPropertyNameContext(ctx *fql.PropertyNameContext, scope *scope) (core.Expression, error) {
|
||||||
var name string
|
var name string
|
||||||
|
|
||||||
identifier := ctx.Identifier()
|
identifier := ctx.Identifier()
|
||||||
@ -884,6 +902,14 @@ func (v *visitor) doVisitPropertyNameContext(ctx *fql.PropertyNameContext, _ *sc
|
|||||||
if stringLiteral != nil {
|
if stringLiteral != nil {
|
||||||
runes := []rune(stringLiteral.GetText())
|
runes := []rune(stringLiteral.GetText())
|
||||||
name = string(runes[1 : len(runes)-1])
|
name = string(runes[1 : len(runes)-1])
|
||||||
|
} else {
|
||||||
|
param, err := v.doVisitParamContext(ctx.Param().(*fql.ParamContext), scope)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return param, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +195,7 @@ computedPropertyName
|
|||||||
propertyName
|
propertyName
|
||||||
: Identifier
|
: Identifier
|
||||||
| stringLiteral
|
| stringLiteral
|
||||||
|
| param
|
||||||
;
|
;
|
||||||
|
|
||||||
expressionGroup
|
expressionGroup
|
||||||
@ -209,11 +210,19 @@ functionCallExpression
|
|||||||
: namespace Identifier arguments
|
: namespace Identifier arguments
|
||||||
;
|
;
|
||||||
|
|
||||||
|
member
|
||||||
|
: Identifier
|
||||||
|
| functionCallExpression
|
||||||
|
| param
|
||||||
|
;
|
||||||
|
|
||||||
|
memberPath
|
||||||
|
: (Dot propertyName (computedPropertyName)*)+
|
||||||
|
| computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
|
||||||
|
;
|
||||||
|
|
||||||
memberExpression
|
memberExpression
|
||||||
: Identifier (Dot propertyName (computedPropertyName)*)+
|
: member memberPath
|
||||||
| Identifier computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
|
|
||||||
| functionCallExpression (Dot propertyName (computedPropertyName)*)+
|
|
||||||
| functionCallExpression computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
|
|
||||||
;
|
;
|
||||||
|
|
||||||
arguments
|
arguments
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -286,6 +286,18 @@ func (s *BaseFqlParserListener) EnterFunctionCallExpression(ctx *FunctionCallExp
|
|||||||
// ExitFunctionCallExpression is called when production functionCallExpression is exited.
|
// ExitFunctionCallExpression is called when production functionCallExpression is exited.
|
||||||
func (s *BaseFqlParserListener) ExitFunctionCallExpression(ctx *FunctionCallExpressionContext) {}
|
func (s *BaseFqlParserListener) ExitFunctionCallExpression(ctx *FunctionCallExpressionContext) {}
|
||||||
|
|
||||||
|
// EnterMember is called when production member is entered.
|
||||||
|
func (s *BaseFqlParserListener) EnterMember(ctx *MemberContext) {}
|
||||||
|
|
||||||
|
// ExitMember is called when production member is exited.
|
||||||
|
func (s *BaseFqlParserListener) ExitMember(ctx *MemberContext) {}
|
||||||
|
|
||||||
|
// EnterMemberPath is called when production memberPath is entered.
|
||||||
|
func (s *BaseFqlParserListener) EnterMemberPath(ctx *MemberPathContext) {}
|
||||||
|
|
||||||
|
// ExitMemberPath is called when production memberPath is exited.
|
||||||
|
func (s *BaseFqlParserListener) ExitMemberPath(ctx *MemberPathContext) {}
|
||||||
|
|
||||||
// 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) {}
|
||||||
|
|
||||||
|
@ -183,6 +183,14 @@ func (v *BaseFqlParserVisitor) VisitFunctionCallExpression(ctx *FunctionCallExpr
|
|||||||
return v.VisitChildren(ctx)
|
return v.VisitChildren(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *BaseFqlParserVisitor) VisitMember(ctx *MemberContext) interface{} {
|
||||||
|
return v.VisitChildren(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *BaseFqlParserVisitor) VisitMemberPath(ctx *MemberPathContext) interface{} {
|
||||||
|
return v.VisitChildren(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
|
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
|
||||||
return v.VisitChildren(ctx)
|
return v.VisitChildren(ctx)
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,12 @@ type FqlParserListener interface {
|
|||||||
// EnterFunctionCallExpression is called when entering the functionCallExpression production.
|
// EnterFunctionCallExpression is called when entering the functionCallExpression production.
|
||||||
EnterFunctionCallExpression(c *FunctionCallExpressionContext)
|
EnterFunctionCallExpression(c *FunctionCallExpressionContext)
|
||||||
|
|
||||||
|
// EnterMember is called when entering the member production.
|
||||||
|
EnterMember(c *MemberContext)
|
||||||
|
|
||||||
|
// EnterMemberPath is called when entering the memberPath production.
|
||||||
|
EnterMemberPath(c *MemberPathContext)
|
||||||
|
|
||||||
// EnterMemberExpression is called when entering the memberExpression production.
|
// EnterMemberExpression is called when entering the memberExpression production.
|
||||||
EnterMemberExpression(c *MemberExpressionContext)
|
EnterMemberExpression(c *MemberExpressionContext)
|
||||||
|
|
||||||
@ -310,6 +316,12 @@ type FqlParserListener interface {
|
|||||||
// ExitFunctionCallExpression is called when exiting the functionCallExpression production.
|
// ExitFunctionCallExpression is called when exiting the functionCallExpression production.
|
||||||
ExitFunctionCallExpression(c *FunctionCallExpressionContext)
|
ExitFunctionCallExpression(c *FunctionCallExpressionContext)
|
||||||
|
|
||||||
|
// ExitMember is called when exiting the member production.
|
||||||
|
ExitMember(c *MemberContext)
|
||||||
|
|
||||||
|
// ExitMemberPath is called when exiting the memberPath production.
|
||||||
|
ExitMemberPath(c *MemberPathContext)
|
||||||
|
|
||||||
// ExitMemberExpression is called when exiting the memberExpression production.
|
// ExitMemberExpression is called when exiting the memberExpression production.
|
||||||
ExitMemberExpression(c *MemberExpressionContext)
|
ExitMemberExpression(c *MemberExpressionContext)
|
||||||
|
|
||||||
|
@ -139,6 +139,12 @@ type FqlParserVisitor interface {
|
|||||||
// Visit a parse tree produced by FqlParser#functionCallExpression.
|
// Visit a parse tree produced by FqlParser#functionCallExpression.
|
||||||
VisitFunctionCallExpression(ctx *FunctionCallExpressionContext) interface{}
|
VisitFunctionCallExpression(ctx *FunctionCallExpressionContext) interface{}
|
||||||
|
|
||||||
|
// Visit a parse tree produced by FqlParser#member.
|
||||||
|
VisitMember(ctx *MemberContext) interface{}
|
||||||
|
|
||||||
|
// Visit a parse tree produced by FqlParser#memberPath.
|
||||||
|
VisitMemberPath(ctx *MemberPathContext) 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{}
|
||||||
|
|
||||||
|
@ -35,22 +35,24 @@ func (e *MemberExpression) Exec(ctx context.Context, scope *core.Scope) (core.Va
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
strPath := make([]core.Value, len(e.path))
|
out := val
|
||||||
|
path := make([]core.Value, 1)
|
||||||
|
|
||||||
for idx, exp := range e.path {
|
for _, exp := range e.path {
|
||||||
segment, err := exp.Exec(ctx, scope)
|
segment, err := exp.Exec(ctx, scope)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
strPath[idx] = segment
|
path[0] = segment
|
||||||
}
|
c, err := values.GetIn(ctx, out, path)
|
||||||
|
|
||||||
out, err := values.GetIn(ctx, val, strPath)
|
if err != nil {
|
||||||
|
return values.None, core.SourceError(e.src, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
out = c
|
||||||
return values.None, core.SourceError(e.src, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user