github.com/bytedance/go-tagexpr/v2@v2.9.8/spec_operand.go (about) 1 // Copyright 2019 Bytedance Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tagexpr 16 17 import ( 18 "context" 19 "fmt" 20 "reflect" 21 "regexp" 22 "strconv" 23 "strings" 24 25 "github.com/andeya/ameda" 26 ) 27 28 // --------------------------- Operand --------------------------- 29 30 type groupExprNode struct { 31 exprBackground 32 boolOpposite *bool 33 signOpposite *bool 34 } 35 36 func newGroupExprNode() ExprNode { return &groupExprNode{} } 37 38 func readGroupExprNode(expr *string) (grp ExprNode, subExprNode *string) { 39 last, boolOpposite, signOpposite := getBoolAndSignOpposite(expr) 40 sptr := readPairedSymbol(&last, '(', ')') 41 if sptr == nil { 42 return nil, nil 43 } 44 *expr = last 45 e := &groupExprNode{boolOpposite: boolOpposite, signOpposite: signOpposite} 46 return e, sptr 47 } 48 49 func (ge *groupExprNode) String() string { 50 return "()" 51 } 52 53 func (ge *groupExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 54 if ge.rightOperand == nil { 55 return nil 56 } 57 return realValue(ge.rightOperand.Run(ctx, currField, tagExpr), ge.boolOpposite, ge.signOpposite) 58 } 59 60 type boolExprNode struct { 61 exprBackground 62 val bool 63 } 64 65 func (be *boolExprNode) String() string { 66 return fmt.Sprintf("%v", be.val) 67 } 68 69 var boolRegexp = regexp.MustCompile(`^!*(true|false)([\)\],\|&!= \t]{1}|$)`) 70 71 func readBoolExprNode(expr *string) ExprNode { 72 s := boolRegexp.FindString(*expr) 73 if s == "" { 74 return nil 75 } 76 last := s[len(s)-1] 77 if last != 'e' { 78 s = s[:len(s)-1] 79 } 80 *expr = (*expr)[len(s):] 81 e := &boolExprNode{} 82 if strings.Contains(s, "t") { 83 e.val = (len(s)-4)&1 == 0 84 } else { 85 e.val = (len(s)-5)&1 == 1 86 } 87 return e 88 } 89 90 func (be *boolExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 91 return be.val 92 } 93 94 type stringExprNode struct { 95 exprBackground 96 val interface{} 97 } 98 99 func (se *stringExprNode) String() string { 100 return fmt.Sprintf("%v", se.val) 101 } 102 103 func readStringExprNode(expr *string) ExprNode { 104 last, boolOpposite, _ := getBoolAndSignOpposite(expr) 105 sptr := readPairedSymbol(&last, '\'', '\'') 106 if sptr == nil { 107 return nil 108 } 109 *expr = last 110 e := &stringExprNode{val: realValue(*sptr, boolOpposite, nil)} 111 return e 112 } 113 114 func (se *stringExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 115 return se.val 116 } 117 118 type digitalExprNode struct { 119 exprBackground 120 val interface{} 121 } 122 123 func (de *digitalExprNode) String() string { 124 return fmt.Sprintf("%v", de.val) 125 } 126 127 var digitalRegexp = regexp.MustCompile(`^[\+\-]?\d+(\.\d+)?([\)\],\+\-\*\/%><\|&!=\^ \t\\]|$)`) 128 129 func readDigitalExprNode(expr *string) ExprNode { 130 last, boolOpposite := getOpposite(expr, "!") 131 s := digitalRegexp.FindString(last) 132 if s == "" { 133 return nil 134 } 135 if r := s[len(s)-1]; r < '0' || r > '9' { 136 s = s[:len(s)-1] 137 } 138 *expr = last[len(s):] 139 f64, _ := strconv.ParseFloat(s, 64) 140 return &digitalExprNode{val: realValue(f64, boolOpposite, nil)} 141 } 142 143 func (de *digitalExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 144 return de.val 145 } 146 147 type nilExprNode struct { 148 exprBackground 149 val interface{} 150 } 151 152 func (ne *nilExprNode) String() string { 153 return "<nil>" 154 } 155 156 var nilRegexp = regexp.MustCompile(`^nil([\)\],\|&!= \t]{1}|$)`) 157 158 func readNilExprNode(expr *string) ExprNode { 159 last, boolOpposite := getOpposite(expr, "!") 160 s := nilRegexp.FindString(last) 161 if s == "" { 162 return nil 163 } 164 *expr = last[3:] 165 return &nilExprNode{val: realValue(nil, boolOpposite, nil)} 166 } 167 168 func (ne *nilExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 169 return ne.val 170 } 171 172 func getBoolAndSignOpposite(expr *string) (last string, boolOpposite *bool, signOpposite *bool) { 173 last = strings.TrimLeft(last, "+") 174 last, boolOpposite = getOpposite(expr, "!") 175 last = strings.TrimLeft(last, "+") 176 last, signOpposite = getOpposite(&last, "-") 177 last = strings.TrimLeft(last, "+") 178 return 179 } 180 181 func getOpposite(expr *string, cutset string) (string, *bool) { 182 last := strings.TrimLeft(*expr, cutset) 183 n := len(*expr) - len(last) 184 if n == 0 { 185 return last, nil 186 } 187 bol := n&1 == 1 188 return last, &bol 189 } 190 191 func toString(i interface{}, enforce bool) (string, bool) { 192 switch vv := i.(type) { 193 case string: 194 return vv, true 195 case nil: 196 return "", false 197 default: 198 rv := ameda.DereferenceValue(reflect.ValueOf(i)) 199 if rv.Kind() == reflect.String { 200 return rv.String(), true 201 } 202 if enforce { 203 if rv.IsValid() && rv.CanInterface() { 204 return fmt.Sprint(rv.Interface()), true 205 } else { 206 return fmt.Sprint(i), true 207 } 208 } 209 } 210 return "", false 211 } 212 213 func toFloat64(i interface{}, tryParse bool) (float64, bool) { 214 var v float64 215 var ok = true 216 switch t := i.(type) { 217 case float64: 218 v = t 219 case float32: 220 v = float64(t) 221 case int: 222 v = float64(t) 223 case int8: 224 v = float64(t) 225 case int16: 226 v = float64(t) 227 case int32: 228 v = float64(t) 229 case int64: 230 v = float64(t) 231 case uint: 232 v = float64(t) 233 case uint8: 234 v = float64(t) 235 case uint16: 236 v = float64(t) 237 case uint32: 238 v = float64(t) 239 case uint64: 240 v = float64(t) 241 case nil: 242 ok = false 243 default: 244 rv := ameda.DereferenceValue(reflect.ValueOf(t)) 245 switch rv.Kind() { 246 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 247 v = float64(rv.Int()) 248 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 249 v = float64(rv.Uint()) 250 case reflect.Float32, reflect.Float64: 251 v = rv.Float() 252 default: 253 if tryParse { 254 if s, ok := toString(i, false); ok { 255 var err error 256 v, err = strconv.ParseFloat(s, 64) 257 return v, err == nil 258 } 259 } 260 ok = false 261 } 262 } 263 return v, ok 264 } 265 266 func realValue(v interface{}, boolOpposite *bool, signOpposite *bool) interface{} { 267 if boolOpposite != nil { 268 bol := FakeBool(v) 269 if *boolOpposite { 270 return !bol 271 } 272 return bol 273 } 274 switch t := v.(type) { 275 case float64, string: 276 case float32: 277 v = float64(t) 278 case int: 279 v = float64(t) 280 case int8: 281 v = float64(t) 282 case int16: 283 v = float64(t) 284 case int32: 285 v = float64(t) 286 case int64: 287 v = float64(t) 288 case uint: 289 v = float64(t) 290 case uint8: 291 v = float64(t) 292 case uint16: 293 v = float64(t) 294 case uint32: 295 v = float64(t) 296 case uint64: 297 v = float64(t) 298 case []interface{}: 299 for k, v := range t { 300 t[k] = realValue(v, boolOpposite, signOpposite) 301 } 302 default: 303 rv := ameda.DereferenceValue(reflect.ValueOf(v)) 304 switch rv.Kind() { 305 case reflect.String: 306 v = rv.String() 307 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 308 v = float64(rv.Int()) 309 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 310 v = float64(rv.Uint()) 311 case reflect.Float32, reflect.Float64: 312 v = rv.Float() 313 } 314 } 315 if signOpposite != nil && *signOpposite { 316 if f, ok := v.(float64); ok { 317 v = -f 318 } 319 } 320 return v 321 }