github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/param.go (about) 1 // Copyright 2016 Google 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 grumpy 16 17 import ( 18 "fmt" 19 ) 20 21 // Param describes a parameter to a Python function. 22 type Param struct { 23 // Name is the argument name. 24 Name string 25 // Def is the default value to use if the argument is not provided. If 26 // no default is specified then Def is nil. 27 Def *Object 28 } 29 30 // ParamSpec describes a Python function's parameters. 31 type ParamSpec struct { 32 Count int 33 name string 34 minArgs int 35 varArgIndex int 36 kwArgIndex int 37 params []Param 38 } 39 40 // NewParamSpec returns a new ParamSpec that accepts the given positional 41 // parameters and optional vararg and/or kwarg parameter. 42 func NewParamSpec(name string, params []Param, varArg bool, kwArg bool) *ParamSpec { 43 s := &ParamSpec{name: name, params: params, varArgIndex: -1, kwArgIndex: -1} 44 numParams := len(params) 45 for ; s.minArgs < numParams; s.minArgs++ { 46 if params[s.minArgs].Def != nil { 47 break 48 } 49 } 50 for _, p := range params[s.minArgs:numParams] { 51 if p.Def == nil { 52 format := "%s() non-keyword arg %s after keyword arg" 53 logFatal(fmt.Sprintf(format, name, p.Name)) 54 } 55 } 56 s.Count = numParams 57 if varArg { 58 s.varArgIndex = s.Count 59 s.Count++ 60 } 61 if kwArg { 62 s.kwArgIndex = s.Count 63 s.Count++ 64 } 65 return s 66 } 67 68 // Validate ensures that a the args and kwargs passed are valid arguments for 69 // the param spec s. The validated parameters are output to the validated slice 70 // which must have len s.Count. 71 func (s *ParamSpec) Validate(f *Frame, validated []*Object, args Args, kwargs KWArgs) *BaseException { 72 if nv := len(validated); nv != s.Count { 73 format := "%s(): validated slice was incorrect size: %d, want %d" 74 return f.RaiseType(SystemErrorType, fmt.Sprintf(format, s.name, nv, s.Count)) 75 } 76 numParams := len(s.params) 77 argc := len(args) 78 if argc > numParams && s.varArgIndex == -1 { 79 format := "%s() takes %d arguments (%d given)" 80 return f.RaiseType(TypeErrorType, fmt.Sprintf(format, s.name, numParams, argc)) 81 } 82 i := 0 83 for ; i < argc && i < numParams; i++ { 84 validated[i] = args[i] 85 } 86 if s.varArgIndex != -1 { 87 validated[s.varArgIndex] = NewTuple(args[i:].makeCopy()...).ToObject() 88 } 89 var kwargDict *Dict 90 if s.kwArgIndex != -1 { 91 kwargDict = NewDict() 92 validated[s.kwArgIndex] = kwargDict.ToObject() 93 } 94 for _, kw := range kwargs { 95 name := kw.Name 96 j := 0 97 for ; j < numParams; j++ { 98 if s.params[j].Name == name { 99 if validated[j] != nil { 100 format := "%s() got multiple values for keyword argument '%s'" 101 return f.RaiseType(TypeErrorType, fmt.Sprintf(format, s.name, name)) 102 } 103 validated[j] = kw.Value 104 break 105 } 106 } 107 if j == numParams { 108 if kwargDict == nil { 109 format := "%s() got an unexpected keyword argument '%s'" 110 return f.RaiseType(TypeErrorType, fmt.Sprintf(format, s.name, name)) 111 } 112 if raised := kwargDict.SetItemString(f, name, kw.Value); raised != nil { 113 return raised 114 } 115 } 116 } 117 for ; i < numParams; i++ { 118 p := s.params[i] 119 if validated[i] == nil { 120 if p.Def == nil { 121 format := "%s() takes at least %d arguments (%d given)" 122 return f.RaiseType(TypeErrorType, fmt.Sprintf(format, s.name, s.minArgs, argc)) 123 } 124 validated[i] = p.Def 125 } 126 } 127 return nil 128 }