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 }