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:
parent
4dbfe85b97
commit
2a8135657d
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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"`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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
@ -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) {}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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{}
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user