1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-08-13 19:52:52 +02:00

Feature/#1 array comparison operators (#71)

* #1 Added ALL IN

* #1 Completed Array operator

* #1 Fixed linting issues
This commit is contained in:
Tim Voronov
2018-10-07 17:54:02 -04:00
committed by GitHub
parent 809a51b217
commit 0dfd58dc89
18 changed files with 1735 additions and 706 deletions

View File

@@ -1632,6 +1632,329 @@ func TestInOperator(t *testing.T) {
}) })
} }
func TestArrayOperator(t *testing.T) {
Convey("ALL", t, func() {
Convey("[1,2,3] ALL IN [1,2,3] should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] ALL IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[1,2,4] ALL IN [1,2,3] should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,4] ALL IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("[4,5,6] ALL NOT IN [1,2,3] should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [4,5,6] ALL NOT IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[1,2,3] ALL > 0 should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] ALL > 0
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[1,2,3] ALL > 2 should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] ALL > 2
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("[1,2,3] ALL >= 3 should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] ALL >= 3
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("['foo','bar'] ALL != 'moo' should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN ['foo', 'bar'] ALL != 'moo'
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
})
Convey("ANY", t, func() {
Convey("[1,2,3] ANY IN [1,2,3] should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] ANY IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[4,2,5] ANY IN [1,2,3] should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [4,2,5] ANY IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[4,5,6] ANY IN [1,2,3] should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [4,5,6] ANY IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("[4,5,6] ANY NOT IN [1,2,3] should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [4,5,6] ANY NOT IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[1,2,3 ] ANY == 2 should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3 ] ANY == 2
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[1,2,3 ] ANY == 4 should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3 ] ANY == 4
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("['foo','bar'] ANY == 'foo' should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN ['foo', 'bar'] ANY == 'foo'
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
})
Convey("NONE", t, func() {
Convey("[1,2,3] NONE IN [1,2,3] should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] NONE IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("[4,2,5] NONE IN [1,2,3] should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [4,2,5] NONE IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("[4,5,6] NONE IN [1,2,3] should return true", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [4,5,6] NONE IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[4,5,6] NONE NOT IN [1,2,3] should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [4,5,6] NONE NOT IN [1,2,3]
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("[1,2,3] NONE > 99 should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] NONE > 99
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `true`)
})
Convey("[1,2,3] NONE < 99 should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN [1,2,3] NONE < 99
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
Convey("['foo','bar'] NONE == 'foo' should return false", func() {
c := compiler.New()
prog, err := c.Compile(`
RETURN ['foo','bar'] NONE == 'foo'
`)
So(err, ShouldBeNil)
out, err := prog.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `false`)
})
})
}
func TestForTernaryExpression(t *testing.T) { func TestForTernaryExpression(t *testing.T) {
Convey("RETURN foo ? TRUE : (FOR i IN 1..5 RETURN i*2)", t, func() { Convey("RETURN foo ? TRUE : (FOR i IN 1..5 RETURN i*2)", t, func() {
c := compiler.New() c := compiler.New()

View File

@@ -758,6 +758,132 @@ func (v *visitor) doVisitAllExpressions(contexts []fql.IExpressionContext, scope
return ret, nil return ret, nil
} }
func (v *visitor) doVisitMathOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
mathOp := ctx.MathOperator().(*fql.MathOperatorContext)
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewMathOperator(
v.getSourceMap(mathOp),
left,
right,
operators.MathOperatorType(mathOp.GetText()),
)
}
func (v *visitor) doVisitNotOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
exp := exps[0]
return operators.NewLogicalOperator(
v.getSourceMap(ctx),
nil,
exp,
"NOT",
)
}
func (v *visitor) doVisitLogicalOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
logicalOp := ctx.LogicalOperator().(*fql.LogicalOperatorContext)
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewLogicalOperator(v.getSourceMap(logicalOp), left, right, logicalOp.GetText())
}
func (v *visitor) doVisitEqualityOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
equalityOp := ctx.EqualityOperator().(*fql.EqualityOperatorContext)
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewEqualityOperator(v.getSourceMap(equalityOp), left, right, equalityOp.GetText())
}
func (v *visitor) doVisitInOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
if len(exps) != 2 {
return nil, v.unexpectedToken(ctx)
}
return operators.NewInOperator(
v.getSourceMap(ctx),
left,
right,
ctx.Not() != nil,
)
}
func (v *visitor) doVisitArrayOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
var comparator core.OperatorExpression
var err error
if ctx.InOperator() != nil {
comparator, err = v.doVisitInOperator(ctx, scope)
} else if ctx.EqualityOperator() != nil {
comparator, err = v.doVisitEqualityOperator(ctx, scope)
} else {
return nil, v.unexpectedToken(ctx)
}
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
if len(exps) != 2 {
return nil, v.unexpectedToken(ctx)
}
left := exps[0]
right := exps[1]
aotype, err := operators.ToIsValidArrayOperatorType(ctx.ArrayOperator().GetText())
if err != nil {
return nil, err
}
return operators.NewArrayOperator(
v.getSourceMap(ctx),
left,
right,
aotype,
comparator,
)
}
func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (core.Expression, error) { func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (core.Expression, error) {
variable := ctx.Variable() variable := ctx.Variable()
@@ -819,52 +945,34 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
return v.doVisitNoneLiteral(none.(*fql.NoneLiteralContext)) return v.doVisitNoneLiteral(none.(*fql.NoneLiteralContext))
} }
arrOp := ctx.ArrayOperator()
if arrOp != nil {
return v.doVisitArrayOperator(ctx, scope)
}
inOp := ctx.InOperator()
if inOp != nil {
return v.doVisitInOperator(ctx, scope)
}
equalityOp := ctx.EqualityOperator() equalityOp := ctx.EqualityOperator()
if equalityOp != nil { if equalityOp != nil {
equalityOp := equalityOp.(*fql.EqualityOperatorContext) return v.doVisitEqualityOperator(ctx, scope)
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewEqualityOperator(v.getSourceMap(equalityOp), left, right, equalityOp.GetText())
} }
logicalOp := ctx.LogicalOperator() logicalOp := ctx.LogicalOperator()
if logicalOp != nil { if logicalOp != nil {
logicalOp := logicalOp.(*fql.LogicalOperatorContext) return v.doVisitLogicalOperator(ctx, scope)
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewLogicalOperator(v.getSourceMap(logicalOp), left, right, logicalOp.GetText())
} }
mathOp := ctx.MathOperator() mathOp := ctx.MathOperator()
if mathOp != nil { if mathOp != nil {
mathOp := mathOp.(*fql.MathOperatorContext) return v.doVisitMathOperator(ctx, scope)
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewMathOperator(v.getSourceMap(mathOp), left, right, mathOp.GetText())
} }
questionCtx := ctx.QuestionMark() questionCtx := ctx.QuestionMark()
@@ -883,43 +991,10 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
) )
} }
inOp := ctx.In()
if inOp != nil {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewInOperator(
v.getSourceMap(ctx),
left,
right,
ctx.Not() != nil,
)
}
notOp := ctx.Not() notOp := ctx.Not()
if notOp != nil { if notOp != nil {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope) return v.doVisitNotOperator(ctx, scope)
if err != nil {
return nil, err
}
exp := exps[0]
return operators.NewLogicalOperator(
v.getSourceMap(ctx),
nil,
exp,
"NOT",
)
} }
rangeOp := ctx.RangeOperator() rangeOp := ctx.RangeOperator()

