1
0
mirror of https://github.com/MontFerret/ferret.git synced 2024-12-14 11:23:02 +02:00

Fixed access to a member property right after a function call (#368)

This commit is contained in:
Tim Voronov 2019-09-01 16:01:03 -04:00 committed by GitHub
parent 4dbfe85b97
commit 2a8135657d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 805 additions and 582 deletions

View File

@ -184,7 +184,7 @@ func (r *Runner) runQueries(ctx context.Context, dir string) ([]Result, error) {
return results, nil return results, nil
} }
func (r *Runner) runQuery(ctx context.Context, c *compiler.FqlCompiler, name, script string) Result { func (r *Runner) runQuery(ctx context.Context, c *compiler.Compiler, name, script string) Result {
start := time.Now() start := time.Now()
p, err := c.Compile(script) p, err := c.Compile(script)

View File

@ -1,19 +1,20 @@
package compiler package compiler
import ( import (
"github.com/pkg/errors"
"github.com/MontFerret/ferret/pkg/parser" "github.com/MontFerret/ferret/pkg/parser"
"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/core"
"github.com/MontFerret/ferret/pkg/stdlib" "github.com/MontFerret/ferret/pkg/stdlib"
"github.com/pkg/errors"
) )
type FqlCompiler struct { type Compiler struct {
*NamespaceContainer *NamespaceContainer
} }
func New(setters ...Option) *FqlCompiler { func New(setters ...Option) *Compiler {
c := &FqlCompiler{} c := &Compiler{}
c.NamespaceContainer = newRootNamespace() c.NamespaceContainer = newRootNamespace()
c.funcs = make(map[string]core.Function) c.funcs = make(map[string]core.Function)
@ -32,7 +33,7 @@ func New(setters ...Option) *FqlCompiler {
return c return c
} }
func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error) { func (c *Compiler) Compile(query string) (program *runtime.Program, err error) {
if query == "" { if query == "" {
return nil, ErrEmptyQuery return nil, ErrEmptyQuery
} }
@ -69,7 +70,7 @@ func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error
return program, err return program, err
} }
func (c *FqlCompiler) MustCompile(query string) *runtime.Program { func (c *Compiler) MustCompile(query string) *runtime.Program {
program, err := c.Compile(query) program, err := c.Compile(query)
if err != nil { if err != nil {

View File

@ -119,6 +119,42 @@ func TestMember(t *testing.T) {
So(string(out), ShouldEqual, `"wsx"`) So(string(out), ShouldEqual, `"wsx"`)
}) })
Convey("Prop after a func call", func() {
c := compiler.New()
p, err := c.Compile(`
LET arr = [{ name: "Bob" }]
RETURN FIRST(arr).name
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `"Bob"`)
})
Convey("Computed prop after a func call", func() {
c := compiler.New()
p, err := c.Compile(`
LET arr = [{ name: { first: "Bob" } }]
RETURN FIRST(arr)['name'].first
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `"Bob"`)
})
}) })
} }

View File

