mirror of
https://github.com/MontFerret/ferret.git
synced 2024-12-12 11:15:14 +02:00
Fixed access to a member property right after a function call (#368)
This commit is contained in:
parent
4dbfe85b97
commit
2a8135657d
@ -184,7 +184,7 @@ func (r *Runner) runQueries(ctx context.Context, dir string) ([]Result, error) {
|
||||
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()
|
||||
|
||||
p, err := c.Compile(script)
|
||||
|
@ -1,19 +1,20 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/parser"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/stdlib"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type FqlCompiler struct {
|
||||
type Compiler struct {
|
||||
*NamespaceContainer
|
||||
}
|
||||
|
||||
func New(setters ...Option) *FqlCompiler {
|
||||
c := &FqlCompiler{}
|
||||
func New(setters ...Option) *Compiler {
|
||||
c := &Compiler{}
|
||||
c.NamespaceContainer = newRootNamespace()
|
||||
c.funcs = make(map[string]core.Function)
|
||||
|
||||
@ -32,7 +33,7 @@ func New(setters ...Option) *FqlCompiler {
|
||||
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 == "" {
|
||||
return nil, ErrEmptyQuery
|
||||
}
|
||||
@ -69,7 +70,7 @@ func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error
|
||||
return program, err
|
||||
}
|
||||
|
||||
func (c *FqlCompiler) MustCompile(query string) *runtime.Program {
|
||||
func (c *Compiler) MustCompile(query string) *runtime.Program {
|
||||
program, err := c.Compile(query)
|
||||
|
||||
if err != nil {
|
||||
|
@ -119,6 +119,42 @@ func TestMember(t *testing.T) {
|
||||
|
||||
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"`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -736,15 +736,39 @@ func (v *visitor) doVisitForExpressionStatement(ctx *fql.ForExpressionStatementC
|
||||
}
|
||||
|
||||
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 {
|
||||
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.GetChildren()
|
||||
path := make([]core.Expression, 0, len(children))
|
||||
|
||||
for _, child := range children {
|
||||
@ -786,7 +810,7 @@ func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scop
|
||||
|
||||
member, err := expressions.NewMemberExpression(
|
||||
v.getSourceMap(ctx),
|
||||
varName,
|
||||
source,
|
||||
path,
|
||||
)
|
||||
|
||||
|
@ -185,11 +185,6 @@ propertyAssignment
|
||||
| shorthandPropertyName
|
||||
;
|
||||
|
||||
memberExpression
|
||||
: Identifier (Dot propertyName (computedPropertyName)*)+
|
||||
| Identifier computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
|
||||
;
|
||||
|
||||
shorthandPropertyName
|
||||
: variable
|
||||
;
|
||||
@ -215,6 +210,13 @@ functionCallExpression
|
||||
: 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
|
||||
: 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
@ -250,12 +250,6 @@ func (s *BaseFqlParserListener) EnterPropertyAssignment(ctx *PropertyAssignmentC
|
||||
// ExitPropertyAssignment is called when production propertyAssignment is exited.
|
||||
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.
|
||||
func (s *BaseFqlParserListener) EnterShorthandPropertyName(ctx *ShorthandPropertyNameContext) {}
|
||||
|
||||
@ -292,6 +286,12 @@ func (s *BaseFqlParserListener) EnterFunctionCallExpression(ctx *FunctionCallExp
|
||||
// ExitFunctionCallExpression is called when production functionCallExpression is exited.
|
||||
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.
|
||||
func (s *BaseFqlParserListener) EnterArguments(ctx *ArgumentsContext) {}
|
||||
|
||||
|
@ -159,10 +159,6 @@ func (v *BaseFqlParserVisitor) VisitPropertyAssignment(ctx *PropertyAssignmentCo
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
@ -187,6 +183,10 @@ func (v *BaseFqlParserVisitor) VisitFunctionCallExpression(ctx *FunctionCallExpr
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitArguments(ctx *ArgumentsContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
@ -121,9 +121,6 @@ type FqlParserListener interface {
|
||||
// EnterPropertyAssignment is called when entering the propertyAssignment production.
|
||||
EnterPropertyAssignment(c *PropertyAssignmentContext)
|
||||
|
||||
// EnterMemberExpression is called when entering the memberExpression production.
|
||||
EnterMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// EnterShorthandPropertyName is called when entering the shorthandPropertyName production.
|
||||
EnterShorthandPropertyName(c *ShorthandPropertyNameContext)
|
||||
|
||||
@ -142,6 +139,9 @@ type FqlParserListener interface {
|
||||
// EnterFunctionCallExpression is called when entering the functionCallExpression production.
|
||||
EnterFunctionCallExpression(c *FunctionCallExpressionContext)
|
||||
|
||||
// EnterMemberExpression is called when entering the memberExpression production.
|
||||
EnterMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// EnterArguments is called when entering the arguments production.
|
||||
EnterArguments(c *ArgumentsContext)
|
||||
|
||||
@ -292,9 +292,6 @@ type FqlParserListener interface {
|
||||
// ExitPropertyAssignment is called when exiting the propertyAssignment production.
|
||||
ExitPropertyAssignment(c *PropertyAssignmentContext)
|
||||
|
||||
// ExitMemberExpression is called when exiting the memberExpression production.
|
||||
ExitMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// ExitShorthandPropertyName is called when exiting the shorthandPropertyName production.
|
||||
ExitShorthandPropertyName(c *ShorthandPropertyNameContext)
|
||||
|
||||
@ -313,6 +310,9 @@ type FqlParserListener interface {
|
||||
// ExitFunctionCallExpression is called when exiting the functionCallExpression production.
|
||||
ExitFunctionCallExpression(c *FunctionCallExpressionContext)
|
||||
|
||||
// ExitMemberExpression is called when exiting the memberExpression production.
|
||||
ExitMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// ExitArguments is called when exiting the arguments production.
|
||||
ExitArguments(c *ArgumentsContext)
|
||||
|
||||
|
@ -121,9 +121,6 @@ type FqlParserVisitor interface {
|
||||
// Visit a parse tree produced by FqlParser#propertyAssignment.
|
||||
VisitPropertyAssignment(ctx *PropertyAssignmentContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#memberExpression.
|
||||
VisitMemberExpression(ctx *MemberExpressionContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#shorthandPropertyName.
|
||||
VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{}
|
||||
|
||||
@ -142,6 +139,9 @@ type FqlParserVisitor interface {
|
||||
// Visit a parse tree produced by FqlParser#functionCallExpression.
|
||||
VisitFunctionCallExpression(ctx *FunctionCallExpressionContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#memberExpression.
|
||||
VisitMemberExpression(ctx *MemberExpressionContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#arguments.
|
||||
VisitArguments(ctx *ArgumentsContext) interface{}
|
||||
|
||||
|
@ -8,25 +8,25 @@ import (
|
||||
)
|
||||
|
||||
type MemberExpression struct {
|
||||
src core.SourceMap
|
||||
variableName string
|
||||
path []core.Expression
|
||||
src core.SourceMap
|
||||
source core.Expression
|
||||
path []core.Expression
|
||||
}
|
||||
|
||||
func NewMemberExpression(src core.SourceMap, variableName string, path []core.Expression) (*MemberExpression, error) {
|
||||
if variableName == "" {
|
||||
return nil, core.Error(core.ErrMissedArgument, "variable name")
|
||||
func NewMemberExpression(src core.SourceMap, source core.Expression, path []core.Expression) (*MemberExpression, error) {
|
||||
if source == nil {
|
||||
return nil, core.Error(core.ErrMissedArgument, "source")
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
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) {
|
||||
val, err := scope.GetVariable(e.variableName)
|
||||
val, err := e.source.Exec(ctx, scope)
|
||||
|
||||
if err != nil {
|
||||
return values.None, core.SourceError(
|
||||
|
Loading…
Reference in New Issue
Block a user