View File

@@ -122,7 +122,6 @@ forExpressionReturn
| forExpression | forExpression
; ;
variableDeclaration variableDeclaration
: Let Identifier Assign expression : Let Identifier Assign expression
| Let Identifier Assign OpenParen forExpression CloseParen | Let Identifier Assign OpenParen forExpression CloseParen
@@ -217,7 +216,8 @@ expression
| OpenParen expressionSequence CloseParen | OpenParen expressionSequence CloseParen
| Plus expression | Plus expression
| Minus expression | Minus expression
| expression (Not)? In expression | expression arrayOperator (Not)? (inOperator | equalityOperator) expression
| expression (Not)? inOperator expression
| Not expression | Not expression
| expression QuestionMark expression? Colon expression | expression QuestionMark expression? Colon expression
| rangeOperator | rangeOperator
@@ -239,6 +239,16 @@ forTernaryExpression
| expression QuestionMark OpenParen forExpression CloseParen Colon OpenParen forExpression CloseParen | expression QuestionMark OpenParen forExpression CloseParen Colon OpenParen forExpression CloseParen
; ;
arrayOperator
: All
| Any
| None
;
inOperator
: In
;
equalityOperator equalityOperator
: Gt : Gt
| Lt | Lt

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,18 @@ func (s *BaseFqlParserListener) EnterForTernaryExpression(ctx *ForTernaryExpress
// ExitForTernaryExpression is called when production forTernaryExpression is exited. // ExitForTernaryExpression is called when production forTernaryExpression is exited.
func (s *BaseFqlParserListener) ExitForTernaryExpression(ctx *ForTernaryExpressionContext) {} func (s *BaseFqlParserListener) ExitForTernaryExpression(ctx *ForTernaryExpressionContext) {}
// EnterArrayOperator is called when production arrayOperator is entered.
func (s *BaseFqlParserListener) EnterArrayOperator(ctx *ArrayOperatorContext) {}
// ExitArrayOperator is called when production arrayOperator is exited.
func (s *BaseFqlParserListener) ExitArrayOperator(ctx *ArrayOperatorContext) {}
// EnterInOperator is called when production inOperator is entered.
func (s *BaseFqlParserListener) EnterInOperator(ctx *InOperatorContext) {}
// ExitInOperator is called when production inOperator is exited.
func (s *BaseFqlParserListener) ExitInOperator(ctx *InOperatorContext) {}
// EnterEqualityOperator is called when production equalityOperator is entered. // EnterEqualityOperator is called when production equalityOperator is entered.
func (s *BaseFqlParserListener) EnterEqualityOperator(ctx *EqualityOperatorContext) {} func (s *BaseFqlParserListener) EnterEqualityOperator(ctx *EqualityOperatorContext) {}

View File

@@ -191,6 +191,14 @@ func (v *BaseFqlParserVisitor) VisitForTernaryExpression(ctx *ForTernaryExpressi
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }
func (v *BaseFqlParserVisitor) VisitArrayOperator(ctx *ArrayOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitInOperator(ctx *InOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitEqualityOperator(ctx *EqualityOperatorContext) interface{} { func (v *BaseFqlParserVisitor) VisitEqualityOperator(ctx *EqualityOperatorContext) interface{} {
return v.VisitChildren(ctx) return v.VisitChildren(ctx)
} }

View File

@@ -145,6 +145,12 @@ type FqlParserListener interface {
// EnterForTernaryExpression is called when entering the forTernaryExpression production. // EnterForTernaryExpression is called when entering the forTernaryExpression production.
EnterForTernaryExpression(c *ForTernaryExpressionContext) EnterForTernaryExpression(c *ForTernaryExpressionContext)
// EnterArrayOperator is called when entering the arrayOperator production.
EnterArrayOperator(c *ArrayOperatorContext)
// EnterInOperator is called when entering the inOperator production.
EnterInOperator(c *InOperatorContext)
// EnterEqualityOperator is called when entering the equalityOperator production. // EnterEqualityOperator is called when entering the equalityOperator production.
EnterEqualityOperator(c *EqualityOperatorContext) EnterEqualityOperator(c *EqualityOperatorContext)
@@ -295,6 +301,12 @@ type FqlParserListener interface {
// ExitForTernaryExpression is called when exiting the forTernaryExpression production. // ExitForTernaryExpression is called when exiting the forTernaryExpression production.
ExitForTernaryExpression(c *ForTernaryExpressionContext) ExitForTernaryExpression(c *ForTernaryExpressionContext)
// ExitArrayOperator is called when exiting the arrayOperator production.
ExitArrayOperator(c *ArrayOperatorContext)
// ExitInOperator is called when exiting the inOperator production.
ExitInOperator(c *InOperatorContext)
// ExitEqualityOperator is called when exiting the equalityOperator production. // ExitEqualityOperator is called when exiting the equalityOperator production.
ExitEqualityOperator(c *EqualityOperatorContext) ExitEqualityOperator(c *EqualityOperatorContext)

View File

@@ -145,6 +145,12 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#forTernaryExpression. // Visit a parse tree produced by FqlParser#forTernaryExpression.
VisitForTernaryExpression(ctx *ForTernaryExpressionContext) interface{} VisitForTernaryExpression(ctx *ForTernaryExpressionContext) interface{}
// Visit a parse tree produced by FqlParser#arrayOperator.
VisitArrayOperator(ctx *ArrayOperatorContext) interface{}
// Visit a parse tree produced by FqlParser#inOperator.
VisitInOperator(ctx *InOperatorContext) interface{}
// Visit a parse tree produced by FqlParser#equalityOperator. // Visit a parse tree produced by FqlParser#equalityOperator.
VisitEqualityOperator(ctx *EqualityOperatorContext) interface{} VisitEqualityOperator(ctx *EqualityOperatorContext) interface{}

View File

@@ -0,0 +1,25 @@
package collections
import "github.com/MontFerret/ferret/pkg/runtime/core"
func ToHashTable(iterator Iterator) (map[uint64]core.Value, error) {
result := make(map[uint64]core.Value)
for iterator.HasNext() {
val, _, err := iterator.Next()
if err != nil {
return nil, err
}
h := val.Hash()
_, exists := result[h]
if !exists {
result[h] = val
}
}
return result, nil
}

View File

@@ -0,0 +1,8 @@
package core
import "context"
type OperatorExpression interface {
Expression
Eval(ctx context.Context, left, right Value) (Value, error)
}

View File

@@ -0,0 +1,193 @@
package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
type (
ArrayOperatorType int
ArrayOperator struct {
*baseOperator
aotype ArrayOperatorType
comparator core.OperatorExpression
}
)
const (
ArrayOperatorTypeAll ArrayOperatorType = 0
ArrayOperatorTypeAny ArrayOperatorType = 1
ArrayOperatorTypeNone ArrayOperatorType = 2
)
func IsValidArrayOperatorType(aotype ArrayOperatorType) bool {
switch aotype {
case ArrayOperatorTypeAll, ArrayOperatorTypeAny, ArrayOperatorTypeNone:
return true
default:
return false
}
}
func ToIsValidArrayOperatorType(stype string) (ArrayOperatorType, error) {
switch stype {
case "ALL":
return ArrayOperatorTypeAll, nil
case "ANY":
return ArrayOperatorTypeAny, nil
case "NONE":
return ArrayOperatorTypeNone, nil
default:
return ArrayOperatorType(-1), core.Error(core.ErrInvalidArgument, stype)
}
}
func NewArrayOperator(
src core.SourceMap,
left core.Expression,
right core.Expression,
aotype ArrayOperatorType,
comparator core.OperatorExpression,
) (*ArrayOperator, error) {
if left == nil {
return nil, core.Error(core.ErrMissedArgument, "left expression")
}
if right == nil {
return nil, core.Error(core.ErrMissedArgument, "right expression")
}
if IsValidArrayOperatorType(aotype) == false {
return nil, core.Error(core.ErrInvalidArgument, "operator type")
}
if comparator == nil {
return nil, core.Error(core.ErrMissedArgument, "comparator expression")
}
base := &baseOperator{src, left, right}
return &ArrayOperator{base, aotype, comparator}, nil
}
func (operator *ArrayOperator) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
left, err := operator.left.Exec(ctx, scope)
if err != nil {
return values.False, core.SourceError(operator.src, err)
}
right, err := operator.right.Exec(ctx, scope)
if err != nil {
return values.False, core.SourceError(operator.src, err)
}
return operator.Eval(ctx, left, right)
}
func (operator *ArrayOperator) Eval(ctx context.Context, left, right core.Value) (core.Value, error) {
err := core.ValidateType(left, core.ArrayType)
if err != nil {
// TODO: Return the error? AQL just returns false
return values.False, nil
}
arr := left.(*values.Array)
switch operator.aotype {
case ArrayOperatorTypeAll:
return operator.all(ctx, arr, right)
case ArrayOperatorTypeAny:
return operator.any(ctx, arr, right)
default:
return operator.none(ctx, arr, right)
}
}
func (operator *ArrayOperator) all(ctx context.Context, arr *values.Array, value core.Value) (core.Value, error) {
result := values.False
var err error
arr.ForEach(func(el core.Value, _ int) bool {
out, e := operator.comparator.Eval(ctx, el, value)
if e != nil {
err = e
return false
}
if out == values.True {
result = values.True
} else {
result = values.False
return false
}
return true
})
if err != nil {
return values.False, err
}
return result, nil
}
func (operator *ArrayOperator) any(ctx context.Context, arr *values.Array, value core.Value) (core.Value, error) {
result := values.False
var err error
arr.ForEach(func(el core.Value, _ int) bool {
out, e := operator.comparator.Eval(ctx, el, value)
if e != nil {
err = e
return false
}
if out == values.True {
result = values.True
return false
}
return true
})
if err != nil {
return values.False, err
}
return result, nil
}
func (operator *ArrayOperator) none(ctx context.Context, arr *values.Array, value core.Value) (core.Value, error) {
result := values.False
var err error
arr.ForEach(func(el core.Value, _ int) bool {
out, e := operator.comparator.Eval(ctx, el, value)
if e != nil {
err = e
return false
}
if out == values.False {
result = values.True
} else {
result = values.False
return false
}
return true
})
if err != nil {
return values.False, err
}
return result, nil
}

View File

@@ -8,11 +8,11 @@ import (
type ( type (
EqualityOperator struct { EqualityOperator struct {
*baseOperator *baseOperator
fn Operator fn OperatorFunc
} }
) )
var equalityOperators = map[string]Operator{ var equalityOperators = map[string]OperatorFunc{
"==": Equal, "==": Equal,
"!=": NotEqual, "!=": NotEqual,
">": Greater, ">": Greater,
@@ -30,7 +30,7 @@ func NewEqualityOperator(
fn, exists := equalityOperators[operator] fn, exists := equalityOperators[operator]
if !exists { if !exists {
return nil, core.Error(core.ErrInvalidArgument, "operator") return nil, core.Error(core.ErrInvalidArgument, "aotype")
} }
return &EqualityOperator{ return &EqualityOperator{
@@ -52,5 +52,9 @@ func (operator *EqualityOperator) Exec(ctx context.Context, scope *core.Scope) (
return nil, err return nil, err
} }
return operator.Eval(ctx, left, right)
}
func (operator *EqualityOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
return operator.fn(left, right), nil return operator.fn(left, right), nil
} }

View File

@@ -17,11 +17,11 @@ func NewInOperator(
right core.Expression, right core.Expression,
not bool, not bool,
) (*InOperator, error) { ) (*InOperator, error) {
if core.IsNil(left) { if left == nil {
return nil, core.Error(core.ErrMissedArgument, "left expression") return nil, core.Error(core.ErrMissedArgument, "left expression")
} }
if core.IsNil(right) { if right == nil {
return nil, core.Error(core.ErrMissedArgument, "right expression") return nil, core.Error(core.ErrMissedArgument, "right expression")
} }
@@ -41,7 +41,11 @@ func (operator *InOperator) Exec(ctx context.Context, scope *core.Scope) (core.V
return values.False, core.SourceError(operator.src, err) return values.False, core.SourceError(operator.src, err)
} }
err = core.ValidateType(right, core.ArrayType) return operator.Eval(ctx, left, right)
}
func (operator *InOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
err := core.ValidateType(right, core.ArrayType)
if err != nil { if err != nil {
// TODO: Return the error? AQL just returns false // TODO: Return the error? AQL just returns false

View File

@@ -15,17 +15,17 @@ type (
) )
const ( const (
AndType LogicalOperatorType = 0 LogicalOperatorTypeAnd LogicalOperatorType = 0
OrType LogicalOperatorType = 1 LogicalOperatorTypeOr LogicalOperatorType = 1
NotType LogicalOperatorType = 2 LogicalOperatorTypeNot LogicalOperatorType = 2
) )
var logicalOperators = map[string]LogicalOperatorType{ var logicalOperators = map[string]LogicalOperatorType{
"&&": AndType, "&&": LogicalOperatorTypeAnd,
"AND": AndType, "AND": LogicalOperatorTypeAnd,
"||": OrType, "||": LogicalOperatorTypeOr,
"OR": OrType, "OR": LogicalOperatorTypeOr,
"NOT": NotType, "NOT": LogicalOperatorTypeNot,
} }
func NewLogicalOperator( func NewLogicalOperator(
@@ -51,7 +51,7 @@ func NewLogicalOperator(
} }
func (operator *LogicalOperator) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) { func (operator *LogicalOperator) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
if operator.value == NotType { if operator.value == LogicalOperatorTypeNot {
val, err := operator.right.Exec(ctx, scope) val, err := operator.right.Exec(ctx, scope)
if err != nil { if err != nil {
@@ -69,7 +69,7 @@ func (operator *LogicalOperator) Exec(ctx context.Context, scope *core.Scope) (c
leftBool := values.ToBoolean(left) leftBool := values.ToBoolean(left)
if operator.value == AndType && leftBool == values.False { if operator.value == LogicalOperatorTypeAnd && leftBool == values.False {
if left.Type() == core.BooleanType { if left.Type() == core.BooleanType {
return values.False, nil return values.False, nil
} }
@@ -77,7 +77,7 @@ func (operator *LogicalOperator) Exec(ctx context.Context, scope *core.Scope) (c
return left, nil return left, nil
} }
if operator.value == OrType && leftBool == values.True { if operator.value == LogicalOperatorTypeOr && leftBool == values.True {
return left, nil return left, nil
} }
@@ -89,3 +89,25 @@ func (operator *LogicalOperator) Exec(ctx context.Context, scope *core.Scope) (c
return right, nil return right, nil
} }
func (operator *LogicalOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
if operator.value == LogicalOperatorTypeNot {
return Not(right, values.None), nil
}
leftBool := values.ToBoolean(left)
if operator.value == LogicalOperatorTypeAnd && leftBool == values.False {
if left.Type() == core.BooleanType {
return values.False, nil
}
return left, nil
}
if operator.value == LogicalOperatorTypeOr && leftBool == values.True {
return left, nil
}
return right, nil
}

View File

@@ -6,32 +6,45 @@ import (
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
) )
type MathOperator struct { type (
*baseOperator MathOperatorType string
fn Operator MathOperator struct {
leftOnly bool *baseOperator
} fn OperatorFunc
leftOnly bool
}
)
var mathOperators = map[string]Operator{ const (
"+": Add, MathOperatorTypeAdd MathOperatorType = "+"
"-": Subtract, MathOperatorTypeSubtract MathOperatorType = "-"
"*": Multiply, MathOperatorTypeMultiply MathOperatorType = "*"
"/": Divide, MathOperatorTypeDivide MathOperatorType = "/"
"%": Modulus, MathOperatorTypeModulus MathOperatorType = "%"
"++": Increment, MathOperatorTypeIncrement MathOperatorType = "++"
"--": Decrement, MathOperatorTypeDecrement MathOperatorType = "--"
)
var mathOperators = map[MathOperatorType]OperatorFunc{
MathOperatorTypeAdd: Add,
MathOperatorTypeSubtract: Subtract,
MathOperatorTypeMultiply: Multiply,
MathOperatorTypeDivide: Divide,
MathOperatorTypeModulus: Modulus,
MathOperatorTypeIncrement: Increment,
MathOperatorTypeDecrement: Decrement,
} }
func NewMathOperator( func NewMathOperator(
src core.SourceMap, src core.SourceMap,
left core.Expression, left core.Expression,
right core.Expression, right core.Expression,
operator string, operator MathOperatorType,
) (*MathOperator, error) { ) (*MathOperator, error) {
fn, exists := mathOperators[operator] fn, exists := mathOperators[operator]
if !exists { if !exists {
return nil, core.Error(core.ErrInvalidArgument, "operator") return nil, core.Error(core.ErrInvalidArgument, "operator type")
} }
var leftOnly bool var leftOnly bool
@@ -55,7 +68,7 @@ func (operator *MathOperator) Exec(ctx context.Context, scope *core.Scope) (core
} }
if operator.leftOnly { if operator.leftOnly {
return operator.fn(left, values.None), nil return operator.Eval(ctx, left, values.None)
} }
right, err := operator.right.Exec(ctx, scope) right, err := operator.right.Exec(ctx, scope)
@@ -64,5 +77,13 @@ func (operator *MathOperator) Exec(ctx context.Context, scope *core.Scope) (core
return nil, err return nil, err
} }
return operator.Eval(ctx, left, right)
}
func (operator *MathOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
if operator.leftOnly {
return operator.fn(left, values.None), nil
}
return operator.fn(left, right), nil return operator.fn(left, right), nil
} }

View File

@@ -6,18 +6,23 @@ import (
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
) )
type Operator func(left, right core.Value) core.Value type (
OperatorFunc func(left, right core.Value) core.Value
type baseOperator struct { baseOperator struct {
src core.SourceMap src core.SourceMap
left core.Expression left core.Expression
right core.Expression right core.Expression
} }
)
func (operator *baseOperator) Exec(_ context.Context, _ *core.Scope) (core.Value, error) { func (operator *baseOperator) Exec(_ context.Context, _ *core.Scope) (core.Value, error) {
return values.None, core.ErrInvalidOperation return values.None, core.ErrInvalidOperation
} }
func (operator *baseOperator) Eval(_ context.Context, _, _ core.Value) (core.Value, error) {
return values.None, core.ErrInvalidOperation
}
// Equality // Equality
func Equal(left, right core.Value) core.Value { func Equal(left, right core.Value) core.Value {
if left.Compare(right) == 0 { if left.Compare(right) == 0 {

View File

@@ -44,13 +44,17 @@ func (operator *RangeOperator) Exec(ctx context.Context, scope *core.Scope) (cor
return values.None, core.SourceError(operator.src, err) return values.None, core.SourceError(operator.src, err)
} }
err = core.ValidateType(left, core.IntType) right, err := operator.right.Exec(ctx, scope)
if err != nil { if err != nil {
return values.None, core.SourceError(operator.src, err) return values.None, core.SourceError(operator.src, err)
} }
right, err := operator.right.Exec(ctx, scope) return operator.Eval(ctx, left, right)
}
func (operator *RangeOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
err := core.ValidateType(left, core.IntType)
if err != nil { if err != nil {
return values.None, core.SourceError(operator.src, err) return values.None, core.SourceError(operator.src, err)