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 }