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

fix: produced array may have side-effect to given args #205

Open
wants to merge 1 commit 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
50 changes: 36 additions & 14 deletions evaluationStage.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ func makeFunctionStage(function ExpressionFunction) evaluationOperator {
}

switch right.(type) {
case slice:
return function(right.(slice)...)
case []interface{}:
return function(right.([]interface{})...)
default:
Expand Down Expand Up @@ -346,6 +348,14 @@ func makeAccessorStage(pair []string) evaluationOperator {
}

switch right.(type) {
case slice:

givenParams := right.(slice)
params = make([]reflect.Value, len(givenParams))
for idx, _ := range givenParams {
params[idx] = reflect.ValueOf(givenParams[idx])
}

case []interface{}:

givenParams := right.([]interface{})
Expand Down Expand Up @@ -404,25 +414,35 @@ func makeAccessorStage(pair []string) evaluationOperator {
}
}

// A type alise for a slice of interface{}, to indentation that this is a produced slice
type slice []interface{}

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

var ret []interface{}
var ret slice

switch left.(type) {
case []interface{}:
ret = append(left.([]interface{}), right)
case slice:
ret = append(left.(slice), right)
default:
ret = []interface{}{left, right}
ret = slice{left, right}
}

return ret, nil
}

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

for _, value := range right.([]interface{}) {
if left == value {
return true, nil
if _, ok := right.(slice); ok {
for _, value := range right.(slice) {
if left == value {
return true, nil
}
}
} else {
for _, value := range right.([]interface{}) {
if left == value {
return true, nil
}
}
}
return false, nil
Expand Down Expand Up @@ -467,8 +487,8 @@ func isFloat64(value interface{}) bool {
}

/*
Addition usually means between numbers, but can also mean string concat.
String concat needs one (or both) of the sides to be a string.
Addition usually means between numbers, but can also mean string concat.
String concat needs one (or both) of the sides to be a string.
*/
func additionTypeCheck(left interface{}, right interface{}) bool {

Expand All @@ -482,8 +502,8 @@ func additionTypeCheck(left interface{}, right interface{}) bool {
}

/*
Comparison can either be between numbers, or lexicographic between two strings,
but never between the two.
Comparison can either be between numbers, or lexicographic between two strings,
but never between the two.
*/
func comparatorTypeCheck(left interface{}, right interface{}) bool {

Expand All @@ -498,15 +518,17 @@ func comparatorTypeCheck(left interface{}, right interface{}) bool {

func isArray(value interface{}) bool {
switch value.(type) {
case slice:
return true
case []interface{}:
return true
}
return false
}

/*
Converting a boolean to an interface{} requires an allocation.
We can use interned bools to avoid this cost.
Converting a boolean to an interface{} requires an allocation.
We can use interned bools to avoid this cost.
*/
func boolIface(b bool) interface{} {
if b {
Expand Down
28 changes: 23 additions & 5 deletions evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package govaluate
import (
"errors"
"fmt"
"reflect"
"regexp"
"testing"
"time"
)

/*
Represents a test of expression evaluation
Represents a test of expression evaluation
*/
type EvaluationTest struct {
Name string
Expand Down Expand Up @@ -710,7 +711,7 @@ func TestNoParameterEvaluation(test *testing.T) {
Expected: true,
},
EvaluationTest{

Name: "Ternary/Java EL ambiguity",
Input: "false ? foo:length()",
Functions: map[string]ExpressionFunction{
Expand Down Expand Up @@ -1425,7 +1426,7 @@ func TestParameterizedEvaluation(test *testing.T) {
}

/*
Tests the behavior of a nil set of parameters.
Tests the behavior of a nil set of parameters.
*/
func TestNilParameters(test *testing.T) {

Expand All @@ -1438,8 +1439,8 @@ func TestNilParameters(test *testing.T) {
}

/*
Tests functionality related to using functions with a struct method receiver.
Created to test #54.
Tests functionality related to using functions with a struct method receiver.
Created to test #54.
*/
func TestStructFunctions(test *testing.T) {

Expand All @@ -1465,6 +1466,23 @@ func TestStructFunctions(test *testing.T) {
}
}

func TestFuncArgs(test *testing.T) {
functions := map[string]ExpressionFunction{
"func1": func(args ...interface{}) (interface{}, error) {
return args, nil
},
}
exp, _ := NewEvaluableExpressionWithFunctions("func1(args, 1, 2, 3)", functions)
result, _ := exp.Evaluate(map[string]interface{}{
"args": []interface{}{1, 2, 3},
})
excepted := []interface{}{[]interface{}{1, 2, 3}, 1.0, 2.0, 3.0}
if !reflect.DeepEqual(result, excepted) {
test.Logf("Function calling method did not return the right value. Got: %v, expected %d\n", result, excepted)
test.Fail()
}
}

func runEvaluationTests(evaluationTests []EvaluationTest, test *testing.T) {

var expression *EvaluableExpression
Expand Down