github.com/expr-lang/expr@v1.16.9/optimizer/predicate_combination.go (about)

     1  package optimizer
     2  
     3  import (
     4  	. "github.com/expr-lang/expr/ast"
     5  	"github.com/expr-lang/expr/parser/operator"
     6  )
     7  
     8  /*
     9  predicateCombination is a visitor that combines multiple predicate calls into a single call.
    10  For example, the following expression:
    11  
    12  	all(x, x > 1) && all(x, x < 10) -> all(x, x > 1 && x < 10)
    13  	any(x, x > 1) || any(x, x < 10) -> any(x, x > 1 || x < 10)
    14  	none(x, x > 1) && none(x, x < 10) -> none(x, x > 1 || x < 10)
    15  */
    16  type predicateCombination struct{}
    17  
    18  func (v *predicateCombination) Visit(node *Node) {
    19  	if op, ok := (*node).(*BinaryNode); ok && operator.IsBoolean(op.Operator) {
    20  		if left, ok := op.Left.(*BuiltinNode); ok {
    21  			if combinedOp, ok := combinedOperator(left.Name, op.Operator); ok {
    22  				if right, ok := op.Right.(*BuiltinNode); ok && right.Name == left.Name {
    23  					if left.Arguments[0].Type() == right.Arguments[0].Type() && left.Arguments[0].String() == right.Arguments[0].String() {
    24  						closure := &ClosureNode{
    25  							Node: &BinaryNode{
    26  								Operator: combinedOp,
    27  								Left:     left.Arguments[1].(*ClosureNode).Node,
    28  								Right:    right.Arguments[1].(*ClosureNode).Node,
    29  							},
    30  						}
    31  						v.Visit(&closure.Node)
    32  						Patch(node, &BuiltinNode{
    33  							Name: left.Name,
    34  							Arguments: []Node{
    35  								left.Arguments[0],
    36  								closure,
    37  							},
    38  						})
    39  					}
    40  				}
    41  			}
    42  		}
    43  	}
    44  }
    45  
    46  func combinedOperator(fn, op string) (string, bool) {
    47  	switch {
    48  	case fn == "all" && (op == "and" || op == "&&"):
    49  		return op, true
    50  	case fn == "any" && (op == "or" || op == "||"):
    51  		return op, true
    52  	case fn == "none" && (op == "and" || op == "&&"):
    53  		switch op {
    54  		case "and":
    55  			return "or", true
    56  		case "&&":
    57  			return "||", true
    58  		}
    59  	}
    60  	return "", false
    61  }