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