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 := &regexpFuncExprNode{
   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  }