Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 94 additions & 38 deletions ast/Expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ package ast
import (
"errors"
"fmt"
"github.com/hyperjumptech/grule-rule-engine/ast/unique"
"reflect"
"strings"

"github.com/hyperjumptech/grule-rule-engine/ast/unique"

"github.com/hyperjumptech/grule-rule-engine/pkg"
)

Expand Down Expand Up @@ -79,7 +80,8 @@ type Expression struct {
Negated bool
Value reflect.Value

Evaluated bool
Evaluated bool
CompareNilValues bool
}

// MakeCatalog will create a catalog entry from Expression node.
Expand Down Expand Up @@ -279,7 +281,18 @@ func (e *Expression) SetGrlText(grlText string) {

// Evaluate will evaluate this AST graph for when scope evaluation
func (e *Expression) Evaluate(dataContext IDataContext, memory *WorkingMemory) (reflect.Value, error) {
if e.Evaluated == true {
compareNode := dataContext.Get("COMPARE_NILS")
if compareNode != nil {
if rv := compareNode.Value(); rv.IsValid() && rv.Kind() == reflect.Bool {
e.CompareNilValues = rv.Bool()
} else {
e.CompareNilValues = false
}
} else {
e.CompareNilValues = false
}

if e.Evaluated {

return e.Value, nil
}
Expand Down Expand Up @@ -350,41 +363,84 @@ func (e *Expression) Evaluate(dataContext IDataContext, memory *WorkingMemory) (
return reflect.Value{}, fmt.Errorf("right hand expression error. got %v", rerr)
}

switch e.Operator {
case OpMul:
val, opErr = pkg.EvaluateMultiplication(lval, rval)
case OpDiv:
val, opErr = pkg.EvaluateDivision(lval, rval)
case OpMod:
val, opErr = pkg.EvaluateModulo(lval, rval)
case OpAdd:
val, opErr = pkg.EvaluateAddition(lval, rval)
case OpSub:
val, opErr = pkg.EvaluateSubtraction(lval, rval)
case OpBitAnd:
val, opErr = pkg.EvaluateBitAnd(lval, rval)
case OpBitOr:
val, opErr = pkg.EvaluateBitOr(lval, rval)
case OpGT:
val, opErr = pkg.EvaluateGreaterThan(lval, rval)
case OpLT:
val, opErr = pkg.EvaluateLesserThan(lval, rval)
case OpGTE:
val, opErr = pkg.EvaluateGreaterThanEqual(lval, rval)
case OpLTE:
val, opErr = pkg.EvaluateLesserThanEqual(lval, rval)
case OpEq:
val, opErr = pkg.EvaluateEqual(lval, rval)
case OpNEq:
val, opErr = pkg.EvaluateNotEqual(lval, rval)
case OpAnd:
val, opErr = pkg.EvaluateLogicAnd(lval, rval)
case OpOr:
val, opErr = pkg.EvaluateLogicOr(lval, rval)
}
if opErr == nil {
e.Value = val
e.Evaluated = true
if e.CompareNilValues && (!lval.IsValid() || !rval.IsValid()) {
if e.CompareNilValues {
AstLog.Debugf("Values have invalid value (%v and %v) but continuing with null handling", lval, rval)
switch e.Operator {
case OpMul, OpDiv, OpBitAnd, OpBitOr, OpMod:
// Can be left as nil, as these operators with Nil are not defined
e.Evaluated = true
case OpAdd:
if lval.IsValid() {
val = lval
} else if rval.IsValid() {
val = rval
}
e.Evaluated = true
case OpSub:
if lval.IsValid() {
val = lval
} else if rval.IsValid() {
val, _ = pkg.EvaluateSubtraction(reflect.ValueOf(0), rval)
}
e.Evaluated = true
case OpOr:
lvale := pkg.GetValueElem(lval)
rvale := pkg.GetValueElem(rval)
if (lvale.IsValid() && lvale.Kind() == reflect.Bool && lvale.Bool()) || (rvale.IsValid() && rvale.Kind() == reflect.Bool && rvale.Bool()) {
val = reflect.ValueOf(true)
} else {
val = reflect.ValueOf(false)
}
e.Value = val
e.Evaluated = true
case OpEq, OpGTE, OpLTE, OpGT, OpLT, OpAnd:
val = reflect.ValueOf(false)
e.Value = val
e.Evaluated = true
case OpNEq:
val = reflect.ValueOf(true)
e.Value = val
e.Evaluated = true
}
}
} else {
switch e.Operator {
case OpMul:
val, opErr = pkg.EvaluateMultiplication(lval, rval)
case OpDiv:
val, opErr = pkg.EvaluateDivision(lval, rval)
case OpMod:
val, opErr = pkg.EvaluateModulo(lval, rval)
case OpAdd:
val, opErr = pkg.EvaluateAddition(lval, rval)
case OpSub:
val, opErr = pkg.EvaluateSubtraction(lval, rval)
case OpBitAnd:
val, opErr = pkg.EvaluateBitAnd(lval, rval)
case OpBitOr:
val, opErr = pkg.EvaluateBitOr(lval, rval)
case OpGT:
val, opErr = pkg.EvaluateGreaterThan(lval, rval)
case OpLT:
val, opErr = pkg.EvaluateLesserThan(lval, rval)
case OpGTE:
val, opErr = pkg.EvaluateGreaterThanEqual(lval, rval)
case OpLTE:
val, opErr = pkg.EvaluateLesserThanEqual(lval, rval)
case OpEq:
val, opErr = pkg.EvaluateEqual(lval, rval)
case OpNEq:
val, opErr = pkg.EvaluateNotEqual(lval, rval)
case OpAnd:
val, opErr = pkg.EvaluateLogicAnd(lval, rval)
case OpOr:
val, opErr = pkg.EvaluateLogicOr(lval, rval)
}
if opErr == nil {
e.Value = val
e.Evaluated = true
}
}

return val, opErr
Expand Down
36 changes: 32 additions & 4 deletions ast/ExpressionAtom.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ package ast
import (
"errors"
"fmt"
"github.com/hyperjumptech/grule-rule-engine/ast/unique"
"github.com/hyperjumptech/grule-rule-engine/model"
"reflect"
"strings"

"github.com/hyperjumptech/grule-rule-engine/ast/unique"
"github.com/hyperjumptech/grule-rule-engine/model"

"github.com/hyperjumptech/grule-rule-engine/pkg"
)

Expand Down Expand Up @@ -49,7 +50,8 @@ type ExpressionAtom struct {
Value reflect.Value
ValueNode model.ValueNode

Evaluated bool
Evaluated bool
CompareNilValues bool
}

// MakeCatalog will create a catalog entry from ExpressionAtom node.
Expand Down Expand Up @@ -265,10 +267,22 @@ func (e *ExpressionAtom) SetGrlText(grlText string) {

// Evaluate will evaluate this AST graph for when scope evaluation
func (e *ExpressionAtom) Evaluate(dataContext IDataContext, memory *WorkingMemory) (val reflect.Value, err error) {
if e.Evaluated == true {
// Extract COMPARE_NILS from dataContext as a bool, defaulting to false when unavailable or not boolean.
compareNode := dataContext.Get("COMPARE_NILS")
if compareNode != nil {
if rv := compareNode.Value(); rv.IsValid() && rv.Kind() == reflect.Bool {
e.CompareNilValues = rv.Bool()
} else {
e.CompareNilValues = false
}
} else {
e.CompareNilValues = false
}

if e.Evaluated {
return e.Value, nil
}

if e.Constant != nil {
val, err := e.Constant.Evaluate(dataContext, memory)
if err != nil {
Expand Down Expand Up @@ -346,8 +360,15 @@ func (e *ExpressionAtom) Evaluate(dataContext IDataContext, memory *WorkingMemor
return reflect.ValueOf(nil), err
}

if e.ExpressionAtom.ValueNode == nil && e.CompareNilValues {
return reflect.ValueOf(nil), nil
}

retVal, err := e.ExpressionAtom.ValueNode.CallFunction(e.FunctionCall.FunctionName, args...)
if err != nil {
if e.CompareNilValues {
return reflect.ValueOf(nil), nil
}

return reflect.ValueOf(nil), err
}
Expand All @@ -368,6 +389,13 @@ func (e *ExpressionAtom) Evaluate(dataContext IDataContext, memory *WorkingMemor
}
valueNode, err := e.ExpressionAtom.ValueNode.GetChildNodeByField(e.VariableName)
if err != nil {
if e.CompareNilValues {
e.ValueNode = model.NewGoValueNode(reflect.ValueOf(nil), fmt.Sprintf("%s.%s->nil", e.ExpressionAtom.ValueNode.IdentifiedAs(), e.VariableName))
e.Value = e.ValueNode.Value()
e.Evaluated = true

return e.Value, nil
}

return reflect.Value{}, err
}
Expand Down
22 changes: 19 additions & 3 deletions ast/Variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ package ast

import (
"fmt"
"github.com/hyperjumptech/grule-rule-engine/ast/unique"
"github.com/hyperjumptech/grule-rule-engine/model"
"reflect"
"strings"

"github.com/hyperjumptech/grule-rule-engine/ast/unique"
"github.com/hyperjumptech/grule-rule-engine/model"

"github.com/hyperjumptech/grule-rule-engine/pkg"
)

Expand All @@ -43,6 +44,8 @@ type Variable struct {

ValueNode model.ValueNode
Value reflect.Value

CompareNilValues bool
}

// MakeCatalog create a catalog entry for this AST Node
Expand Down Expand Up @@ -219,6 +222,17 @@ func (e *Variable) Assign(newVal reflect.Value, dataContext IDataContext, memory

// Evaluate will evaluate this AST graph for when scope evaluation
func (e *Variable) Evaluate(dataContext IDataContext, memory *WorkingMemory) (reflect.Value, error) {
compareNode := dataContext.Get("COMPARE_NILS")
if compareNode != nil {
if rv := compareNode.Value(); rv.IsValid() && rv.Kind() == reflect.Bool {
e.CompareNilValues = rv.Bool()
} else {
e.CompareNilValues = false
}
} else {
e.CompareNilValues = false
}

if len(e.Name) > 0 && e.Variable == nil {
valueNode := dataContext.Get(e.Name)
if valueNode == nil {
Expand All @@ -238,7 +252,9 @@ func (e *Variable) Evaluate(dataContext IDataContext, memory *WorkingMemory) (re
}
valueNode, err := e.Variable.ValueNode.GetChildNodeByField(e.Name)
if err != nil {

if e.CompareNilValues {
return reflect.ValueOf(nil), nil
}
return reflect.Value{}, err
}
e.ValueNode = valueNode
Expand Down
19 changes: 17 additions & 2 deletions engine/GruleEngine.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ package engine
import (
"context"
"fmt"
"sort"
"time"

"github.com/rs/zerolog"
"github.com/sirupsen/logrus"
"go.uber.org/zap"
"sort"
"time"

"github.com/hyperjumptech/grule-rule-engine/ast"
"github.com/hyperjumptech/grule-rule-engine/logger"
Expand Down Expand Up @@ -87,6 +88,7 @@ func NewGruleEngine() *GruleEngine {
type GruleEngine struct {
MaxCycle uint64
ReturnErrOnFailedRuleEvaluation bool
CompareNilValues bool
Listeners []GruleEngineListener
}

Expand Down Expand Up @@ -150,6 +152,13 @@ func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataC
return err
}

err = dataCtx.Add("COMPARE_NILS", g.CompareNilValues)
if err != nil {
log.Error("COMPARE_NILS add err")

return err
}

// Working memory need to be resetted. all Expression will be set as not evaluated.
log.Debugf("Resetting Working memory")
knowledge.WorkingMemory.ResetAll()
Expand Down Expand Up @@ -279,6 +288,12 @@ func (g *GruleEngine) FetchMatchingRules(dataCtx ast.IDataContext, knowledge *as
return nil, err
}

err = dataCtx.Add("COMPARE_NILS", g.CompareNilValues)
if err != nil {
log.Error("COMPARE_NILS add err")

return nil, err
}
// Working memory need to be resetted. all Expression will be set as not evaluated.
log.Debugf("Resetting Working memory")
knowledge.WorkingMemory.ResetAll()
Expand Down
Loading
Loading