@ -736,15 +736,39 @@ 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) {
varName := ctx.Identifier().GetText() var source core.Expression
var children []antlr.Tree
_, err := scope.GetVariable(varName) identifier := ctx.Identifier()
if err != nil { if identifier != nil {
return nil, err 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.GetChildren()
path := make([]core.Expression, 0, len(children)) path := make([]core.Expression, 0, len(children))
for _, child := range children { for _, child := range children {
@ -786,7 +810,7 @@ func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scop
member, err := expressions.NewMemberExpression( member, err := expressions.NewMemberExpression(
v.getSourceMap(ctx), v.getSourceMap(ctx),
varName, source,
path, path,
) )

View File

@ -185,11 +185,6 @@ propertyAssignment
| shorthandPropertyName | shorthandPropertyName
; ;
memberExpression
: Identifier (Dot propertyName (computedPropertyName)*)+
| Identifier computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
;
shorthandPropertyName shorthandPropertyName
: variable : variable
; ;
@ -215,6 +210,13 @@ functionCallExpression
: namespace Identifier arguments : namespace Identifier arguments
; ;
memberExpression
: Identifier (Dot propertyName (computedPropertyName)*)+
| Identifier computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
| functionCallExpression (Dot propertyName (computedPropertyName)*)+
| functionCallExpression computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
;
arguments arguments
: OpenParen(expression (Comma expression)*)?CloseParen : OpenParen(expression (Comma expression)*)?CloseParen
; ;

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -250,12 +250,6 @@ func (s *BaseFqlParserListener) EnterPropertyAssignment(ctx *PropertyAssignmentC
// ExitPropertyAssignment is called when production propertyAssignment is exited. // ExitPropertyAssignment is called when production propertyAssignment is exited.
func (s *BaseFqlParserListener) ExitPropertyAssignment(ctx *PropertyAssignmentContext) {} func (s *BaseFqlParserListener) ExitPropertyAssignment(ctx *PropertyAssignmentContext) {}
// EnterMemberExpression is called when production memberExpression is entered.
func (s *BaseFqlParserListener) EnterMemberExpression(ctx *MemberExpressionContext) {}
// ExitMemberExpression is called when production memberExpression is exited.
func (s *BaseFqlParserListener) ExitMemberExpression(ctx *MemberExpressionContext) {}
// EnterShorthandPropertyName is called when production shorthandPropertyName is entered. // EnterShorthandPropertyName is called when production shorthandPropertyName is entered.
func (s *BaseFqlParserListener) EnterShorthandPropertyName(ctx *ShorthandPropertyNameContext) {} func (s *BaseFqlParserListener) EnterShorthandPropertyName(ctx *ShorthandPropertyNameContext) {}
@ -292,6 +286,12 @@ 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) {}
// EnterMemberExpression is called when production memberExpression is entered.
func (s *BaseFqlParserListener) EnterMemberExpression(ctx *MemberExpressionContext) {}
// ExitMemberExpression is called when production memberExpression is exited.
func (s *BaseFqlParserListener) ExitMemberExpression(ctx *MemberExpressionContext) {}
// EnterArguments is called when production arguments is entered. // EnterArguments is called when production arguments is entered.
func (s *BaseFqlParserListener) EnterArguments(ctx *ArgumentsContext) {} func (s *BaseFqlParserListener) EnterArguments(ctx *ArgumentsContext) {}

View File

@ -159,10 +159,6 @@ func (v *BaseFqlParserVisitor) VisitPropertyAssignment(ctx *PropertyAssignmentCo
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{} { func (v *BaseFqlParserVisitor) VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{} {
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
@ -187,6 +183,10 @@ func (v *BaseFqlParserVisitor) VisitFunctionCallExpression(ctx *FunctionCallExpr
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitArguments(ctx *ArgumentsContext) interface{} { func (v *BaseFqlParserVisitor) VisitArguments(ctx *ArgumentsContext) interface{} {
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }

View File

@ -121,9 +121,6 @@ type FqlParserListener interface {
// EnterPropertyAssignment is called when entering the propertyAssignment production. // EnterPropertyAssignment is called when entering the propertyAssignment production.
EnterPropertyAssignment(c *PropertyAssignmentContext) EnterPropertyAssignment(c *PropertyAssignmentContext)
// EnterMemberExpression is called when entering the memberExpression production.
EnterMemberExpression(c *MemberExpressionContext)
// EnterShorthandPropertyName is called when entering the shorthandPropertyName production. // EnterShorthandPropertyName is called when entering the shorthandPropertyName production.
EnterShorthandPropertyName(c *ShorthandPropertyNameContext) EnterShorthandPropertyName(c *ShorthandPropertyNameContext)
@ -142,6 +139,9 @@ 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)
// EnterMemberExpression is called when entering the memberExpression production.
EnterMemberExpression(c *MemberExpressionContext)
// EnterArguments is called when entering the arguments production. // EnterArguments is called when entering the arguments production.
EnterArguments(c *ArgumentsContext) EnterArguments(c *ArgumentsContext)
@ -292,9 +292,6 @@ type FqlParserListener interface {
// ExitPropertyAssignment is called when exiting the propertyAssignment production. // ExitPropertyAssignment is called when exiting the propertyAssignment production.
ExitPropertyAssignment(c *PropertyAssignmentContext) ExitPropertyAssignment(c *PropertyAssignmentContext)
// ExitMemberExpression is called when exiting the memberExpression production.
ExitMemberExpression(c *MemberExpressionContext)
// ExitShorthandPropertyName is called when exiting the shorthandPropertyName production. // ExitShorthandPropertyName is called when exiting the shorthandPropertyName production.
ExitShorthandPropertyName(c *ShorthandPropertyNameContext) ExitShorthandPropertyName(c *ShorthandPropertyNameContext)
@ -313,6 +310,9 @@ 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)
// ExitMemberExpression is called when exiting the memberExpression production.
ExitMemberExpression(c *MemberExpressionContext)
// ExitArguments is called when exiting the arguments production. // ExitArguments is called when exiting the arguments production.
ExitArguments(c *ArgumentsContext) ExitArguments(c *ArgumentsContext)

View File

@ -121,9 +121,6 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#propertyAssignment. // Visit a parse tree produced by FqlParser#propertyAssignment.
VisitPropertyAssignment(ctx *PropertyAssignmentContext) interface{} VisitPropertyAssignment(ctx *PropertyAssignmentContext) interface{}
// Visit a parse tree produced by FqlParser#memberExpression.
VisitMemberExpression(ctx *MemberExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#shorthandPropertyName. // Visit a parse tree produced by FqlParser#shorthandPropertyName.
VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{} VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{}
@ -142,6 +139,9 @@ 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#memberExpression.
VisitMemberExpression(ctx *MemberExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#arguments. // Visit a parse tree produced by FqlParser#arguments.
VisitArguments(ctx *ArgumentsContext) interface{} VisitArguments(ctx *ArgumentsContext) interface{}

View File

@ -8,25 +8,25 @@ import (
) )
type MemberExpression struct { type MemberExpression struct {
src core.SourceMap src core.SourceMap
variableName string source core.Expression
path []core.Expression path []core.Expression
} }
func NewMemberExpression(src core.SourceMap, variableName string, path []core.Expression) (*MemberExpression, error) { func NewMemberExpression(src core.SourceMap, source core.Expression, path []core.Expression) (*MemberExpression, error) {
if variableName == "" { if source == nil {
return nil, core.Error(core.ErrMissedArgument, "variable name") return nil, core.Error(core.ErrMissedArgument, "source")
} }
if len(path) == 0 { if len(path) == 0 {
return nil, core.Error(core.ErrMissedArgument, "path expressions") return nil, core.Error(core.ErrMissedArgument, "path expressions")
} }
return &MemberExpression{src, variableName, path}, nil return &MemberExpression{src, source, path}, nil
} }
func (e *MemberExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) { func (e *MemberExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
val, err := scope.GetVariable(e.variableName) val, err := e.source.Exec(ctx, scope)
if err != nil { if err != nil {
return values.None, core.SourceError( return values.None, core.SourceError(