Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#199 Case Insensitive check needed for string comparison during expression evaluation #200

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
8 changes: 7 additions & 1 deletion EvaluableExpression.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ type EvaluableExpression struct {
tokens []ExpressionToken
evaluationStages *evaluationStage
inputExpression string

/*
This flag will be used to set the case sensitivity of the expression.
If set to true, the expression will be evaluated using case insensitive comparison.
*/
CaseInsensitive bool
}

/*
Expand Down Expand Up @@ -229,7 +235,7 @@ func (this EvaluableExpression) evaluateStage(stage *evaluationStage, parameters
}
}

return stage.operator(left, right, parameters)
return stage.operator(left, right, parameters, this.CaseInsensitive)
}

func typeCheck(check stageTypeCheck, value interface{}, symbol OperatorSymbol, format string) error {
Expand Down
88 changes: 53 additions & 35 deletions evaluationStage.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const (
prefixErrorFormat string = "Value '%v' cannot be used with the prefix '%v'"
)

type evaluationOperator func(left interface{}, right interface{}, parameters Parameters) (interface{}, error)
type evaluationOperator func(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error)
type stageTypeCheck func(value interface{}) bool
type stageCombinedTypeCheck func(left interface{}, right interface{}) bool

Expand Down Expand Up @@ -82,11 +82,11 @@ func (this *evaluationStage) isShortCircuitable() bool {
return false
}

func noopStageRight(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func noopStageRight(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return right, nil
}

func addStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func addStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {

// string concat if either are strings
if isString(left) || isString(right) {
Expand All @@ -95,80 +95,98 @@ func addStage(left interface{}, right interface{}, parameters Parameters) (inter

return left.(float64) + right.(float64), nil
}
func subtractStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func subtractStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return left.(float64) - right.(float64), nil
}
func multiplyStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func multiplyStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return left.(float64) * right.(float64), nil
}
func divideStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func divideStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return left.(float64) / right.(float64), nil
}
func exponentStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func exponentStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return math.Pow(left.(float64), right.(float64)), nil
}
func modulusStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func modulusStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return math.Mod(left.(float64), right.(float64)), nil
}
func gteStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func gteStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if isString(left) && isString(right) {
if caseInsensitive {
return boolIface(strings.ToLower(left.(string)) >= strings.ToLower(right.(string))), nil
}
return boolIface(left.(string) >= right.(string)), nil
}
return boolIface(left.(float64) >= right.(float64)), nil
}
func gtStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func gtStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if isString(left) && isString(right) {
if caseInsensitive {
return boolIface(strings.ToLower(left.(string)) > strings.ToLower(right.(string))), nil
}
return boolIface(left.(string) > right.(string)), nil
}
return boolIface(left.(float64) > right.(float64)), nil
}
func lteStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func lteStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if isString(left) && isString(right) {
if caseInsensitive {
return boolIface(strings.ToLower(left.(string)) <= strings.ToLower(right.(string))), nil
}
return boolIface(left.(string) <= right.(string)), nil
}
return boolIface(left.(float64) <= right.(float64)), nil
}
func ltStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func ltStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if isString(left) && isString(right) {
if caseInsensitive {
return boolIface(strings.ToLower(left.(string)) < strings.ToLower(right.(string))), nil
}
return boolIface(left.(string) < right.(string)), nil
}
return boolIface(left.(float64) < right.(float64)), nil
}
func equalStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func equalStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if caseInsensitive && isString(left) && isString(right) {
return boolIface(strings.EqualFold(left.(string), right.(string))), nil
}
return boolIface(reflect.DeepEqual(left, right)), nil
}
func notEqualStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func notEqualStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if caseInsensitive && isString(left) && isString(right) {
return boolIface(!strings.EqualFold(left.(string), right.(string))), nil
}
return boolIface(!reflect.DeepEqual(left, right)), nil
}
func andStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func andStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return boolIface(left.(bool) && right.(bool)), nil
}
func orStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func orStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return boolIface(left.(bool) || right.(bool)), nil
}
func negateStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func negateStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return -right.(float64), nil
}
func invertStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func invertStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return boolIface(!right.(bool)), nil
}
func bitwiseNotStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func bitwiseNotStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return float64(^int64(right.(float64))), nil
}
func ternaryIfStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func ternaryIfStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if left.(bool) {
return right, nil
}
return nil, nil
}
func ternaryElseStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func ternaryElseStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
if left != nil {
return left, nil
}
return right, nil
}

func regexStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func regexStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {

var pattern *regexp.Regexp
var err error
Expand All @@ -186,35 +204,35 @@ func regexStage(left interface{}, right interface{}, parameters Parameters) (int
return pattern.Match([]byte(left.(string))), nil
}

func notRegexStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func notRegexStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {

ret, err := regexStage(left, right, parameters)
ret, err := regexStage(left, right, parameters, caseInsensitive)
if err != nil {
return nil, err
}

return !(ret.(bool)), nil
}

func bitwiseOrStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func bitwiseOrStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return float64(int64(left.(float64)) | int64(right.(float64))), nil
}
func bitwiseAndStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func bitwiseAndStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return float64(int64(left.(float64)) & int64(right.(float64))), nil
}
func bitwiseXORStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func bitwiseXORStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return float64(int64(left.(float64)) ^ int64(right.(float64))), nil
}
func leftShiftStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func leftShiftStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return float64(uint64(left.(float64)) << uint64(right.(float64))), nil
}
func rightShiftStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func rightShiftStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return float64(uint64(left.(float64)) >> uint64(right.(float64))), nil
}

func makeParameterStage(parameterName string) evaluationOperator {

return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
return func(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
value, err := parameters.Get(parameterName)
if err != nil {
return nil, err
Expand All @@ -225,14 +243,14 @@ func makeParameterStage(parameterName string) evaluationOperator {
}

func makeLiteralStage(literal interface{}) evaluationOperator {
return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
return func(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {
return literal, nil
}
}

func makeFunctionStage(function ExpressionFunction) evaluationOperator {

return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
return func(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {

if right == nil {
return function()
Expand Down Expand Up @@ -293,7 +311,7 @@ func makeAccessorStage(pair []string) evaluationOperator {

reconstructed := strings.Join(pair, ".")

return func(left interface{}, right interface{}, parameters Parameters) (ret interface{}, err error) {
return func(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (ret interface{}, err error) {

var params []reflect.Value

Expand Down Expand Up @@ -404,7 +422,7 @@ func makeAccessorStage(pair []string) evaluationOperator {
}
}

func separatorStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func separatorStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {

var ret []interface{}

Expand All @@ -418,7 +436,7 @@ func separatorStage(left interface{}, right interface{}, parameters Parameters)
return ret, nil
}

func inStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
func inStage(left interface{}, right interface{}, parameters Parameters, caseInsensitive bool) (interface{}, error) {

for _, value := range right.([]interface{}) {
if left == value {
Expand Down
6 changes: 3 additions & 3 deletions stagePlanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,12 +686,12 @@ func elideStage(root *evaluationStage) *evaluationStage {

// both sides are values, get their actual values.
// errors should be near-impossible here. If we encounter them, just abort this optimization.
leftValue, err = root.leftStage.operator(nil, nil, nil)
leftValue, err = root.leftStage.operator(nil, nil, nil, false)
if err != nil {
return root
}

rightValue, err = root.rightStage.operator(nil, nil, nil)
rightValue, err = root.rightStage.operator(nil, nil, nil, false)
if err != nil {
return root
}
Expand All @@ -712,7 +712,7 @@ func elideStage(root *evaluationStage) *evaluationStage {
}

// pre-calculate, and return a new stage representing the result.
result, err = root.operator(leftValue, rightValue, nil)
result, err = root.operator(leftValue, rightValue, nil, false)
if err != nil {
return root
}
Expand Down