github.com/GuanceCloud/cliutils@v1.1.21/filter/eval.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package filter
     7  
     8  import (
     9  	"math"
    10  	"reflect"
    11  	"regexp"
    12  	"strings"
    13  )
    14  
    15  type KVs interface {
    16  	Get(k string) (v any, ok bool)
    17  }
    18  
    19  type tfData struct {
    20  	tags   map[string]string
    21  	fields map[string]any
    22  }
    23  
    24  func (d *tfData) Get(name string) (any, bool) {
    25  	if v, ok := d.tags[name]; ok {
    26  		return v, true
    27  	}
    28  
    29  	if v, ok := d.fields[name]; ok {
    30  		return v, true
    31  	}
    32  
    33  	return nil, false
    34  }
    35  
    36  func newtf(tags map[string]string, fields map[string]any) *tfData {
    37  	return &tfData{
    38  		tags:   tags,
    39  		fields: fields,
    40  	}
    41  }
    42  
    43  func (p *ParenExpr) Eval(data KVs) bool {
    44  	if p.Param == nil {
    45  		return false
    46  	}
    47  
    48  	switch expr := p.Param.(type) {
    49  	case Evaluable:
    50  		return expr.Eval(data)
    51  	default:
    52  		log.Errorf("ParenExpr's Param should be Evaluable")
    53  	}
    54  
    55  	return false
    56  }
    57  
    58  func (e *BinaryExpr) Eval(data KVs) bool {
    59  	switch e.Op {
    60  	case AND:
    61  		for _, expr := range []Node{e.LHS, e.RHS} {
    62  			switch expr.(type) {
    63  			case Evaluable:
    64  			default:
    65  				log.Errorf("LHS and RHS should be BinaryExpr or ParenExpr")
    66  				return false
    67  			}
    68  		}
    69  
    70  		return e.LHS.(Evaluable).Eval(data) && e.RHS.(Evaluable).Eval(data)
    71  
    72  	case OR: // LHS/RHS should be BinaryExpr
    73  
    74  		for _, expr := range []Node{e.LHS, e.RHS} {
    75  			switch expr.(type) {
    76  			case Evaluable:
    77  			default:
    78  				log.Errorf("LHS and RHS should be BinaryExpr or ParenExpr")
    79  				return false
    80  			}
    81  		}
    82  
    83  		return e.LHS.(Evaluable).Eval(data) || e.RHS.(Evaluable).Eval(data)
    84  
    85  	default:
    86  		return e.doEval(data)
    87  	}
    88  }
    89  
    90  func (e *BinaryExpr) doEval(data KVs) bool {
    91  	switch e.Op {
    92  	case GTE, GT, LT, LTE, NEQ, EQ, IN, NOT_IN, MATCH, NOT_MATCH:
    93  	default:
    94  		log.Errorf("unsupported OP %s", e.Op.String())
    95  		return false
    96  	}
    97  
    98  	return e.singleEval(data)
    99  }
   100  
   101  const float64EqualityThreshold = 1e-9
   102  
   103  // see: https://stackoverflow.com/a/47969546/342348
   104  func almostEqual(a, b float64) bool {
   105  	return math.Abs(a-b) <= float64EqualityThreshold
   106  }
   107  
   108  func toFloat64(f interface{}) float64 {
   109  	switch v := f.(type) {
   110  	case float32:
   111  		return float64(v)
   112  	case float64:
   113  		return v
   114  	default:
   115  		log.Error("should not been here")
   116  		return 0.0
   117  	}
   118  }
   119  
   120  func toInt64(i interface{}) int64 {
   121  	switch v := i.(type) {
   122  	case int:
   123  		return int64(v)
   124  	case int8:
   125  		return int64(v)
   126  	case int16:
   127  		return int64(v)
   128  	case int32:
   129  		return int64(v)
   130  	case int64:
   131  		return v
   132  	case uint:
   133  		return int64(v)
   134  	case uint8:
   135  		return int64(v)
   136  	case uint16:
   137  		return int64(v)
   138  	case uint32:
   139  		return int64(v)
   140  	default:
   141  		log.Error("should not been here")
   142  		return 0
   143  	}
   144  }
   145  
   146  func binEval(op ItemType, lhs, rhs interface{}) bool {
   147  	if _, ok := rhs.(*Regex); ok {
   148  		if _, isStr := lhs.(string); !isStr {
   149  			log.Warnf("non-string(type %s) can not match with regexp", reflect.TypeOf(lhs))
   150  			return false
   151  		}
   152  	} else { // rhs are all literals
   153  		tl := reflect.TypeOf(lhs).String()
   154  		tr := reflect.TypeOf(rhs).String()
   155  		switch op {
   156  		case GTE, GT, LT, LTE, EQ, NEQ: // type conflict detecting on comparison expr
   157  			if _, ok := rhs.(*NilLiteral); !ok && // any type can compare to nil/null
   158  				tl != tr {
   159  				log.Warnf("type conflict %+#v(%s) <> %+#v(%s)", lhs, reflect.TypeOf(lhs), rhs, reflect.TypeOf(rhs))
   160  				return false
   161  			}
   162  
   163  		default:
   164  			log.Warnf("op %s should not been here", op.String())
   165  			return false
   166  		}
   167  	}
   168  
   169  	switch op {
   170  	case EQ:
   171  		switch lv := lhs.(type) {
   172  		case float64:
   173  			if f, ok := rhs.(float64); !ok {
   174  				return false
   175  			} else {
   176  				return almostEqual(lv, f)
   177  			}
   178  
   179  		case *NilLiteral:
   180  			if _, ok := rhs.(*NilLiteral); !ok { // nil compared to non-nil always false
   181  				log.Warnf("rhs %v not nil", rhs)
   182  				return false
   183  			}
   184  
   185  			return lv.String() == Nil
   186  
   187  		default: // NOTE: interface{} EQ/NEQ, see: https://stackoverflow.com/a/34246225/342348
   188  			switch rv := rhs.(type) {
   189  			case *Regex:
   190  				log.Debugf("lhs: %v, rhs: %v", lhs, rhs)
   191  				ok, err := regexp.MatchString(rv.Regex, lhs.(string))
   192  				if err != nil {
   193  					log.Error(err)
   194  				}
   195  
   196  				return ok
   197  
   198  			default:
   199  				return lhs == rhs
   200  			}
   201  		}
   202  
   203  	case MATCH:
   204  		return rhs.(*Regex).Re.MatchString(lhs.(string))
   205  
   206  	case NOT_MATCH:
   207  		return !rhs.(*Regex).Re.MatchString(lhs.(string))
   208  
   209  	case NEQ:
   210  		_, lok := lhs.(*NilLiteral)
   211  		_, rok := rhs.(*NilLiteral)
   212  		if lok && rok {
   213  			return false
   214  		}
   215  
   216  		return lhs != rhs
   217  
   218  	case GTE, GT, LT, LTE: // rhs/lhs should be number or string
   219  		switch lv := lhs.(type) {
   220  		case int, int8, int16, int32, int64,
   221  			uint, uint8, uint16, uint32:
   222  
   223  			if i, ok := rhs.(int64); !ok {
   224  				log.Warnf("rhs not int64")
   225  				return false
   226  			} else {
   227  				return cmpint(op, toInt64(lv), i)
   228  			}
   229  
   230  		case bool: // bool not support >/>=/</<=
   231  			return false
   232  		case string:
   233  			if s, ok := rhs.(string); !ok {
   234  				return false
   235  			} else {
   236  				return cmpstr(op, lv, s)
   237  			}
   238  		case float32, float64:
   239  			if f, ok := rhs.(float64); !ok {
   240  				return false
   241  			} else {
   242  				return cmpfloat(op, toFloat64(lv), f)
   243  			}
   244  		}
   245  	}
   246  
   247  	return false
   248  }
   249  
   250  func cmpstr(op ItemType, l, r string) bool {
   251  	switch op {
   252  	case GTE:
   253  		return strings.Compare(l, r) >= 0
   254  	case LTE:
   255  		return strings.Compare(l, r) <= 0
   256  	case LT:
   257  		return strings.Compare(l, r) < 0
   258  	case GT:
   259  		return strings.Compare(l, r) > 0
   260  	default:
   261  		log.Warn("should not been here, %s %s %s", l, op.String(), r)
   262  	}
   263  	return false
   264  }
   265  
   266  func cmpint(op ItemType, l, r int64) bool {
   267  	switch op {
   268  	case GTE:
   269  		return l >= r
   270  	case LTE:
   271  		return l <= r
   272  	case LT:
   273  		return l < r
   274  	case GT:
   275  		return l > r
   276  	default:
   277  		log.Warn("should not been here, %d %s %d", l, op.String(), r)
   278  	}
   279  	return false
   280  }
   281  
   282  func cmpfloat(op ItemType, l, r float64) bool {
   283  	switch op {
   284  	case GTE:
   285  		return l >= r
   286  	case LTE:
   287  		return l <= r
   288  	case LT:
   289  		return l < r
   290  	case GT:
   291  		return l > r
   292  	default:
   293  		log.Warn("should not been here, %f %s %f", l, op.String(), r)
   294  	}
   295  	return false
   296  }
   297  
   298  func (e *BinaryExpr) singleEval(data KVs) bool {
   299  	if e.LHS == nil || e.RHS == nil {
   300  		log.Warn("LHS or RHS nil, should not been here")
   301  		return false
   302  	}
   303  
   304  	// first: fetch right-handle-symbol
   305  	var lit interface{}
   306  	var arr []interface{}
   307  	switch rhs := e.RHS.(type) {
   308  	case *StringLiteral:
   309  		lit = rhs.Val
   310  
   311  	case *NumberLiteral:
   312  		if rhs.IsInt {
   313  			lit = rhs.Int
   314  		} else {
   315  			lit = rhs.Float
   316  		}
   317  
   318  	case NodeList:
   319  		for _, elem := range rhs {
   320  			switch x := elem.(type) {
   321  			case *StringLiteral:
   322  				arr = append(arr, x.Val)
   323  			case *NumberLiteral:
   324  				if x.IsInt {
   325  					arr = append(arr, x.Int)
   326  				} else {
   327  					arr = append(arr, x.Float)
   328  				}
   329  			case *Regex:
   330  				arr = append(arr, x)
   331  			case *NilLiteral:
   332  				arr = append(arr, x)
   333  			case *BoolLiteral:
   334  				arr = append(arr, x.Val)
   335  			default:
   336  				log.Warnf("unsupported node list with type `%s'", reflect.TypeOf(elem).String())
   337  			}
   338  		}
   339  
   340  	case *Regex:
   341  		lit = rhs
   342  
   343  	case *NilLiteral:
   344  		lit = rhs
   345  
   346  	case *BoolLiteral:
   347  		lit = rhs.Val
   348  
   349  	default:
   350  
   351  		log.Errorf("invalid RHS, got type `%s'", reflect.TypeOf(e.RHS).String())
   352  		return false
   353  	}
   354  
   355  	var lhs *Identifier
   356  	switch left := e.LHS.(type) { // Left part can be string/bool/number/nil literal and identifier
   357  	case *NilLiteral:
   358  		return binEval(e.Op, nilVal, lit)
   359  
   360  	case *NumberLiteral:
   361  		if left.IsInt {
   362  			return binEval(e.Op, left.Int, lit)
   363  		} else {
   364  			return binEval(e.Op, left.Float, lit)
   365  		}
   366  
   367  	case *BoolLiteral:
   368  		return binEval(e.Op, left.Val, lit)
   369  
   370  	case *StringLiteral:
   371  		return binEval(e.Op, left.Val, lit)
   372  
   373  	case *Identifier:
   374  		lhs = left // we get detailed lhs value later...
   375  
   376  	default:
   377  		log.Errorf("unknown LHS type, expect Identifier, got `%s'", reflect.TypeOf(e.LHS).String())
   378  		return false
   379  	}
   380  
   381  	name := lhs.Name
   382  
   383  	switch e.Op {
   384  	case MATCH, NOT_MATCH:
   385  		for _, item := range e.RHS.(NodeList) {
   386  			if v, ok := data.Get(name); ok {
   387  				switch x := v.(type) {
   388  				case string:
   389  					if binEval(e.Op, x, item) {
   390  						return true
   391  					}
   392  				default:
   393  					continue
   394  				}
   395  			}
   396  		}
   397  		return false
   398  
   399  	case IN:
   400  		for _, item := range arr {
   401  			if v, ok := data.Get(name); ok {
   402  				if binEval(EQ, v, item) {
   403  					return true
   404  				}
   405  			} else {
   406  				return binEval(EQ, item, nilVal)
   407  			}
   408  		}
   409  		return false
   410  
   411  	case NOT_IN:
   412  		for _, item := range arr {
   413  			if v, ok := data.Get(name); ok {
   414  				if binEval(EQ, v, item) {
   415  					return false
   416  				}
   417  			} else {
   418  				if binEval(EQ, item, nilVal) {
   419  					return false
   420  				}
   421  			}
   422  		}
   423  
   424  		return true
   425  
   426  	case GTE, GT, LT, LTE, NEQ, EQ:
   427  		if v, ok := data.Get(name); ok {
   428  			if binEval(e.Op, v, lit) {
   429  				return true
   430  			}
   431  		} else { // not exist in data
   432  			return binEval(e.Op, lit, nilVal)
   433  		}
   434  	default:
   435  		log.Warnf("unsupported operation %s on single-eval expr", e.Op)
   436  	}
   437  
   438  	return false
   439  }
   440  
   441  var (
   442  	nilVal = &NilLiteral{}
   443  )