github.com/bytedance/go-tagexpr/v2@v2.9.8/spec_range.go (about)

     1  package tagexpr
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"regexp"
     7  )
     8  
     9  type rangeCtxKey string
    10  
    11  const (
    12  	rangeKey   rangeCtxKey = "#k"
    13  	rangeValue rangeCtxKey = "#v"
    14  	rangeLen   rangeCtxKey = "##"
    15  )
    16  
    17  type rangeKvExprNode struct {
    18  	exprBackground
    19  	ctxKey       rangeCtxKey
    20  	boolOpposite *bool
    21  	signOpposite *bool
    22  }
    23  
    24  func (re *rangeKvExprNode) String() string {
    25  	return string(re.ctxKey)
    26  }
    27  
    28  func (p *Expr) readRangeKvExprNode(expr *string) ExprNode {
    29  	name, boolOpposite, signOpposite, found := findRangeKv(expr)
    30  	if !found {
    31  		return nil
    32  	}
    33  	operand := &rangeKvExprNode{
    34  		ctxKey:       rangeCtxKey(name),
    35  		boolOpposite: boolOpposite,
    36  		signOpposite: signOpposite,
    37  	}
    38  	// fmt.Printf("operand: %#v\n", operand)
    39  	return operand
    40  }
    41  
    42  var rangeKvRegexp = regexp.MustCompile(`^([\!\+\-]*)(#[kv#])([\)\[\],\+\-\*\/%><\|&!=\^ \t\\]|$)`)
    43  
    44  func findRangeKv(expr *string) (name string, boolOpposite, signOpposite *bool, found bool) {
    45  	raw := *expr
    46  	a := rangeKvRegexp.FindAllStringSubmatch(raw, -1)
    47  	if len(a) != 1 {
    48  		return
    49  	}
    50  	r := a[0]
    51  	name = r[2]
    52  	*expr = (*expr)[len(a[0][0])-len(r[3]):]
    53  	prefix := r[1]
    54  	if len(prefix) == 0 {
    55  		found = true
    56  		return
    57  	}
    58  	_, boolOpposite, signOpposite = getBoolAndSignOpposite(&prefix)
    59  	found = true
    60  	return
    61  }
    62  
    63  func (re *rangeKvExprNode) Run(ctx context.Context, _ string, _ *TagExpr) interface{} {
    64  	var v interface{}
    65  	switch val := ctx.Value(re.ctxKey).(type) {
    66  	case reflect.Value:
    67  		if !val.IsValid() || !val.CanInterface() {
    68  			return nil
    69  		}
    70  		v = val.Interface()
    71  	default:
    72  		v = val
    73  	}
    74  	return realValue(v, re.boolOpposite, re.signOpposite)
    75  }
    76  
    77  type rangeFuncExprNode struct {
    78  	exprBackground
    79  	object       ExprNode
    80  	elemExprNode ExprNode
    81  	boolOpposite *bool
    82  	signOpposite *bool
    83  }
    84  
    85  func (e *rangeFuncExprNode) String() string {
    86  	return "range()"
    87  }
    88  
    89  // range($, gt($v,10))
    90  // range($, $v>10)
    91  func readRangeFuncExprNode(p *Expr, expr *string) ExprNode {
    92  	boolOpposite, signOpposite, args, found := p.parseFuncSign("range", expr)
    93  	if !found {
    94  		return nil
    95  	}
    96  	if len(args) != 2 {
    97  		return nil
    98  	}
    99  	return &rangeFuncExprNode{
   100  		boolOpposite: boolOpposite,
   101  		signOpposite: signOpposite,
   102  		object:       args[0],
   103  		elemExprNode: args[1],
   104  	}
   105  }
   106  
   107  func (e *rangeFuncExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} {
   108  	var r []interface{}
   109  	obj := e.object.Run(ctx, currField, tagExpr)
   110  	// fmt.Printf("%v\n", obj)
   111  	objval := reflect.ValueOf(obj)
   112  	switch objval.Kind() {
   113  	case reflect.Array, reflect.Slice:
   114  		count := objval.Len()
   115  		r = make([]interface{}, count)
   116  		ctx = context.WithValue(ctx, rangeLen, count)
   117  		for i := 0; i < count; i++ {
   118  			// fmt.Printf("%#v,  (%v)\n", e.elemExprNode, objval.Index(i))
   119  			r[i] = realValue(e.elemExprNode.Run(
   120  				context.WithValue(
   121  					context.WithValue(
   122  						ctx,
   123  						rangeKey, i,
   124  					),
   125  					rangeValue, objval.Index(i),
   126  				),
   127  				currField, tagExpr,
   128  			), e.boolOpposite, e.signOpposite)
   129  		}
   130  	case reflect.Map:
   131  		keys := objval.MapKeys()
   132  		count := len(keys)
   133  		r = make([]interface{}, count)
   134  		ctx = context.WithValue(ctx, rangeLen, count)
   135  		for i, key := range keys {
   136  			r[i] = realValue(e.elemExprNode.Run(
   137  				context.WithValue(
   138  					context.WithValue(
   139  						ctx,
   140  						rangeKey, key,
   141  					),
   142  					rangeValue, objval.MapIndex(key),
   143  				),
   144  				currField, tagExpr,
   145  			), e.boolOpposite, e.signOpposite)
   146  		}
   147  	default:
   148  	}
   149  	return r
   150  }