1 Star 0 Fork 0

王军 / govaluate

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
evaluationStage.go 14.69 KB
一键复制 编辑 原始数据 按行查看 历史
王军 提交于 2021-12-06 13:59 . 增加调用变量的函数
package govaluate
import (
"errors"
"fmt"
"math"
"reflect"
"regexp"
"strings"
)
const (
logicalErrorFormat string = "Value '%v' cannot be used with the logical operator '%v', it is not a bool"
modifierErrorFormat string = "Value '%v' cannot be used with the modifier '%v', it is not a number"
comparatorErrorFormat string = "Value '%v' cannot be used with the comparator '%v', it is not a number"
ternaryErrorFormat string = "Value '%v' cannot be used with the ternary operator '%v', it is not a bool"
prefixErrorFormat string = "Value '%v' cannot be used with the prefix '%v'"
)
type evaluationOperator func(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error)
type stageTypeCheck func(value interface{}) bool
type stageCombinedTypeCheck func(left interface{}, right interface{}) bool
type evaluationStage struct {
symbol OperatorSymbol
funcName string
leftStage, rightStage *evaluationStage
// the operation that will be used to evaluate this stage (such as adding [left] to [right] and return the result)
operator evaluationOperator
// ensures that both left and right values are appropriate for this stage. Returns an error if they aren't operable.
leftTypeCheck stageTypeCheck
rightTypeCheck stageTypeCheck
// if specified, will override whatever is used in "leftTypeCheck" and "rightTypeCheck".
// primarily used for specific operators that don't care which side a given type is on, but still requires one side to be of a given type
// (like string concat)
typeCheck stageCombinedTypeCheck
// regardless of which type check is used, this string format will be used as the error message for type errors
typeErrorFormat string
}
var (
_true = interface{}(true)
_false = interface{}(false)
)
func (this *evaluationStage) swapWith(other *evaluationStage) {
temp := *other
other.setToNonStage(*this)
this.setToNonStage(temp)
}
func (this *evaluationStage) setToNonStage(other evaluationStage) {
this.symbol = other.symbol
this.operator = other.operator
this.leftTypeCheck = other.leftTypeCheck
this.rightTypeCheck = other.rightTypeCheck
this.typeCheck = other.typeCheck
this.typeErrorFormat = other.typeErrorFormat
}
func (this *evaluationStage) isShortCircuitable() bool {
switch this.symbol {
case AND:
fallthrough
case OR:
fallthrough
case TERNARY_TRUE:
fallthrough
case TERNARY_FALSE:
fallthrough
case COALESCE:
return true
}
return false
}
func noopStageRight(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return right, nil
}
func addStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
// string concat if either are strings
if isString(left) || isString(right) {
return fmt.Sprintf("%v%v", left, right), nil
}
return left.(float64) + right.(float64), nil
}
func subtractStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return left.(float64) - right.(float64), nil
}
func multiplyStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return left.(float64) * right.(float64), nil
}
func divideStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return left.(float64) / right.(float64), nil
}
func exponentStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return math.Pow(left.(float64), right.(float64)), nil
}
func modulusStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return math.Mod(left.(float64), right.(float64)), nil
}
func gteStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
if isString(left) && isString(right) {
return boolIface(left.(string) >= right.(string)), nil
}
return boolIface(left.(float64) >= right.(float64)), nil
}
func gtStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
if isString(left) && isString(right) {
return boolIface(left.(string) > right.(string)), nil
}
return boolIface(left.(float64) > right.(float64)), nil
}
func lteStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
if isString(left) && isString(right) {
return boolIface(left.(string) <= right.(string)), nil
}
return boolIface(left.(float64) <= right.(float64)), nil
}
func ltStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
if isString(left) && isString(right) {
return boolIface(left.(string) < right.(string)), nil
}
return boolIface(left.(float64) < right.(float64)), nil
}
func equalStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return boolIface(reflect.DeepEqual(left, right)), nil
}
func notEqualStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return boolIface(!reflect.DeepEqual(left, right)), nil
}
func andStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return boolIface(left.(bool) && right.(bool)), nil
}
func orStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return boolIface(left.(bool) || right.(bool)), nil
}
func negateStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return -right.(float64), nil
}
func invertStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return boolIface(!right.(bool)), nil
}
func bitwiseNotStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return float64(^int64(right.(float64))), nil
}
func ternaryIfStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
if left.(bool) {
return right, nil
}
return nil, nil
}
func ternaryElseStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
if left != nil {
return left, nil
}
return right, nil
}
func regexStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
var pattern *regexp.Regexp
var err error
switch right.(type) {
case string:
pattern, err = regexp.Compile(right.(string))
if err != nil {
return nil, errors.New(fmt.Sprintf("Unable to compile regexp pattern '%v': %v", right, err))
}
case *regexp.Regexp:
pattern = right.(*regexp.Regexp)
}
return pattern.Match([]byte(left.(string))), nil
}
func notRegexStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
ret, err := regexStage(left, right, parameters, funcName)
if err != nil {
return nil, err
}
return !(ret.(bool)), nil
}
func bitwiseOrStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return float64(int64(left.(float64)) | int64(right.(float64))), nil
}
func bitwiseAndStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return float64(int64(left.(float64)) & int64(right.(float64))), nil
}
func bitwiseXORStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return float64(int64(left.(float64)) ^ int64(right.(float64))), nil
}
func leftShiftStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return float64(uint64(left.(float64)) << uint64(right.(float64))), nil
}
func rightShiftStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return float64(uint64(left.(float64)) >> uint64(right.(float64))), nil
}
func makeParameterStage(parameterName string) evaluationOperator {
return func(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
value, err := parameters.Get(parameterName, funcName)
if err != nil {
return nil, err
}
return value, nil
}
}
func makeLiteralStage(literal interface{}) evaluationOperator {
return func(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
return literal, nil
}
}
func makeFunctionStage(function ExpressionFunction) evaluationOperator {
return func(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
if right == nil {
return function()
}
switch right.(type) {
case []interface{}:
return function(right.([]interface{})...)
default:
return function(right)
}
}
}
func typeConvertParam(p reflect.Value, t reflect.Type) (ret reflect.Value, err error) {
defer func() {
if r := recover(); r != nil {
errorMsg := fmt.Sprintf("Argument type conversion failed: failed to convert '%s' to '%s'", p.Kind().String(), t.Kind().String())
err = errors.New(errorMsg)
ret = p
}
}()
return p.Convert(t), nil
}
func typeConvertParams(method reflect.Value, params []reflect.Value) ([]reflect.Value, error) {
methodType := method.Type()
numIn := methodType.NumIn()
numParams := len(params)
if numIn != numParams {
if numIn > numParams {
return nil, fmt.Errorf("Too few arguments to parameter call: got %d arguments, expected %d", len(params), numIn)
}
return nil, fmt.Errorf("Too many arguments to parameter call: got %d arguments, expected %d", len(params), numIn)
}
for i := 0; i < numIn; i++ {
t := methodType.In(i)
p := params[i]
pt := p.Type()
if t.Kind() != pt.Kind() {
np, err := typeConvertParam(p, t)
if err != nil {
return nil, err
}
params[i] = np
}
}
return params, nil
}
func makeAccessorStage(pair []string) evaluationOperator {
reconstructed := strings.Join(pair, ".")
return func(left interface{}, right interface{}, parameters Parameters, funcName string) (ret interface{}, err error) {
var params []reflect.Value
value, err := parameters.Get(pair[0], funcName)
if err != nil {
return nil, err
}
// while this library generally tries to handle panic-inducing cases on its own,
// accessors are a sticky case which have a lot of possible ways to fail.
// therefore every call to an accessor sets up a defer that tries to recover from panics, converting them to errors.
defer func() {
if r := recover(); r != nil {
errorMsg := fmt.Sprintf("Failed to access '%s': %v", reconstructed, r.(string))
err = errors.New(errorMsg)
ret = nil
}
}()
for i := 1; i < len(pair); i++ {
coreValue := reflect.ValueOf(value)
var corePtrVal reflect.Value
// if this is a pointer, resolve it.
if coreValue.Kind() == reflect.Ptr {
corePtrVal = coreValue
coreValue = coreValue.Elem()
}
if coreValue.Kind() != reflect.Struct {
return nil, errors.New("Unable to access '" + pair[i] + "', '" + pair[i-1] + "' is not a struct")
}
field := coreValue.FieldByName(pair[i])
if field != (reflect.Value{}) {
value = field.Interface()
continue
}
method := coreValue.MethodByName(pair[i])
if method == (reflect.Value{}) {
if corePtrVal.IsValid() {
method = corePtrVal.MethodByName(pair[i])
}
if method == (reflect.Value{}) {
return nil, errors.New("No method or field '" + pair[i] + "' present on parameter '" + pair[i-1] + "'")
}
}
switch right.(type) {
case []interface{}:
givenParams := right.([]interface{})
params = make([]reflect.Value, len(givenParams))
for idx, _ := range givenParams {
params[idx] = reflect.ValueOf(givenParams[idx])
}
default:
if right == nil {
params = []reflect.Value{}
break
}
params = []reflect.Value{reflect.ValueOf(right.(interface{}))}
}
params, err = typeConvertParams(method, params)
if err != nil {
return nil, errors.New("Method call failed - '" + pair[0] + "." + pair[1] + "': " + err.Error())
}
returned := method.Call(params)
retLength := len(returned)
if retLength == 0 {
return nil, errors.New("Method call '" + pair[i-1] + "." + pair[i] + "' did not return any values.")
}
if retLength == 1 {
value = returned[0].Interface()
continue
}
if retLength == 2 {
errIface := returned[1].Interface()
err, validType := errIface.(error)
if validType && errIface != nil {
return returned[0].Interface(), err
}
value = returned[0].Interface()
continue
}
return nil, errors.New("Method call '" + pair[0] + "." + pair[1] + "' did not return either one value, or a value and an error. Cannot interpret meaning.")
}
value = castToFloat64(value)
return value, nil
}
}
func separatorStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
var ret []interface{}
switch left.(type) {
case []interface{}:
ret = append(left.([]interface{}), right)
default:
ret = []interface{}{left, right}
}
return ret, nil
}
func inStage(left interface{}, right interface{}, parameters Parameters, funcName string) (interface{}, error) {
for _, value := range right.([]interface{}) {
if left == value {
return true, nil
}
}
return false, nil
}
//
func isString(value interface{}) bool {
switch value.(type) {
case string:
return true
}
return false
}
func isRegexOrString(value interface{}) bool {
switch value.(type) {
case string:
return true
case *regexp.Regexp:
return true
}
return false
}
func isBool(value interface{}) bool {
switch value.(type) {
case bool:
return true
}
return false
}
func isFloat64(value interface{}) bool {
switch value.(type) {
case float64:
return true
}
return false
}
/*
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 {
if isFloat64(left) && isFloat64(right) {
return true
}
if !isString(left) && !isString(right) {
return false
}
return true
}
/*
Comparison can either be between numbers, or lexicographic between two strings,
but never between the two.
*/
func comparatorTypeCheck(left interface{}, right interface{}) bool {
if isFloat64(left) && isFloat64(right) {
return true
}
if isString(left) && isString(right) {
return true
}
return false
}
func isArray(value interface{}) bool {
switch value.(type) {
case []interface{}:
return true
}
return false
}
/*
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 {
return _true
}
return _false
}
Go
1
https://gitee.com/haodreams/govaluate.git
git@gitee.com:haodreams/govaluate.git
haodreams
govaluate
govaluate
v3.0.1

搜索帮助