github.com/GuanceCloud/cliutils@v1.1.21/pipeline/ptinput/funcs/utils_fn.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package funcs
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/GuanceCloud/platypus/pkg/ast"
    12  	"github.com/GuanceCloud/platypus/pkg/engine/runtime"
    13  	"github.com/GuanceCloud/platypus/pkg/errchain"
    14  )
    15  
    16  type FnCall func(ctx *runtime.Task, funcExpr *ast.CallExpr, vals ...any) *errchain.PlError
    17  
    18  func VarPDefaultVal() (any, ast.DType) {
    19  	return []any(nil), ast.List
    20  }
    21  
    22  func NewFunc(name string, params []*Param, ret []ast.DType, doc [2]*PLDoc, run FnCall) *Function {
    23  	return &Function{
    24  		Name: name,
    25  		Args: params,
    26  		// Return: ret,
    27  		Doc:   doc,
    28  		Call:  WrapFnCall(run, params),
    29  		Check: WrapFnCheck(params),
    30  	}
    31  }
    32  
    33  func WrapFnCall(fn FnCall, paramDesc []*Param) runtime.FuncCall {
    34  	return func(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError {
    35  		// The parameters of the function call expression need to be normalized in advance.
    36  
    37  		// Note that some functions do not take the value of the variable
    38  		// corresponding to the parameter, but its name.
    39  
    40  		vals := make([]any, len(funcExpr.Param))
    41  
    42  		lenP := len(paramDesc)
    43  		varP := false
    44  		if lenP > 0 {
    45  			if paramDesc[lenP-1].VariableP {
    46  				lenP -= 1
    47  				varP = true
    48  			}
    49  
    50  			for i := 0; i < lenP; i++ {
    51  				if val, err := getParam(ctx, paramDesc[i], funcExpr.Param[i]); err != nil {
    52  					return err
    53  				} else {
    54  					vals[i] = val
    55  				}
    56  			}
    57  
    58  			if varP {
    59  				if v, err := getVarParam(ctx, paramDesc[lenP], funcExpr.Param[lenP:]); err != nil {
    60  					return err
    61  				} else {
    62  					vals[lenP] = v
    63  				}
    64  			}
    65  		}
    66  		return fn(ctx, funcExpr, vals...)
    67  	}
    68  }
    69  
    70  func WrapFnCheck(paramDesc []*Param) runtime.FuncCheck {
    71  	kIndex := map[string]int{}
    72  
    73  	prvOptP := false
    74  	for i, p := range paramDesc {
    75  		if _, ok := kIndex[p.Name]; ok {
    76  			panic(fmt.Sprintf("duplicate parameter name: %s", p.Name))
    77  		} else {
    78  			kIndex[p.Name] = i
    79  		}
    80  
    81  		if p.VariableP {
    82  			if i != len(paramDesc)-1 {
    83  				panic(fmt.Sprintf("parameter %s: variable parameter should be the last one",
    84  					p.Name))
    85  			}
    86  			if p.DefaultVal == nil {
    87  				panic(fmt.Sprintf("parameter %s: variable parameter should have default value", p.Name))
    88  			}
    89  
    90  			val, _ := p.DefaultVal()
    91  			switch val := val.(type) {
    92  			case []any:
    93  				for _, v := range val {
    94  					dtyp := getValDtype(v)
    95  					if !typInclude(dtyp, p.Type) {
    96  						panic(fmt.Sprintf("parameter %s: default value data type not match", p.Name))
    97  					}
    98  				}
    99  			case nil:
   100  			default:
   101  				panic(fmt.Sprintf("parameter %s: default value type not match", p.Name))
   102  			}
   103  		} else {
   104  			if p.Optional {
   105  				if p.DefaultVal == nil {
   106  					panic(fmt.Sprintf("parameter %s: optional parameter should have default value",
   107  						p.Name))
   108  				}
   109  				val, dt := p.DefaultVal()
   110  				if getValDtype(val) != dt {
   111  					panic(fmt.Sprintf("parameter %s: value type not match", p.Name))
   112  				}
   113  				if !typInclude(dt, p.Type) {
   114  					panic(fmt.Sprintf("parameter %s: default value data type not match", p.Name))
   115  				}
   116  			}
   117  		}
   118  
   119  		if !p.Optional && !p.VariableP {
   120  			if prvOptP {
   121  				panic(fmt.Sprintf("parameter %s: required parameter should not follow optional parameter",
   122  					p.Name))
   123  			}
   124  		} else {
   125  			prvOptP = true
   126  		}
   127  	}
   128  
   129  	return func(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError {
   130  		if err := normalizeFuncParams(ctx, kIndex, paramDesc, funcExpr); err != nil {
   131  			return err
   132  		}
   133  		if err := checkParams(ctx, funcExpr, paramDesc); err != nil {
   134  			return err
   135  		}
   136  		return nil
   137  	}
   138  }
   139  
   140  func getValDtype(v any) ast.DType {
   141  	var dtyp ast.DType
   142  	switch v.(type) {
   143  	case string:
   144  		dtyp = ast.String
   145  	case int64:
   146  		dtyp = ast.Int
   147  	case float64:
   148  		dtyp = ast.Float
   149  	case bool:
   150  		dtyp = ast.Bool
   151  	case []any:
   152  		dtyp = ast.List
   153  	case map[string]any:
   154  		dtyp = ast.Map
   155  	case nil:
   156  		dtyp = ast.Nil
   157  	default:
   158  	}
   159  	return dtyp
   160  }
   161  
   162  func checkParams(ctx *runtime.Task, funcExpr *ast.CallExpr, paramDesc []*Param) *errchain.PlError {
   163  	lenP := len(paramDesc)
   164  	varP := false
   165  	if paramDesc[lenP-1].VariableP {
   166  		lenP -= 1
   167  		varP = true
   168  	}
   169  
   170  	for i := 0; i < lenP; i++ {
   171  		if p := funcExpr.Param[i]; p != nil {
   172  			if ok := checkLiteralType(p.NodeType, paramDesc[i].Type); !ok {
   173  				return runtime.NewRunError(ctx, "unexpected data type", p.StartPos())
   174  			}
   175  		}
   176  	}
   177  	if varP {
   178  		if p := funcExpr.Param[lenP]; p != nil {
   179  			if p.NodeType == ast.TypeAssignmentExpr {
   180  				expr := p.AssignmentExpr()
   181  				switch expr.RHS.NodeType {
   182  				case ast.TypeListLiteral:
   183  					for _, n := range expr.RHS.ListLiteral().List {
   184  						if ok := checkLiteralType(n.NodeType, paramDesc[lenP].Type); !ok {
   185  							return runtime.NewRunError(ctx, "unexpected data type", p.StartPos())
   186  						}
   187  					}
   188  				case ast.TypeNilLiteral:
   189  				default:
   190  					return runtime.NewRunError(ctx,
   191  						"unexpected data type, for named variable parameter only list and nil are supported",
   192  						p.StartPos())
   193  				}
   194  			} else {
   195  				for i := lenP; i < len(funcExpr.Param); i++ {
   196  					if ok := checkLiteralType(funcExpr.Param[i].NodeType, paramDesc[lenP].Type); !ok {
   197  						return runtime.NewRunError(ctx, "unexpected data type", p.StartPos())
   198  					}
   199  				}
   200  			}
   201  		}
   202  	}
   203  	return nil
   204  }
   205  
   206  func checkLiteralType(typ ast.NodeType, typs []ast.DType) bool {
   207  	var dtype ast.DType
   208  	switch typ {
   209  	case ast.TypeStringLiteral:
   210  		dtype = ast.String
   211  	case ast.TypeIntegerLiteral:
   212  		dtype = ast.Int
   213  	case ast.TypeFloatLiteral:
   214  		dtype = ast.Float
   215  	case ast.TypeBoolLiteral:
   216  		dtype = ast.Bool
   217  	case ast.TypeNilLiteral:
   218  		dtype = ast.Nil
   219  	case ast.TypeListLiteral:
   220  		dtype = ast.List
   221  	case ast.TypeMapLiteral:
   222  		dtype = ast.Map
   223  	}
   224  
   225  	if dtype != ast.Invalid {
   226  		return typInclude(dtype, typs)
   227  	}
   228  
   229  	return true
   230  }
   231  
   232  func typInclude(typ ast.DType, typs []ast.DType) bool {
   233  	for _, dt := range typs {
   234  		if dt == typ {
   235  			return true
   236  		}
   237  	}
   238  	return false
   239  }
   240  
   241  func normalizeFuncParams(ctx *runtime.Task, keyMapp map[string]int,
   242  	paramDesc []*Param, funcExpr *ast.CallExpr) *errchain.PlError {
   243  	includeVarP := false
   244  	if len(paramDesc) > 0 {
   245  		if paramDesc[len(paramDesc)-1].VariableP {
   246  			includeVarP = true
   247  		}
   248  	}
   249  
   250  	if !includeVarP && len(funcExpr.Param) > len(keyMapp) {
   251  		return runtime.NewRunError(ctx, "too many parameters", funcExpr.NamePos)
   252  	}
   253  
   254  	var dstArgs []*ast.Node
   255  	if len(funcExpr.Param) < len(keyMapp) {
   256  		dstArgs = make([]*ast.Node, len(keyMapp))
   257  	} else {
   258  		dstArgs = make([]*ast.Node, len(funcExpr.Param))
   259  	}
   260  
   261  	includeNamedP := false
   262  	prvIsPosVar := true
   263  	for idx, arg := range funcExpr.Param {
   264  		if arg.NodeType == ast.TypeAssignmentExpr {
   265  			includeNamedP = true
   266  			if prvIsPosVar {
   267  				prvIsPosVar = false
   268  			}
   269  			if arg.AssignmentExpr().LHS.NodeType != ast.TypeIdentifier {
   270  				return runtime.NewRunError(ctx, "named parameter must be an identifier", arg.StartPos())
   271  			}
   272  
   273  			kname := arg.AssignmentExpr().LHS.Identifier().Name
   274  
   275  			kIndex, ok := keyMapp[kname]
   276  			if !ok {
   277  				return runtime.NewRunError(ctx, "unknown named parameter", arg.StartPos())
   278  			}
   279  
   280  			dstArgs[kIndex] = arg
   281  		} else {
   282  			if !prvIsPosVar {
   283  				return runtime.NewRunError(ctx, "positional parameter should not follow keyword parameter", arg.StartPos())
   284  			}
   285  			dstArgs[idx] = arg
   286  		}
   287  	}
   288  
   289  	for i, v := range paramDesc {
   290  		if !v.Optional && !v.VariableP {
   291  			if dstArgs[i] == nil {
   292  				return runtime.NewRunError(ctx, fmt.Sprintf("missing required parameter `%s`", v.Name), funcExpr.NamePos)
   293  			}
   294  		}
   295  	}
   296  
   297  	if includeNamedP && len(funcExpr.Param) > len(keyMapp) {
   298  		return runtime.NewRunError(ctx, "too many parameters", funcExpr.NamePos)
   299  	}
   300  
   301  	funcExpr.Param = dstArgs
   302  
   303  	return nil
   304  }
   305  
   306  func normalizeFuncArgsDeprecated(fnStmt *ast.CallExpr, keyList []string, reqParm int) error {
   307  	// reqParm >= 1, if < 0, no optional args
   308  	args := fnStmt.Param
   309  
   310  	if reqParm < 0 || reqParm > len(keyList) {
   311  		reqParm = len(keyList)
   312  	}
   313  
   314  	if len(args) > len(keyList) {
   315  		return fmt.Errorf("the number of parameters does not match")
   316  	}
   317  
   318  	beforPosArg := true
   319  
   320  	kMap := map[string]int{}
   321  	for k, v := range keyList {
   322  		kMap[v] = k
   323  	}
   324  
   325  	ret := make([]*ast.Node, len(keyList))
   326  
   327  	for idx, arg := range args {
   328  		if arg.NodeType == ast.TypeAssignmentExpr {
   329  			if beforPosArg {
   330  				beforPosArg = false
   331  			}
   332  			kname, err := getKeyName(arg.AssignmentExpr().LHS)
   333  			if err != nil {
   334  				return err
   335  			}
   336  			kIndex, ok := kMap[kname]
   337  			if !ok {
   338  				return fmt.Errorf("argument %s does not exist", kname)
   339  			}
   340  			ret[kIndex] = arg.AssignmentExpr().RHS
   341  		} else {
   342  			if !beforPosArg {
   343  				return fmt.Errorf("positional arguments cannot follow keyword arguments")
   344  			}
   345  			ret[idx] = arg
   346  		}
   347  	}
   348  
   349  	for i := 0; i < reqParm; i++ {
   350  		if v := ret[i]; v == nil {
   351  			return fmt.Errorf("parameter %s is required", keyList[i])
   352  		}
   353  	}
   354  
   355  	fnStmt.Param = ret
   356  	return nil
   357  }
   358  
   359  func getVarParam(ctx *runtime.Task, pDesc *Param, p []*ast.Node) ([]any, *errchain.PlError) {
   360  	if p[0] == nil {
   361  		if pDesc.DefaultVal != nil {
   362  			val, _ := pDesc.DefaultVal()
   363  			switch val := val.(type) {
   364  			case []any:
   365  				return val, nil
   366  			}
   367  		}
   368  		return nil, nil
   369  	}
   370  
   371  	if p[0].NodeType == ast.TypeAssignmentExpr {
   372  		val, _, errR := runtime.RunStmt(ctx, p[0])
   373  		if errR != nil {
   374  			return nil, errR
   375  		}
   376  
   377  		switch val := val.(type) {
   378  		case []any:
   379  			return val, nil
   380  		case nil:
   381  			return nil, nil
   382  		default:
   383  			return nil, runtime.NewRunError(ctx, fmt.Sprintf("parameter %s: type not match",
   384  				pDesc.Name), p[0].StartPos())
   385  		}
   386  	} else {
   387  		vals := make([]any, len(p))
   388  		for i, v := range p {
   389  			val, dtype, errR := runtime.RunStmt(ctx, v)
   390  			if errR != nil {
   391  				return nil, errR
   392  			}
   393  
   394  			typNotMatch := true
   395  			for _, d := range pDesc.Type {
   396  				if dtype == d {
   397  					typNotMatch = false
   398  					break
   399  				}
   400  			}
   401  
   402  			if typNotMatch {
   403  				return nil, runtime.NewRunError(ctx, fmt.Sprintf("parameter %s element: type not match",
   404  					pDesc.Name), v.StartPos())
   405  			} else {
   406  				vals[i] = val
   407  			}
   408  		}
   409  		return vals, nil
   410  	}
   411  }
   412  
   413  func getParam(ctx *runtime.Task, pDesc *Param, p *ast.Node) (any, *errchain.PlError) {
   414  	var val any
   415  	var dtype ast.DType
   416  	if p == nil {
   417  		// not need to check type here
   418  		if pDesc.DefaultVal != nil {
   419  			val, _ = pDesc.DefaultVal()
   420  			return val, nil
   421  		} else {
   422  			return nil, nil
   423  		}
   424  	} else {
   425  		var errR *errchain.PlError
   426  		val, dtype, errR = runtime.RunStmt(ctx, p)
   427  		if errR != nil {
   428  			return nil, errR
   429  		}
   430  	}
   431  
   432  	for _, d := range pDesc.Type {
   433  		if dtype == d {
   434  			return val, nil
   435  		}
   436  	}
   437  
   438  	return nil, runtime.NewRunError(ctx, fmt.Sprintf("parameter %s: type not match",
   439  		pDesc.Name), p.StartPos())
   440  }