github.com/bytedance/go-tagexpr/v2@v2.9.8/spec_func.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 "strings" 23 24 "github.com/andeya/goutil/errors" 25 ) 26 27 // --------------------------- Custom function --------------------------- 28 29 var funcList = map[string]func(p *Expr, expr *string) ExprNode{} 30 31 // MustRegFunc registers function expression. 32 // NOTE: 33 // 34 // example: len($), regexp("\\d") or regexp("\\d",$); 35 // If @force=true, allow to cover the existed same @funcName; 36 // The go number types always are float64; 37 // The go string types always are string; 38 // Panic if there is an error. 39 func MustRegFunc(funcName string, fn func(...interface{}) interface{}, force ...bool) { 40 err := RegFunc(funcName, fn, force...) 41 if err != nil { 42 panic(err) 43 } 44 } 45 46 // RegFunc registers function expression. 47 // NOTE: 48 // 49 // example: len($), regexp("\\d") or regexp("\\d",$); 50 // If @force=true, allow to cover the existed same @funcName; 51 // The go number types always are float64; 52 // The go string types always are string. 53 func RegFunc(funcName string, fn func(...interface{}) interface{}, force ...bool) error { 54 if len(force) == 0 || !force[0] { 55 _, ok := funcList[funcName] 56 if ok { 57 return errors.Errorf("duplicate registration expression function: %s", funcName) 58 } 59 } 60 funcList[funcName] = newFunc(funcName, fn) 61 return nil 62 } 63 64 func (p *Expr) parseFuncSign(funcName string, expr *string) (boolOpposite *bool, signOpposite *bool, args []ExprNode, found bool) { 65 prefix := funcName + "(" 66 length := len(funcName) 67 last, boolOpposite, signOpposite := getBoolAndSignOpposite(expr) 68 if !strings.HasPrefix(last, prefix) { 69 return 70 } 71 *expr = last[length:] 72 lastStr := *expr 73 subExprNode := readPairedSymbol(expr, '(', ')') 74 if subExprNode == nil { 75 return 76 } 77 *subExprNode = "," + *subExprNode 78 for { 79 if strings.HasPrefix(*subExprNode, ",") { 80 *subExprNode = (*subExprNode)[1:] 81 operand := newGroupExprNode() 82 err := p.parseExprNode(trimLeftSpace(subExprNode), operand) 83 if err != nil { 84 *expr = lastStr 85 return 86 } 87 sortPriority(operand) 88 args = append(args, operand) 89 } else { 90 *expr = lastStr 91 return 92 } 93 trimLeftSpace(subExprNode) 94 if len(*subExprNode) == 0 { 95 found = true 96 return 97 } 98 } 99 } 100 101 func newFunc(funcName string, fn func(...interface{}) interface{}) func(*Expr, *string) ExprNode { 102 return func(p *Expr, expr *string) ExprNode { 103 boolOpposite, signOpposite, args, found := p.parseFuncSign(funcName, expr) 104 if !found { 105 return nil 106 } 107 return &funcExprNode{ 108 fn: fn, 109 boolOpposite: boolOpposite, 110 signOpposite: signOpposite, 111 args: args, 112 } 113 } 114 } 115 116 type funcExprNode struct { 117 exprBackground 118 args []ExprNode 119 fn func(...interface{}) interface{} 120 boolOpposite *bool 121 signOpposite *bool 122 } 123 124 func (f *funcExprNode) String() string { 125 return "func()" 126 } 127 128 func (f *funcExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 129 var args []interface{} 130 if n := len(f.args); n > 0 { 131 args = make([]interface{}, n) 132 for k, v := range f.args { 133 args[k] = v.Run(ctx, currField, tagExpr) 134 } 135 } 136 return realValue(f.fn(args...), f.boolOpposite, f.signOpposite) 137 } 138 139 // --------------------------- Built-in function --------------------------- 140 func init() { 141 funcList["regexp"] = readRegexpFuncExprNode 142 funcList["sprintf"] = readSprintfFuncExprNode 143 funcList["range"] = readRangeFuncExprNode 144 // len: Built-in function len, the length of struct field X 145 MustRegFunc("len", func(args ...interface{}) (n interface{}) { 146 if len(args) != 1 { 147 return 0 148 } 149 v := args[0] 150 switch e := v.(type) { 151 case string: 152 return float64(len(e)) 153 case float64, bool, nil: 154 return 0 155 } 156 defer func() { 157 if recover() != nil { 158 n = 0 159 } 160 }() 161 return float64(reflect.ValueOf(v).Len()) 162 }, true) 163 // mblen: get the length of string field X (character number) 164 MustRegFunc("mblen", func(args ...interface{}) (n interface{}) { 165 if len(args) != 1 { 166 return 0 167 } 168 v := args[0] 169 switch e := v.(type) { 170 case string: 171 return float64(len([]rune(e))) 172 case float64, bool, nil: 173 return 0 174 } 175 defer func() { 176 if recover() != nil { 177 n = 0 178 } 179 }() 180 return float64(reflect.ValueOf(v).Len()) 181 }, true) 182 183 // in: Check if the first parameter is one of the enumerated parameters 184 MustRegFunc("in", func(args ...interface{}) interface{} { 185 switch len(args) { 186 case 0: 187 return true 188 case 1: 189 return false 190 default: 191 elem := args[0] 192 set := args[1:] 193 for _, e := range set { 194 if elem == e { 195 return true 196 } 197 } 198 return false 199 } 200 }, true) 201 } 202 203 type regexpFuncExprNode struct { 204 exprBackground 205 re *regexp.Regexp 206 boolOpposite bool 207 } 208 209 func (re *regexpFuncExprNode) String() string { 210 return "regexp()" 211 } 212 213 func readRegexpFuncExprNode(p *Expr, expr *string) ExprNode { 214 last, boolOpposite, _ := getBoolAndSignOpposite(expr) 215 if !strings.HasPrefix(last, "regexp(") { 216 return nil 217 } 218 *expr = last[6:] 219 lastStr := *expr 220 subExprNode := readPairedSymbol(expr, '(', ')') 221 if subExprNode == nil { 222 return nil 223 } 224 s := readPairedSymbol(trimLeftSpace(subExprNode), '\'', '\'') 225 if s == nil { 226 *expr = lastStr 227 return nil 228 } 229 rege, err := regexp.Compile(*s) 230 if err != nil { 231 *expr = lastStr 232 return nil 233 } 234 operand := newGroupExprNode() 235 trimLeftSpace(subExprNode) 236 if strings.HasPrefix(*subExprNode, ",") { 237 *subExprNode = (*subExprNode)[1:] 238 err = p.parseExprNode(trimLeftSpace(subExprNode), operand) 239 if err != nil { 240 *expr = lastStr 241 return nil 242 } 243 } else { 244 var currFieldVal = "$" 245 p.parseExprNode(&currFieldVal, operand) 246 } 247 trimLeftSpace(subExprNode) 248 if *subExprNode != "" { 249 *expr = lastStr 250 return nil 251 } 252 e := ®expFuncExprNode{ 253 re: rege, 254 } 255 if boolOpposite != nil { 256 e.boolOpposite = *boolOpposite 257 } 258 e.SetRightOperand(operand) 259 return e 260 } 261 262 func (re *regexpFuncExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 263 param := re.rightOperand.Run(ctx, currField, tagExpr) 264 switch v := param.(type) { 265 case string: 266 bol := re.re.MatchString(v) 267 if re.boolOpposite { 268 return !bol 269 } 270 return bol 271 case float64, bool: 272 return false 273 } 274 v := reflect.ValueOf(param) 275 if v.Kind() == reflect.String { 276 bol := re.re.MatchString(v.String()) 277 if re.boolOpposite { 278 return !bol 279 } 280 return bol 281 } 282 return false 283 } 284 285 type sprintfFuncExprNode struct { 286 exprBackground 287 format string 288 args []ExprNode 289 } 290 291 func (se *sprintfFuncExprNode) String() string { 292 return "sprintf()" 293 } 294 295 func readSprintfFuncExprNode(p *Expr, expr *string) ExprNode { 296 if !strings.HasPrefix(*expr, "sprintf(") { 297 return nil 298 } 299 *expr = (*expr)[7:] 300 lastStr := *expr 301 subExprNode := readPairedSymbol(expr, '(', ')') 302 if subExprNode == nil { 303 return nil 304 } 305 format := readPairedSymbol(trimLeftSpace(subExprNode), '\'', '\'') 306 if format == nil { 307 *expr = lastStr 308 return nil 309 } 310 e := &sprintfFuncExprNode{ 311 format: *format, 312 } 313 for { 314 trimLeftSpace(subExprNode) 315 if len(*subExprNode) == 0 { 316 return e 317 } 318 if strings.HasPrefix(*subExprNode, ",") { 319 *subExprNode = (*subExprNode)[1:] 320 operand := newGroupExprNode() 321 err := p.parseExprNode(trimLeftSpace(subExprNode), operand) 322 if err != nil { 323 *expr = lastStr 324 return nil 325 } 326 sortPriority(operand) 327 e.args = append(e.args, operand) 328 } else { 329 *expr = lastStr 330 return nil 331 } 332 } 333 } 334 335 func (se *sprintfFuncExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} { 336 var args []interface{} 337 if n := len(se.args); n > 0 { 338 args = make([]interface{}, n) 339 for i, e := range se.args { 340 args[i] = e.Run(ctx, currField, tagExpr) 341 } 342 } 343 return fmt.Sprintf(se.format, args...) 344 }